Skip to content

Commit 22a313e

Browse files
authored
Merge pull request #37917 from DougGregor/global-actor-protocol
[SE-0316] Introduce the GlobalActor protocol to describe global actors.
2 parents 5cd7d74 + 8a068e0 commit 22a313e

File tree

12 files changed

+128
-160
lines changed

12 files changed

+128
-160
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4579,22 +4579,6 @@ ERROR(concurrency_lib_missing,none,
45794579
ERROR(async_main_no_concurrency,none,
45804580
"'_Concurrency' module not imported, required for async main", ())
45814581

4582-
ERROR(global_actor_missing_shared,none,
4583-
"global actor %0 requires a static property 'shared' that produces an "
4584-
"actor instance", (Identifier))
4585-
NOTE(global_actor_shared_not_static,none,
4586-
"'shared' property in global actor is not 'static'", ())
4587-
NOTE(global_actor_shared_inaccessible,none,
4588-
"'shared' property has more restrictive access (%0) than its global actor "
4589-
"(%1)",
4590-
(StringRef, StringRef))
4591-
NOTE(global_actor_shared_constrained_extension,none,
4592-
"'shared' property in global actor cannot be in a constrained extension",
4593-
())
4594-
NOTE(global_actor_shared_non_actor_type,none,
4595-
"'shared' property type %0 does not conform to the 'Actor' protocol",
4596-
(Type))
4597-
45984582
ERROR(multiple_global_actors,none,
45994583
"declaration can not have multiple global actor attributes (%0 and %1)",
46004584
(Identifier, Identifier))

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ IDENTIFIER(allKeys)
2828
IDENTIFIER(alloc)
2929
IDENTIFIER(allocWithZone)
3030
IDENTIFIER(allZeros)
31+
IDENTIFIER(ActorType)
3132
IDENTIFIER(Any)
3233
IDENTIFIER(ArrayLiteralElement)
3334
IDENTIFIER(atIndexedSubscript)

include/swift/AST/KnownProtocols.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ PROTOCOL(SIMDScalar)
7474
PROTOCOL(BinaryInteger)
7575
PROTOCOL(RangeReplaceableCollection)
7676
PROTOCOL(SerialExecutor)
77+
PROTOCOL(GlobalActor)
7778

7879
PROTOCOL_(BridgedNSError)
7980
PROTOCOL_(BridgedStoredNSError)

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
10151015
M = getLoadedModule(Id_Differentiation);
10161016
break;
10171017
case KnownProtocolKind::Actor:
1018+
case KnownProtocolKind::GlobalActor:
10181019
case KnownProtocolKind::AsyncSequence:
10191020
case KnownProtocolKind::AsyncIteratorProtocol:
10201021
case KnownProtocolKind::SerialExecutor:

lib/AST/ProtocolConformance.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,11 @@ void NominalTypeDecl::prepareConformanceTable() const {
12551255
if (classDecl->isDistributedActor())
12561256
addSynthesized(KnownProtocolKind::DistributedActor);
12571257
}
1258+
1259+
// Global actors conform to the GlobalActor protocol.
1260+
if (mutableThis->getAttrs().hasAttribute<GlobalActorAttr>()) {
1261+
addSynthesized(KnownProtocolKind::GlobalActor);
1262+
}
12581263
}
12591264

12601265
bool NominalTypeDecl::lookupConformance(

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5170,6 +5170,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
51705170
case KnownProtocolKind::Sendable:
51715171
case KnownProtocolKind::UnsafeSendable:
51725172
case KnownProtocolKind::RangeReplaceableCollection:
5173+
case KnownProtocolKind::GlobalActor:
51735174
return SpecialProtocol::None;
51745175
}
51755176

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 2 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,6 @@ bool IsDefaultActorRequest::evaluate(
127127
return true;
128128
}
129129

130-
static bool isDeclNotAsAccessibleAsParent(ValueDecl *decl,
131-
NominalTypeDecl *parent) {
132-
return decl->getFormalAccess() <
133-
std::min(parent->getFormalAccess(), AccessLevel::Public);
134-
}
135-
136130
VarDecl *GlobalActorInstanceRequest::evaluate(
137131
Evaluator &evaluator, NominalTypeDecl *nominal) const {
138132
auto globalActorAttr = nominal->getAttrs().getAttribute<GlobalActorAttr>();
@@ -147,106 +141,18 @@ VarDecl *GlobalActorInstanceRequest::evaluate(
147141
return nullptr;
148142
}
149143

150-
auto *module = nominal->getParentModule();
151-
152144
// Global actors have a static property "shared" that provides an actor
153145
// instance. The value must
154146
SmallVector<ValueDecl *, 4> decls;
155147
nominal->lookupQualified(
156148
nominal, DeclNameRef(ctx.Id_shared), NL_QualifiedDefault, decls);
157-
VarDecl *sharedVar = nullptr;
158-
llvm::TinyPtrVector<VarDecl *> candidates;
159149
for (auto decl : decls) {
160150
auto var = dyn_cast<VarDecl>(decl);
161151
if (!var)
162152
continue;
163153

164-
auto varDC = var->getDeclContext();
165-
if (var->isStatic() &&
166-
!isDeclNotAsAccessibleAsParent(var, nominal) &&
167-
!(isa<ExtensionDecl>(varDC) &&
168-
cast<ExtensionDecl>(varDC)->isConstrainedExtension()) &&
169-
TypeChecker::conformsToProtocol(
170-
varDC->mapTypeIntoContext(var->getValueInterfaceType()),
171-
actorProto, module)) {
172-
sharedVar = var;
173-
break;
174-
}
175-
176-
candidates.push_back(var);
177-
}
178-
179-
// If we found a suitable candidate, we're done.
180-
if (sharedVar)
181-
return sharedVar;
182-
183-
// Complain about the lack of a suitable 'shared' property.
184-
{
185-
auto primaryDiag = nominal->diagnose(
186-
diag::global_actor_missing_shared, nominal->getName());
187-
188-
// If there were no candidates, provide a Fix-It with a prototype.
189-
if (candidates.empty() && nominal->getBraces().Start.isValid()) {
190-
// Figure out the indentation we need.
191-
SourceLoc sharedInsertionLoc = Lexer::getLocForEndOfToken(
192-
ctx.SourceMgr, nominal->getBraces().Start);
193-
194-
StringRef extraIndent;
195-
StringRef currentIndent = Lexer::getIndentationForLine(
196-
ctx.SourceMgr, sharedInsertionLoc, &extraIndent);
197-
std::string stubIndent = (currentIndent + extraIndent).str();
198-
199-
// From the string to add the declaration.
200-
std::string sharedDeclString = "\n" + stubIndent;
201-
if (nominal->getFormalAccess() >= AccessLevel::Public)
202-
sharedDeclString += "public ";
203-
204-
sharedDeclString += "static let shared = <#actor instance#>";
205-
206-
primaryDiag.fixItInsert(sharedInsertionLoc, sharedDeclString);
207-
}
208-
}
209-
210-
// Remark about all of the candidates that failed (and why).
211-
for (auto candidate : candidates) {
212-
if (!candidate->isStatic()) {
213-
candidate->diagnose(diag::global_actor_shared_not_static)
214-
.fixItInsert(candidate->getAttributeInsertionLoc(true), "static ");
215-
continue;
216-
}
217-
218-
if (isDeclNotAsAccessibleAsParent(candidate, nominal)) {
219-
AccessLevel needAccessLevel = std::min(
220-
nominal->getFormalAccess(), AccessLevel::Public);
221-
auto diag = candidate->diagnose(
222-
diag::global_actor_shared_inaccessible,
223-
getAccessLevelSpelling(candidate->getFormalAccess()),
224-
getAccessLevelSpelling(needAccessLevel));
225-
if (auto attr = candidate->getAttrs().getAttribute<AccessControlAttr>()) {
226-
if (needAccessLevel == AccessLevel::Internal) {
227-
diag.fixItRemove(attr->getRange());
228-
} else {
229-
diag.fixItReplace(
230-
attr->getRange(), getAccessLevelSpelling(needAccessLevel));
231-
}
232-
} else {
233-
diag.fixItInsert(
234-
candidate->getAttributeInsertionLoc(true),
235-
getAccessLevelSpelling(needAccessLevel));
236-
}
237-
continue;
238-
}
239-
240-
if (auto ext = dyn_cast<ExtensionDecl>(candidate->getDeclContext())) {
241-
if (ext->isConstrainedExtension()) {
242-
candidate->diagnose(diag::global_actor_shared_constrained_extension);
243-
continue;
244-
}
245-
}
246-
247-
Type varType = candidate->getDeclContext()->mapTypeIntoContext(
248-
candidate->getValueInterfaceType());
249-
candidate->diagnose(diag::global_actor_shared_non_actor_type, varType);
154+
if (var->getDeclContext() == nominal && var->isStatic())
155+
return var;
250156
}
251157

252158
return nullptr;

stdlib/public/Concurrency/Actor.swift

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,6 @@ public func _defaultActorDestroy(_ actor: AnyObject)
5252
@usableFromInline
5353
internal func _enqueueOnMain(_ job: UnownedJob)
5454

55-
/// A singleton actor whose executor is equivalent to the main
56-
/// dispatch queue.
57-
@available(SwiftStdlib 5.5, *)
58-
@globalActor public final actor MainActor: SerialExecutor {
59-
public static let shared = MainActor()
60-
61-
@inlinable
62-
public nonisolated var unownedExecutor: UnownedSerialExecutor {
63-
return asUnownedSerialExecutor()
64-
}
65-
66-
@inlinable
67-
public nonisolated func asUnownedSerialExecutor() -> UnownedSerialExecutor {
68-
return UnownedSerialExecutor(ordinary: self)
69-
}
70-
71-
@inlinable
72-
public nonisolated func enqueue(_ job: UnownedJob) {
73-
_enqueueOnMain(job)
74-
}
75-
}
76-
7755
// Used by the concurrency runtime
7856
@available(SwiftStdlib 5.5, *)
7957
extension SerialExecutor {
@@ -82,18 +60,3 @@ extension SerialExecutor {
8260
return MainActor.shared.unownedExecutor
8361
}
8462
}
85-
86-
@available(SwiftStdlib 5.5, *)
87-
extension MainActor {
88-
/// Execute the given body closure on the main actor.
89-
public static func run<T>(
90-
resultType: T.Type = T.self,
91-
body: @MainActor @Sendable () throws -> T
92-
) async rethrows -> T {
93-
@MainActor func runOnMain(body: @MainActor @Sendable () throws -> T) async rethrows -> T {
94-
return try body()
95-
}
96-
97-
return try await runOnMain(body: body)
98-
}
99-
}

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
6060
AsyncThrowingFlatMapSequence.swift
6161
AsyncThrowingMapSequence.swift
6262
AsyncThrowingPrefixWhileSequence.swift
63+
GlobalActor.swift
64+
MainActor.swift
6365
PartialAsyncTask.swift
6466
SourceCompatibilityShims.swift
6567
Task.cpp
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Swift
14+
15+
/// A type that represents a globally-unique actor that can be used to isolate
16+
/// various declarations anywhere in the program.
17+
///
18+
/// A type that conforms to the `GlobalActor` protocol and is marked with the
19+
/// the `@globalActor` attribute can be used as a custom attribute. Such types
20+
/// are called global actor types, and can be applied to any declaration to
21+
/// specify that such types are isolated to that global actor type. When using
22+
/// such a declaration from another actor (or from nonisolated code),
23+
/// synchronization is performed through the \c shared actor instance to ensure
24+
/// mutually-exclusive access to the declaration.
25+
@available(SwiftStdlib 5.5, *)
26+
public protocol GlobalActor {
27+
/// The type of the shared actor instance that will be used to provide
28+
/// mutually-exclusive access to declarations annotated with the given global
29+
/// actor type.
30+
associatedtype ActorType: Actor
31+
32+
/// The shared actor instance that will be used to provide mutually-exclusive
33+
/// access to declarations annotated with the given global actor type.
34+
///
35+
/// The value of this property must always evaluate to the same actor
36+
/// instance.
37+
static var shared: ActorType { get }
38+
39+
/// The shared executor instance that will be used to provide
40+
/// mutually-exclusive access for the global actor.
41+
///
42+
/// The value of this property must be equivalent to `shared.unownedExecutor`.
43+
static var sharedUnownedExecutor: UnownedSerialExecutor { get }
44+
}
45+
46+
@available(SwiftStdlib 5.5, *)
47+
extension GlobalActor {
48+
public static var sharedUnownedExecutor: UnownedSerialExecutor {
49+
shared.unownedExecutor
50+
}
51+
}
52+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Swift
14+
15+
/// A singleton actor whose executor is equivalent to the main
16+
/// dispatch queue.
17+
@available(SwiftStdlib 5.5, *)
18+
@globalActor public final actor MainActor: SerialExecutor, GlobalActor {
19+
public static let shared = MainActor()
20+
21+
@inlinable
22+
public nonisolated var unownedExecutor: UnownedSerialExecutor {
23+
return asUnownedSerialExecutor()
24+
}
25+
26+
@inlinable
27+
public nonisolated func asUnownedSerialExecutor() -> UnownedSerialExecutor {
28+
return UnownedSerialExecutor(ordinary: self)
29+
}
30+
31+
@inlinable
32+
public nonisolated func enqueue(_ job: UnownedJob) {
33+
_enqueueOnMain(job)
34+
}
35+
}
36+
37+
@available(SwiftStdlib 5.5, *)
38+
extension MainActor {
39+
/// Execute the given body closure on the main actor.
40+
public static func run<T>(
41+
resultType: T.Type = T.self,
42+
body: @MainActor @Sendable () throws -> T
43+
) async rethrows -> T {
44+
@MainActor func runOnMain(body: @MainActor @Sendable () throws -> T) async rethrows -> T {
45+
return try body()
46+
}
47+
48+
return try await runOnMain(body: body)
49+
}
50+
}

test/attr/global_actor.swift

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,37 @@ struct GenericGlobalActor<T> {
2020

2121
// Ill-formed global actors.
2222
@globalActor
23-
open class GA2 { // expected-error{{global actor 'GA2' requires a static property 'shared' that produces an actor instance}}{{17-17=\n public static let shared = <#actor instance#>}}
23+
open class GA2 { // expected-error{{type 'GA2' does not conform to protocol 'GlobalActor'}}
2424
}
2525

2626
@globalActor
27-
struct GA3 { // expected-error{{global actor 'GA3' requires a static property 'shared' that produces an actor instance}}
28-
let shared = SomeActor() // expected-note{{'shared' property in global actor is not 'static'}}{{3-3=static }}
27+
struct GA3 { // expected-error{{type 'GA3' does not conform to protocol 'GlobalActor'}}
28+
let shared = SomeActor()
2929
}
3030

3131
@globalActor
32-
struct GA4 { // expected-error{{global actor 'GA4' requires a static property 'shared' that produces an actor instance}}
33-
private static let shared = SomeActor() // expected-note{{'shared' property has more restrictive access (private) than its global actor (internal)}}{{3-11=}}
32+
struct GA4 {
33+
private static let shared = SomeActor() // expected-error{{property 'shared' must be as accessible as its enclosing type because it matches a requirement in protocol 'GlobalActor'}}
34+
// expected-note@-1{{mark the static property as 'internal' to satisfy the requirement}}
3435
}
3536

3637
@globalActor
37-
open class GA5 { // expected-error{{global actor 'GA5' requires a static property 'shared' that produces an actor instance}}
38-
static let shared = SomeActor() // expected-note{{'shared' property has more restrictive access (internal) than its global actor (public)}}{{3-3=public}}
38+
open class GA5 {
39+
static let shared = SomeActor() // expected-error{{property 'shared' must be declared public because it matches a requirement in public protocol 'GlobalActor'}}
40+
// expected-note@-1{{mark the static property as 'public' to satisfy the requirement}}
3941
}
4042

4143
@globalActor
42-
struct GA6<T> { // expected-error{{global actor 'GA6' requires a static property 'shared' that produces an actor instance}}
44+
struct GA6<T> { // expected-error{{type 'GA6<T>' does not conform to protocol 'GlobalActor'}}
4345
}
4446

4547
extension GA6 where T: Equatable {
46-
static var shared: SomeActor { SomeActor() } // expected-note{{'shared' property in global actor cannot be in a constrained extension}}
48+
static var shared: SomeActor { SomeActor() }
4749
}
4850

4951
@globalActor
50-
class GA7 { // expected-error{{global actor 'GA7' requires a static property 'shared' that produces an actor instance}}
51-
static let shared = 5 // expected-note{{'shared' property type 'Int' does not conform to the 'Actor' protocol}}
52+
class GA7 { // expected-error{{type 'GA7' does not conform to protocol 'GlobalActor'}}
53+
static let shared = 5 // expected-note{{candidate would match and infer 'ActorType' = 'Int' if 'Int' conformed to 'Actor'}}
5254
}
5355

5456
// -----------------------------------------------------------------------

0 commit comments

Comments
 (0)