Files
swift-mirror/lib/Sema/CodeSynthesisDistributedActor.cpp
Doug Gregor e595b4f68a [SE-0306] Ban "open" and "required" on actors.
Because actors don't have inheritance, ban "open" and "required", which
don't make sense. We will permit "final" which, although it doesn't
have any semantic impact, is still used to determine whether the ABI
of the actor itself might permit subclassing in the future. This
leaves the door slightly ajar for actor inheritance should we need to
revisit that decision.

Fixes SR-14785 / rdar://79401150.
2021-06-30 16:34:18 -07:00

730 lines
28 KiB
C++

//===--- CodeSynthesis.cpp - Type Checking for Declarations ---------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 "TypeCheckDistributed.h"
#include "CodeSynthesis.h"
#include "TypeChecker.h"
#include "TypeCheckDecl.h"
#include "TypeCheckObjC.h"
#include "TypeCheckType.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Availability.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Defer.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Sema/ConstraintSystem.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "DerivedConformances.h"
using namespace swift;
/******************************************************************************/
/******************************* INITIALIZERS *********************************/
/******************************************************************************/
// ==== Distributed Actor: Local Initializer -----------------------------------
/// Creates a new \c CallExpr representing
///
/// transport.assignAddress(Self.self)
///
/// \param C The AST context to create the expression in.
/// \param DC The \c DeclContext to create any decls in.
/// \param base The base expression to make the call on.
/// \param returnType The return type of the call.
/// \param param The parameter to the call.
static CallExpr *
createCall_DistributedActor_transport_assignAddress(ASTContext &C,
DeclContext *DC,
Expr *base, Type returnType,
Type param) {
// (_ actorType:)
auto *paramDecl = new (C) ParamDecl(SourceLoc(),
SourceLoc(), Identifier(),
SourceLoc(), C.Id_actorType, DC);
paramDecl->setImplicit();
paramDecl->setSpecifier(ParamSpecifier::Default);
paramDecl->setInterfaceType(returnType);
// transport.assignAddress(_:) expr
auto *paramList = ParameterList::createWithoutLoc(paramDecl);
auto *unboundCall = UnresolvedDotExpr::createImplicit(C, base,
C.Id_assignAddress,
paramList);
// DC->mapTypeIntoContext(param->getInterfaceType());
auto *selfTypeExpr = TypeExpr::createImplicit(param, C);
auto *dotSelfTypeExpr = new (C) DotSelfExpr(selfTypeExpr, SourceLoc(),
SourceLoc(), param);
// Full bound self.assignAddress(Self.self) call
Expr *args[1] = {dotSelfTypeExpr};
Identifier argLabels[1] = {Identifier()};
return CallExpr::createImplicit(C, unboundCall, C.AllocateCopy(args),
C.AllocateCopy(argLabels));
}
/// Creates a new \c CallExpr representing
///
/// transport.actorReady(self)
static CallExpr *
createCall_DistributedActor_transport_actorReady(ASTContext &C,
DeclContext *DC,
Expr *base, Type actorType,
Expr *initalizedSelf) {
// (_ actor:)
auto *paramDecl = new (C) ParamDecl(SourceLoc(),
SourceLoc(), Identifier(),
SourceLoc(), C.Id_actor, DC);
paramDecl->setImplicit();
paramDecl->setSpecifier(ParamSpecifier::Default);
paramDecl->setInterfaceType(actorType);
// transport.actorReady(_:) expr
auto *paramList = ParameterList::createWithoutLoc(paramDecl);
auto *unboundCall = UnresolvedDotExpr::createImplicit(C, base,
C.Id_actorReady,
paramList);
// Full bound transport.actorReady(self) call
Expr *args[1] = {initalizedSelf};
Identifier argLabels[1] = {Identifier()};
return CallExpr::createImplicit(C, unboundCall, C.AllocateCopy(args),
C.AllocateCopy(argLabels));
}
/// Synthesizes the body of the `init(transport:)` initializer as:
///
/// ```
/// init(transport: ActorTransport)
/// self.actorTransport = transport
/// self.actorAddress = try transport.assignAddress(Self.self)
/// }
/// ```
///
/// \param initDecl The function decl whose body to synthesize.
static std::pair<BraceStmt *, bool>
createBody_DistributedActor_init_transport(AbstractFunctionDecl *initDecl, void *) {
auto *funcDC = cast<DeclContext>(initDecl);
ASTContext &C = funcDC->getASTContext();
SmallVector<ASTNode, 2> statements;
auto transportParam = initDecl->getParameters()->get(0);
auto *transportExpr = new (C) DeclRefExpr(ConcreteDeclRef(transportParam),
DeclNameLoc(), /*Implicit=*/true);
auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl);
// ==== `self.actorTransport = transport`
{
// self.actorTransport
auto *varTransportExpr = UnresolvedDotExpr::createImplicit(C, selfRef,
C.Id_actorTransport);
auto *assignTransportExpr = new (C) AssignExpr(
varTransportExpr, SourceLoc(), transportExpr, /*Implicit=*/true);
statements.push_back(assignTransportExpr);
}
auto selfType = funcDC->getInnermostTypeContext()->getSelfTypeInContext();
// ==== `self.actorAddress = transport.assignAddress<Self>(Self.self)`
{
// self.actorAddress
auto *varAddressExpr = UnresolvedDotExpr::createImplicit(C, selfRef,
C.Id_actorAddress);
// Bound transport.assignAddress(Self.self) call
auto addressType = C.getActorAddressDecl()->getDeclaredInterfaceType();
auto *callExpr = createCall_DistributedActor_transport_assignAddress(C, funcDC,
/*base=*/transportExpr,
/*returnType=*/addressType,
/*param=*/selfType);
auto *assignAddressExpr = new (C) AssignExpr(
varAddressExpr, SourceLoc(), callExpr, /*Implicit=*/true);
statements.push_back(assignAddressExpr);
}
// ---=== Done initializing ===---
// === `transport.actorReady(self)`
{
// Bound transport.actorReady(self) call
auto selfType = funcDC->getInnermostTypeContext()->getSelfTypeInContext();
auto *callExpr = createCall_DistributedActor_transport_actorReady(C, funcDC,
/*base=*/transportExpr,
/*actorType=*/selfType,
/*param=*/selfRef);
statements.push_back(callExpr);
}
auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
/*implicit=*/true);
return { body, /*isTypeChecked=*/false };
}
/// Synthesizes the
///
/// ```
/// init(transport: ActorTransport)
/// ```
///
/// local initializer.
static ConstructorDecl *
createDistributedActor_init_local(ClassDecl *classDecl,
ASTContext &ctx) {
auto &C = ctx;
// auto conformanceDC = derived.getConformanceContext();
auto conformanceDC = classDecl;
// Expected type: (Self) -> (ActorTransport) -> (Self)
//
// Params: (transport transport: ActorTransport)
auto transportType = C.getActorTransportDecl()->getDeclaredInterfaceType();
auto *transportParamDecl = new (C) ParamDecl(
SourceLoc(), SourceLoc(), C.Id_transport,
SourceLoc(), C.Id_transport, conformanceDC);
transportParamDecl->setImplicit();
transportParamDecl->setSpecifier(ParamSpecifier::Default);
transportParamDecl->setInterfaceType(transportType);
auto *paramList = ParameterList::createWithoutLoc(transportParamDecl);
// Func name: init(transport:)
DeclName name(C, DeclBaseName::createConstructor(), paramList);
auto *initDecl =
new (C) ConstructorDecl(name, SourceLoc(),
/*Failable=*/false, SourceLoc(),
/*Async=*/false, SourceLoc(),
/*Throws=*/false, SourceLoc(),
paramList,
/*GenericParams=*/nullptr, conformanceDC);
initDecl->setImplicit();
initDecl->setSynthesized();
initDecl->setBodySynthesizer(&createBody_DistributedActor_init_transport);
auto *nonIsoAttr = new (C) NonisolatedAttr(/*IsImplicit*/true);
initDecl->getAttrs().add(nonIsoAttr);
initDecl->copyFormalAccessFrom(classDecl, /*sourceIsParentContext=*/true);
return initDecl;
}
// ==== Distributed Actor: Resolve Initializer ---------------------------------
/// Synthesizes the body for
///
/// ```
/// init(resolve address: ActorAddress, using transport: ActorTransport) throws {
/// // TODO: implement calling the transport
/// switch try transport.resolve(address: address, as: Self.self) {
/// case .instance(let instance):
/// self = instance
/// case .makeProxy:
/// // TODO: use RebindSelfInConstructorExpr here?
/// self = <<MAGIC MAKE PROXY>>(address, transport) // TODO: implement this
/// }
/// }
/// ```
///
/// \param initDecl The function decl whose body to synthesize.
static std::pair<BraceStmt *, bool>
createDistributedActor_init_resolve_body(AbstractFunctionDecl *initDecl, void *) {
auto *funcDC = cast<DeclContext>(initDecl);
auto &C = funcDC->getASTContext();
SmallVector<ASTNode, 2> statements; // TODO: how many?
auto addressParam = initDecl->getParameters()->get(0);
auto *addressExpr = new (C) DeclRefExpr(ConcreteDeclRef(addressParam),
DeclNameLoc(), /*Implicit=*/true);
auto transportParam = initDecl->getParameters()->get(1);
auto *transportExpr = new (C) DeclRefExpr(ConcreteDeclRef(transportParam),
DeclNameLoc(), /*Implicit=*/true);
auto *selfRef = DerivedConformance::createSelfDeclRef(initDecl);
// ==== `self.actorTransport = transport`
auto *varTransportExpr = UnresolvedDotExpr::createImplicit(C, selfRef,
C.Id_actorTransport);
auto *assignTransportExpr = new (C) AssignExpr(
varTransportExpr, SourceLoc(), transportExpr, /*Implicit=*/true);
statements.push_back(assignTransportExpr);
// ==== `self.actorAddress = transport.assignAddress<Self>(Self.self)`
// self.actorAddress
auto *varAddressExpr = UnresolvedDotExpr::createImplicit(C, selfRef,
C.Id_actorAddress);
// TODO implement calling the transport with the address and Self.self
// FIXME: this must be checking with the transport instead
auto *assignAddressExpr = new (C) AssignExpr(
varAddressExpr, SourceLoc(), addressExpr, /*Implicit=*/true);
statements.push_back(assignAddressExpr);
// end-of-FIXME: this must be checking with the transport instead
auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
/*implicit=*/true);
return { body, /*isTypeChecked=*/false };
}
/// Synthesizes the
///
/// ```
/// init(resolve address: ActorAddress, using transport: ActorTransport) throws
/// ```
///
/// resolve initializer.
static ConstructorDecl *
createDistributedActor_init_resolve(ClassDecl *classDecl,
ASTContext &ctx) {
auto &C = ctx;
auto conformanceDC = classDecl;
// Expected type: (Self) -> (ActorAddress, ActorTransport) -> (Self)
//
// Param: (resolve address: ActorAddress)
auto addressType = C.getActorAddressDecl()->getDeclaredInterfaceType();
auto *addressParamDecl = new (C) ParamDecl(
SourceLoc(), SourceLoc(), C.Id_resolve,
SourceLoc(), C.Id_address, conformanceDC);
addressParamDecl->setImplicit();
addressParamDecl->setSpecifier(ParamSpecifier::Default);
addressParamDecl->setInterfaceType(addressType);
// Param: (using transport: ActorTransport)
auto transportType = C.getActorTransportDecl()->getDeclaredInterfaceType();
auto *transportParamDecl = new (C) ParamDecl(
SourceLoc(), SourceLoc(), C.Id_using,
SourceLoc(), C.Id_transport, conformanceDC);
transportParamDecl->setImplicit();
transportParamDecl->setSpecifier(ParamSpecifier::Default);
transportParamDecl->setInterfaceType(transportType);
auto *paramList = ParameterList::create(
C,
/*LParenLoc=*/SourceLoc(),
/*params=*/{addressParamDecl, transportParamDecl},
/*RParenLoc=*/SourceLoc()
);
// Func name: init(resolve:using:)
DeclName name(C, DeclBaseName::createConstructor(), paramList);
auto *initDecl =
new (C) ConstructorDecl(name, SourceLoc(),
/*Failable=*/false, SourceLoc(),
/*Async=*/false, SourceLoc(),
/*Throws=*/true, SourceLoc(),
paramList,
/*GenericParams=*/nullptr, conformanceDC);
initDecl->setImplicit();
initDecl->setSynthesized();
initDecl->setBodySynthesizer(&createDistributedActor_init_resolve_body);
auto *nonIsoAttr = new (C) NonisolatedAttr(/*IsImplicit*/true);
initDecl->getAttrs().add(nonIsoAttr);
initDecl->copyFormalAccessFrom(classDecl, /*sourceIsParentContext=*/true);
return initDecl;
}
/// Detects which initializer to create, and does so.
static ConstructorDecl *
createDistributedActorInit(ClassDecl *classDecl,
ConstructorDecl *requirement,
ASTContext &ctx) {
assert(classDecl->isDistributedActor());
const auto name = requirement->getName();
auto argumentNames = name.getArgumentNames();
switch (argumentNames.size()) {
case 1: {
if (requirement->isDistributedActorLocalInit()) {
return createDistributedActor_init_local(classDecl, ctx);
}
break;
}
case 2: {
if (requirement->isDistributedActorResolveInit()) {
return createDistributedActor_init_resolve(classDecl, ctx);
}
break;
}
}
return nullptr;
}
static void collectNonOveriddenDistributedActorInits(
ASTContext& Context,
ClassDecl *actorDecl,
SmallVectorImpl<ConstructorDecl *> &results) {
assert(actorDecl->isDistributedActor());
auto protoDecl = Context.getProtocol(KnownProtocolKind::DistributedActor);
// // Record all of the initializers the actorDecl has implemented.
// llvm::SmallPtrSet<ConstructorDecl *, 4> overriddenInits;
// for (auto member : actorDecl->getMembers())
// if (auto ctor = dyn_cast<ConstructorDecl>(member))
// if (!ctor->hasStubImplementation())
// // if (auto overridden = ctor->getOverriddenDecl())
// overriddenInits.insert(ctor);
//
// actorDecl->synthesizeSemanticMembersIfNeeded(
// DeclBaseName::createConstructor());
NLOptions subOptions = (NL_QualifiedDefault | NL_IgnoreAccessControl);
SmallVector<ValueDecl *, 4> lookupResults;
actorDecl->lookupQualified(
protoDecl, DeclNameRef::createConstructor(),
subOptions, lookupResults);
for (auto decl : lookupResults) {
// Distributed Actor Constructor
auto daCtor = cast<ConstructorDecl>(decl);
// TODO: Don't require it if overriden
// if (!overriddenInits.count(daCtor))
results.push_back(daCtor);
}
}
/// For a distributed actor, automatically define initializers
/// that match the DistributedActor requirements.
static void addImplicitDistributedActorConstructors(ClassDecl *decl) {
// Bail out if not a distributed actor definition.
if (!decl->isDistributedActor())
return;
for (auto member : decl->getMembers()) {
if (auto ctor = dyn_cast<ConstructorDecl>(member)) {
if (ctor->isRecursiveValidation())
return;
}
}
decl->setAddedImplicitInitializers();
// Check whether the user has defined a designated initializer for this class,
// and whether all of its stored properties have initial values.
auto &ctx = decl->getASTContext();
// bool foundDesignatedInit = hasUserDefinedDesignatedInit(ctx.evaluator, decl);
// bool defaultInitable =
// areAllStoredPropertiesDefaultInitializable(ctx.evaluator, decl);
//
// // We can't define these overrides if we have any uninitialized
// // stored properties.
// if (!defaultInitable && !foundDesignatedInit)
// return;
SmallVector<ConstructorDecl *, 4> nonOverridenCtors;
collectNonOveriddenDistributedActorInits(
ctx, decl, nonOverridenCtors);
for (auto *daCtor : nonOverridenCtors) {
if (auto ctor = createDistributedActorInit(decl, daCtor, ctx)) {
decl->addMember(ctor);
}
}
}
/******************************************************************************/
/******************************** PROPERTIES **********************************/
/******************************************************************************/
// TODO: deduplicate with 'declareDerivedProperty' from DerivedConformance...
std::pair<VarDecl *, PatternBindingDecl *>
createStoredProperty(ClassDecl *classDecl, ASTContext &ctx,
VarDecl::Introducer introducer, Identifier name,
Type propertyInterfaceType, Type propertyContextType,
bool isStatic, bool isFinal) {
auto parentDC = classDecl;
VarDecl *propDecl = new (ctx)
VarDecl(/*IsStatic*/ isStatic, introducer,
SourceLoc(), name, parentDC);
propDecl->setImplicit();
propDecl->setSynthesized();
propDecl->copyFormalAccessFrom(classDecl, /*sourceIsParentContext*/ true);
propDecl->setInterfaceType(propertyInterfaceType);
Pattern *propPat = NamedPattern::createImplicit(ctx, propDecl);
propPat->setType(propertyContextType);
propPat = TypedPattern::createImplicit(ctx, propPat, propertyContextType);
propPat->setType(propertyContextType);
auto *pbDecl = PatternBindingDecl::createImplicit(
ctx, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr,
parentDC);
return {propDecl, pbDecl};
}
/// Adds the following, fairly special, properties to each distributed actor:
/// - actorTransport
/// - actorAddress
static void addImplicitDistributedActorStoredProperties(ClassDecl *decl) {
assert(decl->isDistributedActor());
auto &C = decl->getASTContext();
// ```
// @_distributedActorIndependent
// let actorAddress: ActorAddress
// ```
// (no need for @actorIndependent because it is an immutable let)
{
auto propertyType = C.getActorAddressDecl()->getDeclaredInterfaceType();
VarDecl *propDecl;
PatternBindingDecl *pbDecl;
std::tie(propDecl, pbDecl) = createStoredProperty(
decl, C,
VarDecl::Introducer::Let, C.Id_actorAddress,
propertyType, propertyType,
/*isStatic=*/false, /*isFinal=*/true);
// mark as @_distributedActorIndependent, allowing access to it from everywhere
propDecl->getAttrs().add(
new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true));
decl->addMember(propDecl);
decl->addMember(pbDecl);
}
// ```
// @_distributedActorIndependent
// let actorTransport: ActorTransport
// ```
// (no need for @actorIndependent because it is an immutable let)
{
auto propertyType = C.getActorTransportDecl()->getDeclaredInterfaceType();
VarDecl *propDecl;
PatternBindingDecl *pbDecl;
std::tie(propDecl, pbDecl) = createStoredProperty(
decl, C,
VarDecl::Introducer::Let, C.Id_actorTransport,
propertyType, propertyType,
/*isStatic=*/false, /*isFinal=*/true);
// mark as @_distributedActorIndependent, allowing access to it from everywhere
propDecl->getAttrs().add(
new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true));
decl->addMember(propDecl);
decl->addMember(pbDecl);
}
}
/******************************************************************************/
/*************************** _REMOTE_ FUNCTIONS *******************************/
/******************************************************************************/
/// Synthesizes the for `_remote_xxx` functions.
///
/// Create a stub body that emits a fatal error message.
static std::pair<BraceStmt *, bool>
synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *context) {
auto distributedFunc = static_cast<AbstractFunctionDecl *>(context);
auto classDecl = func->getDeclContext()->getSelfClassDecl();
auto &ctx = func->getASTContext();
auto &SM = ctx.SourceMgr;
auto *staticStringDecl = ctx.getStaticStringDecl();
auto staticStringType = staticStringDecl->getDeclaredInterfaceType();
auto staticStringInit = ctx.getStringBuiltinInitDecl(staticStringDecl);
auto *uintDecl = ctx.getUIntDecl();
auto uintType = uintDecl->getDeclaredInterfaceType();
auto uintInit = ctx.getIntBuiltinInitDecl(uintDecl);
auto missingTransportDecl = ctx.getMissingDistributedActorTransport();
assert(missingTransportDecl);
// Create a call to _Distributed._missingDistributedActorTransport
auto loc = func->getLoc();
Expr *ref = new (ctx) DeclRefExpr(missingTransportDecl,
DeclNameLoc(loc), /*Implicit=*/true);
ref->setType(missingTransportDecl->getInterfaceType()
->removeArgumentLabels(1));
llvm::SmallString<64> buffer;
StringRef fullClassName = ctx.AllocateCopy(
(classDecl->getModuleContext()->getName().str() +
"." +
classDecl->getName().str()).toStringRef(buffer));
auto *className = new (ctx) StringLiteralExpr(fullClassName, loc,
/*Implicit=*/true);
className->setBuiltinInitializer(staticStringInit);
assert(isa<ConstructorDecl>(className->getBuiltinInitializer().getDecl()));
className->setType(staticStringType);
auto *funcName = new (ctx) StringLiteralExpr(
ctx.AllocateCopy(func->getName().getBaseName().getIdentifier().str()), loc,
/*Implicit=*/true);
funcName->setType(staticStringType);
funcName->setBuiltinInitializer(staticStringInit);
// Note: Sadly we cannot just rely on #function, #file, #line for the location
// (MagicIdentifierLiteralExpr), of the call because the call is made from a thunk.
// That thunk does not carry those info today although it could.
//
// Instead, we offer the location where the distributed func was declared.
auto fileString = SM.getDisplayNameForLoc(distributedFunc->getStartLoc());
auto *file = new (ctx) StringLiteralExpr(fileString, loc, /*Implicit=*/true);
file->setType(staticStringType);
file->setBuiltinInitializer(staticStringInit);
auto startLineAndCol = SM.getPresumedLineAndColumnForLoc(distributedFunc->getStartLoc());
// auto *line = new (ctx) MagicIdentifierLiteralExpr(
// MagicIdentifierLiteralExpr::Line, loc, /*Implicit=*/true);
// auto *line = new (ctx) IntegerLiteralExpr(startLineAndCol.first, loc,
// /*implicit*/ true);
auto *line = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.first);
line->setType(uintType);
line->setBuiltinInitializer(uintInit);
// auto *column = new (ctx) MagicIdentifierLiteralExpr(
// MagicIdentifierLiteralExpr::Column, loc, /*Implicit=*/true);
auto *column = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.second);
column->setType(uintType);
column->setBuiltinInitializer(uintInit);
auto *call = CallExpr::createImplicit(
ctx, ref, { className, funcName, file, line, column }, {});
call->setType(ctx.getNeverType());
call->setThrows(false);
SmallVector<ASTNode, 2> stmts;
stmts.push_back(call); // something() -> Never
// stmts.push_back(new (ctx) ReturnStmt(SourceLoc(), /*Result=*/nullptr)); // FIXME: this causes 'different types for return type: String vs. ()'
auto body = BraceStmt::create(ctx, SourceLoc(), stmts, SourceLoc(),
/*implicit=*/true);
return { body, /*isTypeChecked=*/true };
}
static Identifier makeRemoteFuncIdentifier(FuncDecl* func) {
auto &C = func->getASTContext();
auto localFuncName = func->getBaseIdentifier().str().str();
auto remoteFuncIdent = C.getIdentifier("_remote_" + localFuncName);
return remoteFuncIdent;
}
/// Create a remote stub for the passed in \c func.
/// The remote stub function is not user accessible and mirrors the API of
/// the local function. It is always throwing, async, and user-inaccessible.
///
/// ```
/// // func greet(name: String) { ... }
/// dynamic <access> func _remote_greet(name: String) async throws {
/// fatalError(...)
/// }
/// ```
///
/// and is intended to be replaced by a transport library by providing an
/// appropriate @_dynamicReplacement function.
static void addImplicitRemoteActorFunction(ClassDecl *decl, FuncDecl *func) {
auto &C = decl->getASTContext();
auto parentDC = decl;
auto remoteFuncIdent = makeRemoteFuncIdentifier(func);
auto params = ParameterList::clone(C, func->getParameters());
auto genericParams = func->getGenericParams(); // TODO: also clone those
Type resultTy = func->getResultInterfaceType();
DeclName name(C, remoteFuncIdent, params);
auto *const remoteFuncDecl = FuncDecl::createImplicit(
C, StaticSpellingKind::None, name, /*NameLoc=*/SourceLoc(),
/*Async=*/true, /*Throws=*/true,
/*GenericParams=*/genericParams, params,
resultTy, parentDC);
// *dynamic* because we'll be replacing it with specific transports
remoteFuncDecl->getAttrs().add(
new (C) DynamicAttr(/*implicit=*/true));
// @_distributedActorIndependent
remoteFuncDecl->getAttrs().add(
new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true));
// users should never have to access this function directly;
// it is only invoked from our distributed function thunk if the actor is remote.
remoteFuncDecl->setUserAccessible(false);
remoteFuncDecl->setSynthesized();
remoteFuncDecl->setBodySynthesizer(&synthesizeRemoteFuncStubBody, func);
// same access control as the original function is fine
remoteFuncDecl->copyFormalAccessFrom(func, /*sourceIsParentContext=*/false);
decl->addMember(remoteFuncDecl);
}
/// Synthesize dynamic _remote stub functions for each encountered distributed function.
static void addImplicitRemoteActorFunctions(ClassDecl *decl) {
assert(decl->isDistributedActor());
for (auto member : decl->getMembers()) {
auto func = dyn_cast<FuncDecl>(member);
if (func && func->isDistributed()) {
addImplicitRemoteActorFunction(decl, func);
}
}
}
/******************************************************************************/
/************************ SYNTHESIS ENTRY POINT *******************************/
/******************************************************************************/
/// Entry point for adding all computed members to a distributed actor decl.
void swift::addImplicitDistributedActorMembersToClass(ClassDecl *decl) {
// Bail out if not a distributed actor definition.
if (!decl->isDistributedActor())
return;
auto &C = decl->getASTContext();
if (!C.getLoadedModule(C.Id_Distributed)) {
// seems we're missing the _Distributed module, ask to import it explicitly
decl->diagnose(diag::distributed_actor_needs_explicit_distributed_import);
return;
}
addImplicitDistributedActorConstructors(decl);
addImplicitDistributedActorStoredProperties(decl);
addImplicitRemoteActorFunctions(decl);
}