Skip to content

Commit e2c4584

Browse files
authored
Merge pull request #38209 from DougGregor/global-actor-inheritance
2 parents 850a84a + 83827e7 commit e2c4584

File tree

8 files changed

+193
-34
lines changed

8 files changed

+193
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ _**Note:** This is in reverse chronological order, so newer entries are added to
66
Swift 5.5
77
---------
88

9-
* [SE-0313][]:
9+
* [SE-0316][]:
1010

1111
A type can be defined as a global actor. Global actors extend the notion
1212
of actor isolation outside of a single actor type, so that global state

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4623,6 +4623,9 @@ ERROR(actor_isolation_multiple_attr,none,
46234623
ERROR(actor_isolation_override_mismatch,none,
46244624
"%0 %1 %2 has different actor isolation from %3 overridden declaration",
46254625
(ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation))
4626+
ERROR(actor_isolation_superclass_mismatch,none,
4627+
"%0 class %1 has different actor isolation from %2 superclass %3",
4628+
(ActorIsolation, DeclName, ActorIsolation, DeclName))
46264629

46274630
//------------------------------------------------------------------------------
46284631
// MARK: Type Check Types

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ class InheritedProtocolCollector {
357357
/// For each type in \p directlyInherited, classify the protocols it refers to
358358
/// as included for printing or not, and record them in the appropriate
359359
/// vectors.
360-
void recordProtocols(ArrayRef<TypeLoc> directlyInherited, const Decl *D) {
360+
void recordProtocols(ArrayRef<TypeLoc> directlyInherited, const Decl *D,
361+
bool skipSynthesized = false) {
361362
Optional<AvailableAttrList> availableAttrs;
362363

363364
for (TypeLoc inherited : directlyInherited) {
@@ -378,6 +379,9 @@ class InheritedProtocolCollector {
378379
// any of those besides 'AnyObject'.
379380
}
380381

382+
if (skipSynthesized)
383+
return;
384+
381385
// Check for synthesized protocols, like Hashable on enums.
382386
if (auto *nominal = dyn_cast<NominalTypeDecl>(D)) {
383387
SmallVector<ProtocolConformance *, 4> localConformances =
@@ -455,7 +459,8 @@ class InheritedProtocolCollector {
455459
if (auto *CD = dyn_cast<ClassDecl>(D)) {
456460
for (auto *SD = CD->getSuperclassDecl(); SD;
457461
SD = SD->getSuperclassDecl()) {
458-
map[nominal].recordProtocols(SD->getInherited(), SD);
462+
map[nominal].recordProtocols(
463+
SD->getInherited(), SD, /*skipSynthesized=*/true);
459464
for (auto *Ext: SD->getExtensions()) {
460465
if (shouldInclude(Ext)) {
461466
map[nominal].recordProtocols(Ext->getInherited(), Ext);

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 144 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3027,6 +3027,70 @@ static ActorIsolation getActorIsolationFromWrappedProperty(VarDecl *var) {
30273027
return ActorIsolation::forUnspecified();
30283028
}
30293029

3030+
/// Check rules related to global actor attributes on a class declaration.
3031+
///
3032+
/// \returns true if an error occurred.
3033+
static bool checkClassGlobalActorIsolation(
3034+
ClassDecl *classDecl, ActorIsolation isolation) {
3035+
assert(isolation.isGlobalActor());
3036+
3037+
// A class can only be annotated with a global actor if it has no
3038+
// superclass, the superclass is annotated with the same global actor, or
3039+
// the superclass is NSObject. A subclass of a global-actor-annotated class
3040+
// must be isolated to the same global actor.
3041+
auto superclassDecl = classDecl->getSuperclassDecl();
3042+
if (!superclassDecl)
3043+
return false;
3044+
3045+
if (superclassDecl->isNSObject())
3046+
return false;
3047+
3048+
// Ignore actors outright. They'll be diagnosed later.
3049+
if (classDecl->isActor() || superclassDecl->isActor())
3050+
return false;
3051+
3052+
// Check the superclass's isolation.
3053+
auto superIsolation = getActorIsolation(superclassDecl);
3054+
switch (superIsolation) {
3055+
case ActorIsolation::Unspecified:
3056+
case ActorIsolation::Independent:
3057+
// Allow ObjC superclasses with unspecified global actors, because we do
3058+
// not expect for Objective-C classes to have been universally annotated.
3059+
// FIXME: We might choose to tighten this up in Swift 6.
3060+
if (superclassDecl->getClangNode())
3061+
return false;
3062+
3063+
// Break out to diagnose the error below.
3064+
break;
3065+
3066+
case ActorIsolation::ActorInstance:
3067+
case ActorIsolation::DistributedActorInstance:
3068+
// This is an error that will be diagnosed later. Ignore it here.
3069+
return false;
3070+
3071+
case ActorIsolation::GlobalActor:
3072+
case ActorIsolation::GlobalActorUnsafe: {
3073+
// If the the global actors match, we're fine.
3074+
Type superclassGlobalActor = superIsolation.getGlobalActor();
3075+
auto module = classDecl->getParentModule();
3076+
SubstitutionMap subsMap = classDecl->getDeclaredInterfaceType()
3077+
->getSuperclassForDecl(superclassDecl)
3078+
->getContextSubstitutionMap(module, superclassDecl);
3079+
Type superclassGlobalActorInSub = superclassGlobalActor.subst(subsMap);
3080+
if (isolation.getGlobalActor()->isEqual(superclassGlobalActorInSub))
3081+
return false;
3082+
3083+
break;
3084+
}
3085+
}
3086+
3087+
// Complain about the mismatch.
3088+
classDecl->diagnose(
3089+
diag::actor_isolation_superclass_mismatch, isolation,
3090+
classDecl->getName(), superIsolation, superclassDecl->getName());
3091+
return true;
3092+
}
3093+
30303094
ActorIsolation ActorIsolationRequest::evaluate(
30313095
Evaluator &evaluator, ValueDecl *value) const {
30323096
// If this declaration has actor-isolated "self", it's isolated to that
@@ -3055,6 +3119,12 @@ ActorIsolation ActorIsolationRequest::evaluate(
30553119
ConcurrentReferenceKind::Nonisolated);
30563120
}
30573121

3122+
// Classes with global actors have additional rules regarding inheritance.
3123+
if (isolationFromAttr->isGlobalActor()) {
3124+
if (auto classDecl = dyn_cast<ClassDecl>(value))
3125+
checkClassGlobalActorIsolation(classDecl, *isolationFromAttr);
3126+
}
3127+
30583128
return *isolationFromAttr;
30593129
}
30603130

@@ -3552,6 +3622,20 @@ bool swift::checkSendableConformance(
35523622
return true;
35533623
}
35543624

3625+
// Global-actor-isolated types can be Sendable. We do not check the
3626+
// instance properties.
3627+
switch (getActorIsolation(nominal)) {
3628+
case ActorIsolation::Unspecified:
3629+
case ActorIsolation::ActorInstance:
3630+
case ActorIsolation::DistributedActorInstance:
3631+
case ActorIsolation::Independent:
3632+
break;
3633+
3634+
case ActorIsolation::GlobalActor:
3635+
case ActorIsolation::GlobalActorUnsafe:
3636+
return false;
3637+
}
3638+
35553639
if (classDecl) {
35563640
// An non-final class cannot conform to `Sendable`.
35573641
if (!classDecl->isSemanticallyFinal()) {
@@ -3586,23 +3670,16 @@ bool swift::checkSendableConformance(
35863670

35873671
NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
35883672
Evaluator &evaluator, NominalTypeDecl *nominal) const {
3589-
// Only structs and enums can get implicit Sendable conformances.
3590-
if (!isa<StructDecl>(nominal) && !isa<EnumDecl>(nominal))
3673+
// Protocols never get implicit Sendable conformances.
3674+
if (isa<ProtocolDecl>(nominal))
35913675
return nullptr;
35923676

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>())) {
3677+
// Actor types are always Sendable; they don't get it via this path.
3678+
auto classDecl = dyn_cast<ClassDecl>(nominal);
3679+
if (classDecl && classDecl->isActor())
36023680
return nullptr;
3603-
}
36043681

3605-
// Check the context in which the conformance occurs.
3682+
// Check whether we can infer conformance at all.
36063683
if (auto *file = dyn_cast<FileUnit>(nominal->getModuleScopeContext())) {
36073684
switch (file->getKind()) {
36083685
case FileUnitKind::Source:
@@ -3636,23 +3713,65 @@ NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
36363713
return nullptr;
36373714
}
36383715

3639-
// Check the instance storage for Sendable conformance.
3640-
if (checkSendableInstanceStorage(
3641-
nominal, nominal, SendableCheck::Implicit))
3716+
// Local function to form the implicit conformance.
3717+
auto formConformance = [&]() -> NormalProtocolConformance * {
3718+
ASTContext &ctx = nominal->getASTContext();
3719+
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
3720+
if (!proto)
3721+
return nullptr;
3722+
3723+
auto conformance = ctx.getConformance(
3724+
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),
3725+
nominal, ProtocolConformanceState::Complete);
3726+
conformance->setSourceKindAndImplyingConformance(
3727+
ConformanceEntryKind::Synthesized, nullptr);
3728+
3729+
return conformance;
3730+
};
3731+
3732+
// A non-protocol type with a global actor is implicitly Sendable.
3733+
if (nominal->getGlobalActorAttr()) {
3734+
// If this is a class, check the superclass. We won't infer Sendable
3735+
// if the superclass is already Sendable, to avoid introducing redundant
3736+
// conformances.
3737+
if (classDecl) {
3738+
if (Type superclass = classDecl->getSuperclass()) {
3739+
auto classModule = classDecl->getParentModule();
3740+
if (TypeChecker::conformsToKnownProtocol(
3741+
classDecl->mapTypeIntoContext(superclass),
3742+
KnownProtocolKind::Sendable,
3743+
classModule))
3744+
return nullptr;
3745+
}
3746+
}
3747+
3748+
// Form the implicit conformance to Sendable.
3749+
return formConformance();
3750+
}
3751+
3752+
// Only structs and enums can get implicit Sendable conformances by
3753+
// considering their instance data.
3754+
if (!isa<StructDecl>(nominal) && !isa<EnumDecl>(nominal))
36423755
return nullptr;
36433756

3644-
ASTContext &ctx = nominal->getASTContext();
3645-
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
3646-
if (!proto)
3757+
// Public, non-frozen structs and enums defined in Swift don't get implicit
3758+
// Sendable conformances.
3759+
if (!nominal->getASTContext().LangOpts.EnableInferPublicSendable &&
3760+
nominal->getFormalAccessScope(
3761+
/*useDC=*/nullptr,
3762+
/*treatUsableFromInlineAsPublic=*/true).isPublic() &&
3763+
!(nominal->hasClangNode() ||
3764+
nominal->getAttrs().hasAttribute<FixedLayoutAttr>() ||
3765+
nominal->getAttrs().hasAttribute<FrozenAttr>())) {
36473766
return nullptr;
3767+
}
36483768

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

3655-
return conformance;
3774+
return formConformance();
36563775
}
36573776

36583777
AnyFunctionType *swift::applyGlobalActorType(

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
16811681
(void) VD->isObjC();
16821682
(void) VD->isDynamic();
16831683

1684+
// Check for actor isolation.
1685+
(void)getActorIsolation(VD);
1686+
16841687
// If this is a member of a nominal type, don't allow it to have a name of
16851688
// "Type" or "Protocol" since we reserve the X.Type and X.Protocol
16861689
// expressions to mean something builtin to the language. We *do* allow

test/Concurrency/actor_isolation.swift

Lines changed: 18 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
}
@@ -940,3 +940,18 @@ actor Counter {
940940
return counter
941941
}
942942
}
943+
944+
/// Superclass checks for global actor-qualified class types.
945+
class C2 { }
946+
947+
@SomeGlobalActor
948+
class C3: C2 { } // expected-error{{global actor 'SomeGlobalActor'-isolated class 'C3' has different actor isolation from nonisolated superclass 'C2'}}
949+
950+
@GenericGlobalActor<U>
951+
class GenericSuper<U> { }
952+
953+
@GenericGlobalActor<[T]>
954+
class GenericSub1<T>: GenericSuper<[T]> { }
955+
956+
@GenericGlobalActor<T>
957+
class GenericSub2<T>: GenericSuper<[T]> { } // expected-error{{global actor 'GenericGlobalActor<T>'-isolated class 'GenericSub2' has different actor isolation from global actor 'GenericGlobalActor<U>'-isolated superclass 'GenericSuper'}}

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)

test/Concurrency/global_actor_inference.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,10 @@ struct Container<T> {
195195
}
196196

197197
struct OtherContainer<U> {
198-
// Okay to change the global actor in a subclass.
198+
// NOT Okay to change the global actor in a subclass.
199199
@GenericGlobalActor<[U]> class Subclass1 : Container<[U]>.Superclass { }
200200
@GenericGlobalActor<U> class Subclass2 : Container<[U]>.Superclass { }
201+
// expected-error@-1{{global actor 'GenericGlobalActor<U>'-isolated class 'Subclass2' has different actor isolation from global actor 'GenericGlobalActor<T>'-isolated superclass 'Superclass'}}
201202

202203
// Ensure that substitutions work properly when inheriting.
203204
class Subclass3<V> : Container<(U, V)>.Superclass2 {
@@ -218,7 +219,7 @@ class SuperclassWithGlobalActors {
218219
func j() { }
219220
}
220221

221-
@GenericGlobalActor<String>
222+
@GenericGlobalActor<String> // expected-error@+1{{global actor 'GenericGlobalActor<String>'-isolated class 'SubclassWithGlobalActors' has different actor isolation from nonisolated superclass 'SuperclassWithGlobalActors'}}
222223
class SubclassWithGlobalActors : SuperclassWithGlobalActors {
223224
override func f() { } // okay: inferred to @GenericGlobalActor<Int>
224225

0 commit comments

Comments
 (0)