Skip to content

Commit 37d46fa

Browse files
authored
Merge pull request #39985 from DougGregor/actor-transport-associated-type
Make the actor transport an associated type of the DistributedActor protocol
2 parents 137b2be + f83775b commit 37d46fa

30 files changed

+256
-115
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4417,6 +4417,8 @@ ERROR(actor_protocol_illegal_inheritance,none,
44174417
ERROR(distributed_actor_protocol_illegal_inheritance,none,
44184418
"non-distributed actor type %0 cannot conform to the 'DistributedActor' protocol",
44194419
(DeclName))
4420+
ERROR(broken_distributed_actor_requirement,none,
4421+
"DistributedActor protocol is broken: unexpected requirement", ())
44204422

44214423
ERROR(unowned_executor_outside_actor,none,
44224424
"'unownedExecutor' can only be implemented within the main "

include/swift/AST/KnownIdentifiers.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ IDENTIFIER(decode)
6565
IDENTIFIER(decodeIfPresent)
6666
IDENTIFIER(Decoder)
6767
IDENTIFIER(decoder)
68+
IDENTIFIER(DefaultActorTransport)
6869
IDENTIFIER_(Differentiation)
6970
IDENTIFIER_WITH_NAME(PatternMatchVar, "$match")
7071
IDENTIFIER(dynamicallyCall)
@@ -141,6 +142,7 @@ IDENTIFIER_WITH_NAME(SwiftObject, "_TtCs12_SwiftObject")
141142
IDENTIFIER(SwiftNativeNSObject)
142143
IDENTIFIER(to)
143144
IDENTIFIER(toRaw)
145+
IDENTIFIER(Transport)
144146
IDENTIFIER(Type)
145147
IDENTIFIER(type)
146148
IDENTIFIER(typeMismatch)

lib/SILGen/SILGenDistributed.cpp

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,45 @@ static VarDecl* lookupProperty(NominalTypeDecl *ty, DeclName name) {
4545
return dyn_cast<VarDecl>(refs.front());
4646
}
4747

48+
/// Emit a reference to a specific stored property of the actor.
49+
static SILValue emitActorPropertyReference(
50+
SILGenFunction &SGF, SILLocation loc, SILValue actorSelf,
51+
VarDecl *property) {
52+
Type formalType = SGF.F.mapTypeIntoContext(property->getInterfaceType());
53+
SILType loweredType = SGF.getLoweredType(formalType).getAddressType();
54+
#if false
55+
if (!loweredType.isAddress()) {
56+
loweredType = SILType::getPrimitiveAddressType(
57+
formalType->getCanonicalType());
58+
}
59+
#endif
60+
return SGF.B.createRefElementAddr(loc, actorSelf, property, loweredType);
61+
}
62+
4863
/// Perform an initializing store to the given property using the value
4964
/// \param actorSelf the value representing `self` for the actor instance.
5065
/// \param prop the property to be initialized.
5166
/// \param value the value to use when initializing the property.
5267
static void initializeProperty(SILGenFunction &SGF, SILLocation loc,
5368
SILValue actorSelf,
5469
VarDecl* prop, SILValue value) {
55-
CanType propType = SGF.F.mapTypeIntoContext(prop->getInterfaceType())
56-
->getCanonicalType();
57-
SILType loweredPropType = SGF.getLoweredType(propType);
58-
auto fieldAddr = SGF.B.createRefElementAddr(loc, actorSelf, prop, loweredPropType);
70+
Type formalType = SGF.F.mapTypeIntoContext(prop->getInterfaceType());
71+
SILType loweredType = SGF.getLoweredType(formalType);
72+
73+
auto fieldAddr = emitActorPropertyReference(SGF, loc, actorSelf, prop);
5974

60-
if (fieldAddr->getType().isAddress())
75+
if (loweredType.isAddressOnly(SGF.F)) {
6176
SGF.B.createCopyAddr(loc, value, fieldAddr, IsNotTake, IsInitialization);
62-
else
63-
SGF.B.emitStoreValueOperation(loc, value, fieldAddr, StoreOwnershipQualifier::Init);
77+
} else {
78+
if (value->getType().isAddress()) {
79+
value = SGF.B.createTrivialLoadOr(
80+
loc, value, LoadOwnershipQualifier::Copy);
81+
}
82+
83+
value = SGF.B.emitCopyValueOperation(loc, value);
84+
SGF.B.emitStoreValueOperation(
85+
loc, value, fieldAddr, StoreOwnershipQualifier::Init);
86+
}
6487
}
6588

6689
/******************************************************************************/
@@ -355,26 +378,20 @@ void SILGenFunction::emitResignIdentityCall(SILLocation loc,
355378
FormalEvaluationScope scope(*this);
356379

357380
// ==== locate: self.id
358-
auto *idVarDeclRef = lookupProperty(actorDecl, ctx.Id_id);
359-
CanType idType = F.mapTypeIntoContext(
360-
idVarDeclRef->getInterfaceType())->getCanonicalType();
361-
auto idRef = B.createRefElementAddr(loc, actorSelf, idVarDeclRef,
362-
getLoweredType(idType));
381+
auto idRef = emitActorPropertyReference(
382+
*this, loc, actorSelf.getValue(), lookupProperty(actorDecl, ctx.Id_id));
363383

364384
// ==== locate: self.actorTransport
365-
auto transportVarDeclRef = lookupProperty(actorDecl, ctx.Id_actorTransport);
366-
CanType transportType = F.mapTypeIntoContext(
367-
transportVarDeclRef->getInterfaceType())->getCanonicalType();
368-
auto transportRef =
369-
B.createRefElementAddr(loc, actorSelf, transportVarDeclRef,
370-
getLoweredType(transportType));
385+
auto transportRef = emitActorPropertyReference(
386+
*this, loc, actorSelf.getValue(),
387+
lookupProperty(actorDecl, ctx.Id_actorTransport));
371388

372389
// Perform the call.
373390
emitActorTransportWitnessCall(
374391
B, loc, ctx.Id_resignIdentity,
375-
transportRef.getValue(),
392+
transportRef,
376393
SILType(),
377-
{ idRef.getValue() });
394+
{ idRef });
378395
}
379396

380397
void

lib/SILOptimizer/Utils/DistributedActor.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ void emitActorTransportWitnessCall(
131131
if (methodSILFnTy->getSelfParameter().isFormalIndirect() &&
132132
!transport->getType().isAddress()) {
133133
auto buf = B.createAllocStack(loc, transport->getType(), None);
134+
transport = B.emitCopyValueOperation(loc, transport);
134135
B.emitStoreValueOperation(
135136
loc, transport, buf, StoreOwnershipQualifier::Init);
136137
temporaryTransportBuffer = SILValue(buf);
@@ -169,18 +170,12 @@ void emitActorTransportWitnessCall(
169170
// If we had to create a buffer to pass the transport
170171
if (temporaryTransportBuffer) {
171172
emitCleanup([&](SILBuilder & builder) {
173+
auto value = builder.emitLoadValueOperation(
174+
loc, *temporaryTransportBuffer, LoadOwnershipQualifier::Take);
175+
builder.emitDestroyValueOperation(loc, value);
172176
builder.createDeallocStack(loc, *temporaryTransportBuffer);
173177
});
174178
}
175-
176-
// If we opened an existential, then destroy the existential.
177-
#if false
178-
if (openedExistential) {
179-
emitCleanup([&](SILBuilder & builder) {
180-
builder.emitDestroyAddr(loc, transport);
181-
});
182-
}
183-
#endif
184179
}
185180

186181
void emitActorReadyCall(SILBuilder &B, SILLocation loc, SILValue actor,

lib/Sema/DerivedConformanceDistributedActor.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ static FuncDecl *deriveDistributedActor_resolve(DerivedConformance &derived) {
6161
return param;
6262
};
6363

64-
Type addressType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType();
65-
Type transportType = getDistributedActorTransportType(decl);
64+
auto addressType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType();
65+
auto transportType = getDistributedActorTransportType(decl);
6666

6767
// (_ identity: AnyActorIdentity, using transport: ActorTransport)
6868
auto *params = ParameterList::create(
@@ -133,10 +133,11 @@ static ValueDecl *deriveDistributedActor_actorTransport(
133133

134134
// ```
135135
// nonisolated
136-
// let actorTransport: ActorTransport
136+
// let actorTransport: Transport
137137
// ```
138138
// (no need for @actorIndependent because it is an immutable let)
139-
Type propertyType = getDistributedActorTransportType(derived.Nominal);
139+
auto propertyType = getDistributedActorTransportType(derived.Nominal);
140+
140141
VarDecl *propDecl;
141142
PatternBindingDecl *pbDecl;
142143
std::tie(propDecl, pbDecl) = derived.declareDerivedProperty(
@@ -154,6 +155,36 @@ static ValueDecl *deriveDistributedActor_actorTransport(
154155
return propDecl;
155156
}
156157

158+
static Type deriveDistributedActor_Transport(
159+
DerivedConformance &derived) {
160+
assert(derived.Nominal->isDistributedActor());
161+
auto &C = derived.Context;
162+
163+
// Look for a type DefaultActorTransport within the parent context.
164+
auto defaultTransportLookup = TypeChecker::lookupUnqualified(
165+
derived.getConformanceContext()->getModuleScopeContext(),
166+
DeclNameRef(C.Id_DefaultActorTransport),
167+
derived.ConformanceDecl->getLoc());
168+
TypeDecl *defaultTransportTypeDecl = nullptr;
169+
for (const auto &found : defaultTransportLookup) {
170+
if (auto foundType = dyn_cast_or_null<TypeDecl>(found.getValueDecl())) {
171+
if (defaultTransportTypeDecl) {
172+
// Note: ambiguity, for now just fail.
173+
return nullptr;
174+
}
175+
176+
defaultTransportTypeDecl = foundType;
177+
continue;
178+
}
179+
}
180+
181+
// There is no default, so fail to synthesize.
182+
if (!defaultTransportTypeDecl)
183+
return nullptr;
184+
185+
// Return the default transport type.
186+
return defaultTransportTypeDecl->getDeclaredInterfaceType();
187+
}
157188
// ==== ------------------------------------------------------------------------
158189

159190
ValueDecl *DerivedConformance::deriveDistributedActor(ValueDecl *requirement) {
@@ -174,3 +205,17 @@ ValueDecl *DerivedConformance::deriveDistributedActor(ValueDecl *requirement) {
174205

175206
return nullptr;
176207
}
208+
209+
std::pair<Type, TypeDecl *> DerivedConformance::deriveDistributedActor(
210+
AssociatedTypeDecl *assocType) {
211+
if (!canDeriveDistributedActor(Nominal, cast<DeclContext>(ConformanceDecl)))
212+
return std::make_pair(Type(), nullptr);
213+
214+
if (assocType->getName() == Context.Id_Transport) {
215+
return std::make_pair(deriveDistributedActor_Transport(*this), nullptr);
216+
}
217+
218+
Context.Diags.diagnose(assocType->getLoc(),
219+
diag::broken_distributed_actor_requirement);
220+
return std::make_pair(Type(), nullptr);
221+
}

lib/Sema/DerivedConformances.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,12 @@ class DerivedConformance {
311311
/// \returns the derived member, which will also be added to the type.
312312
ValueDecl *deriveDistributedActor(ValueDecl *requirement);
313313

314+
/// Derive a DistributedActor associated type for a distributed actor.
315+
///
316+
/// \returns the derived type member, which will also be added to the type.
317+
std::pair<Type, TypeDecl *> deriveDistributedActor(
318+
AssociatedTypeDecl *assocType);
319+
314320
/// Determine if \c Actor can be derived for the given type.
315321
static bool canDeriveActor(DeclContext *DC, NominalTypeDecl *NTD);
316322

lib/Sema/TypeCheckDistributed.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,20 +184,15 @@ void swift::checkDistributedActorConstructor(const ClassDecl *decl, ConstructorD
184184
if (!ctor->isDesignatedInit())
185185
return;
186186

187-
// === Designated initializers must accept exactly one ActorTransport
188-
auto &C = ctor->getASTContext();
189-
auto module = ctor->getParentModule();
190-
187+
// === Designated initializers must accept exactly one actor transport that
188+
// matches the actor transport type of the actor.
191189
SmallVector<ParamDecl*, 2> transportParams;
192190
int transportParamsCount = 0;
193-
auto protocolDecl = C.getProtocol(KnownProtocolKind::ActorTransport);
194-
auto protocolTy = protocolDecl->getDeclaredInterfaceType();
195-
191+
Type transportTy = ctor->mapTypeIntoContext(
192+
getDistributedActorTransportType(const_cast<ClassDecl *>(decl)));
196193
for (auto param : *ctor->getParameters()) {
197194
auto paramTy = ctor->mapTypeIntoContext(param->getInterfaceType());
198-
auto conformance = TypeChecker::conformsToProtocol(paramTy, protocolDecl, module);
199-
200-
if (paramTy->isEqual(protocolTy) || !conformance.isInvalid()) {
195+
if (paramTy->isEqual(transportTy)) {
201196
transportParamsCount += 1;
202197
transportParams.push_back(param);
203198
}
@@ -252,9 +247,13 @@ Type swift::getDistributedActorTransportType(NominalTypeDecl *actor) {
252247
assert(actor->isDistributedActor());
253248
auto &ctx = actor->getASTContext();
254249

255-
auto protocol = ctx.getProtocol(KnownProtocolKind::ActorTransport);
250+
auto protocol = ctx.getProtocol(KnownProtocolKind::DistributedActor);
256251
if (!protocol)
257252
return ErrorType::get(ctx);
258253

259-
return protocol->getDeclaredInterfaceType();
254+
// Dig out the actor transport type.
255+
auto module = actor->getParentModule();
256+
Type selfType = actor->getSelfInterfaceType();
257+
auto conformance = module->lookupConformance(selfType, protocol);
258+
return conformance.getTypeWitnessByName(selfType, ctx.Id_Transport);
260259
}

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6607,6 +6607,8 @@ TypeChecker::deriveTypeWitness(DeclContext *DC,
66076607
return std::make_pair(derived.deriveCaseIterable(AssocType), nullptr);
66086608
case KnownProtocolKind::Differentiable:
66096609
return derived.deriveDifferentiable(AssocType);
6610+
case KnownProtocolKind::DistributedActor:
6611+
return derived.deriveDistributedActor(AssocType);
66106612
default:
66116613
return std::make_pair(nullptr, nullptr);
66126614
}

stdlib/public/Distributed/ActorTransport.swift

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,47 @@ public protocol ActorTransport: Sendable {
6767
func assignIdentity<Act>(_ actorType: Act.Type) -> AnyActorIdentity
6868
where Act: DistributedActor
6969

70-
func actorReady<Act>(_ actor: Act) where Act: DistributedActor
70+
func actorReady<Act>(_ actor: Act)
71+
where Act: DistributedActor
7172

7273
/// Called during actor deinit/destroy.
7374
func resignIdentity(_ id: AnyActorIdentity)
75+
}
76+
77+
@available(SwiftStdlib 5.6, *)
78+
public struct AnyActorTransport: ActorTransport {
79+
let transport: ActorTransport
80+
81+
public init<Transport: ActorTransport>(_ transport: Transport) {
82+
self.transport = transport
83+
}
84+
85+
public func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity {
86+
return try transport.decodeIdentity(from: decoder)
87+
}
7488

89+
public func resolve<Act>(
90+
_ identity: AnyActorIdentity, as actorType: Act.Type
91+
) throws -> Act? where Act: DistributedActor {
92+
return try transport.resolve(identity, as: actorType)
93+
}
94+
95+
public func assignIdentity<Act>(_ actorType: Act.Type) -> AnyActorIdentity
96+
where Act: DistributedActor {
97+
return transport.assignIdentity(actorType)
98+
}
99+
100+
public func actorReady<Act>(_ actor: Act)
101+
where Act: DistributedActor {
102+
transport.actorReady(actor)
103+
}
104+
105+
/// Called during actor deinit/destroy.
106+
public func resignIdentity(_ id: AnyActorIdentity) {
107+
transport.resignIdentity(id)
108+
}
75109
}
110+
111+
/// Use the existential wrapper as the default actor transport.
112+
@available(SwiftStdlib 5.6, *)
113+
public typealias DefaultActorTransport = AnyActorTransport

stdlib/public/Distributed/DistributedActor.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public protocol AnyActor: Sendable, AnyObject {}
3636
@available(SwiftStdlib 5.6, *)
3737
public protocol DistributedActor:
3838
AnyActor, Sendable, Identifiable, Hashable, Codable {
39+
/// The type of transport used to communicate with actors of this type.
40+
associatedtype Transport: ActorTransport
41+
3942
/// Resolves the passed in `identity` against the `transport`, returning
4043
/// either a local or remote actor reference.
4144
///
@@ -52,7 +55,7 @@ public protocol DistributedActor:
5255
// We want to move to accepting a generic or existential identity here
5356
// static func resolve<Identity>(_ identity: Identity, using transport: ActorTransport)
5457
// throws -> Self where Identity: ActorIdentity
55-
static func resolve(_ identity: AnyActorIdentity, using transport: ActorTransport)
58+
static func resolve(_ identity: AnyActorIdentity, using transport: Transport)
5659
throws -> Self
5760

5861
/// The `ActorTransport` associated with this actor.
@@ -61,7 +64,7 @@ public protocol DistributedActor:
6164
///
6265
/// Conformance to this requirement is synthesized automatically for any
6366
/// `distributed actor` declaration.
64-
nonisolated var actorTransport: ActorTransport { get } // TODO: rename to `transport`?
67+
nonisolated var actorTransport: Transport { get } // TODO: rename to `transport`?
6568

6669
/// Logical identity of this distributed actor.
6770
///
@@ -100,9 +103,9 @@ extension CodingUserInfoKey {
100103
@available(SwiftStdlib 5.6, *)
101104
extension DistributedActor {
102105
nonisolated public init(from decoder: Decoder) throws {
103-
guard let transport = decoder.userInfo[.actorTransportKey] as? ActorTransport else {
106+
guard let transport = decoder.userInfo[.actorTransportKey] as? Transport else {
104107
throw DistributedActorCodingError(message:
105-
"Missing ActorTransport (for key .actorTransportKey) " +
108+
"Missing Transport (for key .actorTransportKey) " +
106109
"in Decoder.userInfo, while decoding \(Self.self).")
107110
}
108111

test/Distributed/Runtime/distributed_actor_decode.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ struct FakeTransport: ActorTransport {
6666
}
6767
}
6868

69+
@available(SwiftStdlib 5.5, *)
70+
typealias DefaultActorTransport = FakeTransport
71+
6972
// ==== Test Coding ------------------------------------------------------------
7073

7174
class TestEncoder: Encoder {

0 commit comments

Comments
 (0)