mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
9d2d1a3316
The compiler synthesizes an `unownedExecutor` property for every `actor` type. It was also synthesizing availability attributes for this property by intersecting the availability of the actor type and the `UnownedExecutor` type. This may have been necessary early in the development of Swift Concurrency but now it amounts to little more than a convoluted procedure that must result in repeating the availability of the actor on one of its properties (the property can never be less available than the actor itself).
175 lines
6.4 KiB
C++
175 lines
6.4 KiB
C++
//===--- DerivedConformanceActor.cpp ----------------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2018 - 2025 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 "DerivedConformance.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<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 = 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<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()->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->addAttribute(new (ctx) SemanticsAttr(SEMANTICS_DEFAULT_ACTOR,
|
|
SourceLoc(), SourceRange(),
|
|
/*implicit*/ true));
|
|
property->addAttribute(NonisolatedAttr::createImplicit(ctx));
|
|
|
|
// Make the property implicitly final.
|
|
property->addAttribute(new (ctx) FinalAttr(/*IsImplicit=*/true));
|
|
if (property->getFormalAccess() == AccessLevel::Open)
|
|
property->overwriteAccess(AccessLevel::Public);
|
|
|
|
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<VarDecl>(requirement);
|
|
if (!var)
|
|
return nullptr;
|
|
|
|
if (var->getName() == Context.Id_unownedExecutor)
|
|
return deriveActor_unownedExecutor(*this);
|
|
|
|
return nullptr;
|
|
}
|