//===--- DerivedConformanceActor.cpp - Derived Actor Conformance ----------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2020 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 // //===----------------------------------------------------------------------===// // // This file implements implicit derivation of the Actor protocol. // //===----------------------------------------------------------------------===// #include "DerivedConformances.h" #include "TypeChecker.h" #include "TypeCheckConcurrency.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" using namespace swift; bool DerivedConformance::canDeriveActor( NominalTypeDecl *nominal, DeclContext *dc) { auto classDecl = dyn_cast(nominal); return classDecl && classDecl->isActor() && dc == nominal; } static DeclName getEnqueuePartialTaskName(ASTContext &ctx) { return DeclName(ctx, ctx.Id_enqueue, { ctx.Id_partialTask }); } static Type getPartialAsyncTaskType(ASTContext &ctx) { auto concurrencyModule = ctx.getLoadedModule(ctx.Id_Concurrency); if (!concurrencyModule) return Type(); SmallVector decls; concurrencyModule->lookupQualified( concurrencyModule, DeclNameRef(ctx.Id_PartialAsyncTask), NL_QualifiedDefault, decls); for (auto decl : decls) { if (auto typeDecl = dyn_cast(decl)) return typeDecl->getDeclaredInterfaceType(); } return Type(); } /// Look for the default enqueue operation. static FuncDecl *getDefaultActorEnqueue(DeclContext *dc, SourceLoc loc) { ASTContext &ctx = dc->getASTContext(); auto desc = UnqualifiedLookupDescriptor( DeclNameRef(ctx.Id__defaultActorEnqueue), dc, loc, UnqualifiedLookupOptions()); auto lookup = evaluateOrDefault(ctx.evaluator, UnqualifiedLookupRequest{desc}, {}); for (const auto &result : lookup) { // FIXME: Validate this further, because we're assuming the exact type. if (auto func = dyn_cast(result.getValueDecl())) return func; } return nullptr; } static std::pair deriveBodyActor_enqueuePartialTask( AbstractFunctionDecl *enqueuePartialTask, void *) { // func enqueue(partialTask: PartialAsyncTask) { // _defaultActorEnqueue(partialTask: partialTask, actor: self) // } ASTContext &ctx = enqueuePartialTask->getASTContext(); auto classDecl = enqueuePartialTask->getDeclContext()->getSelfClassDecl(); // Produce an empty brace statement on failure. auto failure = [&]() -> std::pair { auto body = BraceStmt::create( ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); return { body, /*isTypeChecked=*/true }; }; // Call into the runtime to enqueue the task. auto fn = getDefaultActorEnqueue(classDecl, classDecl->getLoc()); if (!fn) { classDecl->diagnose( diag::concurrency_lib_missing, ctx.Id__defaultActorEnqueue.str()); return failure(); } // Reference to _defaultActorEnqueue. auto fnRef = new (ctx) DeclRefExpr(fn, DeclNameLoc(), /*Implicit=*/true); fnRef->setType(fn->getInterfaceType()); // self argument to the function. auto selfDecl = enqueuePartialTask->getImplicitSelfDecl(); Type selfType = enqueuePartialTask->mapTypeIntoContext( selfDecl->getValueInterfaceType()); Expr *selfArg = new (ctx) DeclRefExpr( selfDecl, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, selfType); selfArg = ErasureExpr::create(ctx, selfArg, ctx.getAnyObjectType(), { }); selfArg->setImplicit(); // The partial asynchronous task. auto partialTaskParam = enqueuePartialTask->getParameters()->get(0); Expr *partialTask = new (ctx) DeclRefExpr( partialTaskParam, DeclNameLoc(), /*Implicit=*/true, AccessSemantics::Ordinary, enqueuePartialTask->mapTypeIntoContext( partialTaskParam->getValueInterfaceType())); // Form the call itself. auto call = CallExpr::createImplicit( ctx, fnRef, { partialTask, selfArg }, { ctx.Id_partialTask, ctx.getIdentifier("actor") }); call->setType(fn->getResultInterfaceType()); call->setThrows(false); auto body = BraceStmt::create( ctx, SourceLoc(), { call }, SourceLoc(), /*implicit=*/true); return { body, /*isTypeChecked=*/true }; } /// Derive the declaration of Actor's enqueue(partialTask:). static ValueDecl *deriveActor_enqueuePartialTask(DerivedConformance &derived) { ASTContext &ctx = derived.Context; // Retrieve the types and declarations we'll need to form this operation. Type partialTaskType = getPartialAsyncTaskType(ctx); if (!partialTaskType) { derived.Nominal->diagnose( diag::concurrency_lib_missing, ctx.Id_PartialAsyncTask.str()); return nullptr; } auto parentDC = derived.getConformanceContext(); // Partial task parameter to enqueue(partialTask:). auto partialTaskParamDecl = new (ctx) ParamDecl( SourceLoc(), SourceLoc(), ctx.Id_partialTask, SourceLoc(), ctx.Id_partialTask, parentDC); partialTaskParamDecl->setInterfaceType(partialTaskType); partialTaskParamDecl->setSpecifier(ParamSpecifier::Default); partialTaskParamDecl->setImplicit(); // enqueue(partialTask:) method. ParameterList *params = ParameterList::createWithoutLoc(partialTaskParamDecl); auto func = FuncDecl::createImplicit( ctx, StaticSpellingKind::None, getEnqueuePartialTaskName(ctx), SourceLoc(), /*Async=*/false, /*Throws=*/false, /*GenericParams=*/nullptr, params, TupleType::getEmpty(ctx), parentDC); func->copyFormalAccessFrom(derived.Nominal); func->setBodySynthesizer(deriveBodyActor_enqueuePartialTask); func->setSynthesized(); // mark as @actorIndependent(unsafe) func->getAttrs().add(new (ctx) ActorIndependentAttr( ActorIndependentKind::Unsafe, /*IsImplicit=*/true)); derived.addMembersToConformanceContext( { func }); return func; } ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { auto func = dyn_cast(requirement); if (!func) return nullptr; if (FuncDecl::isEnqueuePartialTaskName(Context, func->getName())) return deriveActor_enqueuePartialTask(*this); return nullptr; }