Files
swift-mirror/lib/Sema/DerivedConformanceActor.cpp
John McCall 186c53000d Introduce basic support for custom executors.
- Introduce an UnownedSerialExecutor type into the concurrency library.
- Create a SerialExecutor protocol which allows an executor type to
  change how it executes jobs.
- Add an unownedExecutor requirement to the Actor protocol.
- Change the ABI for ExecutorRef so that it stores a SerialExecutor
  witness table pointer in the implementation field.  This effectively
  makes ExecutorRef an `unowned(unsafe) SerialExecutor`, except that
  default actors are represented without a witness table pointer (just
  a bit-pattern).
- Synthesize the unownedExecutor method for default actors (i.e. actors
  that don't provide an unownedExecutor property).
- Make synthesized unownedExecutor properties `final`, and give them
  a semantics attribute specifying that they're for default actors.
- Split `Builtin.buildSerialExecutorRef` into a few more precise
  builtins.  We're not using the main-actor one yet, though.

Pitch thread:
  https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425
2021-04-30 03:11:56 -04:00

181 lines
6.7 KiB
C++

//===--- 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 "TypeChecker.h"
#include "swift/Strings.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 "DerivedConformances.h"
using namespace swift;
bool DerivedConformance::canDeriveActor(DeclContext *dc,
NominalTypeDecl *nominal) {
auto classDecl = dyn_cast<ClassDecl>(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<ConstructorDecl>(member);
if (!ctor) continue;
auto params = ctor->getParameters();
if (params->size() != 1 ||
!params->get(0)->getInterfaceType()->is<BuiltinExecutorType>())
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<FunctionType>()->getResult();
auto selfApply = new (ctx) ConstructorRefCallExpr(initRef, metatypeRef,
ctorAppliedType);
selfApply->setImplicit(true);
selfApply->setThrows(false);
// Call the constructor, building an expression of type
// UnownedSerialExecutor.
auto call = CallExpr::createImplicit(ctx, selfApply, arg, /*labels*/ {});
call->setType(executorType);
call->setThrows(false);
return call;
}
return nullptr;
}
static std::pair<BraceStmt *, bool>
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<BraceStmt *, bool> {
auto body = BraceStmt::create(
ctx, SourceLoc(), { }, SourceLoc(), /*implicit=*/true);
return { body, /*isTypeChecked=*/true };
};
// Build a reference to self.
Type selfType = getter->getImplicitSelfDecl()->getType();
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 = new (ctx) ReturnStmt(SourceLoc(), initCall, /*implicit*/ true);
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(ctx.Id_unownedExecutor,
executorType, 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(/*IsImplicit=*/true));
// Make the property implicitly final.
property->getAttrs().add(new (ctx) FinalAttr(/*IsImplicit=*/true));
if (property->getFormalAccess() == AccessLevel::Open)
property->overwriteAccess(AccessLevel::Public);
// Clone any @available attributes from UnownedSerialExecutor.
// Really, though, the whole actor probably needs to be marked as
// unavailable.
for (auto attr: executorDecl->getAttrs().getAttributes<AvailableAttr>())
property->getAttrs().add(attr->clone(ctx, /*implicit*/true));
auto getter =
derived.addGetterToReadOnlyDerivedProperty(property, executorType);
getter->setBodySynthesizer(deriveBodyActor_unownedExecutor);
derived.addMembersToConformanceContext(
{ property, propertyPair.second, });
return property;
}
ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) {
auto var = dyn_cast<VarDecl>(requirement);
if (!var)
return nullptr;
if (var->getName() == Context.Id_unownedExecutor)
return deriveActor_unownedExecutor(*this);
return nullptr;
}