Skip to content

Commit 80b6fbd

Browse files
authored
Merge pull request #38186 from DougGregor/actors-and-global-actors
Semantic tweaks to (global) actors to bring them in line with the accepted proposals
2 parents 87cde88 + e595b4f commit 80b6fbd

24 files changed

+138
-55
lines changed

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,44 @@ _**Note:** This is in reverse chronological order, so newer entries are added to
66
Swift 5.5
77
---------
88

9+
* [SE-0313][]:
10+
11+
A type can be defined as a global actor. Global actors extend the notion
12+
of actor isolation outside of a single actor type, so that global state
13+
(and the functions that access it) can benefit from actor isolation,
14+
even if the state and functions are scattered across many different
15+
types, functions and modules. Global actors make it possible to safely
16+
work with global variables in a concurrent program, as well as modeling
17+
other global program constraints such as code that must only execute on
18+
the "main thread" or "UI thread". A new global actor can be defined with
19+
the `globalActor` attribute:
20+
21+
```swift
22+
@globalActor
23+
struct DatabaseActor {
24+
actor ActorType { }
25+
26+
static let shared: ActorType = ActorType()
27+
}
28+
```
29+
30+
Global actor types can be used as custom attributes on various declarations,
31+
which ensures that those declarations are only accessed on the actor described
32+
by the global actor's `shared` instance. For example:
33+
34+
```swift
35+
@DatabaseActor func queryDB(query: Query) throws -> QueryResult
36+
37+
func runQuery(queryString: String) async throws -> QueryResult {
38+
let query = try Query(parsing: queryString)
39+
return try await queryDB(query: query) // 'await' because this implicitly hops to DatabaseActor.shared
40+
}
41+
```
42+
43+
The concurrency library defines one global actor, `MainActor`, which
44+
represents the main thread of execution. It should be used for any code that
45+
must execute on the main thread, e.g., for updating UI.
46+
947
* [SE-0313][]:
1048

1149
Declarations inside an actor that would normally be actor-isolated can
@@ -8525,6 +8563,7 @@ Swift 1.0
85258563
[SE-0306]: <https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md>
85268564
[SE-0310]: <https://github.com/apple/swift-evolution/blob/main/proposals/0310-effectful-readonly-properties.md>
85278565
[SE-0313]: <https://github.com/apple/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md>
8566+
[SE-0316]: <https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md>
85288567

85298568
[SR-75]: <https://bugs.swift.org/browse/SR-75>
85308569
[SR-106]: <https://bugs.swift.org/browse/SR-106>

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(isolated, Isolated,
581581
103)
582582

583583
SIMPLE_DECL_ATTR(globalActor, GlobalActor,
584-
OnClass | OnStruct | OnEnum | ConcurrencyOnly |
584+
OnClass | OnStruct | OnEnum |
585585
ABIStableToAdd | ABIBreakingToRemove |
586586
APIStableToAdd | APIBreakingToRemove,
587587
104)

include/swift/AST/Decl.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2374,6 +2374,10 @@ class ValueDecl : public Decl {
23742374
/// Note whether this declaration is known to be exposed to Objective-C.
23752375
void setIsObjC(bool Value);
23762376

2377+
/// Is this declaration semantically 'final', meaning that the type checker
2378+
/// should treat it as final even if the ABI does not?
2379+
bool isSemanticallyFinal() const;
2380+
23772381
/// Is this declaration 'final'?
23782382
bool isFinal() const;
23792383

@@ -7556,7 +7560,8 @@ inline bool Decl::isPotentiallyOverridable() const {
75567560
isa<SubscriptDecl>(this) ||
75577561
isa<FuncDecl>(this) ||
75587562
isa<DestructorDecl>(this)) {
7559-
return getDeclContext()->getSelfClassDecl();
7563+
auto classDecl = getDeclContext()->getSelfClassDecl();
7564+
return classDecl && !classDecl->isActor();
75607565
} else {
75617566
return false;
75627567
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4608,6 +4608,8 @@ ERROR(global_actor_on_local_variable,none,
46084608
"local variable %0 cannot have a global actor", (DeclName))
46094609
ERROR(global_actor_non_unsafe_init,none,
46104610
"global actor attribute %0 argument can only be '(unsafe)'", (Type))
4611+
ERROR(global_actor_non_final_class,none,
4612+
"non-final class %0 cannot be a global actor", (DeclName))
46114613

46124614
ERROR(actor_isolation_multiple_attr,none,
46134615
"%0 %1 has multiple actor-isolation attributes ('%2' and '%3')",

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2828,7 +2828,7 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD,
28282828
if (Ctx.isSwiftVersionAtLeast(5)) {
28292829
if (wantDynamicSelf && CD->isConvenienceInit())
28302830
if (auto *classDecl = selfTy->getClassOrBoundGenericClass())
2831-
if (!classDecl->isFinal())
2831+
if (!classDecl->isSemanticallyFinal())
28322832
isDynamicSelf = true;
28332833
}
28342834
} else if (isa<DestructorDecl>(AFD)) {

lib/AST/Decl.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,6 +2868,23 @@ void ValueDecl::setIsObjC(bool value) {
28682868
LazySemanticInfo.isObjC = value;
28692869
}
28702870

2871+
bool ValueDecl::isSemanticallyFinal() const {
2872+
// Actor types are semantically final.
2873+
if (auto classDecl = dyn_cast<ClassDecl>(this)) {
2874+
if (classDecl->isActor())
2875+
return true;
2876+
}
2877+
2878+
// As are members of actor types.
2879+
if (auto classDecl = getDeclContext()->getSelfClassDecl()) {
2880+
if (classDecl->isActor())
2881+
return true;
2882+
}
2883+
2884+
// For everything else, the same as 'final'.
2885+
return isFinal();
2886+
}
2887+
28712888
bool ValueDecl::isFinal() const {
28722889
return evaluateOrDefault(getASTContext().evaluator,
28732890
IsFinalRequest { const_cast<ValueDecl *>(this) },
@@ -3212,13 +3229,13 @@ bool ValueDecl::shouldHideFromEditor() const {
32123229
static AccessLevel getMaximallyOpenAccessFor(const ValueDecl *decl) {
32133230
// Non-final classes are considered open to @testable importers.
32143231
if (auto cls = dyn_cast<ClassDecl>(decl)) {
3215-
if (!cls->isFinal())
3232+
if (!cls->isSemanticallyFinal())
32163233
return AccessLevel::Open;
32173234

32183235
// Non-final overridable class members are considered open to
32193236
// @testable importers.
32203237
} else if (decl->isPotentiallyOverridable()) {
3221-
if (!cast<ValueDecl>(decl)->isFinal())
3238+
if (!cast<ValueDecl>(decl)->isSemanticallyFinal())
32223239
return AccessLevel::Open;
32233240
}
32243241

@@ -3332,9 +3349,6 @@ AccessLevel ValueDecl::getFormalAccess() const {
33323349
}
33333350

33343351
bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const {
3335-
assert(isa<ClassDecl>(this) || isa<ConstructorDecl>(this) ||
3336-
isPotentiallyOverridable());
3337-
33383352
AccessLevel access =
33393353
getAdjustedFormalAccess(this, useDC,
33403354
/*treatUsableFromInlineAsPublic*/false);

lib/IDE/CodeCompletion.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5422,7 +5422,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
54225422
switch (Reason) {
54235423
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
54245424
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
5425-
if (!C->isFinal())
5425+
if (!C->isSemanticallyFinal())
54265426
needRequired = true;
54275427
break;
54285428
case DeclVisibilityKind::MemberOfSuper:
@@ -5458,7 +5458,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
54585458
if (D->shouldHideFromEditor())
54595459
return;
54605460

5461-
if (D->isFinal())
5461+
if (D->isSemanticallyFinal())
54625462
return;
54635463

54645464
bool hasIntroducer = hasFuncIntroducer ||

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7720,7 +7720,7 @@ static bool isNonFinalClass(Type type) {
77207720
type = dynamicSelf->getSelfType();
77217721

77227722
if (auto classDecl = type->getClassOrBoundGenericClass())
7723-
return !classDecl->isFinal();
7723+
return !classDecl->isSemanticallyFinal();
77247724

77257725
if (auto archetype = type->getAs<ArchetypeType>())
77267726
if (auto super = archetype->getSuperclass())

lib/Sema/CodeSynthesisDistributedActor.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,6 @@ createDistributedActor_init_local(ClassDecl *classDecl,
223223
initDecl->setSynthesized();
224224
initDecl->setBodySynthesizer(&createBody_DistributedActor_init_transport);
225225

226-
// This constructor is 'required', all distributed actors MUST invoke it.
227-
auto *reqAttr = new (C) RequiredAttr(/*IsImplicit*/true);
228-
initDecl->getAttrs().add(reqAttr);
229-
230226
auto *nonIsoAttr = new (C) NonisolatedAttr(/*IsImplicit*/true);
231227
initDecl->getAttrs().add(nonIsoAttr);
232228

@@ -350,9 +346,6 @@ createDistributedActor_init_resolve(ClassDecl *classDecl,
350346
initDecl->setSynthesized();
351347
initDecl->setBodySynthesizer(&createDistributedActor_init_resolve_body);
352348

353-
// This constructor is 'required', all distributed actors MUST have it.
354-
initDecl->getAttrs().add(new (C) RequiredAttr(/*IsImplicit*/true));
355-
356349
auto *nonIsoAttr = new (C) NonisolatedAttr(/*IsImplicit*/true);
357350
initDecl->getAttrs().add(nonIsoAttr);
358351

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1789,7 +1789,7 @@ static ValueDecl *deriveDecodable_init(DerivedConformance &derived) {
17891789
}
17901790

17911791
// This constructor should be marked as `required` for non-final classes.
1792-
if (classDecl && !classDecl->isFinal()) {
1792+
if (classDecl && !classDecl->isSemanticallyFinal()) {
17931793
auto *reqAttr = new (C) RequiredAttr(/*IsImplicit=*/true);
17941794
initDecl->getAttrs().add(reqAttr);
17951795
}

0 commit comments

Comments
 (0)