//===--- 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 synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *context) { auto distributedFunc = static_cast(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(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 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 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(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)); // 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; }