mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Eliminate the required use of existentials in distributed actors by introducing the `Transport` associated type into the `DistributedActor` protocol. Each distributed actor has a known (concrete) actor transport type, reducing storage requirements and transport dynamism when it isn't needed. Distributed actors can manually specify their `Transport` associated type or pick up a default by looking for a type named `DefaultActorTransport`. A library that vends an actor transport can make create a public typealias `DefaultActorTransport` referring to its transport, so importing that library and defining a distributed actor will use that library's transport. Introduce a type-erased `AnyActorTransport` type to provide an explicitly dynamic actor transport. This is still an important option, e.g., for cases where one wants to be able to dynamically change the transport for testing or different kinds of deployment. For now, we default to this transport in the library (via `DefaultActorTransport`), but we may very well want to eliminate this because it will be ambiguous with client libraries that vend their own `DefaultActorTransport`.
192 lines
6.4 KiB
C++
192 lines
6.4 KiB
C++
//===-- DistributedActor.cpp - SIL utils for distributed actors -*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SILOptimizer/Utils/DistributedActor.h"
|
|
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILLocation.h"
|
|
|
|
namespace swift {
|
|
|
|
SILArgument *findFirstActorTransportArg(SILFunction &F) {
|
|
auto *module = F.getModule().getSwiftModule();
|
|
auto &C = F.getASTContext();
|
|
|
|
auto *transportProto = C.getProtocol(KnownProtocolKind::ActorTransport);
|
|
Type transportTy = transportProto->getDeclaredInterfaceType();
|
|
|
|
for (auto arg : F.getArguments()) {
|
|
// TODO(distributed): also be able to locate a generic transport
|
|
Type argTy = arg->getType().getASTType();
|
|
auto argDecl = arg->getDecl();
|
|
|
|
auto conformsToTransport =
|
|
module->lookupConformance(argDecl->getInterfaceType(), transportProto);
|
|
|
|
// Is it a protocol that conforms to ActorTransport?
|
|
if (argTy->isEqual(transportTy) || conformsToTransport) {
|
|
return arg;
|
|
}
|
|
|
|
// Is it some specific ActorTransport?
|
|
auto result = module->lookupConformance(argTy, transportProto);
|
|
if (!result.isInvalid()) {
|
|
return arg;
|
|
}
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
llvm_unreachable("Missing required ActorTransport argument!");
|
|
#endif
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void emitActorTransportWitnessCall(
|
|
SILBuilder &B, SILLocation loc, DeclName methodName,
|
|
SILValue transport, SILType actorType, ArrayRef<SILValue> args,
|
|
Optional<std::pair<SILBasicBlock *, SILBasicBlock *>> tryTargets) {
|
|
auto &F = B.getFunction();
|
|
auto &M = B.getModule();
|
|
auto &C = F.getASTContext();
|
|
|
|
// Dig out the conformance to ActorTransport.
|
|
ProtocolDecl *transProto = C.getProtocol(KnownProtocolKind::ActorTransport);
|
|
assert(transProto);
|
|
auto transportASTType = transport->getType().getASTType();
|
|
auto *module = M.getSwiftModule();
|
|
ProtocolConformanceRef transportConfRef;
|
|
|
|
// If the transport is an existential open it.
|
|
bool openedExistential = false;
|
|
if (transportASTType->isAnyExistentialType()) {
|
|
OpenedArchetypeType *opened;
|
|
transportASTType =
|
|
transportASTType->openAnyExistentialType(opened)->getCanonicalType();
|
|
transport = B.createOpenExistentialAddr(
|
|
loc, transport, F.getLoweredType(transportASTType),
|
|
OpenedExistentialAccess::Immutable);
|
|
openedExistential = true;
|
|
}
|
|
|
|
if (transportASTType->isTypeParameter() ||
|
|
transportASTType->is<ArchetypeType>()) {
|
|
transportConfRef = ProtocolConformanceRef(transProto);
|
|
} else {
|
|
transportConfRef = module->lookupConformance(transportASTType, transProto);
|
|
}
|
|
|
|
assert(!transportConfRef.isInvalid() &&
|
|
"Missing conformance to `ActorTransport`");
|
|
|
|
// Dig out the method.
|
|
auto method = cast<FuncDecl>(transProto->getSingleRequirement(methodName));
|
|
auto methodRef = SILDeclRef(method, SILDeclRef::Kind::Func);
|
|
auto methodSILTy =
|
|
M.Types.getConstantInfo(B.getTypeExpansionContext(), methodRef)
|
|
.getSILType();
|
|
|
|
auto witnessMethod = B.createWitnessMethod(
|
|
loc, transportASTType, transportConfRef, methodRef, methodSILTy);
|
|
|
|
// prepare conformance substitutions
|
|
SubstitutionMap subs;
|
|
{
|
|
auto genericSig = method->getGenericSignature();
|
|
SmallVector<Type, 2> subTypes;
|
|
SmallVector<ProtocolConformanceRef, 2> subConformances;
|
|
subTypes.push_back(transportASTType);
|
|
subConformances.push_back(transportConfRef);
|
|
if (actorType) {
|
|
ProtocolDecl *actorProto = C.getProtocol(
|
|
KnownProtocolKind::DistributedActor);
|
|
assert(actorProto);
|
|
|
|
ProtocolConformanceRef conformance;
|
|
auto distributedActorConfRef = module->lookupConformance(
|
|
actorType.getASTType(), actorProto);
|
|
assert(!distributedActorConfRef.isInvalid() &&
|
|
"Missing conformance to `DistributedActor`");
|
|
subTypes.push_back(actorType.getASTType());
|
|
subConformances.push_back(distributedActorConfRef);
|
|
}
|
|
|
|
subs = SubstitutionMap::get(genericSig, subTypes, subConformances);
|
|
}
|
|
|
|
// If the self parameter is indirect but the transport is a value, put it
|
|
// into a temporary allocation.
|
|
auto methodSILFnTy = methodSILTy.castTo<SILFunctionType>();
|
|
Optional<SILValue> temporaryTransportBuffer;
|
|
if (methodSILFnTy->getSelfParameter().isFormalIndirect() &&
|
|
!transport->getType().isAddress()) {
|
|
auto buf = B.createAllocStack(loc, transport->getType(), None);
|
|
transport = B.emitCopyValueOperation(loc, transport);
|
|
B.emitStoreValueOperation(
|
|
loc, transport, buf, StoreOwnershipQualifier::Init);
|
|
temporaryTransportBuffer = SILValue(buf);
|
|
}
|
|
|
|
// Call the method.
|
|
SmallVector<SILValue, 2> allArgs(args.begin(), args.end());
|
|
allArgs.push_back(
|
|
temporaryTransportBuffer ? *temporaryTransportBuffer : transport);
|
|
|
|
SILInstruction *apply;
|
|
if (tryTargets) {
|
|
apply = B.createTryApply(
|
|
loc, witnessMethod, subs, allArgs, tryTargets->first,
|
|
tryTargets->second);
|
|
} else {
|
|
apply = B.createApply(loc, witnessMethod, subs, allArgs);
|
|
}
|
|
|
|
// Local function to emit a cleanup after the call.
|
|
auto emitCleanup = [&](llvm::function_ref<void(SILBuilder &builder)> fn) {
|
|
if (tryTargets) {
|
|
{
|
|
SILBuilderWithScope normalBuilder(tryTargets->first, apply);
|
|
fn(normalBuilder);
|
|
}
|
|
{
|
|
SILBuilderWithScope errorBuilder(tryTargets->second, apply);
|
|
fn(errorBuilder);
|
|
}
|
|
} else {
|
|
fn(B);
|
|
}
|
|
};
|
|
|
|
// If we had to create a buffer to pass the transport
|
|
if (temporaryTransportBuffer) {
|
|
emitCleanup([&](SILBuilder & builder) {
|
|
auto value = builder.emitLoadValueOperation(
|
|
loc, *temporaryTransportBuffer, LoadOwnershipQualifier::Take);
|
|
builder.emitDestroyValueOperation(loc, value);
|
|
builder.createDeallocStack(loc, *temporaryTransportBuffer);
|
|
});
|
|
}
|
|
}
|
|
|
|
void emitActorReadyCall(SILBuilder &B, SILLocation loc, SILValue actor,
|
|
SILValue transport) {
|
|
|
|
auto &F = B.getFunction();
|
|
auto &C = F.getASTContext();
|
|
emitActorTransportWitnessCall(
|
|
B, loc, C.Id_actorReady, transport,
|
|
F.mapTypeIntoContext(actor->getType()), { actor });
|
|
}
|
|
|
|
} // namespace swift
|