Skip to content

Commit 6af7324

Browse files
authored
Merge pull request #42136 from ktoso/wip-identifiable-synthesis-fix
[Distributed] ID synthesis must be eager, or we run into issues in real projects
2 parents 07e53c2 + bf37e69 commit 6af7324

File tree

11 files changed

+133
-17
lines changed

11 files changed

+133
-17
lines changed

include/swift/AST/Decl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4345,6 +4345,7 @@ enum class KnownDerivableProtocolKind : uint8_t {
43454345
Decodable,
43464346
AdditiveArithmetic,
43474347
Differentiable,
4348+
Identifiable,
43484349
Actor,
43494350
DistributedActor,
43504351
DistributedActorSystem,

include/swift/AST/KnownProtocols.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
PROTOCOL(Actor)
6262
PROTOCOL(Sequence)
63+
PROTOCOL(Identifiable)
6364
PROTOCOL(IteratorProtocol)
6465
PROTOCOL(RawRepresentable)
6566
PROTOCOL(Equatable)

lib/AST/Decl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5622,6 +5622,8 @@ Optional<KnownDerivableProtocolKind>
56225622
return KnownDerivableProtocolKind::AdditiveArithmetic;
56235623
case KnownProtocolKind::Differentiable:
56245624
return KnownDerivableProtocolKind::Differentiable;
5625+
case KnownProtocolKind::Identifiable:
5626+
return KnownDerivableProtocolKind::Identifiable;
56255627
case KnownProtocolKind::Actor:
56265628
return KnownDerivableProtocolKind::Actor;
56275629
case KnownProtocolKind::DistributedActor:

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5366,6 +5366,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
53665366
case KnownProtocolKind::AdditiveArithmetic:
53675367
case KnownProtocolKind::Differentiable:
53685368
case KnownProtocolKind::FloatingPoint:
5369+
case KnownProtocolKind::Identifiable:
53695370
case KnownProtocolKind::Actor:
53705371
case KnownProtocolKind::DistributedActor:
53715372
case KnownProtocolKind::DistributedActorSystem:

lib/Sema/DerivedConformanceDistributedActor.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,27 @@
2424

2525
using namespace swift;
2626

27+
bool DerivedConformance::canDeriveIdentifiable(
28+
NominalTypeDecl *nominal, DeclContext *dc) {
29+
// we only synthesize for concrete 'distributed actor' decls (which are class)
30+
if (!isa<ClassDecl>(nominal))
31+
return false;
32+
33+
auto &C = nominal->getASTContext();
34+
if (!C.getLoadedModule(C.Id_Distributed))
35+
return false;
36+
37+
return nominal->isDistributedActor();
38+
}
39+
2740
bool DerivedConformance::canDeriveDistributedActor(
2841
NominalTypeDecl *nominal, DeclContext *dc) {
42+
auto &C = nominal->getASTContext();
2943
auto classDecl = dyn_cast<ClassDecl>(nominal);
30-
return classDecl && classDecl->isDistributedActor() && dc == nominal;
44+
45+
return C.getLoadedModule(C.Id_Distributed) &&
46+
classDecl && classDecl->isDistributedActor() &&
47+
dc == nominal;
3148
}
3249

3350
bool DerivedConformance::canDeriveDistributedActorSystem(
@@ -500,6 +517,26 @@ deriveDistributedActorType_ActorSystem(
500517
return defaultDistributedActorSystemTypeDecl->getDeclaredInterfaceType();
501518
}
502519

520+
static Type
521+
deriveDistributedActorType_ID(
522+
DerivedConformance &derived) {
523+
if (!derived.Nominal->isDistributedActor())
524+
return nullptr;
525+
526+
// Look for a type DefaultDistributedActorSystem within the parent context.
527+
auto systemTy = getDistributedActorSystemType(derived.Nominal);
528+
529+
// There is no known actor system type, so fail to synthesize.
530+
if (!systemTy || systemTy->hasError())
531+
return nullptr;
532+
533+
if (auto systemNominal = systemTy->getAnyNominal()) {
534+
return getDistributedActorSystemActorIDType(systemNominal);
535+
}
536+
537+
return nullptr;
538+
}
539+
503540
/******************************************************************************/
504541
/**************************** ENTRY POINTS ************************************/
505542
/******************************************************************************/
@@ -538,6 +575,10 @@ std::pair<Type, TypeDecl *> DerivedConformance::deriveDistributedActor(
538575
return std::make_pair(deriveDistributedActorType_ActorSystem(*this), nullptr);
539576
}
540577

578+
if (assocType->getName() == Context.Id_ID) {
579+
return std::make_pair(deriveDistributedActorType_ID(*this), nullptr);
580+
}
581+
541582
Context.Diags.diagnose(assocType->getLoc(),
542583
diag::broken_distributed_actor_requirement);
543584
return std::make_pair(Type(), nullptr);

lib/Sema/DerivedConformances.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC,
7777

7878
if (*derivableKind == KnownDerivableProtocolKind::Actor)
7979
return canDeriveActor(DC, Nominal);
80+
81+
if (*derivableKind == KnownDerivableProtocolKind::Identifiable)
82+
return canDeriveIdentifiable(Nominal, DC);
8083
if (*derivableKind == KnownDerivableProtocolKind::DistributedActor)
8184
return canDeriveDistributedActor(Nominal, DC);
8285
if (*derivableKind == KnownDerivableProtocolKind::DistributedActorSystem)

lib/Sema/DerivedConformances.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ class DerivedConformance {
323323
/// \returns the derived member, which will also be added to the type.
324324
ValueDecl *deriveDecodable(ValueDecl *requirement);
325325

326+
/// Identifiable may need to have the `ID` type witness synthesized explicitly
327+
static bool canDeriveIdentifiable(NominalTypeDecl *nominal,
328+
DeclContext *dc);
329+
326330
/// Whether we can derive the given DistributedActor requirement in the given context.
327331
static bool canDeriveDistributedActor(NominalTypeDecl *nominal,
328332
DeclContext *dc);

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6911,6 +6911,15 @@ ValueDecl *TypeChecker::deriveProtocolRequirement(DeclContext *DC,
69116911
case KnownDerivableProtocolKind::Differentiable:
69126912
return derived.deriveDifferentiable(Requirement);
69136913

6914+
case KnownDerivableProtocolKind::Identifiable:
6915+
if (derived.Nominal->isDistributedActor()) {
6916+
return derived.deriveDistributedActor(Requirement);
6917+
} else {
6918+
// No synthesis is required for other types; we should only end up
6919+
// attempting synthesis if the nominal was a distributed actor.
6920+
llvm_unreachable("Identifiable is synthesized for distributed actors");
6921+
}
6922+
69146923
case KnownDerivableProtocolKind::DistributedActor:
69156924
return derived.deriveDistributedActor(Requirement);
69166925
case KnownDerivableProtocolKind::DistributedActorSystem:
@@ -6947,6 +6956,12 @@ TypeChecker::deriveTypeWitness(DeclContext *DC,
69476956
return derived.deriveDifferentiable(AssocType);
69486957
case KnownProtocolKind::DistributedActor:
69496958
return derived.deriveDistributedActor(AssocType);
6959+
case KnownProtocolKind::Identifiable:
6960+
// Identifiable only has derivation logic for distributed actors,
6961+
// because how it depends on the ActorSystem the actor is associated with.
6962+
// If the nominal wasn't a distributed actor, we should not end up here,
6963+
// but either way, then we'd return null (fail derivation).
6964+
return derived.deriveDistributedActor(AssocType);
69506965
default:
69516966
return std::make_pair(nullptr, nullptr);
69526967
}

lib/Sema/TypeCheckProtocol.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,20 @@ class AssociatedTypeInference {
11981198
ConformanceChecker &checker,
11991199
SmallVectorImpl<InferredTypeWitnessesSolution> &solutions);
12001200

1201+
/// We may need to determine a type witness, regardless of the existence of a
1202+
/// default value for it, e.g. when a 'distributed actor' is looking up its
1203+
/// 'ID', the default defined in an extension for 'Identifiable' would be
1204+
/// located using the lookup resolve. This would not be correct, since the
1205+
/// type actually must be based on the associated 'ActorSystem'.
1206+
///
1207+
/// TODO(distributed): perhaps there is a better way to avoid this mixup?
1208+
/// Note though that this issue seems to only manifest in "real" builds
1209+
/// involving multiple files/modules, and not in tests within the Swift
1210+
/// project itself.
1211+
bool canAttemptEagerTypeWitnessDerivation(
1212+
ConformanceChecker &checker,
1213+
AssociatedTypeDecl *assocType);
1214+
12011215
public:
12021216
/// Describes a mapping from associated type declarations to their
12031217
/// type witnesses (as interface types).

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,6 +2128,20 @@ bool AssociatedTypeInference::diagnoseAmbiguousSolutions(
21282128
return false;
21292129
}
21302130

2131+
bool AssociatedTypeInference::canAttemptEagerTypeWitnessDerivation(
2132+
ConformanceChecker &checker,
2133+
AssociatedTypeDecl *assocType) {
2134+
2135+
/// Rather than locating the TypeID via the default implementation of
2136+
/// Identifiable, we need to find the type based on the associated ActorSystem
2137+
if (checker.Adoptee->isDistributedActor() &&
2138+
assocType->getProtocol()->isSpecificProtocol(KnownProtocolKind::Identifiable)) {
2139+
return true;
2140+
}
2141+
2142+
return false;
2143+
}
2144+
21312145
auto AssociatedTypeInference::solve(ConformanceChecker &checker)
21322146
-> Optional<InferredTypeWitnesses> {
21332147
// Track when we are checking type witnesses.
@@ -2142,6 +2156,16 @@ auto AssociatedTypeInference::solve(ConformanceChecker &checker)
21422156
if (conformance->hasTypeWitness(assocType))
21432157
continue;
21442158

2159+
if (canAttemptEagerTypeWitnessDerivation(checker, assocType)) {
2160+
auto derivedType = computeDerivedTypeWitness(assocType);
2161+
if (derivedType.first) {
2162+
checker.recordTypeWitness(assocType,
2163+
derivedType.first->mapTypeOutOfContext(),
2164+
derivedType.second);
2165+
continue;
2166+
}
2167+
}
2168+
21452169
// Try to resolve this type witness via name lookup, which is the
21462170
// most direct mechanism, overriding all others.
21472171
switch (checker.resolveTypeWitnessViaLookup(assocType)) {

test/decl/protocol/special/DistributedActor.swift

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,33 @@ import Distributed
77
/// Use the existential wrapper as the default actor system.
88
typealias DefaultDistributedActorSystem = LocalTestingDistributedActorSystem
99

10+
distributed actor D0 {
11+
typealias ActorSystem = LocalTestingDistributedActorSystem
12+
var x: Int = 17
13+
}
14+
1015
distributed actor D1 {
1116
var x: Int = 17
1217
}
1318

19+
class X: Identifiable {
20+
// should work as expected, synthesis not triggering
21+
func test() {
22+
let _: ObjectIdentifier = self.id
23+
}
24+
}
25+
26+
protocol DAP: DistributedActor {
27+
// should work as expected, synthesis not triggering
28+
func test()
29+
}
30+
31+
extension DAP where ActorSystem.ActorID == String {
32+
func test() {
33+
_ = self.id == ""
34+
}
35+
}
36+
1437
distributed actor D2 {
1538
// expected-error@-1{{actor 'D2' has no initializers}}
1639
let actorSystem: String
@@ -20,37 +43,24 @@ distributed actor D2 {
2043
}
2144

2245
distributed actor D3 {
23-
// expected-error@-1{{type 'D3' does not conform to protocol 'Identifiable'}}
24-
// expected-error@-2{{type 'D3' does not conform to protocol 'DistributedActor'}}
25-
// Codable synthesis also will fail since the ID mismatch:
26-
// expected-error@-4{{type 'D3' does not conform to protocol 'Decodable'}}
27-
// expected-error@-5{{type 'D3' does not conform to protocol 'Encodable'}}
28-
2946
var id: Int { 0 }
3047
// expected-error@-1{{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}}
31-
// expected-error@-2{{invalid redeclaration of synthesized property 'id'}}
32-
// expected-note@-3{{matching requirement 'id' to this declaration inferred associated type to 'Int'}}
48+
// expected-error@-2{{invalid redeclaration of synthesized implementation for protocol requirement 'id'}}
3349
}
3450

3551
struct OtherActorIdentity: Sendable, Hashable, Codable {}
3652

3753
distributed actor D4 {
3854
// expected-error@-1{{actor 'D4' has no initializers}}
39-
// expected-error@-2{{type 'D4' does not conform to protocol 'DistributedActor'}}
40-
// expected-error@-3{{type 'D4' does not conform to protocol 'Identifiable'}}
41-
// Codable synthesis also will fail since the ID errors:
42-
// expected-error@-5{{type 'D4' does not conform to protocol 'Decodable'}}
43-
// expected-error@-6{{type 'D4' does not conform to protocol 'Encodable'}}
4455

4556
let actorSystem: String
4657
// expected-error@-1{{property 'actorSystem' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}}
47-
// expected-error@-2 {{invalid redeclaration of synthesized property 'actorSystem'}}
58+
// expected-error@-2{{invalid redeclaration of synthesized implementation for protocol requirement 'actorSystem'}}
4859
// expected-note@-3{{stored property 'actorSystem' without initial value prevents synthesized initializers}}
4960
let id: OtherActorIdentity
5061
// expected-error@-1{{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}}
51-
// expected-error@-2{{invalid redeclaration of synthesized property 'id'}}
62+
// expected-error@-2{{invalid redeclaration of synthesized implementation for protocol requirement 'id'}}
5263
// expected-note@-3{{stored property 'id' without initial value prevents synthesized initializers}}
53-
// expected-note@-4{{matching requirement 'id' to this declaration inferred associated type to 'OtherActorIdentity'}}
5464
}
5565

5666
protocol P1: DistributedActor {

0 commit comments

Comments
 (0)