Skip to content

Commit f089ba9

Browse files
committed
[Concurrency] Propagation of actor constraints.
Implement propagation rules for global actor constraints, which can come from: * Enclosing extension or type * Superclass of a class * Overridden declaration * Requirement witnessed by a declaration * Storage declaration for an accessor
1 parent 2d8f073 commit f089ba9

File tree

4 files changed

+305
-42
lines changed

4 files changed

+305
-42
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class ActorIsolation {
8484

8585
operator Kind() const { return getKind(); }
8686

87+
bool isUnspecified() const { return kind == Unspecified; }
88+
8789
ClassDecl *getActor() const {
8890
assert(getKind() == ActorInstance);
8991
return actor;

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 211 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -971,65 +971,237 @@ void swift::checkActorIsolation(const Expr *expr, const DeclContext *dc) {
971971
const_cast<Expr *>(expr)->walk(walker);
972972
}
973973

974-
ActorIsolation ActorIsolationRequest::evaluate(
975-
Evaluator &evaluator, ValueDecl *value) const {
974+
/// Determine actor isolation solely from attributes.
975+
///
976+
/// \returns the actor isolation determined from attributes alone (with no
977+
/// inference rules). Returns \c None if there were no attributes on this
978+
/// declaration.
979+
static Optional<ActorIsolation> getIsolationFromAttributes(Decl *decl) {
976980
// Look up attributes on the declaration that can affect its actor isolation.
977981
// If any of them are present, use that attribute.
978-
auto independentAttr = value->getAttrs().getAttribute<ActorIndependentAttr>();
979-
auto globalActorAttr = value->getGlobalActorAttr();
982+
auto independentAttr = decl->getAttrs().getAttribute<ActorIndependentAttr>();
983+
auto globalActorAttr = decl->getGlobalActorAttr();
980984
unsigned numIsolationAttrs =
981985
(independentAttr ? 1 : 0) + (globalActorAttr ? 1 : 0);
982-
if (numIsolationAttrs > 0) {
983-
// Only one such attribute is valid.
984-
if (numIsolationAttrs > 1) {
985-
value->diagnose(
986-
diag::actor_isolation_multiple_attr, value->getDescriptiveKind(),
987-
value->getName(), independentAttr->getAttrName(),
988-
globalActorAttr->second->getName().str())
989-
.highlight(independentAttr->getRangeWithAt())
990-
.highlight(globalActorAttr->first->getRangeWithAt());
991-
}
986+
if (numIsolationAttrs == 0)
987+
return None;
992988

993-
// If the declaration is explicitly marked @actorIndependent, report it as
994-
// independent.
995-
if (independentAttr) {
996-
return ActorIsolation::forIndependent();
989+
// Only one such attribute is valid.
990+
if (numIsolationAttrs > 1) {
991+
DeclName name;
992+
if (auto value = dyn_cast<ValueDecl>(decl)) {
993+
name = value->getName();
994+
} else if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
995+
if (auto selfTypeDecl = ext->getSelfNominalTypeDecl())
996+
name = selfTypeDecl->getName();
997997
}
998998

999-
// If the declaration is marked with a global actor, report it as being
1000-
// part of that global actor.
1001-
if (globalActorAttr) {
1002-
TypeResolutionOptions options(TypeResolverContext::None);
1003-
TypeResolution resolver = TypeResolution::forInterface(
1004-
value->getInnermostDeclContext(), options, nullptr);
1005-
Type globalActorType = resolver.resolveType(
1006-
globalActorAttr->first->getTypeRepr(), nullptr);
1007-
if (!globalActorType || globalActorType->hasError())
1008-
return ActorIsolation::forUnspecified();
999+
decl->diagnose(
1000+
diag::actor_isolation_multiple_attr, decl->getDescriptiveKind(),
1001+
name, independentAttr->getAttrName(),
1002+
globalActorAttr->second->getName().str())
1003+
.highlight(independentAttr->getRangeWithAt())
1004+
.highlight(globalActorAttr->first->getRangeWithAt());
1005+
}
1006+
1007+
// If the declaration is explicitly marked @actorIndependent, report it as
1008+
// independent.
1009+
if (independentAttr) {
1010+
return ActorIsolation::forIndependent();
1011+
}
1012+
1013+
// If the declaration is marked with a global actor, report it as being
1014+
// part of that global actor.
1015+
if (globalActorAttr) {
1016+
TypeResolutionOptions options(TypeResolverContext::None);
1017+
TypeResolution resolver = TypeResolution::forInterface(
1018+
decl->getInnermostDeclContext(), options, nullptr);
1019+
Type globalActorType = resolver.resolveType(
1020+
globalActorAttr->first->getTypeRepr(), nullptr);
1021+
if (!globalActorType || globalActorType->hasError())
1022+
return ActorIsolation::forUnspecified();
1023+
1024+
return ActorIsolation::forGlobalActor(globalActorType);
1025+
}
1026+
1027+
llvm_unreachable("Forgot about an attribute?");
1028+
}
1029+
1030+
/// Infer isolation from witnessed protocol requirements.
1031+
static Optional<ActorIsolation> getIsolationFromWitnessedRequirements(
1032+
ValueDecl *value) {
1033+
auto dc = value->getDeclContext();
1034+
auto idc = dyn_cast_or_null<IterableDeclContext>(dc->getAsDecl());
1035+
if (!idc)
1036+
return None;
1037+
1038+
// Walk through each of the conformances in this context, collecting any
1039+
// requirements that have actor isolation.
1040+
auto conformances = evaluateOrDefault(
1041+
dc->getASTContext().evaluator,
1042+
LookupAllConformancesInContextRequest{idc}, { });
1043+
using IsolatedRequirement =
1044+
std::tuple<ProtocolConformance *, ActorIsolation, ValueDecl *>;
1045+
SmallVector<IsolatedRequirement, 2> isolatedRequirements;
1046+
for (auto conformance : conformances) {
1047+
auto protocol = conformance->getProtocol();
1048+
for (auto found : protocol->lookupDirect(value->getName())) {
1049+
if (!isa<ProtocolDecl>(found->getDeclContext()))
1050+
continue;
1051+
1052+
auto requirement = dyn_cast<ValueDecl>(found);
1053+
if (!requirement || isa<TypeDecl>(requirement))
1054+
continue;
1055+
1056+
auto requirementIsolation = getActorIsolation(requirement);
1057+
if (requirementIsolation.isUnspecified())
1058+
continue;
1059+
1060+
auto witness = conformance->getWitnessDecl(requirement);
1061+
if (witness != value)
1062+
continue;
10091063

1010-
return ActorIsolation::forGlobalActor(globalActorType);
1064+
isolatedRequirements.push_back(
1065+
IsolatedRequirement{conformance, requirementIsolation, requirement});
10111066
}
1067+
}
1068+
1069+
// Filter out duplicate actors.
1070+
SmallPtrSet<CanType, 2> globalActorTypes;
1071+
bool sawActorIndependent = false;
1072+
isolatedRequirements.erase(
1073+
std::remove_if(isolatedRequirements.begin(), isolatedRequirements.end(),
1074+
[&](IsolatedRequirement &isolated) {
1075+
auto isolation = std::get<1>(isolated);
1076+
switch (isolation) {
1077+
case ActorIsolation::ActorInstance:
1078+
llvm_unreachable("protocol requirements cannot be actor instances");
1079+
1080+
case ActorIsolation::Independent:
1081+
// We only need one @actorIndependent.
1082+
if (sawActorIndependent)
1083+
return true;
1084+
1085+
sawActorIndependent = true;
1086+
return false;
1087+
1088+
case ActorIsolation::Unspecified:
1089+
return true;
1090+
1091+
case ActorIsolation::GlobalActor: {
1092+
// Substitute into the global actor type.
1093+
auto conformance = std::get<0>(isolated);
1094+
auto requirementSubs = SubstitutionMap::getProtocolSubstitutions(
1095+
conformance->getProtocol(), dc->getSelfTypeInContext(),
1096+
ProtocolConformanceRef(conformance));
1097+
Type globalActor = isolation.getGlobalActor().subst(requirementSubs);
1098+
if (!globalActorTypes.insert(globalActor->getCanonicalType()).second)
1099+
return true;
1100+
1101+
// Update the global actor type, now that we've done this substitution.
1102+
std::get<1>(isolated) = ActorIsolation::forGlobalActor(globalActor);
1103+
return false;
1104+
}
1105+
}
1106+
}),
1107+
isolatedRequirements.end());
1108+
1109+
if (isolatedRequirements.size() != 1)
1110+
return None;
1111+
1112+
return std::get<1>(isolatedRequirements.front());
1113+
}
1114+
1115+
ActorIsolation ActorIsolationRequest::evaluate(
1116+
Evaluator &evaluator, ValueDecl *value) const {
1117+
// If this declaration has one of the actor isolation attributes, report
1118+
// that.
1119+
if (auto isolationFromAttr = getIsolationFromAttributes(value)) {
1120+
return *isolationFromAttr;
1121+
}
1122+
1123+
// Determine the default isolation for this declaration, which may still be
1124+
// overridden by other inference rules.
1125+
ActorIsolation defaultIsolation = ActorIsolation::forUnspecified();
1126+
1127+
// Check for instance members of actor classes, which are part of
1128+
// actor-isolated state.
1129+
auto classDecl = value->getDeclContext()->getSelfClassDecl();
1130+
if (classDecl && classDecl->isActor() && value->isInstanceMember()) {
1131+
defaultIsolation = ActorIsolation::forActorInstance(classDecl);
1132+
}
10121133

1013-
llvm_unreachable("Forgot about an attribute?");
1134+
// Disable inference of actor attributes outside of normal Swift source files.
1135+
if (auto sourceFile = value->getDeclContext()->getParentSourceFile()) {
1136+
switch (sourceFile->Kind) {
1137+
case SourceFileKind::Interface:
1138+
case SourceFileKind::SIL:
1139+
return defaultIsolation;
1140+
1141+
case SourceFileKind::Library:
1142+
case SourceFileKind::Main:
1143+
// Attempt inference below.
1144+
break;
1145+
}
1146+
} else {
1147+
return defaultIsolation;
10141148
}
10151149

1150+
// Function used when returning an inferred isolation.
1151+
auto inferredIsolation = [&](ActorIsolation inferred) {
1152+
return inferred;
1153+
};
1154+
10161155
// If the declaration overrides another declaration, it must have the same
10171156
// actor isolation.
10181157
if (auto overriddenValue = value->getOverriddenDecl()) {
10191158
if (auto isolation = getActorIsolation(overriddenValue))
1020-
return isolation;
1159+
return inferredIsolation(isolation);
10211160
}
10221161

1023-
// Check for instance members of actor classes, which are part of
1024-
// actor-isolated state.
1025-
auto classDecl = value->getDeclContext()->getSelfClassDecl();
1026-
if (classDecl && classDecl->isActor() && value->isInstanceMember()) {
1027-
// Part of the actor's isolated state.
1028-
return ActorIsolation::forActorInstance(classDecl);
1162+
// If the declaration witnesses a protocol requirement that is isolated,
1163+
// use that.
1164+
if (auto witnessedIsolation = getIsolationFromWitnessedRequirements(value)) {
1165+
return inferredIsolation(*witnessedIsolation);
1166+
}
1167+
1168+
// If the declaration is a class with a superclass that has specified
1169+
// isolation, use that.
1170+
if (auto classDecl = dyn_cast<ClassDecl>(value)) {
1171+
if (auto superclassDecl = classDecl->getSuperclassDecl()) {
1172+
auto superclassIsolation = getActorIsolation(superclassDecl);
1173+
if (!superclassIsolation.isUnspecified())
1174+
return inferredIsolation(superclassIsolation);
1175+
}
1176+
}
1177+
1178+
// If this is an accessor, use the actor isolation of its storage
1179+
// declaration.
1180+
if (auto accessor = dyn_cast<AccessorDecl>(value)) {
1181+
auto storageIsolation = getActorIsolation(accessor->getStorage());
1182+
if (!storageIsolation.isUnspecified())
1183+
return inferredIsolation(storageIsolation);
1184+
}
1185+
1186+
// If the declaration is in an extension that has one of the isolation
1187+
// attributes, use that.
1188+
if (auto ext = dyn_cast<ExtensionDecl>(value->getDeclContext())) {
1189+
if (auto isolationFromAttr = getIsolationFromAttributes(ext)) {
1190+
return inferredIsolation(*isolationFromAttr);
1191+
}
1192+
}
1193+
1194+
// If the declaration is in a nominal type (or extension thereof) that
1195+
// has isolation, use that.
1196+
if (auto selfTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl()) {
1197+
auto selfTypeIsolation = getActorIsolation(selfTypeDecl);
1198+
if (!selfTypeIsolation.isUnspecified()) {
1199+
return inferredIsolation(selfTypeIsolation);
1200+
}
10291201
}
10301202

1031-
// Everything else is unspecified.
1032-
return ActorIsolation::forUnspecified();
1203+
// Default isolation for this member.
1204+
return defaultIsolation;
10331205
}
10341206

10351207
ActorIsolation swift::getActorIsolation(ValueDecl *value) {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency
2+
// REQUIRES: concurrency
3+
4+
import _Concurrency
5+
6+
actor class SomeActor { }
7+
8+
@globalActor
9+
struct SomeGlobalActor {
10+
static let shared = SomeActor()
11+
}
12+
13+
@globalActor
14+
struct OtherGlobalActor {
15+
static let shared = SomeActor()
16+
}
17+
18+
// ----------------------------------------------------------------------
19+
// Global actor inference for protocols
20+
// ----------------------------------------------------------------------
21+
22+
@SomeGlobalActor
23+
protocol P1 {
24+
func method()
25+
}
26+
27+
protocol P2 {
28+
@SomeGlobalActor func method1()
29+
func method2()
30+
}
31+
32+
33+
class C1: P1 {
34+
func method() { } // expected-note{{only asynchronous methods can be used outside the actor instance}}
35+
36+
@OtherGlobalActor func testMethod() {
37+
method() // expected-error{{instance method 'method()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}}
38+
}
39+
}
40+
41+
class C2: P2 {
42+
func method1() { } // expected-note{{only asynchronous methods can be used outside the actor instance}}
43+
func method2() { }
44+
45+
@OtherGlobalActor func testMethod() {
46+
method1() // expected-error{{instance method 'method1()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}}
47+
method2() // okay
48+
}
49+
}
50+
51+
// ----------------------------------------------------------------------
52+
// Global actor inference for classes and extensions
53+
// ----------------------------------------------------------------------
54+
@SomeGlobalActor class C3 {
55+
func method1() { } // expected-note{{only asynchronous methods can be used outside the actor instance}}
56+
}
57+
58+
extension C3 {
59+
func method2() { } // expected-note{{only asynchronous methods can be used outside the actor instance}}
60+
}
61+
62+
class C4: C3 {
63+
func method3() { } // expected-note{{only asynchronous methods can be used outside the actor instance}}
64+
}
65+
66+
extension C4 {
67+
func method4() { } // expected-note{{only asynchronous methods can be used outside the actor instance}}
68+
}
69+
70+
class C5 {
71+
func method1() { }
72+
}
73+
74+
@SomeGlobalActor extension C5 {
75+
func method2() { } // expected-note{{only asynchronous methods can be used outside the actor instance}}
76+
}
77+
78+
@OtherGlobalActor func testGlobalActorInference(c3: C3, c4: C4, c5: C5) {
79+
// Propagation via class annotation
80+
c3.method1() // expected-error{{instance method 'method1()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}}
81+
c3.method2() // expected-error{{instance method 'method2()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}}
82+
83+
// Propagation via subclassing
84+
c4.method3() // expected-error{{instance method 'method3()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}}
85+
c4.method4() // expected-error{{instance method 'method4()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}}
86+
87+
// Propagation in an extension.
88+
c5.method1() // OK: no propagation
89+
c5.method2() // expected-error{{instance method 'method2()' isolated to global actor 'SomeGlobalActor' can not be referenced from different global actor 'OtherGlobalActor'}}
90+
}

test/decl/class/actor/global_actor_conformance.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ struct GenericGlobalActor<T> {
1818
protocol P1 {
1919
associatedtype Assoc
2020

21-
@GlobalActor func method1() // expected-note{{declared here}}
21+
@GlobalActor func method1()
2222
@GenericGlobalActor<Int> func method2() // expected-note{{declared here}}
2323
@GenericGlobalActor<Assoc> func method3()
2424
func method4() // expected-note{{declared here}}
@@ -33,8 +33,7 @@ protocol P2 {
3333
class C1 : P1, P2 {
3434
typealias Assoc = String
3535

36-
// FIXME: This will be inferred
37-
func method1() { } // expected-error{{instance method 'method1()' must be isolated to the global actor 'GlobalActor' to satisfy corresponding requirement from protocol 'P1'}}{{3-3=@GlobalActor}}
36+
func method1() { }
3837

3938
@GenericGlobalActor<String> func method2() { } // expected-error{{instance method 'method2()' isolated to global actor 'GenericGlobalActor<String>' can not satisfy corresponding requirement from protocol 'P1' isolated to global actor 'GenericGlobalActor<Int>'}}
4039
@GenericGlobalActor<String >func method3() { }

0 commit comments

Comments
 (0)