Skip to content

Commit 3593562

Browse files
authored
Merge pull request #69296 from ktoso/pick-wip-handle-library-evolution-mode-with-distributed-actors
2 parents c0df6cc + fb4bcfe commit 3593562

File tree

5 files changed

+190
-10
lines changed

5 files changed

+190
-10
lines changed

lib/AST/ASTContext.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1469,7 +1469,11 @@ AbstractFunctionDecl *ASTContext::getOnReturnOnDistributedTargetInvocationResult
14691469

14701470
FuncDecl *ASTContext::getDoneRecordingOnDistributedInvocationEncoder(
14711471
NominalTypeDecl *nominal) const {
1472-
for (auto result : nominal->lookupDirect(Id_doneRecording)) {
1472+
1473+
llvm::SmallVector<ValueDecl *, 2> results;
1474+
nominal->lookupQualified(nominal, DeclNameRef(Id_doneRecording),
1475+
SourceLoc(), NL_QualifiedDefault, results);
1476+
for (auto result : results) {
14731477
auto *fd = dyn_cast<FuncDecl>(result);
14741478
if (!fd)
14751479
continue;

lib/IRGen/GenDistributed.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,9 @@ ArgumentDecoderInfo DistributedAccessor::findArgumentDecoder(
817817
// is passed indirectly. This is good for structs and enums because
818818
// `decodeNextArgument` is a mutating method, but not for classes because
819819
// in that case heap object is mutated directly.
820-
if (isa<ClassDecl>(decoderDecl)) {
820+
bool usesDispatchThunk = false;
821+
822+
if (auto classDecl = dyn_cast<ClassDecl>(decoderDecl)) {
821823
auto selfTy = methodTy->getSelfParameter().getSILStorageType(
822824
IGM.getSILModule(), methodTy, expansionContext);
823825

@@ -836,15 +838,28 @@ ArgumentDecoderInfo DistributedAccessor::findArgumentDecoder(
836838
instance);
837839

838840
decoder = instance.claimNext();
839-
}
840841

841-
auto *decodeSIL = IGM.getSILModule().lookUpFunction(SILDeclRef(decodeFn));
842-
auto *fnPtr = IGM.getAddrOfSILFunction(decodeSIL, NotForDefinition,
843-
/*isDynamicallyReplaceable=*/false);
842+
/// When using library evolution functions have another "dispatch thunk"
843+
/// so we must use this instead of the decodeFn directly.
844+
usesDispatchThunk =
845+
getMethodDispatch(decodeFn) == swift::MethodDispatch::Class &&
846+
classDecl->hasResilientMetadata();
847+
}
844848

845-
auto methodPtr = FunctionPointer::forDirect(
846-
classifyFunctionPointerKind(decodeSIL), fnPtr,
847-
/*secondaryValue=*/nullptr, signature);
849+
FunctionPointer methodPtr;
850+
851+
if (usesDispatchThunk) {
852+
auto fnPtr = IGM.getAddrOfDispatchThunk(SILDeclRef(decodeFn), NotForDefinition);
853+
methodPtr = FunctionPointer::createUnsigned(
854+
methodTy, fnPtr, signature, /*useSignature=*/true);
855+
} else {
856+
SILFunction *decodeSILFn = IGM.getSILModule().lookUpFunction(SILDeclRef(decodeFn));
857+
auto fnPtr = IGM.getAddrOfSILFunction(decodeSILFn, NotForDefinition,
858+
/*isDynamicallyReplaceable=*/false);
859+
methodPtr = FunctionPointer::forDirect(
860+
classifyFunctionPointerKind(decodeSILFn), fnPtr,
861+
/*secondaryValue=*/nullptr, signature);
862+
}
848863

849864
return {decoder, decoderTy, witnessTable, methodPtr, methodTy};
850865
}

lib/Sema/CodeSynthesisDistributedActor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ deriveBodyDistributed_thunk(AbstractFunctionDecl *thunk, void *context) {
524524
{
525525
auto doneRecordingDecl =
526526
C.getDoneRecordingOnDistributedInvocationEncoder(invocationEncoderDecl);
527+
assert(doneRecordingDecl && "Could not find 'doneRecording' ad-hoc requirement witness.");
527528
auto doneRecordingDeclRef =
528529
UnresolvedDeclRefExpr::createImplicit(C, doneRecordingDecl->getName());
529530

lib/Sema/TypeCheckDistributed.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ static AbstractFunctionDecl *findDistributedAdHocRequirement(
7171
return nullptr;
7272
}
7373

74-
for (auto value : decl->lookupDirect(identifier)) {
74+
llvm::SmallVector<ValueDecl *, 2> results;
75+
decl->lookupQualified(decl, DeclNameRef(identifier),
76+
SourceLoc(), NL_QualifiedDefault, results);
77+
for (auto value : results) {
7578
auto func = dyn_cast<AbstractFunctionDecl>(value);
7679
if (func && matchFn(func))
7780
return func;
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: distributed
6+
7+
// UNSUPPORTED: use_os_stdlib
8+
// UNSUPPORTED: back_deployment_runtime
9+
10+
import Distributed
11+
12+
typealias DefaultDistributedActorSystem = FakeActorSystem
13+
14+
distributed actor DA {
15+
distributed func test() {}
16+
}
17+
18+
func test() async throws {
19+
let system = DefaultDistributedActorSystem()
20+
21+
// CHECK: assign type:DA, address:ActorAddress(address: "xxx")
22+
// CHECK: ready actor:main.DA, address:ActorAddress(address: "xxx")
23+
let da = DA(actorSystem: system)
24+
try await da.test()
25+
// CHECK: resign address:ActorAddress(address: "xxx")
26+
}
27+
28+
@main struct Main {
29+
static func main() async {
30+
try! await test()
31+
}
32+
}
33+
34+
35+
// ==== Fake Transport ---------------------------------------------------------
36+
37+
struct ActorAddress: Hashable, Sendable, Codable {
38+
let address: String
39+
init(parse address : String) {
40+
self.address = address
41+
}
42+
43+
// Explicit implementations to make our TestEncoder/Decoder simpler
44+
init(from decoder: Decoder) throws {
45+
let container = try decoder.singleValueContainer()
46+
self.address = try container.decode(String.self)
47+
print("decode ActorAddress -> \(self)")
48+
}
49+
50+
func encode(to encoder: Encoder) throws {
51+
print("encode \(self)")
52+
var container = encoder.singleValueContainer()
53+
try container.encode(self.address)
54+
}
55+
}
56+
57+
final class FakeActorSystem: DistributedActorSystem {
58+
typealias ActorID = ActorAddress
59+
typealias InvocationDecoder = FakeInvocation
60+
typealias InvocationEncoder = FakeInvocation
61+
typealias SerializationRequirement = Codable
62+
typealias ResultHandler = FakeResultHandler
63+
64+
func resolve<Act>(id: ActorID, as actorType: Act.Type) throws -> Act?
65+
where Act: DistributedActor,
66+
Act.ID == ActorID {
67+
print("resolve type:\(actorType), address:\(id)")
68+
return nil
69+
}
70+
71+
func assignID<Act>(_ actorType: Act.Type) -> ActorID
72+
where Act: DistributedActor, Act.ID == ActorID {
73+
let address = ActorAddress(parse: "xxx")
74+
print("assign type:\(actorType), address:\(address)")
75+
return address
76+
}
77+
78+
func actorReady<Act>(_ actor: Act)
79+
where Act: DistributedActor,
80+
Act.ID == ActorID {
81+
print("ready actor:\(actor), address:\(actor.id)")
82+
}
83+
84+
func resignID(_ id: ActorID) {
85+
print("resign address:\(id)")
86+
}
87+
88+
func makeInvocationEncoder() -> InvocationEncoder {
89+
.init()
90+
}
91+
92+
func remoteCall<Act, Err, Res>(
93+
on actor: Act,
94+
target: RemoteCallTarget,
95+
invocation invocationEncoder: inout InvocationEncoder,
96+
throwing: Err.Type,
97+
returning: Res.Type
98+
) async throws -> Res
99+
where Act: DistributedActor,
100+
Act.ID == ActorID,
101+
Err: Error,
102+
Res: SerializationRequirement {
103+
fatalError("not implemented: \(#function)")
104+
}
105+
106+
func remoteCallVoid<Act, Err>(
107+
on actor: Act,
108+
target: RemoteCallTarget,
109+
invocation invocationEncoder: inout InvocationEncoder,
110+
throwing: Err.Type
111+
) async throws
112+
where Act: DistributedActor,
113+
Act.ID == ActorID,
114+
Err: Error {
115+
fatalError("not implemented: \(#function)")
116+
}
117+
118+
}
119+
120+
class EncoderBase: DistributedTargetInvocationEncoder {
121+
typealias SerializationRequirement = Codable
122+
123+
func recordGenericSubstitution<T>(_ type: T.Type) throws {}
124+
func recordArgument<Value: SerializationRequirement>(_ argument: RemoteCallArgument<Value>) throws {}
125+
func recordReturnType<R: SerializationRequirement>(_ type: R.Type) throws {}
126+
func recordErrorType<E: Error>(_ type: E.Type) throws {}
127+
func doneRecording() throws {}
128+
129+
}
130+
131+
class DecoderBase: EncoderBase, DistributedTargetInvocationDecoder {
132+
typealias SerializationRequirement = Codable
133+
134+
func decodeGenericSubstitutions() throws -> [Any.Type] { [] }
135+
func decodeNextArgument<Argument: SerializationRequirement>() throws -> Argument { fatalError() }
136+
func decodeReturnType() throws -> Any.Type? { nil }
137+
func decodeErrorType() throws -> Any.Type? { nil }
138+
}
139+
140+
class FakeInvocation: DecoderBase {}
141+
142+
@available(SwiftStdlib 5.5, *)
143+
public struct FakeResultHandler: DistributedTargetInvocationResultHandler {
144+
public typealias SerializationRequirement = Codable
145+
146+
public func onReturn<Success: SerializationRequirement>(value: Success) async throws {
147+
fatalError("Not implemented: \(#function)")
148+
}
149+
150+
public func onReturnVoid() async throws {
151+
fatalError("Not implemented: \(#function)")
152+
}
153+
154+
public func onThrow<Err: Error>(error: Err) async throws {
155+
fatalError("Not implemented: \(#function)")
156+
}
157+
}

0 commit comments

Comments
 (0)