//===--- DerivedConformanceActor.cpp --------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2018 - 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 explicit derivation of the Actor protocol // for actor types. // // Swift Evolution pitch thread: // https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425 // //===----------------------------------------------------------------------===// #include "CodeSynthesis.h" #include "DerivedConformances.h" #include "TypeChecker.h" #include "swift/AST/AvailabilityInference.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Stmt.h" #include "swift/AST/Types.h" #include "swift/Strings.h" using namespace swift; bool DerivedConformance::canDeriveActor(DeclContext *dc, NominalTypeDecl *nominal) { auto classDecl = dyn_cast(nominal); return classDecl && classDecl->isActor() && dc == nominal && !classDecl->getUnownedExecutorProperty(); } /// Turn a Builtin.Executor value into an UnownedSerialExecutor. static Expr *constructUnownedSerialExecutor(ASTContext &ctx, Expr *arg) { auto executorDecl = ctx.getUnownedSerialExecutorDecl(); if (!executorDecl) return nullptr; for (auto member: executorDecl->getAllMembers()) { auto ctor = dyn_cast(member); if (!ctor) continue; auto params = ctor->getParameters(); if (params->size() != 1 || !params->get(0)->getInterfaceType()->is()) continue; Type executorType = executorDecl->getDeclaredInterfaceType(); Type ctorType = ctor->getInterfaceType(); // We have the right initializer. Build a reference to it of type: // (UnownedSerialExecutor.Type) // -> (Builtin.Executor) -> UnownedSerialExecutor auto initRef = new (ctx) DeclRefExpr(ctor, DeclNameLoc(), /*implicit*/true, AccessSemantics::Ordinary, ctorType); // Apply the initializer to the metatype, building an expression of type: // (Builtin.Executor) -> UnownedSerialExecutor auto metatypeRef = TypeExpr::createImplicit(executorType, ctx); Type ctorAppliedType = ctorType->getAs()->getResult(); auto selfApply = ConstructorRefCallExpr::create(ctx, initRef, metatypeRef, ctorAppliedType); selfApply->setImplicit(true); selfApply->setThrows(nullptr); // Call the constructor, building an expression of type // UnownedSerialExecutor. auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {arg}); auto call = CallExpr::createImplicit(ctx, selfApply, argList); call->setType(executorType); call->setThrows(nullptr); return call; } return nullptr; } static std::pair deriveBodyActor_unownedExecutor(AbstractFunctionDecl *getter, void *) { // var unownedExecutor: UnownedSerialExecutor { // get { // return Builtin.buildDefaultActorExecutorRef(self) // } // } ASTContext &ctx = getter->getASTContext(); // Produce an empty brace statement on failure. auto failure = [&]() -> std::pair { auto body = BraceStmt::create( ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true); return { body, /*isTypeChecked=*/true }; }; // Build a reference to self. Type selfType = getter->getImplicitSelfDecl()->getTypeInContext(); Expr *selfArg = DerivedConformance::createSelfDeclRef(getter); selfArg->setType(selfType); // The builtin call gives us a Builtin.Executor. auto builtinCall = DerivedConformance::createBuiltinCall(ctx, BuiltinValueKind::BuildDefaultActorExecutorRef, {selfType}, {selfArg}); // Turn that into an UnownedSerialExecutor. auto initCall = constructUnownedSerialExecutor(ctx, builtinCall); if (!initCall) return failure(); auto *ret = ReturnStmt::createImplicit(ctx, initCall); auto body = BraceStmt::create( ctx, SourceLoc(), { ret }, SourceLoc(), /*implicit=*/true); return { body, /*isTypeChecked=*/true }; } /// Derive the declaration of Actor's unownedExecutor property. static ValueDecl *deriveActor_unownedExecutor(DerivedConformance &derived) { ASTContext &ctx = derived.Context; // Retrieve the types and declarations we'll need to form this operation. auto executorDecl = ctx.getUnownedSerialExecutorDecl(); if (!executorDecl) { derived.Nominal->diagnose( diag::concurrency_lib_missing, "UnownedSerialExecutor"); return nullptr; } Type executorType = executorDecl->getDeclaredInterfaceType(); auto propertyPair = derived.declareDerivedProperty( DerivedConformance::SynthesizedIntroducer::Var, ctx.Id_unownedExecutor, executorType, /*static*/ false, /*final*/ false); auto property = propertyPair.first; property->setSynthesized(true); property->getAttrs().add(new (ctx) SemanticsAttr(SEMANTICS_DEFAULT_ACTOR, SourceLoc(), SourceRange(), /*implicit*/ true)); property->getAttrs().add( new (ctx) NonisolatedAttr(/*unsafe=*/false, /*implicit=*/true)); // Make the property implicitly final. property->getAttrs().add(new (ctx) FinalAttr(/*IsImplicit=*/true)); if (property->getFormalAccess() == AccessLevel::Open) property->overwriteAccess(AccessLevel::Public); // Infer availability. SmallVector asAvailableAs; asAvailableAs.push_back(executorDecl); if (auto enclosingDecl = property->getInnermostDeclWithAvailability()) asAvailableAs.push_back(enclosingDecl); AvailabilityInference::applyInferredAvailableAttrs(property, asAvailableAs); auto getter = derived.addGetterToReadOnlyDerivedProperty(property); getter->setBodySynthesizer(deriveBodyActor_unownedExecutor); derived.addMembersToConformanceContext( { property, propertyPair.second, }); return property; } ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) { auto var = dyn_cast(requirement); if (!var) return nullptr; if (var->getName() == Context.Id_unownedExecutor) return deriveActor_unownedExecutor(*this); return nullptr; }