Files
swift-mirror/lib/Sema/DerivedConformanceActor.cpp
Ben Barham 33bc38cdb8 [IDE] SourceEntityWalker should walk all explicit declarations
`SourceEntityWalker` had an unbalanced `walkToDeclPre` and
`walkToDeclPost`, ie. `walkToDeclPost` could be called even though
`walkToDeclPre` was not. Specifically, this would occur for both
`OperatorDecl` and `PrecedenceGroupDecl` declarations.

These could both be added to the `if` in `walkToDeclPost`, but this
seems fairly errorprone in general - especially as new decls are added.
Indeed, there's already declarations that are being skipped because they
aren't explicitly tested for in `walkToDeclPre`, ie.
`PatternBindingDecl`.

Instead of skipping if not explcitly handled, only skip running the
`SEWalker` walk methods if the declaration is implicit (and not a
constructor decl, see TODO). This should probably also always visit
children, with various decls changed to become implicit (eg.
TopLevelCodeDecl), but we can do that later - breaks too many tests for
now.

This change exposed a few parameter declarations that were missing their
implicit flag, as well as unbalanced walk methods in `RangeResolver`.
2021-02-11 10:34:07 +10:00

176 lines
6.3 KiB
C++

//===--- 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<ClassDecl>(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<ValueDecl *, 2> decls;
concurrencyModule->lookupQualified(
concurrencyModule, DeclNameRef(ctx.Id_PartialAsyncTask),
NL_QualifiedDefault, decls);
for (auto decl : decls) {
if (auto typeDecl = dyn_cast<TypeDecl>(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<FuncDecl>(result.getValueDecl()))
return func;
}
return nullptr;
}
static std::pair<BraceStmt *, bool>
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<BraceStmt *, bool> {
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<FuncDecl>(requirement);
if (!func)
return nullptr;
if (FuncDecl::isEnqueuePartialTaskName(Context, func->getName()))
return deriveActor_enqueuePartialTask(*this);
return nullptr;
}