//===--- 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; /******************************************************************************/ /******************************* RESOLVE FUNCTION *****************************/ /******************************************************************************/ /// Synthesizes the /// /// \verbatim /// static resolve(_ address: ActorAddress, /// using transport: ActorTransport) throws -> Self { /// /// } /// \endverbatim /// /// factory function in the AST, with an empty body. Its body is /// expected to be filled-in during SILGen. // TODO(distributed): move this synthesis to DerivedConformance style static void addFactoryResolveFunction(ClassDecl *decl) { assert(decl->isDistributedActor()); auto &C = decl->getASTContext(); auto mkParam = [&](Identifier argName, Identifier paramName, Type ty) -> ParamDecl* { auto *param = new (C) ParamDecl(SourceLoc(), SourceLoc(), argName, SourceLoc(), paramName, decl); param->setImplicit(); param->setSpecifier(ParamSpecifier::Default); param->setInterfaceType(ty); return param; }; auto addressType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType(); auto transportType = C.getActorTransportDecl()->getDeclaredInterfaceType(); // (_ identity: AnyActorIdentity, using transport: ActorTransport) auto *params = ParameterList::create( C, /*LParenLoc=*/SourceLoc(), /*params=*/{ mkParam(Identifier(), C.Id_identity, addressType), mkParam(C.Id_using, C.Id_transport, transportType) }, /*RParenLoc=*/SourceLoc() ); // Func name: resolve(_:using:) DeclName name(C, C.Id_resolve, params); // Expected type: (Self) -> (AnyActorIdentity, ActorTransport) throws -> (Self) auto *factoryDecl = FuncDecl::createImplicit(C, StaticSpellingKind::KeywordStatic, name, SourceLoc(), /*async=*/false, /*throws=*/true, /*genericParams=*/nullptr, params, /*returnType*/decl->getDeclaredInterfaceType(), decl); factoryDecl->setDistributedActorFactory(); // TODO(distributed): should we mark this specifically as the resolve factory? factoryDecl->copyFormalAccessFrom(decl, /*sourceIsParentContext=*/true); decl->addMember(factoryDecl); } /******************************************************************************/ /*************************** _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 = 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 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 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(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; // If the _Distributed module is missing we cannot synthesize anything. if (!swift::ensureDistributedModuleLoaded(decl)) return; addFactoryResolveFunction(decl); // addImplicitDistributedActorStoredProperties(decl); addImplicitRemoteActorFunctions(decl); }