Skip to content

Commit c57cb45

Browse files
committed
[Distributed] Handle SerializationRequirement in dist get props
- reuse more code about getting the type and requirements - handle protocols properly, including extensions with where clauses - improve error messages
1 parent d9b79cc commit c57cb45

13 files changed

+184
-72
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4566,10 +4566,10 @@ ERROR(distributed_actor_isolated_method,none,
45664566
"only 'distributed' instance methods can be called on a potentially remote distributed actor",
45674567
())
45684568
ERROR(distributed_actor_func_param_not_codable,none,
4569-
"parameter '%0' of type %1 in %2 does not conform to '%3'",
4569+
"parameter '%0' of type %1 in %2 does not conform to serialization requirement '%3'",
45704570
(StringRef, Type, DescriptiveDeclKind, StringRef))
45714571
ERROR(distributed_actor_target_result_not_codable,none,
4572-
"result type %0 of %1 %2 does not conform to '%3'",
4572+
"result type %0 of %1 %2 does not conform to serialization requirement '%3'",
45734573
(Type, DescriptiveDeclKind, Identifier, StringRef))
45744574
ERROR(distributed_actor_remote_func_implemented_manually,none,
45754575
"distributed instance method's %0 remote counterpart %1 cannot not be implemented manually.",

lib/SILGen/SILGenDistributed.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,14 +1155,14 @@ void SILGenFunction::emitDistributedThunk(SILDeclRef thunk) {
11551155

11561156
// --- Codable: Decodable
11571157
auto decodableRequirementTy =
1158-
ctx.getProtocol(KnownProtocolKind::Decodable); // FIXME: actually use SerializatioNRequirement
1158+
ctx.getProtocol(KnownProtocolKind::Decodable); // FIXME(distributed): actually use SerializationRequirement
11591159
auto paramDecodableTypeConfRef = module->lookupConformance(
11601160
paramTy, decodableRequirementTy);
11611161
subConformances.push_back(paramDecodableTypeConfRef);
11621162

11631163
// --- Codable: Encodable
11641164
auto encodableRequirementTy = ctx.getProtocol(
1165-
KnownProtocolKind::Encodable); // FIXME: actually use SerializatioNRequirement
1165+
KnownProtocolKind::Encodable); // FIXME(distributed): actually use SerializationRequirement
11661166
auto paramEncodableTypeConfRef = module->lookupConformance(
11671167
paramTy, encodableRequirementTy);
11681168
subConformances.push_back(paramEncodableTypeConfRef);

lib/Sema/TypeCheckDistributed.cpp

Lines changed: 84 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,6 @@ static bool checkDistributedTargetResultType(
248248
TypeChecker::conformsToProtocol(resultType, serializationReq, module);
249249
if (conformance.isInvalid()) {
250250
if (diagnose) {
251-
serializationReq->dump();
252251
llvm::StringRef conformanceToSuggest = isCodableRequirement ?
253252
"Codable" : // Codable is a typealias, easier to diagnose like that
254253
serializationReq->getNameStr();
@@ -282,15 +281,32 @@ static bool checkDistributedTargetResultType(
282281
/// \returns \c true if there was a problem with adding the attribute, \c false
283282
/// otherwise.
284283
bool swift::checkDistributedFunction(FuncDecl *func, bool diagnose) {
285-
// === All parameters and the result type must conform SerializationRequirement
286-
287-
auto actorDecl = func->getDeclContext()->getSelfNominalTypeDecl();
288-
289-
llvm::SmallPtrSet<ProtocolDecl *, 2> serializationRequirements =
290-
getDistributedSerializationRequirementProtocols(actorDecl);
284+
assert(func->isDistributed());
291285

286+
auto &C = func->getASTContext();
287+
auto declContext = func->getDeclContext();
292288
auto module = func->getParentModule();
293289

290+
// === All parameters and the result type must conform SerializationRequirement
291+
llvm::SmallPtrSet<ProtocolDecl *, 2> serializationRequirements;
292+
if (auto extension = dyn_cast<ExtensionDecl>(declContext)) {
293+
serializationRequirements = extractDistributedSerializationRequirements(
294+
C, extension->getGenericRequirements());
295+
} else if (auto actor = dyn_cast<ClassDecl>(declContext)) {
296+
serializationRequirements =
297+
getDistributedSerializationRequirementProtocols(actor);
298+
} // TODO(distributed): need to handle ProtocolDecl too?
299+
300+
// If the requirement is exactly `Codable` we diagnose it ia bit nicer.
301+
auto serializationRequirementIsCodable = false;
302+
if (serializationRequirements.size() == 2) {
303+
auto encodableType = C.getProtocol(KnownProtocolKind::Encodable);
304+
auto decodableType = C.getProtocol(KnownProtocolKind::Decodable);
305+
llvm::SmallPtrSet<ProtocolDecl *, 2> codableRequirements = {encodableType, decodableType};
306+
serializationRequirementIsCodable =
307+
serializationRequirements == codableRequirements;
308+
}
309+
294310
// --- Check parameters for 'Codable' conformance
295311
for (auto param : *func->getParameters()) {
296312
auto paramTy = func->mapTypeIntoContext(param->getInterfaceType());
@@ -303,7 +319,7 @@ bool swift::checkDistributedFunction(FuncDecl *func, bool diagnose) {
303319
param->getArgumentName().str(),
304320
param->getInterfaceType(),
305321
func->getDescriptiveKind(),
306-
req->getNameStr()); // TODO(distributed): handle Codable better
322+
serializationRequirementIsCodable ? "Codable" : req->getNameStr());
307323

308324
if (auto paramNominalTy = paramTy->getAnyNominal()) {
309325
addCodableFixIt(paramNominalTy, diag);
@@ -384,10 +400,6 @@ bool swift::checkDistributedActorProperty(VarDecl *var, bool diagnose) {
384400
getDistributedSerializationRequirementProtocols(
385401
var->getDeclContext()->getSelfNominalTypeDecl());
386402

387-
// auto encodableType = C.getProtocol(KnownProtocolKind::Encodable);
388-
// auto decodableType = C.getProtocol(KnownProtocolKind::Decodable);
389-
// llvm::ArrayRef<ProtocolDecl*> serializationRequirements = {encodableType, decodableType};
390-
391403
auto module = var->getModuleContext();
392404
if (checkDistributedTargetResultType(module, var, serializationRequirements, diagnose)) {
393405
return true;
@@ -487,7 +499,6 @@ void TypeChecker::checkDistributedActor(ClassDecl *decl) {
487499

488500
static Type getAssociatedTypeOfDistributedSystem(NominalTypeDecl *actor,
489501
Identifier member) {
490-
assert(actor->isDistributedActor());
491502
auto &ctx = actor->getASTContext();
492503

493504
auto actorProtocol = ctx.getProtocol(KnownProtocolKind::DistributedActor);
@@ -501,12 +512,12 @@ static Type getAssociatedTypeOfDistributedSystem(NominalTypeDecl *actor,
501512

502513
auto actorSystemProtocol = ctx.getProtocol(KnownProtocolKind::DistributedActorSystem);
503514
if (!actorSystemProtocol)
504-
return ErrorType::get(ctx);
515+
return Type();
505516

506517
AssociatedTypeDecl *assocTypeDecl =
507518
actorSystemProtocol->getAssociatedType(member);
508519
if (!assocTypeDecl)
509-
return ErrorType::get(ctx);
520+
return Type();
510521

511522
auto module = actor->getParentModule();
512523
Type selfType = actor->getSelfInterfaceType();
@@ -523,27 +534,79 @@ llvm::SmallPtrSet<ProtocolDecl *, 2>
523534
swift::getDistributedSerializationRequirementProtocols(NominalTypeDecl *nominal) {
524535
auto &ctx = nominal->getASTContext();
525536

526-
auto serialReqType = ctx.getDistributedSerializationRequirementType(nominal)
527-
->castTo<ExistentialType>()
528-
->getConstraintType()
529-
->getDesugaredType();
537+
auto ty = ctx.getDistributedSerializationRequirementType(nominal);
538+
if (ty->hasError())
539+
return {};
540+
541+
auto serialReqType =
542+
ty->castTo<ExistentialType>()->getConstraintType()->getDesugaredType();
530543

531544
// TODO(distributed): check what happens with Any
545+
return flattenDistributedSerializationTypeToRequiredProtocols(serialReqType);
546+
}
532547

548+
llvm::SmallPtrSet<ProtocolDecl *, 2>
549+
swift::flattenDistributedSerializationTypeToRequiredProtocols(TypeBase *serializationRequirement) {
533550
llvm::SmallPtrSet<ProtocolDecl *, 2> serializationReqs;
534-
if (auto composition = serialReqType->getAs<ProtocolCompositionType>()) {
551+
if (auto composition = serializationRequirement->getAs<ProtocolCompositionType>()) {
535552
for (auto member : composition->getMembers()) {
536553
if (auto *protocol = member->getAs<ProtocolType>())
537554
serializationReqs.insert(protocol->getDecl());
538555
}
539556
} else {
540-
auto protocol = serialReqType->castTo<ProtocolType>()->getDecl();
557+
auto protocol = serializationRequirement->castTo<ProtocolType>()->getDecl();
541558
serializationReqs.insert(protocol);
542559
}
543560

544561
return serializationReqs;
545562
}
546563

564+
llvm::SmallPtrSet<ProtocolDecl *, 2>
565+
swift::extractDistributedSerializationRequirements(
566+
ASTContext &C,
567+
ArrayRef<Requirement> allRequirements) {
568+
llvm::SmallPtrSet<ProtocolDecl *, 2> serializationReqs;
569+
570+
auto systemProto = C.getProtocol(KnownProtocolKind::DistributedActorSystem);
571+
auto serializationReqAssocType =
572+
systemProto->getAssociatedType(C.Id_SerializationRequirement);
573+
auto systemSerializationReqTy =
574+
serializationReqAssocType->getInterfaceType();
575+
576+
for (auto req : allRequirements) {
577+
if (req.getSecondType()->isAny()) {
578+
continue;
579+
}
580+
if (!req.getFirstType()->hasDependentMember())
581+
continue;
582+
583+
if (auto dependentMemberType = req.getFirstType()->castTo<DependentMemberType>()) {
584+
auto dependentTy =
585+
dependentMemberType->getAssocType()->getInterfaceType();
586+
587+
if (dependentTy->isEqual(systemSerializationReqTy)) {
588+
auto requirementProto = req.getSecondType();
589+
if (auto proto = dyn_cast_or_null<ProtocolDecl>(requirementProto->getAnyNominal())) {
590+
serializationReqs.insert(proto);
591+
} else {
592+
auto serialReqType = requirementProto
593+
->castTo<ExistentialType>()
594+
->getConstraintType()
595+
->getDesugaredType();
596+
auto flattenedRequirements =
597+
flattenDistributedSerializationTypeToRequiredProtocols(
598+
serialReqType);
599+
for (auto p : flattenedRequirements) {
600+
serializationReqs.insert(p);
601+
}
602+
}
603+
}
604+
}
605+
}
606+
607+
return serializationReqs;
608+
}
609+
547610
Type swift::getDistributedActorSystemType(NominalTypeDecl *actor) {
548611
assert(actor->isDistributedActor());
549612
auto &ctx = actor->getASTContext();
@@ -613,21 +676,6 @@ GetDistributedActorArgumentDecodingMethodRequest::evaluate(Evaluator &evaluator,
613676
// typealias SerializationRequirement = any ...
614677
llvm::SmallPtrSet<ProtocolDecl *, 2> serializationReqs =
615678
getDistributedSerializationRequirementProtocols(actor);
616-
// auto serialReqType = ctx.getDistributedSerializationRequirementType(actor)
617-
// ->castTo<ExistentialType>()
618-
// ->getConstraintType()
619-
// ->getDesugaredType();
620-
//
621-
// llvm::SmallPtrSet<ProtocolDecl *, 2> serializationReqs;
622-
// if (auto composition = serialReqType->getAs<ProtocolCompositionType>()) {
623-
// for (auto member : composition->getMembers()) {
624-
// if (auto *protocol = member->getAs<ProtocolType>())
625-
// serializationReqs.insert(protocol->getDecl());
626-
// }
627-
// } else {
628-
// auto protocol = serialReqType->castTo<ProtocolType>()->getDecl();
629-
// serializationReqs.insert(protocol);
630-
// }
631679

632680
SmallVector<FuncDecl *, 2> candidates;
633681
// Looking for `decodeNextArgument<Arg: <SerializationReq>>() throws -> Arg`

lib/Sema/TypeCheckDistributed.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,19 @@ Type getDistributedSerializationRequirementType(NominalTypeDecl *nominal);
8080
llvm::SmallPtrSet<ProtocolDecl *, 2>
8181
getDistributedSerializationRequirementProtocols(NominalTypeDecl *decl);
8282

83+
llvm::SmallPtrSet<ProtocolDecl *, 2>
84+
flattenDistributedSerializationTypeToRequiredProtocols(
85+
TypeBase *serializationRequirement);
86+
87+
/// Given any set of generic requirements, locate those which are about the
88+
/// `SerializationRequirement`. Those need to be applied in the parameter and
89+
/// return type checking of distributed targets.
90+
llvm::SmallPtrSet<ProtocolDecl *, 2>
91+
extractDistributedSerializationRequirements(
92+
ASTContext &C,
93+
ArrayRef<Requirement> allRequirements);
94+
95+
8396
/// Diagnose a distributed func declaration in a not-distributed actor protocol.
8497
void diagnoseDistributedFunctionInNonDistributedActorProtocol(
8598
const ProtocolDecl *proto, InFlightDiagnostic &diag);

test/Distributed/Runtime/distributed_actor_remoteCall.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
// FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574
1515
// UNSUPPORTED: windows
1616

17+
// FIXME(distributed): remote calls seem to hang on linux - rdar://87240034
18+
// UNSUPPORTED: linux
19+
1720
import _Distributed
1821

1922
final class Obj: @unchecked Sendable, Codable {}

test/Distributed/distributed_actor_inference.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ distributed actor SomeDistributedActor_6 {
5858
}
5959

6060
distributed actor BadValuesDistributedActor_7 {
61-
distributed var varItNope: Int { 13 } // expected-error{{'distributed' modifier cannot be applied to this declaration}}
62-
distributed let letItNope: Int = 13 // expected-error{{'distributed' modifier cannot be applied to this declaration}}
63-
distributed lazy var lazyVarNope: Int = 13 // expected-error{{'distributed' modifier cannot be applied to this declaration}}
61+
distributed var varItNope: Int { 13 } // we allow these
62+
distributed let letItNope: Int = 13 // expected-error{{static property 'letItNope' cannot be 'distributed', because it is not a computed get-only property}}
63+
distributed lazy var lazyVarNope: Int = 13 // expected-error{{'distributed' computed property 'lazyVarNope' can only be have a 'get' implementation}}
6464
distributed subscript(nope: Int) -> Int { nope * 2 } // expected-error{{'distributed' modifier cannot be applied to this declaration}}
65-
distributed static let staticLetNope: Int = 13 // expected-error{{'distributed' modifier cannot be applied to this declaration}}
66-
distributed static var staticVarNope: Int { 13 } // expected-error{{'distributed' modifier cannot be applied to this declaration}}
65+
distributed static let staticLetNope: Int = 13 // expected-error{{'distributed' property 'staticLetNope' cannot be 'static'}}
66+
distributed static var staticVarNope: Int { 13 } // expected-error{{'distributed' property 'staticVarNope' cannot be 'static'}}
6767
distributed static func staticNope() async throws -> Int { 13 } // expected-error{{'distributed' method cannot be 'static'}}
6868
}
6969

test/Distributed/distributed_actor_is_experimental_enabled_missing_import.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ actor A {
1616
distributed func dist() {} // expected-error{{'distributed' method can only be declared within 'distributed actor'}}
1717
distributed func distAsync() async {} // expected-error{{'distributed' method can only be declared within 'distributed actor'}}
1818

19-
distributed var neverOk: String { // expected-error{{'distributed' modifier cannot be applied to this declaration}}
20-
"vars are not allowed to be distributed *ever* anyway"
19+
distributed var neverOk: String {
20+
""
2121
}
2222
}
2323

@@ -27,8 +27,8 @@ distributed actor DA2 {
2727
distributed func dist() {}
2828
distributed func distAsync() async {}
2929

30-
distributed var neverOk: String { // expected-error{{'distributed' modifier cannot be applied to this declaration}}
31-
"vars are not allowed to be distributed *ever* anyway"
30+
distributed var neverOk: String {
31+
""
3232
}
3333
}
3434

test/Distributed/distributed_actor_isolation.swift

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ struct NotCodableValue { }
2828

2929
distributed actor DistributedActor_1 {
3030

31-
let name: String = "alice" // expected-note{{distributed actor state is only available within the actor instance}}
32-
var mutable: String = "alice" // expected-note{{distributed actor state is only available within the actor instance}}
31+
let name: String = "alice" // expected-note{{access to this property is only permitted within the distributed actor 'DistributedActor_1'}}
32+
var mutable: String = "alice" // expected-note{{access to this property is only permitted within the distributed actor 'DistributedActor_1'}}
3333
var computedMutable: String {
3434
get {
3535
"hey"
@@ -39,11 +39,8 @@ distributed actor DistributedActor_1 {
3939
}
4040
}
4141

42-
distributed let letProperty: String = "" // expected-error{{'distributed' modifier cannot be applied to this declaration}}
43-
distributed var varProperty: String = "" // expected-error{{'distributed' modifier cannot be applied to this declaration}}
44-
distributed var computedProperty: String { // expected-error{{'distributed' modifier cannot be applied to this declaration}}
45-
""
46-
}
42+
distributed let letProperty: String = "" // expected-error{{static property 'letProperty' cannot be 'distributed', because it is not a computed get-only property}}
43+
distributed var varProperty: String = "" // expected-error{{'distributed' computed property 'varProperty' can only be have a 'get' implementation}}
4744

4845
distributed static func distributedStatic() {} // expected-error{{'distributed' method cannot be 'static'}}
4946
distributed class func distributedClass() {}
@@ -64,10 +61,10 @@ distributed actor DistributedActor_1 {
6461
distributed func distIntString(int: Int, two: String) async throws -> (String) { "\(int) + \(two)" } // ok
6562

6663
distributed func dist(notCodable: NotCodableValue) async throws {
67-
// expected-error@-1 {{parameter 'notCodable' of type 'NotCodableValue' in distributed instance method does not conform to 'Codable'}}
64+
// expected-error@-1 {{parameter 'notCodable' of type 'NotCodableValue' in distributed instance method does not conform to serialization requirement 'Codable'}}
6865
}
6966
distributed func distBadReturn(int: Int) async throws -> NotCodableValue {
70-
// expected-error@-1 {{result type 'NotCodableValue' of distributed instance method does not conform to 'Codable'}}
67+
// expected-error@-1 {{result type 'NotCodableValue' of distributed instance method 'distBadReturn' does not conform to serialization requirement 'Codable'}}
7168
fatalError()
7269
}
7370

@@ -76,7 +73,7 @@ distributed actor DistributedActor_1 {
7673
}
7774

7875
distributed func closure(close: () -> String) {
79-
// expected-error@-1{{parameter 'close' of type '() -> String' in distributed instance method does not conform to 'Codable'}}
76+
// expected-error@-1{{parameter 'close' of type '() -> String' in distributed instance method does not conform to serialization requirement 'Codable'}}
8077
}
8178

8279
distributed func noInout(inNOut burger: inout String) {
@@ -90,7 +87,7 @@ distributed actor DistributedActor_1 {
9087
fatalError()
9188
}
9289
distributed func distBadReturnGeneric<T: Sendable>(int: Int) async throws -> T {
93-
// expected-error@-1 {{result type 'T' of distributed instance method does not conform to 'Codable'}}
90+
// expected-error@-1 {{result type 'T' of distributed instance method 'distBadReturnGeneric' does not conform to serialization requirement 'Codable'}}
9491
fatalError()
9592
}
9693

@@ -101,7 +98,7 @@ distributed actor DistributedActor_1 {
10198
value
10299
}
103100
distributed func distBadGenericParam<T: Sendable>(int: T) async throws {
104-
// expected-error@-1 {{parameter 'int' of type 'T' in distributed instance method does not conform to 'Codable'}}
101+
// expected-error@-1 {{parameter 'int' of type 'T' in distributed instance method does not conform to serialization requirement 'Codable'}}
105102
fatalError()
106103
}
107104

@@ -146,8 +143,8 @@ func test_outside(
146143

147144
_ = local.name // ok, special case that let constants are okey
148145
let _: String = local.mutable // ok, special case that let constants are okey
149-
_ = distributed.name // expected-error{{distributed actor-isolated property 'name' can only be referenced inside the distributed actor}}
150-
_ = distributed.mutable // expected-error{{distributed actor-isolated property 'mutable' can only be referenced inside the distributed actor}}
146+
_ = distributed.name // expected-error{{distributed actor-isolated property 'name' can not be accessed from a non-isolated context}}
147+
_ = distributed.mutable // expected-error{{distributed actor-isolated property 'mutable' can not be accessed from a non-isolated context}}
151148

152149
// ==== special properties (nonisolated, implicitly replicated)
153150
// the distributed actor's special fields may always be referred to

test/Distributed/distributed_actor_isolation_and_tasks.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ struct Logger {
1818

1919
distributed actor Philosopher {
2020
let log: Logger
21-
// expected-note@-1{{distributed actor state is only available within the actor instance}}
21+
// expected-note@-1{{access to this property is only permitted within the distributed actor 'Philosopher'}}
2222
var variable = 12
2323
var variable_fromDetach = 12
2424
let INITIALIZED: Int
@@ -62,7 +62,7 @@ distributed actor Philosopher {
6262

6363
func test_outside(system: FakeActorSystem) async throws {
6464
_ = try await Philosopher(system: system).dist()
65-
_ = Philosopher(system: system).log // expected-error{{distributed actor-isolated property 'log' can only be referenced inside the distributed actor}}
65+
_ = Philosopher(system: system).log // expected-error{{distributed actor-isolated property 'log' can not be accessed from a non-isolated context}}
6666

6767
_ = Philosopher(system: system).id
6868
_ = Philosopher(system: system).actorSystem

0 commit comments

Comments
 (0)