Skip to content

Commit df883f8

Browse files
committed
[Concurrency] Allow global actor annotations on declarations.
Global actor types can be used as attributes on various kinds of declarations to indicate that those declarations are part of the isolated state of that global actor. Allow such annotation and perform basic correctness checks.
1 parent 98903b7 commit df883f8

File tree

8 files changed

+171
-0
lines changed

8 files changed

+171
-0
lines changed

include/swift/AST/Decl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,16 @@ class alignas(1 << DeclAlignInBits) Decl {
950950
/// If this returns true, the decl can be safely casted to ValueDecl.
951951
bool isPotentiallyOverridable() const;
952952

953+
/// Returns true if this Decl cannot be seen by any other source file
954+
bool isPrivateToEnclosingFile() const;
955+
956+
/// Retrieve the global actor attribute that applies to this declaration,
957+
/// if any.
958+
///
959+
/// This is the "raw" global actor attribute as written directly on the
960+
/// declaration, with any inference rules applied.
961+
CustomAttr *getGlobalActorAttr() const;
962+
953963
/// If an alternative module name is specified for this decl, e.g. using
954964
/// @_originalDefinedIn attribute, this function returns this module name.
955965
StringRef getAlternateModuleName() const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4221,6 +4221,16 @@ NOTE(global_actor_shared_non_actor_type,none,
42214221
"'shared' property type %0 does not conform to the 'Actor' protocol",
42224222
(Type))
42234223

4224+
ERROR(multiple_global_actors,none,
4225+
"declaration can not have multiple global actor attributes (%0 and %1)",
4226+
(Identifier, Identifier))
4227+
ERROR(global_actor_disallowed,none,
4228+
"%0 cannot have a global actor", (DescriptiveDeclKind))
4229+
ERROR(global_actor_on_actor_class,none,
4230+
"actor class %0 cannot have a global actor", (Identifier))
4231+
ERROR(global_actor_on_local_variable,none,
4232+
"local variable %0 cannot have a global actor", (DeclName))
4233+
42244234
//------------------------------------------------------------------------------
42254235
// MARK: Type Check Types
42264236
//------------------------------------------------------------------------------

include/swift/AST/TypeCheckRequests.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,30 @@ class GlobalActorInstanceRequest :
880880
bool isCached() const { return true; }
881881
};
882882

883+
/// Request the custom attribute which denotes the global actor for the given
884+
/// declaration.
885+
///
886+
/// This is the "raw" global actor attribute as written directly on the
887+
/// declaration, with any inference rules applied.
888+
class GlobalActorAttributeRequest :
889+
public SimpleRequest<GlobalActorAttributeRequest,
890+
CustomAttr *(Decl *),
891+
RequestFlags::Cached> {
892+
public:
893+
using SimpleRequest::SimpleRequest;
894+
895+
private:
896+
friend SimpleRequest;
897+
898+
// Evaluation.
899+
CustomAttr *
900+
evaluate(Evaluator &evaluator, Decl *decl) const;
901+
902+
public:
903+
// Caching
904+
bool isCached() const { return true; }
905+
};
906+
883907
/// Determine the actor isolation for the given declaration.
884908
class ActorIsolationRequest :
885909
public SimpleRequest<ActorIsolationRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ SWIFT_REQUEST(TypeChecker, IsActorRequest, bool(ClassDecl *),
9090
SWIFT_REQUEST(TypeChecker, GlobalActorInstanceRequest,
9191
VarDecl *(NominalTypeDecl *),
9292
Cached, NoLocationInfo)
93+
SWIFT_REQUEST(TypeChecker, GlobalActorAttributeRequest,
94+
CustomAttr *(Decl *), Cached, NoLocationInfo)
9395
SWIFT_REQUEST(TypeChecker, ActorIsolationRequest,
9496
ActorIsolationState(ValueDecl *),
9597
Cached, NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,14 @@ static_assert(sizeof(checkSourceLocType(&ID##Decl::getLoc)) == 2, \
672672
llvm_unreachable("invalid file kind");
673673
}
674674

675+
CustomAttr *Decl::getGlobalActorAttr() const {
676+
auto &ctx = getASTContext();
677+
auto mutableThis = const_cast<Decl *>(this);
678+
return evaluateOrDefault(ctx.evaluator,
679+
GlobalActorAttributeRequest{mutableThis},
680+
nullptr);
681+
}
682+
675683
Expr *AbstractFunctionDecl::getSingleExpressionBody() const {
676684
assert(hasSingleExpressionBody() && "Not a single-expression body");
677685
auto braceStmt = getBody();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,6 +3065,13 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
30653065
return;
30663066
}
30673067

3068+
// If the nominal type is a global actor, let the global actor attribute
3069+
// retrieval request perform checking for us.
3070+
if (nominal->isGlobalActor()) {
3071+
(void)D->getGlobalActorAttr();
3072+
return;
3073+
}
3074+
30683075
diagnose(attr->getLocation(), diag::nominal_type_not_attribute,
30693076
nominal->getDescriptiveKind(), nominal->getName());
30703077
nominal->diagnose(diag::decl_declared_here, nominal->getName());

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "swift/AST/ASTWalker.h"
1919
#include "swift/AST/ParameterList.h"
2020
#include "swift/AST/ProtocolConformance.h"
21+
#include "swift/AST/NameLookupRequests.h"
2122
#include "swift/AST/TypeCheckRequests.h"
2223

2324
using namespace swift;
@@ -327,6 +328,77 @@ VarDecl *GlobalActorInstanceRequest::evaluate(
327328
return nullptr;
328329
}
329330

331+
CustomAttr *GlobalActorAttributeRequest::evaluate(
332+
Evaluator &evaluator, Decl *decl) const {
333+
ASTContext &ctx = decl->getASTContext();
334+
auto dc = decl->getDeclContext();
335+
CustomAttr *globalActorAttr = nullptr;
336+
NominalTypeDecl *globalActorNominal = nullptr;
337+
338+
for (auto attr : decl->getAttrs().getAttributes<CustomAttr>()) {
339+
auto mutableAttr = const_cast<CustomAttr *>(attr);
340+
// Figure out which nominal declaration this custom attribute refers to.
341+
auto nominal = evaluateOrDefault(ctx.evaluator,
342+
CustomAttrNominalRequest{mutableAttr, dc},
343+
nullptr);
344+
345+
// Ignore unresolvable custom attributes.
346+
if (!nominal)
347+
continue;
348+
349+
// We are only interested in global actor types.
350+
if (!nominal->isGlobalActor())
351+
continue;
352+
353+
// Only a single global actor can be applied to a given declaration.
354+
if (globalActorAttr) {
355+
decl->diagnose(
356+
diag::multiple_global_actors, globalActorNominal->getName(),
357+
nominal->getName());
358+
continue;
359+
}
360+
361+
globalActorAttr = const_cast<CustomAttr *>(attr);
362+
globalActorNominal = nominal;
363+
}
364+
365+
if (!globalActorAttr)
366+
return nullptr;
367+
368+
// Check that a global actor attribute makes sense on this kind of
369+
// declaration.
370+
if (auto nominal = dyn_cast<NominalTypeDecl>(decl)) {
371+
// Nominal types are okay...
372+
if (auto classDecl = dyn_cast<ClassDecl>(nominal)){
373+
if (classDecl->isActor()) {
374+
// ... except for actor classes.
375+
nominal->diagnose(diag::global_actor_on_actor_class, nominal->getName())
376+
.highlight(globalActorAttr->getRangeWithAt());
377+
return nullptr;
378+
}
379+
}
380+
} else if (auto storage = dyn_cast<AbstractStorageDecl>(decl)) {
381+
// Subscripts and properties are fine...
382+
if (auto var = dyn_cast<VarDecl>(storage)) {
383+
if (var->getDeclContext()->isLocalContext()) {
384+
var->diagnose(diag::global_actor_on_local_variable, var->getName())
385+
.highlight(globalActorAttr->getRangeWithAt());
386+
return nullptr;
387+
}
388+
}
389+
} else if (isa<ExtensionDecl>(decl)) {
390+
// Extensions are okay.
391+
} else if (isa<ConstructorDecl>(decl) || isa<FuncDecl>(decl)) {
392+
// Functions are okay.
393+
} else {
394+
// Everything else is disallowed.
395+
decl->diagnose(diag::global_actor_disallowed, decl->getDescriptiveKind());
396+
return nullptr;
397+
}
398+
399+
return globalActorAttr;
400+
}
401+
330402
namespace {
331403

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

test/attr/global_actor.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import _Concurrency
55

66
actor class SomeActor { }
77

8+
// -----------------------------------------------------------------------
9+
// @globalActor attribute itself.
10+
// -----------------------------------------------------------------------
11+
812
// Well-formed global actor.
913
@globalActor
1014
struct GA1 {
@@ -43,3 +47,37 @@ extension GA6 where T: Equatable {
4347
class GA7 { // expected-error{{global actor 'GA7' requires a static property 'shared' that produces an actor instance}}
4448
static let shared = 5 // expected-note{{'shared' property type 'Int' does not conform to the 'Actor' protocol}}
4549
}
50+
51+
// -----------------------------------------------------------------------
52+
// Applying global actors to entities.
53+
// -----------------------------------------------------------------------
54+
@globalActor
55+
struct OtherGlobalActor {
56+
static let shared = SomeActor()
57+
}
58+
59+
@GA1 func f() {
60+
@GA1 let x = 17 // expected-error{{local variable 'x' cannot have a global actor}}
61+
_ = x
62+
}
63+
64+
@GA1 struct X { }
65+
66+
struct Y {
67+
@GA1 subscript(i: Int) -> Int { i }
68+
}
69+
70+
@GA1 extension Y { }
71+
72+
@GA1 func g() { }
73+
74+
class SomeClass {
75+
@GA1 init() { }
76+
@GA1 deinit { } // expected-error{{deinitializer cannot have a global actor}}
77+
}
78+
79+
@GA1 typealias Integer = Int // expected-error{{type alias cannot have a global actor}}
80+
81+
@GA1 actor class ActorInTooManyPlaces { } // expected-error{{actor class 'ActorInTooManyPlaces' cannot have a global actor}}
82+
83+
@GA1 @OtherGlobalActor func twoGlobalActors() { } // expected-error{{declaration can not have multiple global actor attributes ('OtherGlobalActor' and 'GA1')}}

0 commit comments

Comments
 (0)