Skip to content

Commit 1292972

Browse files
authored
Merge pull request swiftlang#38213 from DougGregor/actor-inheritance
Finish actor inheritance and global actors semantics
2 parents a12f1b3 + ea9d390 commit 1292972

27 files changed

+330
-81
lines changed

CHANGELOG.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,47 @@ CHANGELOG
2929
Swift 5.5
3030
---------
3131

32+
* [SE-0316][]:
33+
34+
A type can be defined as a global actor. Global actors extend the notion
35+
of actor isolation outside of a single actor type, so that global state
36+
(and the functions that access it) can benefit from actor isolation,
37+
even if the state and functions are scattered across many different
38+
types, functions and modules. Global actors make it possible to safely
39+
work with global variables in a concurrent program, as well as modeling
40+
other global program constraints such as code that must only execute on
41+
the "main thread" or "UI thread". A new global actor can be defined with
42+
the `globalActor` attribute:
43+
44+
```swift
45+
@globalActor
46+
struct DatabaseActor {
47+
actor ActorType { }
48+
49+
static let shared: ActorType = ActorType()
50+
}
51+
```
52+
53+
Global actor types can be used as custom attributes on various declarations,
54+
which ensures that those declarations are only accessed on the actor described
55+
by the global actor's `shared` instance. For example:
56+
57+
```swift
58+
@DatabaseActor func queryDB(query: Query) throws -> QueryResult
59+
60+
func runQuery(queryString: String) async throws -> QueryResult {
61+
let query = try Query(parsing: queryString)
62+
return try await queryDB(query: query) // 'await' because this implicitly hops to DatabaseActor.shared
63+
}
64+
```
65+
66+
The concurrency library defines one global actor, `MainActor`, which
67+
represents the main thread of execution. It should be used for any code that
68+
must execute on the main thread, e.g., for updating UI.
69+
3270
* [SE-0313][]:
3371

34-
Declarations inside an actor that would normally by actor-isolated can
72+
Declarations inside an actor that would normally be actor-isolated can
3573
explicitly become non-isolated using the `nonisolated` keyword. Non-isolated
3674
declarations can be used to conform to synchronous protocol requirements:
3775

@@ -8535,6 +8573,7 @@ Swift 1.0
85358573
[SE-0306]: <https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md>
85368574
[SE-0310]: <https://github.com/apple/swift-evolution/blob/main/proposals/0310-effectful-readonly-properties.md>
85378575
[SE-0313]: <https://github.com/apple/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md>
8576+
[SE-0316]: <https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md>
85388577

85398578
[SR-75]: <https://bugs.swift.org/browse/SR-75>
85408579
[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
@@ -585,7 +585,7 @@ DECL_ATTR(actorIndependent, ActorIndependent,
585585
103)
586586

587587
SIMPLE_DECL_ATTR(globalActor, GlobalActor,
588-
OnClass | OnStruct | OnEnum | ConcurrencyOnly |
588+
OnClass | OnStruct | OnEnum |
589589
ABIStableToAdd | ABIBreakingToRemove |
590590
APIStableToAdd | APIBreakingToRemove,
591591
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

@@ -7528,7 +7532,8 @@ inline bool Decl::isPotentiallyOverridable() const {
75287532
isa<SubscriptDecl>(this) ||
75297533
isa<FuncDecl>(this) ||
75307534
isa<DestructorDecl>(this)) {
7531-
return getDeclContext()->getSelfClassDecl();
7535+
auto classDecl = getDeclContext()->getSelfClassDecl();
7536+
return classDecl && !classDecl->isActor();
75327537
} else {
75337538
return false;
75347539
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4545,13 +4545,18 @@ ERROR(global_actor_on_local_variable,none,
45454545
"local variable %0 cannot have a global actor", (DeclName))
45464546
ERROR(global_actor_non_unsafe_init,none,
45474547
"global actor attribute %0 argument can only be '(unsafe)'", (Type))
4548+
ERROR(global_actor_non_final_class,none,
4549+
"non-final class %0 cannot be a global actor", (DeclName))
45484550

45494551
ERROR(actor_isolation_multiple_attr,none,
45504552
"%0 %1 has multiple actor-isolation attributes ('%2' and '%3')",
45514553
(DescriptiveDeclKind, DeclName, StringRef, StringRef))
45524554
ERROR(actor_isolation_override_mismatch,none,
45534555
"%0 %1 %2 has different actor isolation from %3 overridden declaration",
45544556
(ActorIsolation, DescriptiveDeclKind, DeclName, ActorIsolation))
4557+
ERROR(actor_isolation_superclass_mismatch,none,
4558+
"%0 class %1 has different actor isolation from %2 superclass %3",
4559+
(ActorIsolation, DeclName, ActorIsolation, DeclName))
45554560

45564561
//------------------------------------------------------------------------------
45574562
// MARK: Type Check Types

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2725,7 +2725,7 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD,
27252725
if (Ctx.isSwiftVersionAtLeast(5)) {
27262726
if (wantDynamicSelf && CD->isConvenienceInit())
27272727
if (auto *classDecl = selfTy->getClassOrBoundGenericClass())
2728-
if (!classDecl->isFinal())
2728+
if (!classDecl->isSemanticallyFinal())
27292729
isDynamicSelf = true;
27302730
}
27312731
} 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) },
@@ -3208,13 +3225,13 @@ bool ValueDecl::shouldHideFromEditor() const {
32083225
static AccessLevel getMaximallyOpenAccessFor(const ValueDecl *decl) {
32093226
// Non-final classes are considered open to @testable importers.
32103227
if (auto cls = dyn_cast<ClassDecl>(decl)) {
3211-
if (!cls->isFinal())
3228+
if (!cls->isSemanticallyFinal())
32123229
return AccessLevel::Open;
32133230

32143231
// Non-final overridable class members are considered open to
32153232
// @testable importers.
32163233
} else if (decl->isPotentiallyOverridable()) {
3217-
if (!cast<ValueDecl>(decl)->isFinal())
3234+
if (!cast<ValueDecl>(decl)->isSemanticallyFinal())
32183235
return AccessLevel::Open;
32193236
}
32203237

@@ -3328,9 +3345,6 @@ AccessLevel ValueDecl::getFormalAccess() const {
33283345
}
33293346

33303347
bool ValueDecl::hasOpenAccess(const DeclContext *useDC) const {
3331-
assert(isa<ClassDecl>(this) || isa<ConstructorDecl>(this) ||
3332-
isPotentiallyOverridable());
3333-
33343348
AccessLevel access =
33353349
getAdjustedFormalAccess(this, useDC,
33363350
/*treatUsableFromInlineAsPublic*/false);

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,8 @@ class InheritedProtocolCollector {
358358
/// For each type in \p directlyInherited, classify the protocols it refers to
359359
/// as included for printing or not, and record them in the appropriate
360360
/// vectors.
361-
void recordProtocols(ArrayRef<TypeLoc> directlyInherited, const Decl *D) {
361+
void recordProtocols(ArrayRef<TypeLoc> directlyInherited, const Decl *D,
362+
bool skipSynthesized = false) {
362363
Optional<AvailableAttrList> availableAttrs;
363364

364365
for (TypeLoc inherited : directlyInherited) {
@@ -379,6 +380,9 @@ class InheritedProtocolCollector {
379380
// any of those besides 'AnyObject'.
380381
}
381382

383+
if (skipSynthesized)
384+
return;
385+
382386
// Check for synthesized protocols, like Hashable on enums.
383387
if (auto *nominal = dyn_cast<NominalTypeDecl>(D)) {
384388
SmallVector<ProtocolConformance *, 4> localConformances =
@@ -456,7 +460,8 @@ class InheritedProtocolCollector {
456460
if (auto *CD = dyn_cast<ClassDecl>(D)) {
457461
for (auto *SD = CD->getSuperclassDecl(); SD;
458462
SD = SD->getSuperclassDecl()) {
459-
map[nominal].recordProtocols(SD->getInherited(), SD);
463+
map[nominal].recordProtocols(
464+
SD->getInherited(), SD, /*skipSynthesized=*/true);
460465
for (auto *Ext: SD->getExtensions()) {
461466
if (shouldInclude(Ext)) {
462467
map[nominal].recordProtocols(Ext->getInherited(), Ext);

lib/IDE/CodeCompletion.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5410,7 +5410,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
54105410
switch (Reason) {
54115411
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
54125412
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
5413-
if (!C->isFinal())
5413+
if (!C->isSemanticallyFinal())
54145414
needRequired = true;
54155415
break;
54165416
case DeclVisibilityKind::MemberOfSuper:
@@ -5446,7 +5446,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
54465446
if (D->shouldHideFromEditor())
54475447
return;
54485448

5449-
if (D->isFinal())
5449+
if (D->isSemanticallyFinal())
54505450
return;
54515451

54525452
bool hasIntroducer = hasFuncIntroducer ||

lib/Sema/CSSimplify.cpp

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

77047704
if (auto classDecl = type->getClassOrBoundGenericClass())
7705-
return !classDecl->isFinal();
7705+
return !classDecl->isSemanticallyFinal();
77067706

77077707
if (auto archetype = type->getAs<ArchetypeType>())
77087708
if (auto super = archetype->getSuperclass())

lib/Sema/DerivedConformanceCodable.cpp

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

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

0 commit comments

Comments
 (0)