Skip to content

Commit 96ced72

Browse files
committed
[SE-0316] Check global actor isolation of a class vs. its superclass.
Close an actor-isolation hole where we permitted a class to have a different global actor isolation than its superclass.
1 parent 20a540e commit 96ced72

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4617,6 +4617,9 @@ ERROR(actor_isolation_multiple_attr,none,
46174617
ERROR(actor_isolation_override_mismatch,none,
46184618
"%0 %1 %2 has different actor isolation from %3 overridden declaration",
46194619
(ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation))
4620+
ERROR(actor_isolation_superclass_mismatch,none,
4621+
"%0 class %1 has different actor isolation from %2 superclass %3",
4622+
(ActorIsolation, DeclName, ActorIsolation, DeclName))
46204623

46214624
//------------------------------------------------------------------------------
46224625
// MARK: Type Check Types

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 70 additions & 0 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

test/Concurrency/actor_isolation.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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/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)