Skip to content

Commit b09aed0

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 9c285c5 commit b09aed0

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
@@ -4554,6 +4554,9 @@ ERROR(actor_isolation_multiple_attr,none,
45544554
ERROR(actor_isolation_override_mismatch,none,
45554555
"%0 %1 %2 has different actor isolation from %3 overridden declaration",
45564556
(ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation))
4557+
ERROR(actor_isolation_superclass_mismatch,none,
4558+
"%0 class %1 has different actor isolation from %2 superclass %3",
4559+
(ActorIsolation, DeclName, ActorIsolation, DeclName))
45574560

45584561
//------------------------------------------------------------------------------
45594562
// MARK: Type Check Types

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2973,6 +2973,70 @@ static ActorIsolation getActorIsolationFromWrappedProperty(VarDecl *var) {
29732973
return ActorIsolation::forUnspecified();
29742974
}
29752975

2976+
/// Check rules related to global actor attributes on a class declaration.
2977+
///
2978+
/// \returns true if an error occurred.
2979+
static bool checkClassGlobalActorIsolation(
2980+
ClassDecl *classDecl, ActorIsolation isolation) {
2981+
assert(isolation.isGlobalActor());
2982+
2983+
// A class can only be annotated with a global actor if it has no
2984+
// superclass, the superclass is annotated with the same global actor, or
2985+
// the superclass is NSObject. A subclass of a global-actor-annotated class
2986+
// must be isolated to the same global actor.
2987+
auto superclassDecl = classDecl->getSuperclassDecl();
2988+
if (!superclassDecl)
2989+
return false;
2990+
2991+
if (superclassDecl->isNSObject())
2992+
return false;
2993+
2994+
// Ignore actors outright. They'll be diagnosed later.
2995+
if (classDecl->isActor() || superclassDecl->isActor())
2996+
return false;
2997+
2998+
// Check the superclass's isolation.
2999+
auto superIsolation = getActorIsolation(superclassDecl);
3000+
switch (superIsolation) {
3001+
case ActorIsolation::Unspecified:
3002+
case ActorIsolation::Independent:
3003+
// Allow ObjC superclasses with unspecified global actors, because we do
3004+
// not expect for Objective-C classes to have been universally annotated.
3005+
// FIXME: We might choose to tighten this up in Swift 6.
3006+
if (superclassDecl->getClangNode())
3007+
return false;
3008+
3009+
// Break out to diagnose the error below.
3010+
break;
3011+
3012+
case ActorIsolation::ActorInstance:
3013+
case ActorIsolation::DistributedActorInstance:
3014+
// This is an error that will be diagnosed later. Ignore it here.
3015+
return false;
3016+
3017+
case ActorIsolation::GlobalActor:
3018+
case ActorIsolation::GlobalActorUnsafe: {
3019+
// If the the global actors match, we're fine.
3020+
Type superclassGlobalActor = superIsolation.getGlobalActor();
3021+
auto module = classDecl->getParentModule();
3022+
SubstitutionMap subsMap = classDecl->getDeclaredInterfaceType()
3023+
->getSuperclassForDecl(superclassDecl)
3024+
->getContextSubstitutionMap(module, superclassDecl);
3025+
Type superclassGlobalActorInSub = superclassGlobalActor.subst(subsMap);
3026+
if (isolation.getGlobalActor()->isEqual(superclassGlobalActorInSub))
3027+
return false;
3028+
3029+
break;
3030+
}
3031+
}
3032+
3033+
// Complain about the mismatch.
3034+
classDecl->diagnose(
3035+
diag::actor_isolation_superclass_mismatch, isolation,
3036+
classDecl->getName(), superIsolation, superclassDecl->getName());
3037+
return true;
3038+
}
3039+
29763040
ActorIsolation ActorIsolationRequest::evaluate(
29773041
Evaluator &evaluator, ValueDecl *value) const {
29783042
// If this declaration has actor-isolated "self", it's isolated to that
@@ -2999,6 +3063,12 @@ ActorIsolation ActorIsolationRequest::evaluate(
29993063
ConcurrentReferenceKind::Nonisolated);
30003064
}
30013065

3066+
// Classes with global actors have additional rules regarding inheritance.
3067+
if (isolationFromAttr->isGlobalActor()) {
3068+
if (auto classDecl = dyn_cast<ClassDecl>(value))
3069+
checkClassGlobalActorIsolation(classDecl, *isolationFromAttr);
3070+
}
3071+
30023072
return *isolationFromAttr;
30033073
}
30043074

test/Concurrency/actor_isolation.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,3 +951,18 @@ actor Counter {
951951
return counter
952952
}
953953
}
954+
955+
/// Superclass checks for global actor-qualified class types.
956+
class C2 { }
957+
958+
@SomeGlobalActor
959+
class C3: C2 { } // expected-error{{global actor 'SomeGlobalActor'-isolated class 'C3' has different actor isolation from nonisolated superclass 'C2'}}
960+
961+
@GenericGlobalActor<U>
962+
class GenericSuper<U> { }
963+
964+
@GenericGlobalActor<[T]>
965+
class GenericSub1<T>: GenericSuper<[T]> { }
966+
967+
@GenericGlobalActor<T>
968+
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)