Skip to content

Commit 18fd4be

Browse files
committed
[Concurrency] Check actor isolation consistency for overrides & subclasses.
Both overriding declarations and subclasses must have the actor isolation as their overridden declarations or superclasses, respectively. Enforce this, ensuring that we're also doing the appropriate substitutions.
1 parent 2f7ff6a commit 18fd4be

File tree

7 files changed

+182
-5
lines changed

7 files changed

+182
-5
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class raw_ostream;
2525

2626
namespace swift {
2727
class ClassDecl;
28+
class SubstitutionMap;
2829
class Type;
2930

3031
/// Determine whether the given types are (canonically) equal, declared here
@@ -97,6 +98,13 @@ class ActorIsolation {
9798
return globalActor;
9899
}
99100

101+
/// Determine whether this isolation will require substitution to be
102+
/// evaluated.
103+
bool requiresSubstitution() const;
104+
105+
/// Substitute into types within the actor isolation.
106+
ActorIsolation subst(SubstitutionMap subs) const;
107+
100108
friend bool operator==(const ActorIsolation &lhs,
101109
const ActorIsolation &rhs) {
102110
if (lhs.kind != rhs.kind)

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4262,6 +4262,13 @@ ERROR(actor_isolation_multiple_attr,none,
42624262
"%0 %1 has multiple actor-isolation attributes ('%2' and '%3')",
42634263
(DescriptiveDeclKind, DeclName, StringRef, StringRef))
42644264

4265+
ERROR(actor_isolation_override_mismatch,none,
4266+
"%0 %1 %2 has different actor isolation from %3 overridden declaration",
4267+
(ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation))
4268+
ERROR(actor_isolation_superclass_mismatch,none,
4269+
"%0 class %1 has different actor isolation from %2 superclass %3",
4270+
(ActorIsolation, Identifier, ActorIsolation, Identifier))
4271+
42654272
//------------------------------------------------------------------------------
42664273
// MARK: Type Check Types
42674274
//------------------------------------------------------------------------------

lib/AST/TypeCheckRequests.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,6 +1447,29 @@ void CustomAttrTypeRequest::cacheResult(Type value) const {
14471447
attr->setType(value);
14481448
}
14491449

1450+
bool ActorIsolation::requiresSubstitution() const {
1451+
switch (kind) {
1452+
case ActorInstance:
1453+
case Independent:
1454+
case Unspecified:
1455+
return false;
1456+
1457+
case GlobalActor:
1458+
return getGlobalActor()->hasTypeParameter();
1459+
}
1460+
}
1461+
1462+
ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const {
1463+
switch (kind) {
1464+
case ActorInstance:
1465+
case Independent:
1466+
case Unspecified:
1467+
return *this;
1468+
1469+
case GlobalActor:
1470+
return forGlobalActor(getGlobalActor().subst(subs));
1471+
}
1472+
}
14501473

14511474
void swift::simple_display(
14521475
llvm::raw_ostream &out, const ActorIsolation &state) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,8 +1155,16 @@ ActorIsolation ActorIsolationRequest::evaluate(
11551155
// If the declaration overrides another declaration, it must have the same
11561156
// actor isolation.
11571157
if (auto overriddenValue = value->getOverriddenDecl()) {
1158-
if (auto isolation = getActorIsolation(overriddenValue))
1159-
return inferredIsolation(isolation);
1158+
if (auto isolation = getActorIsolation(overriddenValue)) {
1159+
SubstitutionMap subs;
1160+
if (auto env = value->getInnermostDeclContext()
1161+
->getGenericEnvironmentOfContext()) {
1162+
subs = SubstitutionMap::getOverrideSubstitutions(
1163+
overriddenValue, value, subs);
1164+
}
1165+
1166+
return inferredIsolation(isolation.subst(subs));
1167+
}
11601168
}
11611169

11621170
// If the declaration witnesses a protocol requirement that is isolated,
@@ -1210,3 +1218,65 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) {
12101218
ctx.evaluator, ActorIsolationRequest{value},
12111219
ActorIsolation::forUnspecified());
12121220
}
1221+
1222+
void swift::checkOverrideActorIsolation(ValueDecl *value) {
1223+
if (isa<TypeDecl>(value))
1224+
return;
1225+
1226+
auto overridden = value->getOverriddenDecl();
1227+
if (!overridden)
1228+
return;
1229+
1230+
// Determine the actor isolation of this declaration.
1231+
auto isolation = getActorIsolation(value);
1232+
1233+
// Determine the actor isolation of the overridden function.=
1234+
auto overriddenIsolation = getActorIsolation(overridden);
1235+
1236+
if (overriddenIsolation.requiresSubstitution()) {
1237+
SubstitutionMap subs;
1238+
if (auto env = value->getInnermostDeclContext()
1239+
->getGenericEnvironmentOfContext()) {
1240+
subs = SubstitutionMap::getOverrideSubstitutions(overridden, value, subs);
1241+
overriddenIsolation = overriddenIsolation.subst(subs);
1242+
}
1243+
}
1244+
1245+
// If the isolation matches, we're done.
1246+
if (isolation == overriddenIsolation)
1247+
return;
1248+
1249+
// Isolation mismatch. Diagnose it.
1250+
value->diagnose(
1251+
diag::actor_isolation_override_mismatch, isolation,
1252+
value->getDescriptiveKind(), value->getName(), overriddenIsolation);
1253+
overridden->diagnose(diag::overridden_here);
1254+
}
1255+
1256+
void swift::checkSubclassActorIsolation(ClassDecl *classDecl) {
1257+
auto superclassDecl = classDecl->getSuperclassDecl();
1258+
if (!superclassDecl)
1259+
return;
1260+
1261+
auto isolation = getActorIsolation(classDecl);
1262+
auto superclassIsolation = getActorIsolation(superclassDecl);
1263+
1264+
if (superclassIsolation.requiresSubstitution()) {
1265+
Type superclassType = classDecl->getSuperclass();
1266+
if (!superclassType)
1267+
return;
1268+
1269+
SubstitutionMap subs = superclassType->getMemberSubstitutionMap(
1270+
classDecl->getModuleContext(), classDecl);
1271+
superclassIsolation = superclassIsolation.subst(subs);
1272+
}
1273+
1274+
if (isolation == superclassIsolation)
1275+
return;
1276+
1277+
// Diagnose mismatch.
1278+
classDecl->diagnose(
1279+
diag::actor_isolation_superclass_mismatch, isolation,
1280+
classDecl->getName(), superclassIsolation, superclassDecl->getName());
1281+
superclassDecl->diagnose(diag::decl_declared_here, superclassDecl->getName());
1282+
}

lib/Sema/TypeCheckConcurrency.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ class ActorIsolationRestriction {
144144
operator Kind() const { return kind; };
145145
};
146146

147+
/// Check that the actor isolation of an override matches that of its
148+
/// overridden declaration.
149+
void checkOverrideActorIsolation(ValueDecl *value);
150+
151+
/// Check that the actor isolation of a class matches that of its superclass.
152+
void checkSubclassActorIsolation(ClassDecl *classDecl);
153+
147154
} // end namespace swift
148155

149156
#endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "DerivedConformances.h"
2222
#include "TypeChecker.h"
2323
#include "TypeCheckAccess.h"
24+
#include "TypeCheckConcurrency.h"
2425
#include "TypeCheckDecl.h"
2526
#include "TypeCheckAvailability.h"
2627
#include "TypeCheckObjC.h"
@@ -1382,12 +1383,17 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
13821383
(void) VD->getFormalAccess();
13831384

13841385
// Compute overrides.
1385-
(void) VD->getOverriddenDecls();
1386+
if (!VD->getOverriddenDecls().empty())
1387+
checkOverrideActorIsolation(VD);
13861388

13871389
// Check whether the member is @objc or dynamic.
13881390
(void) VD->isObjC();
13891391
(void) VD->isDynamic();
13901392

1393+
// For a class, check actor isolation.
1394+
if (auto classDecl = dyn_cast<ClassDecl>(VD))
1395+
checkSubclassActorIsolation(classDecl);
1396+
13911397
// If this is a member of a nominal type, don't allow it to have a name of
13921398
// "Type" or "Protocol" since we reserve the X.Type and X.Protocol
13931399
// expressions to mean something builtin to the language. We *do* allow

test/Concurrency/global_actor_inference.swift

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ struct OtherGlobalActor {
1515
static let shared = SomeActor()
1616
}
1717

18+
@globalActor
19+
struct GenericGlobalActor<T> {
20+
static var shared: SomeActor { SomeActor() }
21+
}
22+
1823
// ----------------------------------------------------------------------
1924
// Global actor inference for protocols
2025
// ----------------------------------------------------------------------
@@ -25,11 +30,10 @@ protocol P1 {
2530
}
2631

2732
protocol P2 {
28-
@SomeGlobalActor func method1()
33+
@SomeGlobalActor func method1() // expected-note {{'method1()' declared here}}
2934
func method2()
3035
}
3136

32-
3337
class C1: P1 {
3438
func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance}}
3539

@@ -88,3 +92,55 @@ class C5 {
8892
c5.method1() // OK: no propagation
8993
c5.method2() // expected-error{{instance method 'method2()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}}
9094
}
95+
96+
protocol P3 {
97+
@OtherGlobalActor func method1() // expected-note{{'method1()' declared here}}
98+
func method2()
99+
}
100+
101+
class C6: P2, P3 {
102+
func method1() { }
103+
// expected-error@-1{{instance method 'method1()' must be isolated to the global actor 'SomeGlobalActor' to satisfy corresponding requirement from protocol 'P2'}}
104+
// expected-error@-2{{instance method 'method1()' must be isolated to the global actor 'OtherGlobalActor' to satisfy corresponding requirement from protocol 'P3'}}
105+
func method2() { }
106+
107+
func testMethod() {
108+
method1() // okay: no inference
109+
method2() // okay: no inference
110+
}
111+
}
112+
113+
// ----------------------------------------------------------------------
114+
// Global actor checking for overrides
115+
// ----------------------------------------------------------------------
116+
actor class GenericSuper<T> {
117+
@GenericGlobalActor<T> func method() { }
118+
119+
@GenericGlobalActor<T> func method2() { } // expected-note {{overridden declaration is here}}
120+
@GenericGlobalActor<T> func method3() { } // expected-note {{overridden declaration is here}}
121+
@GenericGlobalActor<T> func method4() { }
122+
@GenericGlobalActor<T> func method5() { }
123+
}
124+
125+
actor class GenericSub<T> : GenericSuper<[T]> {
126+
override func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance; do you want to add 'async'?}}
127+
128+
@GenericGlobalActor<T> override func method2() { } // expected-error{{global actor 'GenericGlobalActor<T>'-isolated instance method 'method2()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}}
129+
@actorIndependent override func method3() { } // expected-error{{actor-independent instance method 'method3()' has different actor isolation from global actor 'GenericGlobalActor<[T]>'-isolated overridden declaration}}
130+
131+
@OtherGlobalActor func testMethod() {
132+
method() // expected-error{{instance method 'method()' isolated to global actor 'GenericGlobalActor<[T]>' can not be referenced from different global actor 'OtherGlobalActor'}}
133+
}
134+
}
135+
136+
// ----------------------------------------------------------------------
137+
// Global actor checking for supeclasses
138+
// ----------------------------------------------------------------------
139+
struct Container<T> {
140+
@GenericGlobalActor<T> class Superclass { } // expected-note{{'Superclass' declared here}}
141+
}
142+
143+
struct OtherContainer<U> {
144+
@GenericGlobalActor<[U]> class Subclass1 : Container<[U]>.Superclass { }
145+
@GenericGlobalActor<U> class Subclass2 : Container<[U]>.Superclass { } // expected-error{{global actor 'GenericGlobalActor<U>'-isolated class 'Subclass2' has different actor isolation from global actor 'GenericGlobalActor<[U]>'-isolated superclass 'Superclass'}}
146+
}

0 commit comments

Comments
 (0)