mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This is an non-user-visible attribute that is semantically identical to `nonisolated` except that it allows use in distributed actors. It is only, and can only, be used for the two fields that distributed actors have in both the local actor and in the remote proxy, "id" and "actorTransport". So, special-case the "nonisolated" check for those fields and stop using `@_distributedActorIndependent`.
192 lines
7.5 KiB
C++
192 lines
7.5 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.
|
|
AbstractFunctionDecl *TypeChecker::addImplicitDistributedActorRemoteFunction(
|
|
ClassDecl *decl, AbstractFunctionDecl *AFD) {
|
|
if (!decl->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 = decl->lookupDirectRemoteFunc(func))
|
|
return existing;
|
|
|
|
// ==== Synthesize and add 'remote' func to the actor decl
|
|
|
|
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));
|
|
|
|
// 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);
|
|
|
|
decl->addMember(remoteFuncDecl);
|
|
|
|
return remoteFuncDecl;
|
|
}
|