Skip to content

Commit 98903b7

Browse files
committed
[Concurrency] Add globalActor attribute.
The globalActor attribute indicates that a particular type describes a global actor. Global actors allow the notion of actor state isolation to be spread across various declarations throughout a program, rather than being centered around a single actor class. There are useful primarily for existing notions such as "main thread" or subsystems accessed through global/singleton state.
1 parent a665ba6 commit 98903b7

File tree

11 files changed

+246
-0
lines changed

11 files changed

+246
-0
lines changed

include/swift/AST/Attr.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,12 @@ SIMPLE_DECL_ATTR(actorIndependent, ActorIndependent,
578578
APIStableToAdd | APIBreakingToRemove,
579579
103)
580580

581+
SIMPLE_DECL_ATTR(globalActor, GlobalActor,
582+
OnClass | OnStruct | OnEnum | ConcurrencyOnly |
583+
ABIStableToAdd | ABIBreakingToRemove |
584+
APIStableToAdd | APIBreakingToRemove,
585+
104)
586+
581587
#undef TYPE_ATTR
582588
#undef DECL_ATTR_ALIAS
583589
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/Decl.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3172,6 +3172,20 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
31723172

31733173
void synthesizeSemanticMembersIfNeeded(DeclName member);
31743174

3175+
/// Retrieves the static 'shared' property of a global actor type, which
3176+
/// is used to extract the actor instance.
3177+
///
3178+
/// \returns the static 'shared' property for a global actor, or \c nullptr
3179+
/// for types that are not global actors.
3180+
VarDecl *getGlobalActorInstance() const;
3181+
3182+
/// Whether this type is a global actor, which can be used as an
3183+
/// attribute to decorate declarations for inclusion in the actor-isolated
3184+
/// state denoted by this type.
3185+
bool isGlobalActor() const {
3186+
return getGlobalActorInstance() != nullptr;
3187+
}
3188+
31753189
// Implement isa/cast/dyncast/etc.
31763190
static bool classof(const Decl *D) {
31773191
return D->getKind() >= DeclKind::First_NominalTypeDecl &&

include/swift/AST/DiagnosticsSema.def

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4205,6 +4205,22 @@ ERROR(enqueue_partial_task_not_in_context,none,
42054205
"'enqueue(partialTask:)' can only be implemented in the definition of "
42064206
"actor class %0", (Type))
42074207

4208+
ERROR(global_actor_missing_shared,none,
4209+
"global actor %0 requires a static property 'shared' that produces an "
4210+
"actor instance", (Identifier))
4211+
NOTE(global_actor_shared_not_static,none,
4212+
"'shared' property in global actor is not 'static'", ())
4213+
NOTE(global_actor_shared_inaccessible,none,
4214+
"'shared' property has more restrictive access (%0) than its global actor "
4215+
"(%1)",
4216+
(StringRef, StringRef))
4217+
NOTE(global_actor_shared_constrained_extension,none,
4218+
"'shared' property in global actor cannot be in a constrained extension",
4219+
())
4220+
NOTE(global_actor_shared_non_actor_type,none,
4221+
"'shared' property type %0 does not conform to the 'Actor' protocol",
4222+
(Type))
4223+
42084224
//------------------------------------------------------------------------------
42094225
// MARK: Type Check Types
42104226
//------------------------------------------------------------------------------

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ IDENTIFIER(Selector)
119119
IDENTIFIER(self)
120120
IDENTIFIER(Self)
121121
IDENTIFIER(setObject)
122+
IDENTIFIER(shared)
122123
IDENTIFIER(simd)
123124
IDENTIFIER(storage)
124125
IDENTIFIER(stringValue)

include/swift/AST/TypeCheckRequests.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,28 @@ class IsActorRequest :
858858
bool isCached() const { return true; }
859859
};
860860

861+
/// Retrieve the static "shared" property within a global actor that provides
862+
/// the actor instance representing the global actor.
863+
///
864+
/// Global actors can be applied to a declaration to indicate that the
865+
/// declaration operations on state that is protected by the global actor.
866+
class GlobalActorInstanceRequest :
867+
public SimpleRequest<GlobalActorInstanceRequest,
868+
VarDecl *(NominalTypeDecl *),
869+
RequestFlags::Cached> {
870+
public:
871+
using SimpleRequest::SimpleRequest;
872+
873+
private:
874+
friend SimpleRequest;
875+
876+
VarDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *nominal) const;
877+
878+
public:
879+
// Caching
880+
bool isCached() const { return true; }
881+
};
882+
861883
/// Determine the actor isolation for the given declaration.
862884
class ActorIsolationRequest :
863885
public SimpleRequest<ActorIsolationRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ SWIFT_REQUEST(TypeChecker, CanBeAsyncHandlerRequest, bool(FuncDecl *),
8787
Cached, NoLocationInfo)
8888
SWIFT_REQUEST(TypeChecker, IsActorRequest, bool(ClassDecl *),
8989
Cached, NoLocationInfo)
90+
SWIFT_REQUEST(TypeChecker, GlobalActorInstanceRequest,
91+
VarDecl *(NominalTypeDecl *),
92+
Cached, NoLocationInfo)
9093
SWIFT_REQUEST(TypeChecker, ActorIsolationRequest,
9194
ActorIsolationState(ValueDecl *),
9295
Cached, NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4044,6 +4044,13 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) {
40444044
}
40454045
}
40464046

4047+
VarDecl *NominalTypeDecl::getGlobalActorInstance() const {
4048+
auto mutableThis = const_cast<NominalTypeDecl *>(this);
4049+
return evaluateOrDefault(getASTContext().evaluator,
4050+
GlobalActorInstanceRequest{mutableThis},
4051+
nullptr);
4052+
}
4053+
40474054
ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
40484055
MutableArrayRef<TypeLoc> Inherited,
40494056
GenericParamList *GenericParams, DeclContext *Parent)

lib/Sema/TypeCheckAttr.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,14 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
323323
return;
324324
}
325325
}
326+
327+
void visitGlobalActorAttr(GlobalActorAttr *attr) {
328+
auto nominal = dyn_cast<NominalTypeDecl>(D);
329+
if (!nominal)
330+
return; // already diagnosed
331+
332+
(void)nominal->isGlobalActor();
333+
}
326334
};
327335
} // end anonymous namespace
328336

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,129 @@ bool IsActorRequest::evaluate(
204204
return actorAttr != nullptr;
205205
}
206206

207+
static bool isDeclNotAsAccessibleAsParent(ValueDecl *decl,
208+
NominalTypeDecl *parent) {
209+
return decl->getFormalAccess() <
210+
std::min(parent->getFormalAccess(), AccessLevel::Public);
211+
}
212+
213+
VarDecl *GlobalActorInstanceRequest::evaluate(
214+
Evaluator &evaluator, NominalTypeDecl *nominal) const {
215+
auto globalActorAttr = nominal->getAttrs().getAttribute<GlobalActorAttr>();
216+
if (!globalActorAttr)
217+
return nullptr;
218+
219+
// Ensure that the actor protocol has been loaded.
220+
ASTContext &ctx = nominal->getASTContext();
221+
auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor);
222+
if (!actorProto) {
223+
nominal->diagnose(diag::concurrency_lib_missing, "Actor");
224+
return nullptr;
225+
}
226+
227+
// Global actors have a static property "shared" that provides an actor
228+
// instance. The value must
229+
SmallVector<ValueDecl *, 4> decls;
230+
nominal->lookupQualified(
231+
nominal, DeclNameRef(ctx.Id_shared), NL_QualifiedDefault, decls);
232+
VarDecl *sharedVar = nullptr;
233+
llvm::TinyPtrVector<VarDecl *> candidates;
234+
for (auto decl : decls) {
235+
auto var = dyn_cast<VarDecl>(decl);
236+
if (!var)
237+
continue;
238+
239+
auto varDC = var->getDeclContext();
240+
if (var->isStatic() &&
241+
!isDeclNotAsAccessibleAsParent(var, nominal) &&
242+
!(isa<ExtensionDecl>(varDC) &&
243+
cast<ExtensionDecl>(varDC)->isConstrainedExtension()) &&
244+
TypeChecker::conformsToProtocol(
245+
varDC->mapTypeIntoContext(var->getValueInterfaceType()),
246+
actorProto, nominal)) {
247+
sharedVar = var;
248+
break;
249+
}
250+
251+
candidates.push_back(var);
252+
}
253+
254+
// If we found a suitable candidate, we're done.
255+
if (sharedVar)
256+
return sharedVar;
257+
258+
// Complain about the lack of a suitable 'shared' property.
259+
{
260+
auto primaryDiag = nominal->diagnose(
261+
diag::global_actor_missing_shared, nominal->getName());
262+
263+
// If there were no candidates, provide a Fix-It with a prototype.
264+
if (candidates.empty() && nominal->getBraces().Start.isValid()) {
265+
// Figure out the indentation we need.
266+
SourceLoc sharedInsertionLoc = Lexer::getLocForEndOfToken(
267+
ctx.SourceMgr, nominal->getBraces().Start);
268+
269+
StringRef extraIndent;
270+
StringRef currentIndent = Lexer::getIndentationForLine(
271+
ctx.SourceMgr, sharedInsertionLoc, &extraIndent);
272+
std::string stubIndent = (currentIndent + extraIndent).str();
273+
274+
// From the string to add the declaration.
275+
std::string sharedDeclString = "\n" + stubIndent;
276+
if (nominal->getFormalAccess() >= AccessLevel::Public)
277+
sharedDeclString += "public ";
278+
279+
sharedDeclString += "static let shared = <#actor instance#>";
280+
281+
primaryDiag.fixItInsert(sharedInsertionLoc, sharedDeclString);
282+
}
283+
}
284+
285+
// Remark about all of the candidates that failed (and why).
286+
for (auto candidate : candidates) {
287+
if (!candidate->isStatic()) {
288+
candidate->diagnose(diag::global_actor_shared_not_static)
289+
.fixItInsert(candidate->getAttributeInsertionLoc(true), "static ");
290+
continue;
291+
}
292+
293+
if (isDeclNotAsAccessibleAsParent(candidate, nominal)) {
294+
AccessLevel needAccessLevel = std::min(
295+
nominal->getFormalAccess(), AccessLevel::Public);
296+
auto diag = candidate->diagnose(
297+
diag::global_actor_shared_inaccessible,
298+
getAccessLevelSpelling(candidate->getFormalAccess()),
299+
getAccessLevelSpelling(needAccessLevel));
300+
if (auto attr = candidate->getAttrs().getAttribute<AccessControlAttr>()) {
301+
if (needAccessLevel == AccessLevel::Internal) {
302+
diag.fixItRemove(attr->getRange());
303+
} else {
304+
diag.fixItReplace(
305+
attr->getRange(), getAccessLevelSpelling(needAccessLevel));
306+
}
307+
} else {
308+
diag.fixItInsert(
309+
candidate->getAttributeInsertionLoc(true),
310+
getAccessLevelSpelling(needAccessLevel));
311+
}
312+
continue;
313+
}
314+
315+
if (auto ext = dyn_cast<ExtensionDecl>(candidate->getDeclContext())) {
316+
if (ext->isConstrainedExtension()) {
317+
candidate->diagnose(diag::global_actor_shared_constrained_extension);
318+
continue;
319+
}
320+
}
321+
322+
Type varType = candidate->getDeclContext()->mapTypeIntoContext(
323+
candidate->getValueInterfaceType());
324+
candidate->diagnose(diag::global_actor_shared_non_actor_type, varType);
325+
}
326+
327+
return nullptr;
328+
}
329+
207330
namespace {
208331

209332
/// The isolation restriction in effect for a given declaration that is

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,7 @@ namespace {
15141514
UNINTERESTING_ATTR(OriginallyDefinedIn)
15151515
UNINTERESTING_ATTR(Actor)
15161516
UNINTERESTING_ATTR(ActorIndependent)
1517+
UNINTERESTING_ATTR(GlobalActor)
15171518
#undef UNINTERESTING_ATTR
15181519

15191520
void visitAvailableAttr(AvailableAttr *attr) {

0 commit comments

Comments
 (0)