Skip to content

Commit fbc82b4

Browse files
committed
[Distributed] Only synthesize Codable for DA where the ID is Codable
1 parent 8b03686 commit fbc82b4

13 files changed

+208
-16
lines changed

include/swift/AST/DistributedDecl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ Type getAssociatedTypeOfDistributedSystemOfActor(DeclContext *actorOrExtension,
3737
/// Find the concrete invocation decoder associated with the given actor.
3838
NominalTypeDecl *getDistributedActorInvocationDecoder(NominalTypeDecl *);
3939

40+
/// Determine if this distributed actor can synthesize a `Codable` conformance.
41+
/// This is based on the actor's `ID` being `Codable`.
42+
///
43+
/// It is possible for the `ID` to be `Codable` but the
44+
/// `SerializationRequirement` used by the actor (and its actor system to not
45+
/// be `Codable`). In such situation the conformance is synthesized, however
46+
/// the user may need to provide an explicit conformance to the
47+
/// `SerializationRequirement` if they wanted to pass the actor to distributed
48+
/// methods.
49+
bool canSynthesizeDistributedActorCodableConformance(NominalTypeDecl *actor);
50+
4051
/// Find `decodeNextArgument<T>(type: T.Type) -> T` method associated with
4152
/// invocation decoder of the given distributed actor.
4253
FuncDecl *getDistributedActorArgumentDecodingMethod(NominalTypeDecl *);

include/swift/AST/TypeCheckRequests.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,24 @@ class IsDistributedActorRequest :
10541054
bool isCached() const { return true; }
10551055
};
10561056

1057+
/// Determine whether the given class is a distributed actor.
1058+
class CanSynthesizeDistributedActorCodableConformanceRequest :
1059+
public SimpleRequest<CanSynthesizeDistributedActorCodableConformanceRequest,
1060+
bool(NominalTypeDecl *),
1061+
RequestFlags::Cached> {
1062+
public:
1063+
using SimpleRequest::SimpleRequest;
1064+
1065+
private:
1066+
friend SimpleRequest;
1067+
1068+
bool evaluate(Evaluator &evaluator, NominalTypeDecl *nominal) const;
1069+
1070+
public:
1071+
// Caching
1072+
bool isCached() const { return true; }
1073+
};
1074+
10571075
/// Retrieve the implicit conformance for the given distributed actor type to
10581076
/// the Codable protocol protocol.
10591077
///

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ SWIFT_REQUEST(TypeChecker, IsDistributedActorRequest, bool(NominalTypeDecl *),
120120
SWIFT_REQUEST(TypeChecker, GetDistributedActorImplicitCodableRequest,
121121
NormalProtocolConformance *(NominalTypeDecl *, KnownProtocolKind),
122122
Cached, NoLocationInfo)
123+
SWIFT_REQUEST(TypeChecker, CanSynthesizeDistributedActorCodableConformanceRequest,
124+
bool (NominalTypeDecl *),
125+
Cached, NoLocationInfo)
123126
SWIFT_REQUEST(TypeChecker, GetDistributedActorSystemRemoteCallFunctionRequest,
124127
AbstractFunctionDecl *(NominalTypeDecl *, bool),
125128
Cached, NoLocationInfo)

lib/AST/ConformanceLookup.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "swift/AST/Module.h"
2727
#include "swift/AST/ASTContext.h"
2828
#include "swift/AST/Builtins.h"
29+
#include "swift/AST/DistributedDecl.h"
2930
#include "swift/AST/DiagnosticsSema.h"
3031
#include "swift/AST/ExistentialLayout.h"
3132
#include "swift/AST/GenericEnvironment.h"
@@ -660,7 +661,8 @@ LookupConformanceInModuleRequest::evaluate(
660661
}
661662
} else if (protocol->isSpecificProtocol(KnownProtocolKind::Encodable) ||
662663
protocol->isSpecificProtocol(KnownProtocolKind::Decodable)) {
663-
if (nominal->isDistributedActor()) {
664+
// if (nominal->isDistributedActor()) {
665+
if (canSynthesizeDistributedActorCodableConformance(nominal)) {
664666
auto protoKind =
665667
protocol->isSpecificProtocol(KnownProtocolKind::Encodable)
666668
? KnownProtocolKind::Encodable

lib/AST/DistributedDecl.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@
6060

6161
using namespace swift;
6262

63+
/******************************************************************************/
64+
/************* Implicit Distributed Actor Codable Conformance *****************/
65+
/******************************************************************************/
66+
67+
bool swift::canSynthesizeDistributedActorCodableConformance(NominalTypeDecl *actor) {
68+
auto &C = actor->getASTContext();
69+
70+
if (!actor->isDistributedActor())
71+
return false;
72+
73+
return evaluateOrDefault(
74+
C.evaluator,
75+
CanSynthesizeDistributedActorCodableConformanceRequest{actor},
76+
false);
77+
}
78+
79+
6380
/******************************************************************************/
6481
/************** Distributed Actor System Associated Types *********************/
6582
/******************************************************************************/
@@ -210,17 +227,20 @@ Type swift::getDistributedActorSystemInvocationDecoderType(NominalTypeDecl *syst
210227
Type swift::getDistributedSerializationRequirementType(
211228
NominalTypeDecl *nominal, ProtocolDecl *protocol) {
212229
assert(nominal);
213-
assert(protocol);
214230
auto &ctx = nominal->getASTContext();
215231

232+
if (!protocol)
233+
return Type();
234+
216235
// Dig out the serialization requirement type.
217236
auto module = nominal->getParentModule();
218237
Type selfType = nominal->getSelfInterfaceType();
219238
auto conformance = module->lookupConformance(selfType, protocol);
220239
if (conformance.isInvalid())
221240
return Type();
222241

223-
return conformance.getTypeWitnessByName(selfType, ctx.Id_SerializationRequirement);
242+
return conformance.getTypeWitnessByName(selfType,
243+
ctx.Id_SerializationRequirement);
224244
}
225245

226246
AbstractFunctionDecl *

lib/AST/ProtocolConformance.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,7 @@ static SmallVector<ProtocolConformance *, 2> findSynthesizedConformances(
12961296
}
12971297

12981298
/// Distributed actors can synthesize Encodable/Decodable, so look for those
1299-
if (nominal->isDistributedActor()) {
1299+
if (canSynthesizeDistributedActorCodableConformance(nominal)) {
13001300
trySynthesize(KnownProtocolKind::Encodable);
13011301
trySynthesize(KnownProtocolKind::Decodable);
13021302
}

lib/Sema/CodeSynthesisDistributedActor.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,3 +1043,29 @@ NormalProtocolConformance *GetDistributedActorImplicitCodableRequest::evaluate(
10431043
return addDistributedActorCodableConformance(classDecl,
10441044
C.getProtocol(protoKind));
10451045
}
1046+
1047+
bool CanSynthesizeDistributedActorCodableConformanceRequest::evaluate(
1048+
Evaluator &evaluator, NominalTypeDecl *actor) const {
1049+
1050+
if (actor && !isa<ClassDecl>(actor))
1051+
return false;
1052+
1053+
if (!actor->isDistributedActor())
1054+
return false;
1055+
1056+
auto systemTy = getConcreteReplacementForProtocolActorSystemType(actor);
1057+
if (!systemTy)
1058+
return false;
1059+
1060+
if (!systemTy->getAnyNominal())
1061+
return false;
1062+
1063+
auto idTy = getDistributedActorSystemActorIDType(systemTy->getAnyNominal());
1064+
if (!idTy)
1065+
return false;
1066+
1067+
return TypeChecker::conformsToKnownProtocol(
1068+
idTy, KnownProtocolKind::Decodable, actor->getParentModule()) &&
1069+
TypeChecker::conformsToKnownProtocol(
1070+
idTy, KnownProtocolKind::Encodable, actor->getParentModule());
1071+
}

test/Distributed/Inputs/FakeDistributedActorSystems.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder {
385385
var returnType: Any.Type? = nil
386386
var errorType: Any.Type? = nil
387387

388+
public init() {}
389+
388390
public mutating func recordGenericSubstitution<T>(_ type: T.Type) throws {
389391
print(" > encode generic sub: \(String(reflecting: type))")
390392
genericSubs.append(type)

test/Distributed/Runtime/distributed_actor_localSystem_generic.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,11 @@
1717
import Distributed
1818

1919
@available(SwiftStdlib 6.0, *)
20-
distributed actor Worker<ActorSystem> where ActorSystem: DistributedActorSystem<any Codable>, ActorSystem.ActorID: Codable {
20+
//distributed actor Worker<ActorSystem> where ActorSystem: DistributedActorSystem<any Codable>, ActorSystem.ActorID: Codable {
21+
distributed actor Worker<ActorSystem> where ActorSystem: DistributedActorSystem<any Codable> {
2122
distributed func hi(name: String) {
2223
print("Hi, \(name)!")
2324
}
24-
25-
nonisolated var description: Swift.String {
26-
"Worker(\(id))"
27-
}
2825
}
2926

3027
// ==== Execute ----------------------------------------------------------------
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/Inputs/FakeDistributedActorSystems.swift
3+
// RUN: %target-swift-frontend -typecheck -verify -disable-availability-checking -I %t 2>&1 %s
4+
// REQUIRES: concurrency
5+
// REQUIRES: distributed
6+
7+
import Distributed
8+
import FakeDistributedActorSystems
9+
10+
distributed actor YesVeryMuchSo {
11+
typealias ActorSystem = FakeRoundtripActorSystem
12+
}
13+
func test_YesVeryMuchSo(_ actor: YesVeryMuchSo) {
14+
let _: any Codable = actor // implicit conformance, ID was Codable
15+
16+
// unrelated protocol
17+
let _: any CustomSerializationProtocol = actor // expected-error{{value of type 'YesVeryMuchSo' does not conform to specified type 'CustomSerializationProtocol'}}
18+
}
19+
20+
// ==== ------------------------------------------------------------------------
21+
22+
distributed actor SerReqNotCodable {
23+
typealias ActorSystem = FakeCustomSerializationRoundtripActorSystem
24+
}
25+
func test_NotAtAll_NoImplicit(_ actor: SerReqNotCodable) {
26+
let _: any Codable = actor // OK, the ID was Codable, even though SerializationRequirement was something else
27+
28+
// no implicit conformance
29+
let _: any CustomSerializationProtocol = actor // expected-error{{value of type 'SerReqNotCodable' does not conform to specified type 'CustomSerializationProtocol'}}
30+
}
31+
32+
// ==== ------------------------------------------------------------------------
33+
34+
distributed actor NotAtAll_NothingCodable { // expected-error{{type 'NotAtAll_NothingCodable' does not conform to protocol 'Decodable'}}
35+
typealias ActorSystem = FakeIdIsNotCodableActorSystem
36+
}
37+
func test_NotAtAll_NoImplicit(_ actor: NotAtAll_NothingCodable) {
38+
let _: any Codable = actor
39+
40+
// no implicit conformance
41+
let _: any CustomSerializationProtocol = actor // expected-error{{value of type 'NotAtAll_NothingCodable' does not conform to specified type 'CustomSerializationProtocol'}}
42+
}
43+
44+
public struct NotCodableID: Sendable, Hashable {}
45+
46+
@available(SwiftStdlib 5.7, *)
47+
public struct FakeIdIsNotCodableActorSystem: DistributedActorSystem, CustomStringConvertible {
48+
public typealias ActorID = NotCodableID
49+
public typealias InvocationDecoder = FakeInvocationDecoder
50+
public typealias InvocationEncoder = FakeInvocationEncoder
51+
public typealias SerializationRequirement = Codable
52+
public typealias ResultHandler = FakeRoundtripResultHandler
53+
54+
// just so that the struct does not become "trivial"
55+
let someValue: String = ""
56+
let someValue2: String = ""
57+
let someValue3: String = ""
58+
let someValue4: String = ""
59+
60+
public init() {
61+
}
62+
63+
public func resolve<Act>(id: ActorID, as actorType: Act.Type) throws -> Act?
64+
where Act: DistributedActor,
65+
Act.ID == ActorID {
66+
nil
67+
}
68+
69+
public func assignID<Act>(_ actorType: Act.Type) -> ActorID
70+
where Act: DistributedActor,
71+
Act.ID == ActorID {
72+
.init()
73+
}
74+
75+
public func actorReady<Act>(_ actor: Act)
76+
where Act: DistributedActor,
77+
Act.ID == ActorID {
78+
}
79+
80+
public func resignID(_ id: ActorID) {
81+
}
82+
83+
public func makeInvocationEncoder() -> InvocationEncoder {
84+
.init()
85+
}
86+
87+
public func remoteCall<Act, Err, Res>(
88+
on actor: Act,
89+
target: RemoteCallTarget,
90+
invocation invocationEncoder: inout InvocationEncoder,
91+
throwing: Err.Type,
92+
returning: Res.Type
93+
) async throws -> Res
94+
where Act: DistributedActor,
95+
Act.ID == ActorID,
96+
Err: Error,
97+
Res: SerializationRequirement {
98+
throw ExecuteDistributedTargetError(message: "\(#function) not implemented.")
99+
}
100+
101+
public func remoteCallVoid<Act, Err>(
102+
on actor: Act,
103+
target: RemoteCallTarget,
104+
invocation invocationEncoder: inout InvocationEncoder,
105+
throwing: Err.Type
106+
) async throws
107+
where Act: DistributedActor,
108+
Act.ID == ActorID,
109+
Err: Error {
110+
throw ExecuteDistributedTargetError(message: "\(#function) not implemented.")
111+
}
112+
113+
public nonisolated var description: Swift.String {
114+
"\(Self.self)()"
115+
}
116+
}

test/Distributed/distributed_actor_system_missing.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ distributed actor DA {
1111
// expected-error@-2 {{type 'DA' does not conform to protocol 'DistributedActor'}}
1212

1313
// Since synthesis would have failed due to the missing ActorSystem:
14-
// expected-error@-5{{type 'DA' does not conform to protocol 'Encodable'}}
15-
// expected-error@-6{{type 'DA' does not conform to protocol 'Decodable'}}
14+
// expected-error@-5{{type 'DA' does not conform to protocol 'Decodable'}}
1615

17-
// expected-note@-8{{you can provide a module-wide default actor system by declaring:}}
16+
// expected-note@-7{{you can provide a module-wide default actor system by declaring:}}
1817

1918
// Note to add the typealias is diagnosed on the protocol:
2019
// _Distributed.DistributedActor:3:20: note: diagnostic produced elsewhere: protocol requires nested type 'ActorSystem'; do you want to add it?

test/Distributed/distributed_actor_system_missing_type_no_crash.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ distributed actor Fish {
1212
// expected-error@-2{{distributed actor 'Fish' does not declare ActorSystem it can be used with}}
1313
// expected-error@-3{{type 'Fish' does not conform to protocol 'DistributedActor'}}
1414
// expected-note@-4{{you can provide a module-wide default actor system by declaring:\ntypealias DefaultDistributedActorSystem = <#ConcreteActorSystem#>}}
15-
// expected-error@-5{{type 'Fish' does not conform to protocol 'Encodable'}}
16-
// expected-error@-6{{type 'Fish' does not conform to protocol 'Decodable'}}
15+
// expected-error@-5{{type 'Fish' does not conform to protocol 'Decodable'}}
1716

1817
distributed func tell(_ text: String, by: Fish) {
1918
// What would the fish say, if it could talk?

test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ distributed actor ProtocolWithChecksSeqReqDA_MissingSystem: ProtocolWithChecksSe
5353
// expected-error@-4{{distributed actor 'ProtocolWithChecksSeqReqDA_MissingSystem' does not declare ActorSystem it can be used with}}
5454
//
5555
// expected-error@-6{{type 'ProtocolWithChecksSeqReqDA_MissingSystem' does not conform to protocol 'DistributedActor'}}
56-
// expected-error@-7{{type 'ProtocolWithChecksSeqReqDA_MissingSystem' does not conform to protocol 'Encodable'}}
57-
// expected-error@-8{{type 'ProtocolWithChecksSeqReqDA_MissingSystem' does not conform to protocol 'Decodable'}}
56+
// expected-error@-7{{type 'ProtocolWithChecksSeqReqDA_MissingSystem' does not conform to protocol 'Decodable'}}
5857

5958
// Entire conformance is doomed, so we didn't proceed to checking the functions; that's fine
6059
distributed func testAT() async throws -> NotCodable { .init() }

0 commit comments

Comments
 (0)