Files
swift-mirror/lib/Sema/CodeSynthesisDistributedActor.cpp
Doug Gregor 214b1bd454 Add support for distributed functions in extensions of distributed actors.
Fix a few minor issues in the type checker and SILGen to properly cope with
distributed functions defined within extensions of distributed actors.
While here, centralize the logic that adds the "_remote_" function.

Fixes rdar://84325525.
2021-10-15 22:46:44 -07:00

212 lines
8.2 KiB
C++

//===--- CodeSynthesisDistributedActor.cpp --------------------------------===//
//
// 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 "TypeCheckDistributed.h"
#include "TypeChecker.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/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;
/******************************************************************************/
/*************************** _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 && "Could not locate '_missingDistributedActorTransport' function");
// 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 = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.first);
line->setType(uintType);
line->setBuiltinInitializer(uintInit);
auto *column = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.second);
column->setType(uintType);
column->setBuiltinInitializer(uintInit);
auto *argList = ArgumentList::forImplicitUnlabeled(
ctx, {className, funcName, file, line, column});
auto *call = CallExpr::createImplicit(ctx, ref, argList);
call->setType(ctx.getNeverType());
call->setThrows(false);
SmallVector<ASTNode, 2> stmts;
stmts.push_back(call);
auto body = BraceStmt::create(ctx, SourceLoc(), stmts, SourceLoc(),
/*implicit=*/true);
return { body, /*isTypeChecked=*/true };
}
static Identifier makeRemoteFuncIdentifier(FuncDecl* distributedFunc) {
auto &C = distributedFunc->getASTContext();
assert(distributedFunc->isDistributed());
auto localFuncName = distributedFunc->getBaseIdentifier().str().str();
auto remoteFuncIdent = C.getIdentifier("_remote_" + localFuncName);
return remoteFuncIdent;
}
/******************************************************************************/
/************************ SYNTHESIS ENTRY POINT *******************************/
/******************************************************************************/
/// 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 AbstractFunctionDecl *addImplicitDistributedActorRemoteFunction(
DeclContext *parentDC, AbstractFunctionDecl *AFD) {
auto nominal = parentDC->getSelfNominalTypeDecl();
if (!nominal || !nominal->isDistributedActor())
return nullptr;
auto func = dyn_cast<FuncDecl>(AFD);
if (!func || !func->isDistributed())
return nullptr;
// ==== if the remote func already exists, return it
if (auto existing = nominal->lookupDirectRemoteFunc(func))
return existing;
// ==== Synthesize and add 'remote' func to the actor decl
auto &C = func->getASTContext();
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));
// nonisolated
remoteFuncDecl->getAttrs().add(new (C) NonisolatedAttr(/*IsImplicit=*/true));
// nonisolated
remoteFuncDecl->getAttrs().add(
new (C) NonisolatedAttr(/*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);
cast<IterableDeclContext>(parentDC->getAsDecl())->addMember(remoteFuncDecl);
return remoteFuncDecl;
}
AbstractFunctionDecl *GetDistributedRemoteFuncRequest::evaluate(
Evaluator &evaluator, AbstractFunctionDecl *func) const {
if (!func->isDistributed())
return nullptr;
auto &C = func->getASTContext();
DeclContext *DC = func->getDeclContext();
// not via `ensureDistributedModuleLoaded` to avoid generating a warning,
// we won't be emitting the offending decl after all.
if (!C.getLoadedModule(C.Id_Distributed))
return nullptr;
return addImplicitDistributedActorRemoteFunction(DC, func);
}