mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Let's use a ClosureParametersScope for all closures, even those without an 'in' keyword. This eliminates the need for the ClosureBodyScope and WholeClosureScope. Also, let's move the lookup of capture list bindings from CaptureParametersScope to CaptureListScope. This eliminates the need for CaptureParametersScope to store a reference to the capture list, which allows us to remove the AbstractClosureScope base class entirely.
813 lines
29 KiB
C++
813 lines
29 KiB
C++
//===--- ASTScopeLookup.cpp - Swift Object-Oriented AST Scope -------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 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 the lookup functionality of the ASTScopeImpl ontology.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
#include "swift/AST/ASTScope.h"
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Initializer.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/SourceFile.h"
|
|
#include "swift/AST/Stmt.h"
|
|
#include "swift/AST/TypeRepr.h"
|
|
#include "swift/Basic/STLExtras.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
|
|
using namespace swift;
|
|
using namespace namelookup;
|
|
using namespace ast_scope;
|
|
|
|
llvm::SmallVector<const ASTScopeImpl *, 0> ASTScopeImpl::unqualifiedLookup(
|
|
SourceFile *sourceFile, const DeclNameRef name, const SourceLoc loc,
|
|
DeclConsumer consumer) {
|
|
SmallVector<const ASTScopeImpl *, 0> history;
|
|
const auto *start =
|
|
findStartingScopeForLookup(sourceFile, name, loc);
|
|
if (start)
|
|
start->lookup(history, nullptr, nullptr, consumer);
|
|
return history;
|
|
}
|
|
|
|
const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup(
|
|
SourceFile *sourceFile, const DeclNameRef name, const SourceLoc loc) {
|
|
auto *const fileScope = sourceFile->getScope().impl;
|
|
// Parser may have added decls to source file, since previous lookup
|
|
if (name.isOperator())
|
|
return fileScope; // operators always at file scope
|
|
|
|
const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr);
|
|
ASTScopeAssert(innermost->getWasExpanded(),
|
|
"If looking in a scope, it must have been expanded.");
|
|
|
|
return innermost;
|
|
}
|
|
|
|
ASTScopeImpl *
|
|
ASTScopeImpl::findInnermostEnclosingScope(SourceLoc loc,
|
|
NullablePtr<raw_ostream> os) {
|
|
return findInnermostEnclosingScopeImpl(loc, os, getSourceManager(),
|
|
getScopeCreator());
|
|
}
|
|
|
|
ASTScopeImpl *ASTScopeImpl::findInnermostEnclosingScopeImpl(
|
|
SourceLoc loc, NullablePtr<raw_ostream> os, SourceManager &sourceMgr,
|
|
ScopeCreator &scopeCreator) {
|
|
expandAndBeCurrentDetectingRecursion(scopeCreator);
|
|
auto child = findChildContaining(loc, sourceMgr);
|
|
if (!child)
|
|
return this;
|
|
return child.get()->findInnermostEnclosingScopeImpl(loc, os, sourceMgr,
|
|
scopeCreator);
|
|
}
|
|
|
|
bool ASTScopeImpl::checkSourceRangeOfThisASTNode() const {
|
|
const auto r = getSourceRangeOfThisASTNode();
|
|
(void)r;
|
|
ASTScopeAssert(!getSourceManager().isBeforeInBuffer(r.End, r.Start),
|
|
"Range is backwards.");
|
|
return true;
|
|
}
|
|
|
|
/// If the \p loc is in a new buffer but \p range is not, consider the location
|
|
/// is at the start of replaced range. Otherwise, returns \p loc as is.
|
|
static SourceLoc translateLocForReplacedRange(SourceManager &sourceMgr,
|
|
SourceRange range,
|
|
SourceLoc loc) {
|
|
if (const auto &replacedRange = sourceMgr.getReplacedRange()) {
|
|
if (sourceMgr.rangeContainsTokenLoc(replacedRange.New, loc) &&
|
|
!sourceMgr.rangeContains(replacedRange.New, range)) {
|
|
return replacedRange.Original.Start;
|
|
}
|
|
}
|
|
return loc;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl>
|
|
ASTScopeImpl::findChildContaining(SourceLoc loc,
|
|
SourceManager &sourceMgr) const {
|
|
// Use binary search to find the child that contains this location.
|
|
auto *const *child = llvm::lower_bound(
|
|
getChildren(), loc,
|
|
[&sourceMgr](const ASTScopeImpl *scope, SourceLoc loc) {
|
|
ASTScopeAssert(scope->checkSourceRangeOfThisASTNode(), "Bad range.");
|
|
auto rangeOfScope = scope->getSourceRangeOfScope();
|
|
loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc);
|
|
return -1 == ASTScopeImpl::compare(rangeOfScope, loc, sourceMgr,
|
|
/*ensureDisjoint=*/false);
|
|
});
|
|
|
|
if (child != getChildren().end()) {
|
|
auto rangeOfScope = (*child)->getSourceRangeOfScope();
|
|
loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc);
|
|
if (sourceMgr.rangeContainsTokenLoc(rangeOfScope, loc))
|
|
return *child;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
#pragma mark lookup methods that run once per scope
|
|
|
|
void ASTScopeImpl::lookup(SmallVectorImpl<const ASTScopeImpl *> &history,
|
|
const NullablePtr<const ASTScopeImpl> limit,
|
|
NullablePtr<const GenericParamList> lastListSearched,
|
|
DeclConsumer consumer) const {
|
|
|
|
history.push_back(this);
|
|
|
|
#ifndef NDEBUG
|
|
consumer.startingNextLookupStep();
|
|
#endif
|
|
|
|
// Certain illegal nestings, e.g. protocol nestled inside a struct,
|
|
// require that lookup stop at the outer scope.
|
|
if (this == limit.getPtrOrNull()) {
|
|
#ifndef NDEBUG
|
|
consumer.finishingLookup("limit return");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// Look for generics before members in violation of lexical ordering because
|
|
// you can say "self.name" to get a name shadowed by a generic but you
|
|
// can't do the opposite to get a generic shadowed by a name.
|
|
const auto doneAndListSearched =
|
|
lookInMyGenericParameters(lastListSearched, consumer);
|
|
if (doneAndListSearched.first)
|
|
return;
|
|
|
|
if (lookupLocalsOrMembers(history, consumer))
|
|
return;
|
|
|
|
const auto *const lookupParent = getLookupParent().getPtrOrNull();
|
|
if (!lookupParent) {
|
|
#ifndef NDEBUG
|
|
consumer.finishingLookup("Finished lookup; no parent");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// If there is no limit and this scope induces one, pass that on.
|
|
const NullablePtr<const ASTScopeImpl> limitForParent =
|
|
limit ? limit : getLookupLimit();
|
|
|
|
return lookupParent->lookup(history, limitForParent, lastListSearched,
|
|
consumer);
|
|
}
|
|
|
|
#pragma mark genericParams()
|
|
|
|
NullablePtr<const GenericParamList> ASTScopeImpl::genericParams() const {
|
|
return nullptr;
|
|
}
|
|
NullablePtr<const GenericParamList>
|
|
AbstractFunctionDeclScope::genericParams() const {
|
|
return decl->getGenericParams();
|
|
}
|
|
NullablePtr<const GenericParamList> SubscriptDeclScope::genericParams() const {
|
|
return decl->getGenericParams();
|
|
}
|
|
NullablePtr<const GenericParamList> GenericTypeScope::genericParams() const {
|
|
// For Decls:
|
|
// WAIT, WHAT?! Isn't this covered by the GenericParamScope
|
|
// lookupLocalsOrMembers? No, that's for use of generics in the body. This is
|
|
// for generic restrictions.
|
|
|
|
// For Bodies:
|
|
// Sigh... These must be here so that from body, we search generics before
|
|
// members. But they also must be on the Decl scope for lookups starting from
|
|
// generic parameters, where clauses, etc.
|
|
auto *context = getGenericContext();
|
|
if (isa<TypeAliasDecl>(context))
|
|
return context->getParsedGenericParams();
|
|
return context->getGenericParams();
|
|
}
|
|
NullablePtr<const GenericParamList> ExtensionScope::genericParams() const {
|
|
return decl->getGenericParams();
|
|
}
|
|
|
|
#pragma mark lookInMyGenericParameters
|
|
|
|
std::pair<bool, NullablePtr<const GenericParamList>>
|
|
ASTScopeImpl::lookInMyGenericParameters(
|
|
NullablePtr<const GenericParamList> formerListSearched,
|
|
ASTScopeImpl::DeclConsumer consumer) const {
|
|
auto listToSearch = genericParams();
|
|
if (listToSearch == formerListSearched)
|
|
return std::make_pair(false, formerListSearched);
|
|
|
|
// For extensions of nested types, must check outer parameters
|
|
for (auto *params = listToSearch.getPtrOrNull(); params;
|
|
params = params->getOuterParameters()) {
|
|
if (lookInGenericParametersOf(params, consumer))
|
|
return std::make_pair(true, listToSearch);
|
|
}
|
|
return std::make_pair(false, listToSearch);
|
|
}
|
|
|
|
bool ASTScopeImpl::lookInGenericParametersOf(
|
|
const NullablePtr<const GenericParamList> paramList,
|
|
ASTScopeImpl::DeclConsumer consumer) {
|
|
if (!paramList)
|
|
return false;
|
|
SmallVector<ValueDecl *, 32> bindings;
|
|
for (auto *param : paramList.get()->getParams())
|
|
bindings.push_back(param);
|
|
if (consumer.consume(bindings, DeclVisibilityKind::GenericParameter))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
#pragma mark looking in locals or members - members
|
|
|
|
bool ASTScopeImpl::lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
|
|
DeclConsumer) const {
|
|
return false; // many kinds of scopes have none
|
|
}
|
|
|
|
bool GenericTypeOrExtensionScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *> history,
|
|
ASTScopeImpl::DeclConsumer consumer) const {
|
|
// isCascadingUseArg must have already been resolved, for a real lookup
|
|
// but may be \c None for dumping.
|
|
return portion->lookupMembersOf(this, history, consumer);
|
|
}
|
|
|
|
bool Portion::lookupMembersOf(const GenericTypeOrExtensionScope *,
|
|
ArrayRef<const ASTScopeImpl *>,
|
|
ASTScopeImpl::DeclConsumer) const {
|
|
return false;
|
|
}
|
|
|
|
bool GenericTypeOrExtensionWhereOrBodyPortion::lookupMembersOf(
|
|
const GenericTypeOrExtensionScope *scope,
|
|
ArrayRef<const ASTScopeImpl *> history,
|
|
ASTScopeImpl::DeclConsumer consumer) const {
|
|
auto nt = scope->getCorrespondingNominalTypeDecl().getPtrOrNull();
|
|
if (!nt)
|
|
return false;
|
|
auto selfDC = computeSelfDC(history);
|
|
return consumer.lookInMembers(selfDC, scope->getDeclContext().get(), nt,
|
|
[&](Optional<bool> initialIsCascadingUse) {
|
|
return ASTScopeImpl::computeIsCascadingUse(
|
|
history, initialIsCascadingUse)
|
|
.getValueOr(true);
|
|
});
|
|
}
|
|
|
|
bool GenericTypeOrExtensionWherePortion::lookupMembersOf(
|
|
const GenericTypeOrExtensionScope *scope,
|
|
ArrayRef<const ASTScopeImpl *> history,
|
|
ASTScopeImpl::DeclConsumer consumer) const {
|
|
if (!scope->areMembersVisibleFromWhereClause())
|
|
return false;
|
|
|
|
return GenericTypeOrExtensionWhereOrBodyPortion::lookupMembersOf(
|
|
scope, history, consumer);
|
|
}
|
|
|
|
bool GenericTypeOrExtensionScope::areMembersVisibleFromWhereClause() const {
|
|
auto *decl = getDecl();
|
|
return isa<ProtocolDecl>(decl) || isa<ExtensionDecl>(decl);
|
|
}
|
|
|
|
#pragma mark looking in locals or members - locals
|
|
|
|
bool GenericParamScope::lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
|
|
DeclConsumer consumer) const {
|
|
auto *param = paramList->getParams()[index];
|
|
return consumer.consume({param}, DeclVisibilityKind::GenericParameter);
|
|
}
|
|
|
|
bool PatternEntryDeclScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *>, DeclConsumer consumer) const {
|
|
if (vis != DeclVisibilityKind::LocalVariable)
|
|
return false; // look in self type will find this later
|
|
return lookupLocalBindingsInPattern(getPattern(), vis, consumer);
|
|
}
|
|
|
|
bool ForEachPatternScope::lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
|
|
DeclConsumer consumer) const {
|
|
return lookupLocalBindingsInPattern(
|
|
stmt->getPattern(), DeclVisibilityKind::LocalVariable, consumer);
|
|
}
|
|
|
|
bool CaseLabelItemScope::lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
|
|
DeclConsumer consumer) const {
|
|
return lookupLocalBindingsInPattern(
|
|
item.getPattern(), DeclVisibilityKind::LocalVariable, consumer);
|
|
}
|
|
|
|
bool CaseStmtBodyScope::lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
|
|
DeclConsumer consumer) const {
|
|
for (auto *var : stmt->getCaseBodyVariablesOrEmptyArray())
|
|
if (consumer.consume({var}, DeclVisibilityKind::LocalVariable))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AbstractFunctionBodyScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *>, DeclConsumer consumer) const {
|
|
if (auto *paramList = decl->getParameters()) {
|
|
for (auto *paramDecl : *paramList)
|
|
if (consumer.consume({paramDecl}, DeclVisibilityKind::FunctionParameter))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MethodBodyScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *> history, DeclConsumer consumer) const {
|
|
ASTScopeAssert(isAMethod(decl), "Asking for members of a non-method.");
|
|
if (AbstractFunctionBodyScope::lookupLocalsOrMembers(history, consumer))
|
|
return true;
|
|
return consumer.consume({decl->getImplicitSelfDecl()},
|
|
DeclVisibilityKind::FunctionParameter);
|
|
}
|
|
|
|
bool PureFunctionBodyScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *> history, DeclConsumer consumer) const {
|
|
ASTScopeAssert(
|
|
!isAMethod(decl),
|
|
"Should have called lookupLocalsOrMembers instead of this function.");
|
|
if (AbstractFunctionBodyScope::lookupLocalsOrMembers(history, consumer))
|
|
return true;
|
|
|
|
// Consider \c var t: T { (did/will/)get/set { ... t }}
|
|
// Lookup needs to find t, but if the var is inside of a type the baseDC needs
|
|
// to be set. It all works fine, except: if the var is not inside of a type,
|
|
// then t needs to be found as a local binding:
|
|
if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
|
|
if (auto *storage = accessor->getStorage())
|
|
if (consumer.consume({storage}, DeclVisibilityKind::LocalVariable))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SpecializeAttributeScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *>, DeclConsumer consumer) const {
|
|
if (auto *params = whatWasSpecialized->getGenericParams())
|
|
for (auto *param : params->getParams())
|
|
if (consumer.consume({param}, DeclVisibilityKind::GenericParameter))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool DifferentiableAttributeScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *>, DeclConsumer consumer) const {
|
|
auto visitAbstractFunctionDecl = [&](AbstractFunctionDecl *afd) {
|
|
if (auto *params = afd->getGenericParams())
|
|
for (auto *param : params->getParams())
|
|
if (consumer.consume({param}, DeclVisibilityKind::GenericParameter))
|
|
return true;
|
|
return false;
|
|
};
|
|
if (auto *afd = dyn_cast<AbstractFunctionDecl>(attributedDeclaration)) {
|
|
return visitAbstractFunctionDecl(afd);
|
|
} else if (auto *asd = dyn_cast<AbstractStorageDecl>(attributedDeclaration)) {
|
|
for (auto *accessor : asd->getAllAccessors())
|
|
if (visitAbstractFunctionDecl(accessor))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BraceStmtScope::lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
|
|
DeclConsumer consumer) const {
|
|
// All types and functions are visible anywhere within a brace statement
|
|
// scope. When ordering matters (i.e. var decl) we will have split the brace
|
|
// statement into nested scopes.
|
|
//
|
|
// Don't stop at the first one, there may be local funcs with same base name
|
|
// and want them all.
|
|
SmallVector<ValueDecl *, 32> localBindings;
|
|
for (auto braceElement : stmt->getElements()) {
|
|
if (auto localBinding = braceElement.dyn_cast<Decl *>()) {
|
|
if (auto *vd = dyn_cast<ValueDecl>(localBinding))
|
|
localBindings.push_back(vd);
|
|
}
|
|
}
|
|
return consumer.consume(localBindings, DeclVisibilityKind::LocalVariable);
|
|
}
|
|
|
|
bool PatternEntryInitializerScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *>, DeclConsumer consumer) const {
|
|
// 'self' is available within the pattern initializer of a 'lazy' variable.
|
|
auto *initContext = dyn_cast_or_null<PatternBindingInitializer>(
|
|
decl->getInitContext(0));
|
|
if (initContext) {
|
|
if (auto *selfParam = initContext->getImplicitSelfDecl()) {
|
|
return consumer.consume({selfParam},
|
|
DeclVisibilityKind::FunctionParameter);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CaptureListScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *>, DeclConsumer consumer) const {
|
|
for (auto &e : expr->getCaptureList()) {
|
|
if (consumer.consume(
|
|
{e.Var},
|
|
DeclVisibilityKind::LocalVariable)) // or FunctionParameter??
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClosureParametersScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *>, DeclConsumer consumer) const {
|
|
for (auto param : *closureExpr->getParameters())
|
|
if (consumer.consume({param}, DeclVisibilityKind::FunctionParameter))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool ConditionalClausePatternUseScope::lookupLocalsOrMembers(
|
|
ArrayRef<const ASTScopeImpl *>, DeclConsumer consumer) const {
|
|
return lookupLocalBindingsInPattern(
|
|
pattern, DeclVisibilityKind::LocalVariable, consumer);
|
|
}
|
|
|
|
bool ASTScopeImpl::lookupLocalBindingsInPattern(const Pattern *p,
|
|
DeclVisibilityKind vis,
|
|
DeclConsumer consumer) {
|
|
if (!p)
|
|
return false;
|
|
bool isDone = false;
|
|
p->forEachVariable([&](VarDecl *var) {
|
|
if (!isDone)
|
|
isDone = consumer.consume({var}, vis);
|
|
});
|
|
return isDone;
|
|
}
|
|
|
|
#pragma mark computeSelfDC
|
|
|
|
NullablePtr<DeclContext>
|
|
GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC(
|
|
ArrayRef<const ASTScopeImpl *> history) {
|
|
ASTScopeAssert(history.size() != 0, "includes current scope");
|
|
size_t i = history.size() - 1; // skip last entry (this scope)
|
|
while (i != 0) {
|
|
Optional<NullablePtr<DeclContext>> maybeSelfDC =
|
|
history[--i]->computeSelfDCForParent();
|
|
if (maybeSelfDC) {
|
|
// If we've found a selfDC, we'll definitely be returning something.
|
|
// However, we may have captured 'self' somewhere down the tree, so we
|
|
// can't return outright without checking the nested scopes.
|
|
NullablePtr<DeclContext> nestedCapturedSelfDC =
|
|
checkNestedScopesForSelfCapture(history, i);
|
|
return nestedCapturedSelfDC ? nestedCapturedSelfDC : *maybeSelfDC;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
#pragma mark checkNestedScopesForSelfCapture
|
|
|
|
NullablePtr<DeclContext>
|
|
GenericTypeOrExtensionWhereOrBodyPortion::checkNestedScopesForSelfCapture(
|
|
ArrayRef<const ASTScopeImpl *> history, size_t start) {
|
|
NullablePtr<DeclContext> innerCapturedSelfDC;
|
|
// Start with the next scope down the tree.
|
|
size_t j = start;
|
|
|
|
// Note: even though having this loop nested inside the while loop from
|
|
// GenericTypeOrExtensionWhereOrBodyPortion::computeSelfDC may appear to
|
|
// result in quadratic blowup, complexity actually remains linear with respect
|
|
// to the size of history. This relies on the fact that
|
|
// GenericTypeOrExtensionScope::computeSelfDCForParent returns a null pointer,
|
|
// which will cause this method to bail out of the search early. Thus, this
|
|
// method is called once per type body in the lookup history, and will not
|
|
// end up re-checking the bodies of nested types that have already been
|
|
// covered by earlier calls, so the total impact of this method across all
|
|
// calls in a single lookup is O(n).
|
|
while (j != 0) {
|
|
auto *entry = history[--j];
|
|
Optional<NullablePtr<DeclContext>> selfDCForParent =
|
|
entry->computeSelfDCForParent();
|
|
|
|
// If we encounter a scope that should cause us to forget the self
|
|
// context (such as a nested type), bail out and use whatever the
|
|
// the last inner captured context was.
|
|
if (selfDCForParent && (*selfDCForParent).isNull())
|
|
break;
|
|
|
|
// Otherwise, if we have a captured self context for this scope, then
|
|
// remember it since it is now the innermost scope we have encountered.
|
|
NullablePtr<DeclContext> capturedSelfDC = entry->capturedSelfDC();
|
|
if (!capturedSelfDC.isNull())
|
|
innerCapturedSelfDC = entry->capturedSelfDC();
|
|
|
|
// Continue searching in the next scope down.
|
|
}
|
|
return innerCapturedSelfDC;
|
|
}
|
|
|
|
#pragma mark compute isCascadingUse
|
|
|
|
Optional<bool> ASTScopeImpl::computeIsCascadingUse(
|
|
ArrayRef<const ASTScopeImpl *> history,
|
|
const Optional<bool> initialIsCascadingUse) {
|
|
Optional<bool> isCascadingUse = initialIsCascadingUse;
|
|
for (const auto *scope : history)
|
|
isCascadingUse = scope->resolveIsCascadingUseForThisScope(isCascadingUse);
|
|
return isCascadingUse;
|
|
}
|
|
|
|
#pragma mark getLookupLimit
|
|
|
|
NullablePtr<const ASTScopeImpl> ASTScopeImpl::getLookupLimit() const {
|
|
return nullptr;
|
|
}
|
|
|
|
NullablePtr<const ASTScopeImpl>
|
|
GenericTypeOrExtensionScope::getLookupLimit() const {
|
|
return portion->getLookupLimitFor(this);
|
|
}
|
|
|
|
NullablePtr<const ASTScopeImpl>
|
|
Portion::getLookupLimitFor(const GenericTypeOrExtensionScope *) const {
|
|
return nullptr;
|
|
}
|
|
NullablePtr<const ASTScopeImpl>
|
|
GenericTypeOrExtensionWholePortion::getLookupLimitFor(
|
|
const GenericTypeOrExtensionScope *scope) const {
|
|
return scope->getLookupLimitForDecl();
|
|
}
|
|
|
|
NullablePtr<const ASTScopeImpl>
|
|
GenericTypeOrExtensionScope::getLookupLimitForDecl() const {
|
|
return nullptr;
|
|
}
|
|
|
|
NullablePtr<const ASTScopeImpl>
|
|
NominalTypeScope::getLookupLimitForDecl() const {
|
|
if (isa<ProtocolDecl>(decl)) {
|
|
// ProtocolDecl can only be legally nested in a SourceFile,
|
|
// so any other kind of Decl is illegal
|
|
return parentIfNotChildOfTopScope();
|
|
}
|
|
// AFAICT, a struct, decl, or enum can be nested inside anything
|
|
// but a ProtocolDecl.
|
|
return ancestorWithDeclSatisfying(
|
|
[&](const Decl *const d) { return isa<ProtocolDecl>(d); });
|
|
}
|
|
|
|
NullablePtr<const ASTScopeImpl> ExtensionScope::getLookupLimitForDecl() const {
|
|
// Extensions can only be legally nested in a SourceFile,
|
|
// so any other kind of Decl is illegal
|
|
return parentIfNotChildOfTopScope();
|
|
}
|
|
|
|
NullablePtr<const ASTScopeImpl> ASTScopeImpl::ancestorWithDeclSatisfying(
|
|
function_ref<bool(const Decl *)> predicate) const {
|
|
for (NullablePtr<const ASTScopeImpl> s = getParent(); s;
|
|
s = s.get()->getParent()) {
|
|
if (Decl *d = s.get()->getDeclIfAny().getPtrOrNull()) {
|
|
if (predicate(d))
|
|
return s;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
#pragma mark computeSelfDCForParent
|
|
|
|
// If the lookup depends on implicit self, selfDC is its context.
|
|
// (Names in extensions never depend on self.)
|
|
// Lookup can propagate it up from, say a method to the enclosing type body.
|
|
|
|
// By default, propagate the selfDC up to a NomExt decl, body,
|
|
// or where clause
|
|
Optional<NullablePtr<DeclContext>>
|
|
ASTScopeImpl::computeSelfDCForParent() const {
|
|
return None;
|
|
}
|
|
|
|
// Forget the "self" declaration:
|
|
Optional<NullablePtr<DeclContext>>
|
|
GenericTypeOrExtensionScope::computeSelfDCForParent() const {
|
|
return NullablePtr<DeclContext>();
|
|
}
|
|
|
|
Optional<NullablePtr<DeclContext>>
|
|
PatternEntryInitializerScope::computeSelfDCForParent() const {
|
|
// Pattern binding initializers are only interesting insofar as they
|
|
// affect lookup in an enclosing nominal type or extension thereof.
|
|
if (auto *ic = getPatternEntry().getInitContext()) {
|
|
if (auto *bindingInit = dyn_cast<PatternBindingInitializer>(ic)) {
|
|
// Lazy variable initializer contexts have a 'self' parameter for
|
|
// instance member lookup.
|
|
if (bindingInit->getImplicitSelfDecl()) {
|
|
return NullablePtr<DeclContext>(bindingInit);
|
|
}
|
|
}
|
|
}
|
|
return None;
|
|
}
|
|
|
|
Optional<NullablePtr<DeclContext>>
|
|
MethodBodyScope::computeSelfDCForParent() const {
|
|
return NullablePtr<DeclContext>(decl);
|
|
}
|
|
|
|
#pragma mark capturedSelfDC
|
|
|
|
// Closures may explicitly capture the self param, in which case the lookup
|
|
// should use the closure as the context for implicit self lookups.
|
|
|
|
// By default, there is no such context to return.
|
|
NullablePtr<DeclContext> ASTScopeImpl::capturedSelfDC() const {
|
|
return NullablePtr<DeclContext>();
|
|
}
|
|
|
|
// Closures track this information explicitly.
|
|
NullablePtr<DeclContext> ClosureParametersScope::capturedSelfDC() const {
|
|
if (closureExpr->capturesSelfEnablingImplictSelf())
|
|
return NullablePtr<DeclContext>(closureExpr);
|
|
return NullablePtr<DeclContext>();
|
|
}
|
|
|
|
#pragma mark ifUnknownIsCascadingUseAccordingTo
|
|
|
|
static bool isCascadingUseAccordingTo(const DeclContext *const dc) {
|
|
return dc->isCascadingContextForLookup(false);
|
|
}
|
|
|
|
static bool ifUnknownIsCascadingUseAccordingTo(Optional<bool> isCascadingUse,
|
|
const DeclContext *const dc) {
|
|
return isCascadingUse.getValueOr(isCascadingUseAccordingTo(dc));
|
|
}
|
|
|
|
#pragma mark resolveIsCascadingUseForThisScope
|
|
|
|
Optional<bool> ASTScopeImpl::resolveIsCascadingUseForThisScope(
|
|
Optional<bool> isCascadingUse) const {
|
|
return isCascadingUse;
|
|
}
|
|
|
|
Optional<bool> GenericParamScope::resolveIsCascadingUseForThisScope(
|
|
Optional<bool> isCascadingUse) const {
|
|
if (auto *dc = getDeclContext().getPtrOrNull())
|
|
return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, dc);
|
|
ASTScope_unreachable("generic what?");
|
|
}
|
|
|
|
Optional<bool> AbstractFunctionDeclScope::resolveIsCascadingUseForThisScope(
|
|
Optional<bool> isCascadingUse) const {
|
|
return decl->isCascadingContextForLookup(false) &&
|
|
isCascadingUse.getValueOr(true);
|
|
}
|
|
|
|
Optional<bool> AbstractFunctionBodyScope::resolveIsCascadingUseForThisScope(
|
|
Optional<bool> isCascadingUse) const {
|
|
return false;
|
|
}
|
|
|
|
Optional<bool> GenericTypeOrExtensionScope::resolveIsCascadingUseForThisScope(
|
|
Optional<bool> isCascadingUse) const {
|
|
// Could override for ExtensionScope and just return true
|
|
return ifUnknownIsCascadingUseAccordingTo(isCascadingUse,
|
|
getDeclContext().get());
|
|
}
|
|
|
|
Optional<bool>
|
|
DefaultArgumentInitializerScope::resolveIsCascadingUseForThisScope(
|
|
Optional<bool>) const {
|
|
return false;
|
|
}
|
|
|
|
Optional<bool> ClosureParametersScope::resolveIsCascadingUseForThisScope(
|
|
Optional<bool> isCascadingUse) const {
|
|
return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, closureExpr);
|
|
}
|
|
|
|
Optional<bool> PatternEntryInitializerScope::resolveIsCascadingUseForThisScope(
|
|
Optional<bool> isCascadingUse) const {
|
|
auto *const initContext = getPatternEntry().getInitContext();
|
|
auto *PBI = dyn_cast_or_null<PatternBindingInitializer>(initContext);
|
|
auto *isd = PBI ? PBI->getImplicitSelfDecl() : nullptr;
|
|
|
|
// 'self' is available within the pattern initializer of a 'lazy' variable.
|
|
if (isd)
|
|
return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, PBI);
|
|
|
|
// initializing stored property of a type
|
|
auto *const patternDeclContext = decl->getDeclContext();
|
|
if (patternDeclContext->isTypeContext())
|
|
return isCascadingUseAccordingTo(PBI->getParent());
|
|
|
|
// initializing global or local
|
|
if (PBI)
|
|
return ifUnknownIsCascadingUseAccordingTo(isCascadingUse, PBI);
|
|
|
|
return isCascadingUse;
|
|
}
|
|
|
|
#pragma mark isLabeledStmtLookupTerminator implementations
|
|
bool ASTScopeImpl::isLabeledStmtLookupTerminator() const {
|
|
return true;
|
|
}
|
|
|
|
bool LookupParentDiversionScope::isLabeledStmtLookupTerminator() const {
|
|
return false;
|
|
}
|
|
|
|
bool ConditionalClauseScope::isLabeledStmtLookupTerminator() const {
|
|
return false;
|
|
}
|
|
|
|
bool ConditionalClausePatternUseScope::isLabeledStmtLookupTerminator() const {
|
|
return false;
|
|
}
|
|
|
|
bool AbstractStmtScope::isLabeledStmtLookupTerminator() const {
|
|
return false;
|
|
}
|
|
|
|
bool ForEachPatternScope::isLabeledStmtLookupTerminator() const {
|
|
return false;
|
|
}
|
|
|
|
bool CaseStmtBodyScope::isLabeledStmtLookupTerminator() const {
|
|
return false;
|
|
}
|
|
|
|
llvm::SmallVector<LabeledStmt *, 4>
|
|
ASTScopeImpl::lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc) {
|
|
// Find the innermost scope from which to start our search.
|
|
auto *const fileScope = sourceFile->getScope().impl;
|
|
const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr);
|
|
ASTScopeAssert(innermost->getWasExpanded(),
|
|
"If looking in a scope, it must have been expanded.");
|
|
|
|
llvm::SmallVector<LabeledStmt *, 4> labeledStmts;
|
|
for (auto scope = innermost; scope && !scope->isLabeledStmtLookupTerminator();
|
|
scope = scope->getParent().getPtrOrNull()) {
|
|
// If we have a labeled statement, record it.
|
|
auto stmt = scope->getStmtIfAny();
|
|
if (!stmt) continue;
|
|
|
|
auto labeledStmt = dyn_cast<LabeledStmt>(stmt.get());
|
|
if (!labeledStmt) continue;
|
|
|
|
// Skip guard statements; they aren't actually targets for break or
|
|
// continue.
|
|
if (isa<GuardStmt>(labeledStmt)) continue;
|
|
|
|
labeledStmts.push_back(labeledStmt);
|
|
}
|
|
|
|
return labeledStmts;
|
|
}
|
|
|
|
std::pair<CaseStmt *, CaseStmt *> ASTScopeImpl::lookupFallthroughSourceAndDest(
|
|
SourceFile *sourceFile, SourceLoc loc) {
|
|
// Find the innermost scope from which to start our search.
|
|
auto *const fileScope = sourceFile->getScope().impl;
|
|
const auto *innermost = fileScope->findInnermostEnclosingScope(loc, nullptr);
|
|
ASTScopeAssert(innermost->getWasExpanded(),
|
|
"If looking in a scope, it must have been expanded.");
|
|
|
|
// Look for the enclosing case statement of a 'switch'.
|
|
for (auto scope = innermost; scope && !scope->isLabeledStmtLookupTerminator();
|
|
scope = scope->getParent().getPtrOrNull()) {
|
|
// If we have a case statement, record it.
|
|
auto stmt = scope->getStmtIfAny();
|
|
if (!stmt) continue;
|
|
|
|
// If we've found the first case statement of a switch, record it as the
|
|
// fallthrough source. do-catch statements don't support fallthrough.
|
|
if (auto caseStmt = dyn_cast<CaseStmt>(stmt.get())) {
|
|
if (caseStmt->getParentKind() == CaseParentKind::Switch)
|
|
return { caseStmt, caseStmt->findNextCaseStmt() };
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return { nullptr, nullptr };
|
|
}
|