blob: bb817f58b88fccc5c3d491eed6720b6bf7ea8ab6 [file] [log] [blame]
//===--- TypeCheckCaptures.cpp - Capture Analysis -------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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 computing capture info for closure expressions and named
// local functions.
//
//===----------------------------------------------------------------------===//
#include "TypeChecker.h"
#include "TypeCheckObjC.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ForeignErrorConvention.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeWalker.h"
#include "swift/Basic/Defer.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace swift;
namespace {
class FindCapturedVars : public ASTWalker {
ASTContext &Context;
SmallVector<CapturedValue, 4> Captures;
llvm::SmallDenseMap<ValueDecl*, unsigned, 4> captureEntryNumber;
SourceLoc GenericParamCaptureLoc;
SourceLoc DynamicSelfCaptureLoc;
DynamicSelfType *DynamicSelf = nullptr;
OpaqueValueExpr *OpaqueValue = nullptr;
SourceLoc CaptureLoc;
DeclContext *CurDC;
bool NoEscape, ObjC;
bool HasGenericParamCaptures;
public:
FindCapturedVars(SourceLoc CaptureLoc,
DeclContext *CurDC,
bool NoEscape,
bool ObjC,
bool IsGenericFunction)
: Context(CurDC->getASTContext()), CaptureLoc(CaptureLoc), CurDC(CurDC),
NoEscape(NoEscape), ObjC(ObjC), HasGenericParamCaptures(IsGenericFunction) {}
CaptureInfo getCaptureInfo() const {
DynamicSelfType *dynamicSelfToRecord = nullptr;
// Only local functions capture dynamic 'Self'.
if (CurDC->getParent()->isLocalContext()) {
if (DynamicSelfCaptureLoc.isValid())
dynamicSelfToRecord = DynamicSelf;
}
return CaptureInfo(Context, Captures, dynamicSelfToRecord, OpaqueValue,
HasGenericParamCaptures);
}
bool hasGenericParamCaptures() const {
return HasGenericParamCaptures;
}
SourceLoc getGenericParamCaptureLoc() const {
return GenericParamCaptureLoc;
}
SourceLoc getDynamicSelfCaptureLoc() const {
return DynamicSelfCaptureLoc;
}
/// Check if the type of an expression references any generic
/// type parameters, or the dynamic Self type.
///
/// Note that we do not need to distinguish inner from outer generic
/// parameters here -- if a local function has its own inner parameter
/// list, it also implicitly captures outer parameters, even if they're
/// not used anywhere inside the body.
void checkType(Type type, SourceLoc loc) {
if (!type)
return;
// We want to look through type aliases here.
type = type->getCanonicalType();
class TypeCaptureWalker : public TypeWalker {
bool ObjC;
std::function<void(Type)> Callback;
public:
explicit TypeCaptureWalker(bool ObjC,
std::function<void(Type)> callback)
: ObjC(ObjC), Callback(std::move(callback)) {}
Action walkToTypePre(Type ty) override {
Callback(ty);
// Pseudogeneric classes don't use their generic parameters so we
// don't need to visit them.
if (ObjC) {
if (auto clas = dyn_cast_or_null<ClassDecl>(ty->getAnyNominal())) {
if (clas->usesObjCGenericsModel()) {
return Action::SkipChildren;
}
}
}
return Action::Continue;
}
};
// If the type contains dynamic 'Self', conservatively assume we will
// need 'Self' metadata at runtime. We could generalize the analysis
// used below for usages of generic parameters in Objective-C
// extensions, and re-use it here.
//
// For example, forming an existential from a value of type 'Self'
// does not need the dynamic 'Self' type -- the static type will
// suffice. Also, just passing around a value of type 'Self' does
// not need metadata either, since it is represented as a single
// retainable pointer. Similarly stored property access does not
// need it, etc.
if (type->hasDynamicSelfType()) {
type.walk(TypeCaptureWalker(ObjC, [&](Type t) {
if (auto *dynamicSelf = t->getAs<DynamicSelfType>()) {
if (DynamicSelfCaptureLoc.isInvalid()) {
DynamicSelfCaptureLoc = loc;
DynamicSelf = dynamicSelf;
}
}
}));
}
// Similar to dynamic 'Self', IRGen doesn't really need type metadata
// for class-bound archetypes in nearly as many cases as with opaque
// archetypes.
//
// Perhaps this entire analysis should happen at the SILGen level,
// instead, but even there we don't really have enough information to
// perform it accurately.
if (type->hasArchetype() || type->hasTypeParameter()) {
type.walk(TypeCaptureWalker(ObjC, [&](Type t) {
if ((t->is<ArchetypeType>() ||
t->is<GenericTypeParamType>()) &&
!t->isOpenedExistential() &&
!HasGenericParamCaptures) {
GenericParamCaptureLoc = loc;
HasGenericParamCaptures = true;
}
}));
}
if (auto *gft = type->getAs<GenericFunctionType>()) {
TypeCaptureWalker walker(ObjC, [&](Type t) {
if (t->is<GenericTypeParamType>() &&
!HasGenericParamCaptures) {
GenericParamCaptureLoc = loc;
HasGenericParamCaptures = true;
}
});
for (const auto &param : gft->getParams())
param.getPlainType().walk(walker);
gft->getResult().walk(walker);
}
}
/// Add the specified capture to the closure's capture list, diagnosing it
/// if invalid.
void addCapture(CapturedValue capture) {
auto VD = capture.getDecl();
// Check to see if we already have an entry for this decl.
unsigned &entryNumber = captureEntryNumber[VD];
if (entryNumber == 0) {
Captures.push_back(capture);
entryNumber = Captures.size();
} else {
// If this already had an entry in the capture list, make sure to merge
// the information together. If one is noescape but the other isn't,
// then the result is escaping.
auto existing = Captures[entryNumber-1];
unsigned flags = existing.getFlags() & capture.getFlags();
capture = CapturedValue(VD, flags, existing.getLoc());
Captures[entryNumber-1] = capture;
}
// Visit the type of the capture, if it isn't a class reference, since
// we'd need the metadata to do so.
if (VD->hasInterfaceType()
&& (!ObjC
|| !isa<VarDecl>(VD)
|| !cast<VarDecl>(VD)->getType()->hasRetainablePointerRepresentation()))
checkType(VD->getInterfaceType(), VD->getLoc());
}
bool shouldWalkIntoLazyInitializers() override {
// We don't want to walk into lazy initializers because they're not
// really present at this level. We'll catch them when processing
// the getter.
return false;
}
std::pair<bool, Expr *> walkToDeclRefExpr(DeclRefExpr *DRE) {
auto *D = DRE->getDecl();
// HACK: $interpolation variables are seen as needing to be captured.
// The good news is, we literally never need to capture them, so we
// can safely ignore them.
// FIXME(TapExpr): This is probably caused by the scoping
// algorithm's ignorance of TapExpr. We should fix that.
if (D->getBaseName() == Context.Id_dollarInterpolation)
return { false, DRE };
// DC is the DeclContext where D was defined
// CurDC is the DeclContext where D was referenced
auto DC = D->getDeclContext();
// Capture the generic parameters of the decl, unless it's a
// local declaration in which case we will pick up generic
// parameter references transitively.
if (!DC->isLocalContext()) {
if (!ObjC || !D->isObjC() || isa<ConstructorDecl>(D)) {
if (auto subMap = DRE->getDeclRef().getSubstitutions()) {
for (auto type : subMap.getReplacementTypes()) {
checkType(type, DRE->getLoc());
}
}
}
}
// Don't "capture" type definitions at all.
if (isa<TypeDecl>(D))
return { false, DRE };
// A local reference is not a capture.
if (CurDC == DC || isa<TopLevelCodeDecl>(CurDC))
return { false, DRE };
auto TmpDC = CurDC;
while (TmpDC != nullptr) {
// Variables defined inside TopLevelCodeDecls are semantically
// local variables. If the reference is not from the top level,
// we have a capture.
if (isa<TopLevelCodeDecl>(DC) &&
(isa<SourceFile>(TmpDC) || isa<TopLevelCodeDecl>(TmpDC)))
break;
if (TmpDC == DC)
break;
// The initializer of a lazy property will eventually get
// recontextualized into it, so treat it as if it's already there.
if (auto init = dyn_cast<PatternBindingInitializer>(TmpDC)) {
if (auto lazyVar = init->getInitializedLazyVar()) {
// If we have a getter with a body, we're already re-parented
// everything so pretend we're inside the getter.
if (auto getter = lazyVar->getAccessor(AccessorKind::Get)) {
if (getter->getBody(/*canSynthesize=*/false)) {
TmpDC = getter;
continue;
}
}
}
}
// We have an intervening nominal type context that is not the
// declaration context, and the declaration context is not global.
// This is not supported since nominal types cannot capture values.
if (auto NTD = dyn_cast<NominalTypeDecl>(TmpDC)) {
// Allow references to local functions from inside methods of a
// local type, because if the local function has captures, we'll
// diagnose them in SILGen. It's a bit unfortunate that we can't
// ban this outright, but people rely on code like this working:
//
// do {
// func local() {}
// class C {
// func method() { local() }
// }
// }
if (!isa<FuncDecl>(D)) {
if (DC->isLocalContext()) {
Context.Diags.diagnose(DRE->getLoc(), diag::capture_across_type_decl,
NTD->getDescriptiveKind(),
D->getBaseIdentifier());
NTD->diagnose(diag::kind_declared_here,
DescriptiveDeclKind::Type);
D->diagnose(diag::decl_declared_here, D->getName());
return { false, DRE };
}
}
}
TmpDC = TmpDC->getParent();
}
// We walked all the way up to the root without finding the declaration,
// so this is not a capture.
if (TmpDC == nullptr)
return { false, DRE };
// Only capture var decls at global scope. Other things can be captured
// if they are local.
if (!isa<VarDecl>(D) && !D->isLocalCapture())
return { false, DRE };
// We're going to capture this, compute flags for the capture.
unsigned Flags = 0;
// If this is a direct reference to underlying storage, then this is a
// capture of the storage address - not a capture of the getter/setter.
if (auto var = dyn_cast<VarDecl>(D)) {
if (var->getAccessStrategy(DRE->getAccessSemantics(),
var->supportsMutation()
? AccessKind::ReadWrite
: AccessKind::Read,
CurDC->getParentModule(),
CurDC->getResilienceExpansion())
.getKind() == AccessStrategy::Storage)
Flags |= CapturedValue::IsDirect;
}
// If the closure is noescape, then we can capture the decl as noescape.
if (NoEscape)
Flags |= CapturedValue::IsNoEscape;
addCapture(CapturedValue(D, Flags, DRE->getStartLoc()));
return { false, DRE };
}
void propagateCaptures(CaptureInfo captureInfo, SourceLoc loc) {
for (auto capture : captureInfo.getCaptures()) {
// If the decl was captured from us, it isn't captured *by* us.
if (capture.getDecl()->getDeclContext() == CurDC)
continue;
// Compute adjusted flags.
unsigned Flags = capture.getFlags();
// The decl is captured normally, even if it was captured directly
// in the subclosure.
Flags &= ~CapturedValue::IsDirect;
// If this is an escaping closure, then any captured decls are also
// escaping, even if they are coming from an inner noescape closure.
if (!NoEscape)
Flags &= ~CapturedValue::IsNoEscape;
addCapture(CapturedValue(capture.getDecl(), Flags, capture.getLoc()));
}
if (!HasGenericParamCaptures) {
if (captureInfo.hasGenericParamCaptures()) {
GenericParamCaptureLoc = loc;
HasGenericParamCaptures = true;
}
}
if (DynamicSelfCaptureLoc.isInvalid()) {
if (captureInfo.hasDynamicSelfCapture()) {
DynamicSelfCaptureLoc = loc;
DynamicSelf = captureInfo.getDynamicSelfType();
}
}
if (!OpaqueValue) {
if (captureInfo.hasOpaqueValueCapture())
OpaqueValue = captureInfo.getOpaqueValue();
}
}
bool walkToDeclPre(Decl *D) override {
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
TypeChecker::computeCaptures(AFD);
propagateCaptures(AFD->getCaptureInfo(), AFD->getLoc());
return false;
}
// Don't walk into local types; we'll walk their initializers when we check
// the local type itself.
if (isa<NominalTypeDecl>(D))
return false;
return true;
}
bool usesTypeMetadataOfFormalType(Expr *E) {
// For non-ObjC closures, assume the type metadata is always used.
if (!ObjC)
return true;
if (!E->getType() || E->getType()->hasError())
return false;
// We can use Objective-C generics in limited ways without reifying
// their type metadata, meaning we don't need to capture their generic
// params.
// Look through one layer of optionality when considering the class-
// Referring to a class-constrained generic or metatype
// doesn't require its type metadata.
if (auto declRef = dyn_cast<DeclRefExpr>(E))
return (!declRef->getDecl()->isObjC()
&& !E->getType()->getWithoutSpecifierType()
->hasRetainablePointerRepresentation()
&& !E->getType()->getWithoutSpecifierType()
->is<AnyMetatypeType>());
// Loading classes or metatypes doesn't require their metadata.
if (isa<LoadExpr>(E))
return (!E->getType()->hasRetainablePointerRepresentation()
&& !E->getType()->is<AnyMetatypeType>());
// Accessing @objc members doesn't require type metadata.
// rdar://problem/27796375 -- allocating init entry points for ObjC
// initializers are generated as true Swift generics, so reify type
// parameters.
if (auto memberRef = dyn_cast<MemberRefExpr>(E))
return !memberRef->getMember().getDecl()->hasClangNode();
if (auto applyExpr = dyn_cast<ApplyExpr>(E)) {
if (auto methodApply = dyn_cast<ApplyExpr>(applyExpr->getFn())) {
if (auto callee = dyn_cast<DeclRefExpr>(methodApply->getFn())) {
return !callee->getDecl()->isObjC()
|| isa<ConstructorDecl>(callee->getDecl());
}
}
if (auto callee = dyn_cast<DeclRefExpr>(applyExpr->getFn())) {
return !callee->getDecl()->isObjC()
|| isa<ConstructorDecl>(callee->getDecl());
}
}
if (auto subscriptExpr = dyn_cast<SubscriptExpr>(E)) {
return (subscriptExpr->hasDecl() &&
!subscriptExpr->getDecl().getDecl()->isObjC());
}
// Getting the dynamic type of a class doesn't require type metadata.
if (isa<DynamicTypeExpr>(E))
return (!E->getType()->castTo<AnyMetatypeType>()->getInstanceType()
->hasRetainablePointerRepresentation());
// Building a fixed-size tuple doesn't require type metadata.
// Approximate this for the purposes of being able to invoke @objc methods
// by considering tuples of ObjC-representable types to not use metadata.
if (auto tuple = dyn_cast<TupleExpr>(E)) {
for (auto elt : tuple->getType()->castTo<TupleType>()->getElements()) {
if (!elt.getType()->isRepresentableIn(ForeignLanguage::ObjectiveC,
CurDC))
return true;
}
return false;
}
// Coercion by itself is a no-op.
if (isa<CoerceExpr>(E))
return false;
// Upcasting doesn't require type metadata.
if (isa<DerivedToBaseExpr>(E))
return false;
if (isa<ArchetypeToSuperExpr>(E))
return false;
if (isa<CovariantReturnConversionExpr>(E))
return false;
if (isa<MetatypeConversionExpr>(E))
return false;
// Identity expressions are no-ops.
if (isa<IdentityExpr>(E))
return false;
// Discarding an assignment is a no-op.
if (isa<DiscardAssignmentExpr>(E))
return false;
// Opening an @objc existential or metatype is a no-op.
if (auto open = dyn_cast<OpenExistentialExpr>(E))
return (!open->getSubExpr()->getType()->isObjCExistentialType()
&& !open->getSubExpr()->getType()->is<AnyMetatypeType>());
// Erasure to an ObjC existential or between metatypes doesn't require
// type metadata.
if (auto erasure = dyn_cast<ErasureExpr>(E)) {
if (E->getType()->isObjCExistentialType()
|| E->getType()->is<AnyMetatypeType>())
return false;
// We also special case Any erasure in pseudogeneric contexts
// not to rely on concrete type metadata by erasing from AnyObject
// as a waypoint.
if (E->getType()->isAny()
&& erasure->getSubExpr()->getType()->is<ArchetypeType>())
return false;
// Erasure to a Swift protocol always captures the type metadata from
// its subexpression.
checkType(erasure->getSubExpr()->getType(),
erasure->getSubExpr()->getLoc());
return true;
}
// Converting an @objc metatype to AnyObject doesn't require type
// metadata.
if (isa<ClassMetatypeToObjectExpr>(E)
|| isa<ExistentialMetatypeToObjectExpr>(E))
return false;
// Casting to an ObjC class doesn't require the metadata of its type
// parameters, if any.
if (auto cast = dyn_cast<CheckedCastExpr>(E)) {
// If we failed to resolve the written type, we've emitted an
// earlier diagnostic and should bail.
const auto toTy = cast->getCastType();
if (!toTy || toTy->hasError())
return false;
if (auto clas = dyn_cast_or_null<ClassDecl>(toTy->getAnyNominal())) {
if (clas->usesObjCGenericsModel()) {
return false;
}
}
}
// Assigning an object doesn't require type metadata.
if (auto assignment = dyn_cast<AssignExpr>(E))
return assignment->getSrc()->getType() &&
!assignment->getSrc()->getType()
->hasRetainablePointerRepresentation();
return true;
}
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
if (usesTypeMetadataOfFormalType(E)) {
checkType(E->getType(), E->getLoc());
}
// Some kinds of expression don't really evaluate their subexpression,
// so we don't need to traverse.
if (isa<ObjCSelectorExpr>(E)) {
return { false, E };
}
if (auto *ECE = dyn_cast<ExplicitCastExpr>(E)) {
checkType(ECE->getCastType(), ECE->getLoc());
return { true, E };
}
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
return walkToDeclRefExpr(DRE);
// Look into lazy initializers.
if (auto *LIE = dyn_cast<LazyInitializerExpr>(E)) {
LIE->getSubExpr()->walk(*this);
return { true, E };
}
// When we see a reference to the 'super' expression, capture 'self' decl.
if (auto *superE = dyn_cast<SuperRefExpr>(E)) {
if (auto *selfDecl = superE->getSelf()) {
if (CurDC->isChildContextOf(selfDecl->getDeclContext()))
addCapture(CapturedValue(selfDecl, 0, superE->getLoc()));
}
return { false, superE };
}
// Don't recur into child closures. They should already have a capture
// list computed; we just propagate it, filtering out stuff that they
// capture from us.
if (auto *SubCE = dyn_cast<AbstractClosureExpr>(E)) {
TypeChecker::computeCaptures(SubCE);
propagateCaptures(SubCE->getCaptureInfo(), SubCE->getLoc());
return { false, E };
}
// Capture a placeholder opaque value.
if (auto opaqueValue = dyn_cast<OpaqueValueExpr>(E)) {
if (opaqueValue->isPlaceholder()) {
assert(!OpaqueValue || OpaqueValue == opaqueValue);
OpaqueValue = opaqueValue;
return { true, E };
}
}
return { true, E };
}
};
} // end anonymous namespace
void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
if (AFR.getCaptureInfo().hasBeenComputed())
return;
if (!AFR.getBody())
return;
PrettyStackTraceAnyFunctionRef trace("computing captures for", AFR);
// A generic function always captures outer generic parameters.
bool isGeneric = false;
auto *AFD = AFR.getAbstractFunctionDecl();
if (AFD)
isGeneric = (AFD->getGenericParams() != nullptr);
auto &Context = AFR.getAsDeclContext()->getASTContext();
FindCapturedVars finder(AFR.getLoc(),
AFR.getAsDeclContext(),
AFR.isKnownNoEscape(),
AFR.isObjC(),
isGeneric);
AFR.getBody()->walk(finder);
if (AFR.hasType() && !AFR.isObjC()) {
finder.checkType(AFR.getType(), AFR.getLoc());
}
AFR.setCaptureInfo(finder.getCaptureInfo());
// Compute captures for default argument expressions.
if (auto *AFD = AFR.getAbstractFunctionDecl()) {
for (auto *P : *AFD->getParameters()) {
if (auto E = P->getTypeCheckedDefaultExpr()) {
FindCapturedVars finder(E->getLoc(),
AFD,
/*isNoEscape=*/false,
/*isObjC=*/false,
/*IsGeneric*/isGeneric);
E->walk(finder);
if (!AFD->getDeclContext()->isLocalContext() &&
finder.getDynamicSelfCaptureLoc().isValid()) {
Context.Diags.diagnose(finder.getDynamicSelfCaptureLoc(),
diag::dynamic_self_default_arg);
}
P->setDefaultArgumentCaptureInfo(finder.getCaptureInfo());
}
}
}
// Extensions of generic ObjC functions can't use generic parameters from
// their context.
if (AFD && finder.hasGenericParamCaptures()) {
if (auto Clas = AFD->getParent()->getSelfClassDecl()) {
if (Clas->usesObjCGenericsModel()) {
AFD->diagnose(diag::objc_generic_extension_using_type_parameter);
// If it's possible, suggest adding @objc.
Optional<ForeignAsyncConvention> asyncConvention;
Optional<ForeignErrorConvention> errorConvention;
if (!AFD->isObjC() &&
isRepresentableInObjC(AFD, ObjCReason::MemberOfObjCMembersClass,
asyncConvention, errorConvention)) {
AFD->diagnose(
diag::objc_generic_extension_using_type_parameter_try_objc)
.fixItInsert(AFD->getAttributeInsertionLoc(false), "@objc ");
}
Context.Diags.diagnose(
finder.getGenericParamCaptureLoc(),
diag::objc_generic_extension_using_type_parameter_here);
}
}
}
}
static bool isLazy(PatternBindingDecl *PBD) {
if (auto var = PBD->getSingleVar())
return var->getAttrs().hasAttribute<LazyAttr>();
return false;
}
void TypeChecker::checkPatternBindingCaptures(IterableDeclContext *DC) {
for (auto member : DC->getMembers()) {
// Ignore everything other than PBDs.
auto *PBD = dyn_cast<PatternBindingDecl>(member);
if (!PBD) continue;
// Walk the initializers for all properties declared in the type with
// an initializer.
for (unsigned i : range(PBD->getNumPatternEntries())) {
if (PBD->isInitializerSubsumed(i))
continue;
auto *init = PBD->getInit(i);
if (init == nullptr)
continue;
auto *DC = PBD->getInitContext(i);
FindCapturedVars finder(init->getLoc(),
DC,
/*NoEscape=*/false,
/*ObjC=*/false,
/*IsGenericFunction*/false);
init->walk(finder);
auto &ctx = DC->getASTContext();
if (finder.getDynamicSelfCaptureLoc().isValid() && !isLazy(PBD)) {
ctx.Diags.diagnose(finder.getDynamicSelfCaptureLoc(),
diag::dynamic_self_stored_property_init);
}
auto captures = finder.getCaptureInfo();
PBD->setCaptureInfo(i, captures);
}
}
}