Skip to content

Commit fdba938

Browse files
committed
[SE-0316] Introduce the GlobalActor protocol to describe global actors.
Based on the discussion in the first review of the global actors proposal, introduce a `GlobalActor` protocol that describes types that can be global actors. Introduce this protocol, make `@globalActor` types implicitly conform to it, and remove all of the bespoke validation logic that was used to check the "shared" member. Addresses rdar://79339591 (cherry picked from commit 8a068e0)
1 parent 7a8dfa9 commit fdba938

File tree

12 files changed

+128
-158
lines changed

12 files changed

+128
-158
lines changed

include/swift/AST/DiagnosticsSema.def

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

4539-
ERROR(global_actor_missing_shared,none,
4540-
"global actor %0 requires a static property 'shared' that produces an "
4541-
"actor instance", (Identifier))
4542-
NOTE(global_actor_shared_not_static,none,
4543-
"'shared' property in global actor is not 'static'", ())
4544-
NOTE(global_actor_shared_inaccessible,none,
4545-
"'shared' property has more restrictive access (%0) than its global actor "
4546-
"(%1)",
4547-
(StringRef, StringRef))
4548-
NOTE(global_actor_shared_constrained_extension,none,
4549-
"'shared' property in global actor cannot be in a constrained extension",
4550-
())
4551-
NOTE(global_actor_shared_non_actor_type,none,
4552-
"'shared' property type %0 does not conform to the 'Actor' protocol",
4553-
(Type))
4554-
45554539
ERROR(multiple_global_actors,none,
45564540
"declaration can not have multiple global actor attributes (%0 and %1)",
45574541
(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
@@ -72,6 +72,7 @@ PROTOCOL(CaseIterable)
7272
PROTOCOL(SIMDScalar)
7373
PROTOCOL(BinaryInteger)
7474
PROTOCOL(SerialExecutor)
75+
PROTOCOL(GlobalActor)
7576

7677
PROTOCOL_(BridgedNSError)
7778
PROTOCOL_(BridgedStoredNSError)

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
977977
M = getLoadedModule(Id_Differentiation);
978978
break;
979979
case KnownProtocolKind::Actor:
980+
case KnownProtocolKind::GlobalActor:
980981
case KnownProtocolKind::AsyncSequence:
981982
case KnownProtocolKind::AsyncIteratorProtocol:
982983
case KnownProtocolKind::SerialExecutor:

lib/AST/ProtocolConformance.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,11 @@ void NominalTypeDecl::prepareConformanceTable() const {
12531253
if (classDecl->isActor())
12541254
addSynthesized(KnownProtocolKind::Actor);
12551255
}
1256+
1257+
// Global actors conform to the GlobalActor protocol.
1258+
if (mutableThis->getAttrs().hasAttribute<GlobalActorAttr>()) {
1259+
addSynthesized(KnownProtocolKind::GlobalActor);
1260+
}
12561261
}
12571262

12581263
bool NominalTypeDecl::lookupConformance(

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5166,6 +5166,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
51665166
case KnownProtocolKind::SerialExecutor:
51675167
case KnownProtocolKind::Sendable:
51685168
case KnownProtocolKind::UnsafeSendable:
5169+
case KnownProtocolKind::GlobalActor:
51695170
return SpecialProtocol::None;
51705171
}
51715172

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 2 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,6 @@ bool IsDefaultActorRequest::evaluate(
298298
return true;
299299
}
300300

301-
static bool isDeclNotAsAccessibleAsParent(ValueDecl *decl,
302-
NominalTypeDecl *parent) {
303-
return decl->getFormalAccess() <
304-
std::min(parent->getFormalAccess(), AccessLevel::Public);
305-
}
306-
307301
VarDecl *GlobalActorInstanceRequest::evaluate(
308302
Evaluator &evaluator, NominalTypeDecl *nominal) const {
309303
auto globalActorAttr = nominal->getAttrs().getAttribute<GlobalActorAttr>();
@@ -323,99 +317,13 @@ VarDecl *GlobalActorInstanceRequest::evaluate(
323317
SmallVector<ValueDecl *, 4> decls;
324318
nominal->lookupQualified(
325319
nominal, DeclNameRef(ctx.Id_shared), NL_QualifiedDefault, decls);
326-
VarDecl *sharedVar = nullptr;
327-
llvm::TinyPtrVector<VarDecl *> candidates;
328320
for (auto decl : decls) {
329321
auto var = dyn_cast<VarDecl>(decl);
330322
if (!var)
331323
continue;
332324

333-
auto varDC = var->getDeclContext();
334-
if (var->isStatic() &&
335-
!isDeclNotAsAccessibleAsParent(var, nominal) &&
336-
!(isa<ExtensionDecl>(varDC) &&
337-
cast<ExtensionDecl>(varDC)->isConstrainedExtension()) &&
338-
TypeChecker::conformsToProtocol(
339-
varDC->mapTypeIntoContext(var->getValueInterfaceType()),
340-
actorProto, nominal)) {
341-
sharedVar = var;
342-
break;
343-
}
344-
345-
candidates.push_back(var);
346-
}
347-
348-
// If we found a suitable candidate, we're done.
349-
if (sharedVar)
350-
return sharedVar;
351-
352-
// Complain about the lack of a suitable 'shared' property.
353-
{
354-
auto primaryDiag = nominal->diagnose(
355-
diag::global_actor_missing_shared, nominal->getName());
356-
357-
// If there were no candidates, provide a Fix-It with a prototype.
358-
if (candidates.empty() && nominal->getBraces().Start.isValid()) {
359-
// Figure out the indentation we need.
360-
SourceLoc sharedInsertionLoc = Lexer::getLocForEndOfToken(
361-
ctx.SourceMgr, nominal->getBraces().Start);
362-
363-
StringRef extraIndent;
364-
StringRef currentIndent = Lexer::getIndentationForLine(
365-
ctx.SourceMgr, sharedInsertionLoc, &extraIndent);
366-
std::string stubIndent = (currentIndent + extraIndent).str();
367-
368-
// From the string to add the declaration.
369-
std::string sharedDeclString = "\n" + stubIndent;
370-
if (nominal->getFormalAccess() >= AccessLevel::Public)
371-
sharedDeclString += "public ";
372-
373-
sharedDeclString += "static let shared = <#actor instance#>";
374-
375-
primaryDiag.fixItInsert(sharedInsertionLoc, sharedDeclString);
376-
}
377-
}
378-
379-
// Remark about all of the candidates that failed (and why).
380-
for (auto candidate : candidates) {
381-
if (!candidate->isStatic()) {
382-
candidate->diagnose(diag::global_actor_shared_not_static)
383-
.fixItInsert(candidate->getAttributeInsertionLoc(true), "static ");
384-
continue;
385-
}
386-
387-
if (isDeclNotAsAccessibleAsParent(candidate, nominal)) {
388-
AccessLevel needAccessLevel = std::min(
389-
nominal->getFormalAccess(), AccessLevel::Public);
390-
auto diag = candidate->diagnose(
391-
diag::global_actor_shared_inaccessible,
392-
getAccessLevelSpelling(candidate->getFormalAccess()),
393-
getAccessLevelSpelling(needAccessLevel));
394-
if (auto attr = candidate->getAttrs().getAttribute<AccessControlAttr>()) {
395-
if (needAccessLevel == AccessLevel::Internal) {
396-
diag.fixItRemove(attr->getRange());
397-
} else {
398-
diag.fixItReplace(
399-
attr->getRange(), getAccessLevelSpelling(needAccessLevel));
400-
}
401-
} else {
402-
diag.fixItInsert(
403-
candidate->getAttributeInsertionLoc(true),
404-
getAccessLevelSpelling(needAccessLevel));
405-
}
406-
continue;
407-
}
408-
409-
if (auto ext = dyn_cast<ExtensionDecl>(candidate->getDeclContext())) {
410-
if (ext->isConstrainedExtension()) {
411-
candidate->diagnose(diag::global_actor_shared_constrained_extension);
412-
continue;
413-
}
414-
}
415-
416-
Type varType = candidate->getDeclContext()->mapTypeIntoContext(
417-
candidate->getValueInterfaceType());
418-
candidate->diagnose(diag::global_actor_shared_non_actor_type, varType);
325+
if (var->getDeclContext() == nominal && var->isStatic())
326+
return var;
419327
}
420328

421329
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
@@ -59,6 +59,8 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
5959
AsyncThrowingFlatMapSequence.swift
6060
AsyncThrowingMapSequence.swift
6161
AsyncThrowingPrefixWhileSequence.swift
62+
GlobalActor.swift
63+
MainActor.swift
6264
PartialAsyncTask.swift
6365
SourceCompatibilityShims.swift
6466
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)