Skip to content

Commit 738ac14

Browse files
committed
[IRGen] Distributed: Dispatch decodeNextArgument via witness thunk if concrete decoder is not available
Since distributed actors can now be generic over actor system it's no longer guaranteed for concrete decoder to be present, we need to handle that case specifically during accessor emission.
1 parent c0dc888 commit 738ac14

File tree

3 files changed

+93
-22
lines changed

3 files changed

+93
-22
lines changed

lib/IRGen/GenDistributed.cpp

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -81,44 +81,48 @@ struct ArgumentDecoderInfo {
8181
/// The type of `decodeNextArgument` method.
8282
CanSILFunctionType MethodType;
8383

84-
/// Protocol requirements associated with the generic
85-
/// parameter `Argument` of this decode method.
86-
GenericSignature::RequiredProtocols ProtocolRequirements;
87-
88-
// Witness metadata for conformance to DistributedTargetInvocationDecoder
89-
// protocol.
84+
/// Witness metadata for conformance to DistributedTargetInvocationDecoder
85+
/// protocol.
9086
WitnessMetadata Witness;
9187

88+
/// Indicates whether `decodeNextArgument` is referenced through
89+
/// a protocol witness thunk.
90+
bool UsesWitnessDispatch;
91+
9292
ArgumentDecoderInfo(llvm::Value *decoder, llvm::Value *decoderType,
9393
llvm::Value *decoderWitnessTable,
9494
FunctionPointer decodeNextArgumentPtr,
95-
CanSILFunctionType decodeNextArgumentTy)
95+
CanSILFunctionType decodeNextArgumentTy,
96+
bool usesWitnessDispatch)
9697
: Decoder(decoder), MethodPtr(decodeNextArgumentPtr),
9798
MethodType(decodeNextArgumentTy),
98-
ProtocolRequirements(findProtocolRequirements(decodeNextArgumentTy)) {
99+
UsesWitnessDispatch(usesWitnessDispatch) {
99100
Witness.SelfMetadata = decoderType;
100101
Witness.SelfWitnessTable = decoderWitnessTable;
101102
}
102103

103104
CanSILFunctionType getMethodType() const { return MethodType; }
104105

105-
ArrayRef<ProtocolDecl *> getProtocolRequirements() const {
106-
return ProtocolRequirements;
106+
WitnessMetadata *getWitnessMetadata() const {
107+
return const_cast<WitnessMetadata *>(&Witness);
107108
}
108109

109-
/// Form a callee to a decode method - `decodeNextArgument`.
110-
Callee getCallee() const;
110+
/// Protocol requirements associated with the generic
111+
/// parameter `Argument` of this decode method.
112+
GenericSignature::RequiredProtocols getProtocolRequirements() const {
113+
if (UsesWitnessDispatch)
114+
return {};
111115

112-
private:
113-
static GenericSignature::RequiredProtocols
114-
findProtocolRequirements(CanSILFunctionType decodeMethodTy) {
115-
auto signature = decodeMethodTy->getInvocationGenericSignature();
116+
auto signature = MethodType->getInvocationGenericSignature();
116117
auto genericParams = signature.getGenericParams();
117118

118119
// func decodeNextArgument<Arg : #SerializationRequirement#>() throws -> Arg
119120
assert(genericParams.size() == 1);
120121
return signature->getRequiredProtocols(genericParams.front());
121122
}
123+
124+
/// Form a callee to a decode method - `decodeNextArgument`.
125+
Callee getCallee() const;
122126
};
123127

124128
class DistributedAccessor {
@@ -415,7 +419,8 @@ void DistributedAccessor::decodeArgument(unsigned argumentIdx,
415419
emission->begin();
416420
{
417421
emission->setArgs(decodeArgs, /*isOutlined=*/false,
418-
/*witnessMetadata=*/nullptr);
422+
decoder.UsesWitnessDispatch ? decoder.getWitnessMetadata()
423+
: nullptr);
419424

420425
Explosion result;
421426
emission->emitToExplosion(result, /*isOutlined=*/false);
@@ -519,6 +524,9 @@ void DistributedAccessor::decodeArgument(unsigned argumentIdx,
519524
void DistributedAccessor::lookupWitnessTables(
520525
llvm::Value *value, ArrayRef<ProtocolDecl *> protocols,
521526
Explosion &witnessTables) {
527+
if (protocols.empty())
528+
return;
529+
522530
auto conformsToProtocol = IGM.getConformsToProtocolFunctionPointer();
523531

524532
for (auto *protocol : protocols) {
@@ -785,11 +793,32 @@ DistributedAccessor::getCalleeForDistributedTarget(llvm::Value *self) const {
785793

786794
ArgumentDecoderInfo DistributedAccessor::findArgumentDecoder(
787795
llvm::Value *decoder, llvm::Value *decoderTy, llvm::Value *witnessTable) {
796+
auto &C = IGM.Context;
788797
auto *actor = getDistributedActorOf(Target);
789798
auto expansionContext = IGM.getMaximalTypeExpansionContext();
790799

791-
auto *decodeFn = IGM.Context.getDistributedActorArgumentDecodingMethod(actor);
792-
assert(decodeFn && "no suitable decoder?");
800+
auto *decodeFn = C.getDistributedActorArgumentDecodingMethod(actor);
801+
802+
// If distributed actor is generic over actor system, we have to
803+
// use witness to reference `decodeNextArgument`.
804+
if (!decodeFn) {
805+
auto decoderProtocol = C.getDistributedTargetInvocationDecoderDecl();
806+
auto decodeNextArgRequirement =
807+
decoderProtocol->getSingleRequirement(C.Id_decodeNextArgument);
808+
assert(decodeNextArgRequirement);
809+
SILDeclRef decodeNextArgumentRef(decodeNextArgRequirement);
810+
811+
llvm::Constant *fnPtr =
812+
IGM.getAddrOfDispatchThunk(decodeNextArgumentRef, NotForDefinition);
813+
auto fnType = IGM.getSILTypes().getConstantFunctionType(
814+
IGM.getMaximalTypeExpansionContext(), decodeNextArgumentRef);
815+
816+
auto sig = IGM.getSignature(fnType);
817+
auto fn = FunctionPointer::forDirect(fnType, fnPtr,
818+
/*secondaryValue=*/nullptr, sig, true);
819+
return {decoder, decoderTy, witnessTable,
820+
fn, fnType, /*usesWitnessDispatch=*/true};
821+
}
793822

794823
auto methodTy = IGM.getSILTypes().getConstantFunctionType(
795824
expansionContext, SILDeclRef(decodeFn));
@@ -848,7 +877,8 @@ ArgumentDecoderInfo DistributedAccessor::findArgumentDecoder(
848877
/*secondaryValue=*/nullptr, signature);
849878
}
850879

851-
return {decoder, decoderTy, witnessTable, methodPtr, methodTy};
880+
return {decoder, decoderTy, witnessTable,
881+
methodPtr, methodTy, /*usesWitnessDispatch=*/false};
852882
}
853883

854884
SILType DistributedAccessor::getResultType() const {

lib/Sema/TypeCheckDistributed.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,10 @@ GetDistributedActorArgumentDecodingMethodRequest::evaluate(Evaluator &evaluator,
869869
auto &ctx = actor->getASTContext();
870870

871871
auto *decoder = ctx.getDistributedActorInvocationDecoder(actor);
872-
assert(decoder);
872+
// If distributed actor is generic over actor system, there is not
873+
// going to be a concrete decoder.
874+
if (!decoder)
875+
return nullptr;
873876

874877
auto decoderTy = decoder->getDeclaredInterfaceType();
875878

@@ -942,4 +945,4 @@ GetDistributedMethodWitnessedProtocolRequirements::evaluate(
942945
}
943946

944947
return C.AllocateCopy(result);
945-
}
948+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out | %FileCheck %s --color
5+
6+
// REQUIRES: executable_test
7+
// REQUIRES: concurrency
8+
// REQUIRES: distributed
9+
10+
// rdar://76038845
11+
// UNSUPPORTED: use_os_stdlib
12+
// UNSUPPORTED: back_deployment_runtime
13+
14+
// rdar://90373022
15+
// UNSUPPORTED: OS=watchos
16+
17+
import Distributed
18+
19+
distributed actor Worker<ActorSystem> where ActorSystem: DistributedActorSystem<any Codable>, ActorSystem.ActorID: Codable {
20+
distributed func hi(name: String) {
21+
print("Hi, \(name)!")
22+
}
23+
24+
nonisolated var description: Swift.String {
25+
"Worker(\(id))"
26+
}
27+
}
28+
29+
// ==== Execute ----------------------------------------------------------------
30+
@main struct Main {
31+
static func main() async throws {
32+
let system = LocalTestingDistributedActorSystem()
33+
34+
let actor = Worker(actorSystem: system)
35+
try await actor.hi(name: "P") // local calls should still just work
36+
// CHECK: Hi, P!
37+
}
38+
}

0 commit comments

Comments
 (0)