Skip to content

Commit 085a1a9

Browse files
committed
[Concurrency,Distributed] ban inheriting Actor and DistActor explicitly by class
1 parent 11e3a65 commit 085a1a9

File tree

16 files changed

+251
-56
lines changed

16 files changed

+251
-56
lines changed

docs/Diagnostics.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Clang also has a kind of diagnostic called a "remark", which represents informat
5151
- "...to silence this warning"
5252
- "...here" (for a purely locational note)
5353

54+
- If possible, it is best to include the name of the type or function that has the error, e.g. "non-actor type 'Nope' cannot ..." is better than "non-actor type cannot ...". It helps developers relate the error message to the specific type the error is about, even if the error would highlight the appropriate line / function in other ways.
5455

5556
### Locations and Highlights ###
5657

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4349,7 +4349,15 @@ ERROR(async_objc_dynamic_self,none,
43494349
"asynchronous method returning 'Self' cannot be '@objc'", ())
43504350

43514351
ERROR(actor_inheritance,none,
4352-
"actor types do not support inheritance", ())
4352+
"%select{actor|distributed actor}0 types do not support inheritance",
4353+
(bool))
4354+
4355+
ERROR(actor_protocol_illegal_inheritance,none,
4356+
"non-actor type %0 cannot conform to the 'Actor' protocol",
4357+
(DeclName))
4358+
ERROR(distributed_actor_protocol_illegal_inheritance,none,
4359+
"non-distributed actor type %0 cannot conform to the 'DistributedActor' protocol",
4360+
(DeclName))
43534361

43544362
// FIXME: This diagnostic was temporarily downgraded from an error because
43554363
// it spuriously triggers when building the Foundation module from its textual

include/swift/AST/TypeCheckRequests.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,7 @@ class IsDefaultActorRequest :
915915
bool isCached() const { return true; }
916916
};
917917

918-
/// Determine whether the given class is an distributed actor.
918+
/// Determine whether the given class is a distributed actor.
919919
class IsDistributedActorRequest :
920920
public SimpleRequest<IsDistributedActorRequest,
921921
bool(NominalTypeDecl *),

lib/AST/Decl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5167,7 +5167,8 @@ void ProtocolDecl::computeKnownProtocolKind() const {
51675167
if (module != module->getASTContext().getStdlibModule() &&
51685168
!module->getName().is("Foundation") &&
51695169
!module->getName().is("_Differentiation") &&
5170-
!module->getName().is("_Concurrency")) {
5170+
!module->getName().is("_Concurrency") &&
5171+
!module->getName().is("_Distributed")) {
51715172
const_cast<ProtocolDecl *>(this)->Bits.ProtocolDecl.KnownProtocol = 1;
51725173
return;
51735174
}

lib/AST/ProtocolConformance.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,10 +1250,10 @@ void NominalTypeDecl::prepareConformanceTable() const {
12501250

12511251
// Actor classes conform to the actor protocol.
12521252
if (auto classDecl = dyn_cast<ClassDecl>(mutableThis)) {
1253-
if (classDecl->isActor())
1254-
addSynthesized(KnownProtocolKind::Actor);
12551253
if (classDecl->isDistributedActor())
12561254
addSynthesized(KnownProtocolKind::DistributedActor);
1255+
else if (classDecl->isActor())
1256+
addSynthesized(KnownProtocolKind::Actor);
12571257
}
12581258

12591259
// Global actors conform to the GlobalActor protocol.

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ static void checkInheritanceClause(
169169
if (!inheritedTy || inheritedTy->hasError())
170170
continue;
171171

172+
173+
// if (inheritedTy->isActorType()) {
174+
// auto classDecl = dyn_cast<ClassDecl>(decl);
175+
// if(isa<ProtocolDecl>(decl) || (classDecl && !classDecl->isExplicitActor())) {
176+
// assert(false && "only explicit actor or protocol may conform to 'Actor' protocol");
177+
// }
178+
// }
179+
172180
// For generic parameters and associated types, the GSB checks constraints;
173181
// however, we still want to fire off the requests to produce diagnostics
174182
// in some circular validation cases.
@@ -2396,9 +2404,11 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
23962404
if (auto superclass = CD->getSuperclassDecl()) {
23972405
// Actors cannot have superclasses, nor can they be superclasses.
23982406
if (CD->isActor() && !superclass->isNSObject())
2399-
CD->diagnose(diag::actor_inheritance);
2407+
CD->diagnose(diag::actor_inheritance,
2408+
/*distributed=*/CD->isDistributedActor());
24002409
else if (superclass->isActor())
2401-
CD->diagnose(diag::actor_inheritance);
2410+
CD->diagnose(diag::actor_inheritance,
2411+
/*distributed=*/CD->isDistributedActor());
24022412
}
24032413

24042414
// Force lowering of stored properties.

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5851,6 +5851,31 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
58515851
}
58525852
} else if (proto->isSpecificProtocol(KnownProtocolKind::Sendable)) {
58535853
SendableConformance = conformance;
5854+
} else if (proto->isSpecificProtocol(KnownProtocolKind::DistributedActor)) {
5855+
if (auto classDecl = dyn_cast<ClassDecl>(nominal)) {
5856+
if (!classDecl->isDistributedActor()) {
5857+
if (classDecl->isActor()) {
5858+
dc->getSelfNominalTypeDecl()
5859+
->diagnose(diag::distributed_actor_protocol_illegal_inheritance,
5860+
dc->getSelfNominalTypeDecl()->getName())
5861+
.fixItInsert(classDecl->getStartLoc(), "distributed ");
5862+
} else {
5863+
dc->getSelfNominalTypeDecl()
5864+
->diagnose(diag::actor_protocol_illegal_inheritance,
5865+
dc->getSelfNominalTypeDecl()->getName())
5866+
.fixItReplace(nominal->getStartLoc(), "distributed actor");
5867+
}
5868+
}
5869+
}
5870+
} else if (proto->isSpecificProtocol(KnownProtocolKind::Actor)) {
5871+
if (auto classDecl = dyn_cast<ClassDecl>(nominal)) {
5872+
if (!classDecl->isExplicitActor()) {
5873+
dc->getSelfNominalTypeDecl()
5874+
->diagnose(diag::actor_protocol_illegal_inheritance,
5875+
dc->getSelfNominalTypeDecl()->getName())
5876+
.fixItReplace(nominal->getStartLoc(), "actor");
5877+
}
5878+
}
58545879
} else if (proto->isSpecificProtocol(
58555880
KnownProtocolKind::UnsafeSendable)) {
58565881
unsafeSendableConformance = conformance;

stdlib/public/Concurrency/MainActor.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import Swift
1515
/// A singleton actor whose executor is equivalent to the main
1616
/// dispatch queue.
1717
@available(SwiftStdlib 5.5, *)
18-
@globalActor public final actor MainActor: GlobalActor {
18+
@globalActor public final actor MainActor {
19+
//@globalActor public final actor MainActor: GlobalActor { // TODO: specialize this case?
1920
public static let shared = MainActor()
2021

2122
@inlinable
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020-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+
import _Concurrency
15+
16+
@available(SwiftStdlib 5.5, *)
17+
public protocol ActorTransport: Sendable {
18+
/// Resolve a local or remote actor address to a real actor instance, or throw if unable to.
19+
/// The returned value is either a local actor or proxy to a remote actor.
20+
func resolve<Act>(address: ActorAddress, as actorType: Act.Type)
21+
throws -> ActorResolved<Act> where Act: DistributedActor
22+
23+
/// Create an `ActorAddress` for the passed actor type.
24+
///
25+
/// This function is invoked by an distributed actor during its initialization,
26+
/// and the returned address value is stored along with it for the time of its
27+
/// lifetime.
28+
///
29+
/// The address MUST uniquely identify the actor, and allow resolving it.
30+
/// E.g. if an actor is created under address `addr1` then immediately invoking
31+
/// `transport.resolve(address: addr1, as: Greeter.self)` MUST return a reference
32+
/// to the same actor.
33+
func assignAddress<Act>(_ actorType: Act.Type) -> ActorAddress
34+
where Act: DistributedActor
35+
36+
func actorReady<Act>(_ actor: Act) where Act: DistributedActor
37+
38+
/// Called during actor deinit/destroy.
39+
func resignAddress(_ address: ActorAddress)
40+
41+
}
42+
43+
@available(SwiftStdlib 5.5, *)
44+
public enum ActorResolved<Act: DistributedActor> {
45+
case resolved(Act)
46+
case makeProxy
47+
}

stdlib/public/Distributed/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ set(swift_distributed_link_libraries
1515

1616
add_swift_target_library(swift_Distributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB
1717
AssertDistributed.swift
18+
ActorTransport.swift
1819
DistributedActor.swift
1920

2021
SWIFT_MODULE_DEPENDS_LINUX Glibc

stdlib/public/Distributed/DistributedActor.swift

Lines changed: 11 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@
1313
import Swift
1414
import _Concurrency
1515

16+
// ==== Any Actor -------------------------------------------------------------
17+
18+
/// Shared "base" protocol for both (local) `Actor` and (potentially remote)
19+
/// `DistributedActor`.
20+
///
21+
/// FIXME: !!! We'd need Actor to also conform to this, but don't want to add that conformance in _Concurrency yet.
22+
public protocol AnyActor: AnyObject {}
23+
24+
// ==== Distributed Actor -----------------------------------------------------
25+
1626
/// Common protocol to which all distributed actors conform implicitly.
1727
///
1828
/// It is not possible to conform to this protocol manually explicitly.
@@ -24,7 +34,7 @@ import _Concurrency
2434
/// which involves enqueuing new partial tasks to be executed at some
2535
/// point.
2636
@available(SwiftStdlib 5.5, *)
27-
public protocol DistributedActor: Actor, Codable {
37+
public protocol DistributedActor: AnyActor, Codable {
2838

2939
/// Creates new (local) distributed actor instance, bound to the passed transport.
3040
///
@@ -91,48 +101,6 @@ extension DistributedActor {
91101
try container.encode(self.actorAddress)
92102
}
93103
}
94-
/******************************************************************************/
95-
/***************************** Actor Transport ********************************/
96-
/******************************************************************************/
97-
98-
@available(SwiftStdlib 5.5, *)
99-
public protocol ActorTransport: Sendable {
100-
/// Resolve a local or remote actor address to a real actor instance, or throw if unable to.
101-
/// The returned value is either a local actor or proxy to a remote actor.
102-
func resolve<Act>(address: ActorAddress, as actorType: Act.Type)
103-
throws -> ActorResolved<Act> where Act: DistributedActor
104-
105-
/// Create an `ActorAddress` for the passed actor type.
106-
///
107-
/// This function is invoked by an distributed actor during its initialization,
108-
/// and the returned address value is stored along with it for the time of its
109-
/// lifetime.
110-
///
111-
/// The address MUST uniquely identify the actor, and allow resolving it.
112-
/// E.g. if an actor is created under address `addr1` then immediately invoking
113-
/// `transport.resolve(address: addr1, as: Greeter.self)` MUST return a reference
114-
/// to the same actor.
115-
func assignAddress<Act>(
116-
_ actorType: Act.Type
117-
) -> ActorAddress
118-
where Act: DistributedActor
119-
120-
func actorReady<Act>(
121-
_ actor: Act
122-
) where Act: DistributedActor
123-
124-
/// Called during actor deinit/destroy.
125-
func resignAddress(
126-
_ address: ActorAddress
127-
)
128-
129-
}
130-
131-
@available(SwiftStdlib 5.5, *)
132-
public enum ActorResolved<Act: DistributedActor> {
133-
case resolved(Act)
134-
case makeProxy
135-
}
136104

137105
/******************************************************************************/
138106
/***************************** Actor Address **********************************/

test/Concurrency/actor_isolation.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,20 @@ func outsideSomeClassWithInits() { // expected-note 3 {{add '@MainActor' to make
752752
// ----------------------------------------------------------------------
753753
// Actor protocols.
754754
// ----------------------------------------------------------------------
755+
756+
@available(SwiftStdlib 5.5, *)
757+
actor A: Actor { // ok
758+
}
759+
760+
@available(SwiftStdlib 5.5, *)
761+
class C: Actor, UnsafeSendable {
762+
// expected-error@-1{{non-actor type 'C' cannot conform to the 'Actor' protocol}}
763+
// expected-warning@-2{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}}
764+
nonisolated var unownedExecutor: UnownedSerialExecutor {
765+
fatalError()
766+
}
767+
}
768+
755769
@available(SwiftStdlib 5.5, *)
756770
protocol P: Actor {
757771
func f()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-distributed
2+
// REQUIRES: concurrency
3+
// REQUIRES: distributed
4+
5+
import _Distributed
6+
7+
// ==== -----------------------------------------------------------------------
8+
9+
@available(SwiftStdlib 5.5, *)
10+
actor A: Actor {} // ok
11+
12+
@available(SwiftStdlib 5.5, *)
13+
class C: Actor, UnsafeSendable {
14+
// expected-error@-1{{non-actor type 'C' cannot conform to the 'Actor' protocol}} {{1-6=actor}}
15+
// expected-warning@-2{{'UnsafeSendable' is deprecated: Use @unchecked Sendable instead}}
16+
nonisolated var unownedExecutor: UnownedSerialExecutor {
17+
fatalError()
18+
}
19+
}
20+
21+
@available(SwiftStdlib 5.5, *)
22+
struct S: Actor {
23+
// expected-error@-1{{non-class type 'S' cannot conform to class protocol 'Actor'}}
24+
nonisolated var unownedExecutor: UnownedSerialExecutor {
25+
fatalError()
26+
}
27+
}
28+
29+
@available(SwiftStdlib 5.5, *)
30+
struct E: Actor {
31+
// expected-error@-1{{non-class type 'E' cannot conform to class protocol 'Actor'}}
32+
nonisolated var unownedExecutor: UnownedSerialExecutor {
33+
fatalError()
34+
}
35+
}
36+
37+
// ==== -----------------------------------------------------------------------
38+
39+
@available(SwiftStdlib 5.5, *)
40+
distributed actor DA: DistributedActor {} // ok
41+
42+
@available(SwiftStdlib 5.5, *)
43+
actor A2: DistributedActor {
44+
// expected-error@-1{{non-distributed actor type 'A2' cannot conform to the 'DistributedActor' protocol}} {{1-1=distributed }}
45+
nonisolated var actorAddress: ActorAddress {
46+
fatalError()
47+
}
48+
nonisolated var actorTransport: ActorTransport {
49+
fatalError()
50+
}
51+
52+
required init(transport: ActorTransport) {
53+
fatalError()
54+
}
55+
required init(resolve address: ActorAddress, using transport: ActorTransport) throws {
56+
fatalError()
57+
}
58+
}
59+
60+
@available(SwiftStdlib 5.5, *)
61+
class C2: DistributedActor {
62+
// expected-error@-1{{non-actor type 'C2' cannot conform to the 'Actor' protocol}}
63+
nonisolated var actorAddress: ActorAddress {
64+
fatalError()
65+
}
66+
nonisolated var actorTransport: ActorTransport {
67+
fatalError()
68+
}
69+
70+
required init(transport: ActorTransport) {
71+
fatalError()
72+
}
73+
required init(resolve address: ActorAddress, using transport: ActorTransport) throws {
74+
fatalError()
75+
}
76+
}
77+
78+
@available(SwiftStdlib 5.5, *)
79+
struct S2: DistributedActor {
80+
// expected-error@-1{{non-class type 'S2' cannot conform to class protocol 'DistributedActor'}}
81+
// expected-error@-2{{non-class type 'S2' cannot conform to class protocol 'AnyActor'}}
82+
}
83+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-distributed
2+
// REQUIRES: concurrency
3+
// REQUIRES: distributed
4+
5+
import _Distributed
6+
7+
@available(SwiftStdlib 5.5, *)
8+
extension Actor {
9+
func f() -> String { "Life is Study!" }
10+
}
11+
12+
@available(SwiftStdlib 5.5, *)
13+
func g<A: Actor>(a: A) async { // expected-note{{where 'A' = 'MA'}}
14+
print(await a.f())
15+
}
16+
17+
@available(SwiftStdlib 5.5, *)
18+
distributed actor MA {
19+
}
20+
21+
@available(SwiftStdlib 5.5, *)
22+
func h(ma: MA) async {
23+
// this would have been a bug, a non distributed function might have been called here,
24+
// so we must not allow for it, because if the actor was remote calling a non-distributed func
25+
// would result in a hard crash (as there is no local actor to safely call the function on).
26+
await g(a: ma) // expected-error{{global function 'g(a:)' requires that 'MA' conform to 'Actor'}}
27+
}

0 commit comments

Comments
 (0)