mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This commit adds a new type DynamicLookupInfo that provides information about how a dynamic member lookup found a particular Decl. This is needed to correctly handle KeyPath dynamic member lookups, but for now just plumb it through everywhere.
544 lines
18 KiB
C++
544 lines
18 KiB
C++
//===--- NameLookup.h - Swift Name Lookup Routines --------------*- C++ -*-===//
|
|
//
|
|
// 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 defines interfaces for performing name lookup.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_AST_NAME_LOOKUP_H
|
|
#define SWIFT_AST_NAME_LOOKUP_H
|
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "swift/AST/ASTVisitor.h"
|
|
#include "swift/AST/Identifier.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/Basic/SourceLoc.h"
|
|
|
|
namespace swift {
|
|
class ASTContext;
|
|
class DeclContext;
|
|
class DeclName;
|
|
class Expr;
|
|
class GenericSignatureBuilder;
|
|
class LazyResolver;
|
|
class TupleType;
|
|
class Type;
|
|
class TypeDecl;
|
|
class ValueDecl;
|
|
struct SelfBounds;
|
|
|
|
/// LookupResultEntry - One result of unqualified lookup.
|
|
struct LookupResultEntry {
|
|
private:
|
|
|
|
/// The declaration context through which we found Value. For instance,
|
|
/// class BaseClass {
|
|
/// func foo() {}
|
|
/// }
|
|
///
|
|
/// class DerivedClass : BaseClass {
|
|
/// func bar() {}
|
|
/// }
|
|
///
|
|
/// When finding foo() from the body of DerivedClass, BaseDC is DerivedClass.
|
|
///
|
|
/// Another example:
|
|
///
|
|
/// class BaseClass {
|
|
/// func bar() {}
|
|
/// func foo() {}
|
|
/// }
|
|
///
|
|
/// When finding bar() from the function body of foo(), BaseDC is the method
|
|
/// foo().
|
|
DeclContext *BaseDC;
|
|
|
|
/// The declaration corresponds to the given name; i.e. the decl we are
|
|
/// looking up.
|
|
ValueDecl *Value;
|
|
|
|
public:
|
|
LookupResultEntry(ValueDecl *value) : BaseDC(nullptr), Value(value) { }
|
|
|
|
LookupResultEntry(DeclContext *baseDC, ValueDecl *value)
|
|
: BaseDC(baseDC), Value(value) { }
|
|
|
|
ValueDecl *getValueDecl() const {
|
|
return Value;
|
|
}
|
|
|
|
DeclContext *getDeclContext() const {
|
|
return BaseDC;
|
|
}
|
|
|
|
ValueDecl *getBaseDecl() const;
|
|
};
|
|
|
|
/// This class implements and represents the result of performing
|
|
/// unqualified lookup (i.e. lookup for a plain identifier).
|
|
class UnqualifiedLookup {
|
|
public:
|
|
enum class Flags {
|
|
/// This lookup is known to not affect downstream files.
|
|
KnownPrivate = 0x01,
|
|
/// This lookup should only return types.
|
|
TypeLookup = 0x02,
|
|
/// This lookup should consider declarations within protocols to which the
|
|
/// context type conforms.
|
|
AllowProtocolMembers = 0x04,
|
|
/// Don't check access when doing lookup into a type.
|
|
IgnoreAccessControl = 0x08,
|
|
/// This lookup should include results from outside the innermost scope with
|
|
/// results.
|
|
IncludeOuterResults = 0x10,
|
|
};
|
|
using Options = OptionSet<Flags>;
|
|
|
|
/// Lookup an unqualified identifier \p Name in the context.
|
|
///
|
|
/// If the current DeclContext is nested in a function body, the SourceLoc
|
|
/// is used to determine which declarations in that body are visible.
|
|
UnqualifiedLookup(DeclName Name, DeclContext *DC, LazyResolver *TypeResolver,
|
|
SourceLoc Loc = SourceLoc(), Options options = Options());
|
|
|
|
SmallVector<LookupResultEntry, 4> Results;
|
|
/// The index of the first result that isn't from the innermost scope
|
|
/// with results.
|
|
///
|
|
/// That is, \c makeArrayRef(Results).take_front(IndexOfFirstOuterResults)
|
|
/// will be Results from the innermost scope that had results, and the
|
|
/// remaining elements of Results will be from parent scopes of this one.
|
|
///
|
|
/// Allows unqualified name lookup to return results from outer scopes.
|
|
/// This is necessary for disambiguating calls to functions like `min` and
|
|
/// `max`.
|
|
size_t IndexOfFirstOuterResult;
|
|
|
|
/// Return true if anything was found by the name lookup.
|
|
bool isSuccess() const { return !Results.empty(); }
|
|
|
|
/// Get the result as a single type, or a null type if that fails.
|
|
TypeDecl *getSingleTypeResult() const;
|
|
};
|
|
|
|
inline UnqualifiedLookup::Options operator|(UnqualifiedLookup::Flags flag1,
|
|
UnqualifiedLookup::Flags flag2) {
|
|
return UnqualifiedLookup::Options(flag1) | flag2;
|
|
}
|
|
|
|
|
|
/// Describes the reason why a certain declaration is visible.
|
|
enum class DeclVisibilityKind {
|
|
/// Declaration is a local variable or type.
|
|
LocalVariable,
|
|
|
|
/// Declaration is a function parameter.
|
|
FunctionParameter,
|
|
|
|
/// Declaration is a generic parameter.
|
|
GenericParameter,
|
|
|
|
/// Declaration is a member of the immediately enclosing nominal decl.
|
|
///
|
|
/// For example, 'Foo' is visible at (1) because of this.
|
|
/// \code
|
|
/// struct A {
|
|
/// typealias Foo = Int
|
|
/// func f() {
|
|
/// // (1)
|
|
/// }
|
|
/// }
|
|
/// \endcode
|
|
MemberOfCurrentNominal,
|
|
|
|
/// Declaration that is a requirement of a protocol implemented by the
|
|
/// immediately enclosing nominal decl, in case the nominal decl does not
|
|
/// supply a witness for this requirement.
|
|
///
|
|
/// For example, 'foo' is visible at (1) because of this.
|
|
/// \code
|
|
/// protocol P {
|
|
/// func foo()
|
|
/// }
|
|
/// struct A : P {
|
|
/// func bar() {
|
|
/// // (1)
|
|
/// }
|
|
/// }
|
|
/// \endcode
|
|
MemberOfProtocolImplementedByCurrentNominal,
|
|
|
|
/// Declaration is a member of the superclass of the immediately enclosing
|
|
/// nominal decl.
|
|
MemberOfSuper,
|
|
|
|
/// Declaration is a member of the non-immediately enclosing nominal decl.
|
|
///
|
|
/// For example, 'Foo' is visible at (1) because of this.
|
|
/// \code
|
|
/// struct A {
|
|
/// typealias Foo = Int
|
|
/// struct B {
|
|
/// func foo() {
|
|
/// // (1)
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// \endcode
|
|
MemberOfOutsideNominal,
|
|
|
|
/// Declaration is visible at the top level because it is declared in this
|
|
/// module or in an imported module.
|
|
VisibleAtTopLevel,
|
|
|
|
/// Declaration was found via \c AnyObject or \c AnyObject.Type.
|
|
DynamicLookup,
|
|
};
|
|
|
|
/// For Decls found with DeclVisibilityKind::DynamicLookup, contains details of
|
|
/// how they were looked up. For example, the SubscriptDecl used to find a
|
|
/// KeyPath dynamic member.
|
|
class DynamicLookupInfo {
|
|
public:
|
|
enum Kind {
|
|
None,
|
|
AnyObject,
|
|
KeyPathDynamicMember,
|
|
};
|
|
|
|
struct KeyPathDynamicMemberInfo {
|
|
/// The subscript(dynamicMember:) by which we found the declaration.
|
|
SubscriptDecl *subscript = nullptr;
|
|
|
|
/// The type context of `subscript`, which may be different than the
|
|
/// original base type of the lookup if this declaration was found by nested
|
|
/// dynamic lookups.
|
|
Type baseType = Type();
|
|
|
|
/// Visibility of the declaration itself without dynamic lookup.
|
|
///
|
|
/// For example, dynamic lookup for KeyPath<Derived, U>, might find
|
|
/// Base::foo with originalVisibility == MemberOfSuper.
|
|
DeclVisibilityKind originalVisibility = DeclVisibilityKind::DynamicLookup;
|
|
};
|
|
|
|
Kind getKind() const { return kind; }
|
|
|
|
const KeyPathDynamicMemberInfo &getKeyPathDynamicMember() const;
|
|
|
|
DynamicLookupInfo() : kind(None) {}
|
|
DynamicLookupInfo(Kind kind) : kind(kind) {
|
|
assert(kind != KeyPathDynamicMember && "use KeyPathDynamicMemberInfo ctor");
|
|
}
|
|
|
|
/// Construct for a KeyPath dynamic member lookup.
|
|
DynamicLookupInfo(SubscriptDecl *subscript, Type baseType,
|
|
DeclVisibilityKind originalVisibility);
|
|
|
|
private:
|
|
Kind kind;
|
|
KeyPathDynamicMemberInfo keypath = {};
|
|
};
|
|
|
|
/// An abstract base class for a visitor that consumes declarations found within
|
|
/// a given context.
|
|
class VisibleDeclConsumer {
|
|
virtual void anchor();
|
|
public:
|
|
virtual ~VisibleDeclConsumer() = default;
|
|
|
|
/// This method is called by findVisibleDecls() every time it finds a decl.
|
|
virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo = {}) = 0;
|
|
};
|
|
|
|
/// An implementation of VisibleDeclConsumer that's built from a lambda.
|
|
template <class Fn>
|
|
class LambdaDeclConsumer : public VisibleDeclConsumer {
|
|
Fn Callback;
|
|
public:
|
|
LambdaDeclConsumer(Fn &&callback) : Callback(std::move(callback)) {}
|
|
|
|
void foundDecl(ValueDecl *VD, DeclVisibilityKind reason, DynamicLookupInfo) {
|
|
Callback(VD, reason);
|
|
}
|
|
};
|
|
template <class Fn>
|
|
LambdaDeclConsumer<Fn> makeDeclConsumer(Fn &&callback) {
|
|
return LambdaDeclConsumer<Fn>(std::move(callback));
|
|
}
|
|
|
|
/// A consumer that inserts found decls into an externally-owned SmallVector.
|
|
class VectorDeclConsumer : public VisibleDeclConsumer {
|
|
virtual void anchor() override;
|
|
public:
|
|
SmallVectorImpl<ValueDecl *> &results;
|
|
explicit VectorDeclConsumer(SmallVectorImpl<ValueDecl *> &decls)
|
|
: results(decls) {}
|
|
|
|
virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo) override {
|
|
results.push_back(VD);
|
|
}
|
|
};
|
|
|
|
/// A consumer that inserts found decls with a matching name into an
|
|
/// externally-owned SmallVector.
|
|
class NamedDeclConsumer : public VisibleDeclConsumer {
|
|
virtual void anchor() override;
|
|
public:
|
|
DeclName name;
|
|
SmallVectorImpl<LookupResultEntry> &results;
|
|
bool isTypeLookup;
|
|
|
|
NamedDeclConsumer(DeclName name,
|
|
SmallVectorImpl<LookupResultEntry> &results,
|
|
bool isTypeLookup)
|
|
: name(name), results(results), isTypeLookup(isTypeLookup) {}
|
|
|
|
virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo = {}) override {
|
|
// Give clients an opportunity to filter out non-type declarations early,
|
|
// to avoid circular validation.
|
|
if (isTypeLookup && !isa<TypeDecl>(VD))
|
|
return;
|
|
if (VD->getFullName().matchesRef(name))
|
|
results.push_back(LookupResultEntry(VD));
|
|
}
|
|
};
|
|
|
|
/// A consumer that filters out decls that are not accessible from a given
|
|
/// DeclContext.
|
|
class AccessFilteringDeclConsumer final : public VisibleDeclConsumer {
|
|
const DeclContext *DC;
|
|
VisibleDeclConsumer &ChainedConsumer;
|
|
|
|
public:
|
|
AccessFilteringDeclConsumer(const DeclContext *DC,
|
|
VisibleDeclConsumer &consumer)
|
|
: DC(DC), ChainedConsumer(consumer) {}
|
|
|
|
void foundDecl(ValueDecl *D, DeclVisibilityKind reason,
|
|
DynamicLookupInfo dynamicLookupInfo = {}) override;
|
|
};
|
|
|
|
/// Remove any declarations in the given set that were overridden by
|
|
/// other declarations in that set.
|
|
///
|
|
/// \returns true if any declarations were removed, false otherwise.
|
|
bool removeOverriddenDecls(SmallVectorImpl<ValueDecl*> &decls);
|
|
|
|
/// Remove any declarations in the given set that are shadowed by
|
|
/// other declarations in that set.
|
|
///
|
|
/// \param decls The set of declarations being considered.
|
|
/// \param curModule The current module.
|
|
///
|
|
/// \returns true if any shadowed declarations were removed.
|
|
bool removeShadowedDecls(SmallVectorImpl<ValueDecl*> &decls,
|
|
const ModuleDecl *curModule);
|
|
|
|
/// Finds decls visible in the given context and feeds them to the given
|
|
/// VisibleDeclConsumer. If the current DeclContext is nested in a function,
|
|
/// the SourceLoc is used to determine which declarations in that function
|
|
/// are visible.
|
|
void lookupVisibleDecls(VisibleDeclConsumer &Consumer,
|
|
const DeclContext *DC,
|
|
LazyResolver *typeResolver,
|
|
bool IncludeTopLevel,
|
|
SourceLoc Loc = SourceLoc());
|
|
|
|
/// Finds decls visible as members of the given type and feeds them to the given
|
|
/// VisibleDeclConsumer.
|
|
///
|
|
/// \param CurrDC the DeclContext from which the lookup is done.
|
|
void lookupVisibleMemberDecls(VisibleDeclConsumer &Consumer,
|
|
Type BaseTy,
|
|
const DeclContext *CurrDC,
|
|
LazyResolver *typeResolver,
|
|
bool includeInstanceMembers,
|
|
GenericSignatureBuilder *GSB = nullptr);
|
|
|
|
namespace namelookup {
|
|
enum class ResolutionKind {
|
|
/// Lookup can match any number of decls, as long as they are all
|
|
/// overloadable.
|
|
///
|
|
/// If non-overloadable decls are returned, this indicates ambiguous lookup.
|
|
Overloadable,
|
|
|
|
/// Lookup should match a single decl.
|
|
Exact,
|
|
|
|
/// Lookup should match a single decl that declares a type.
|
|
TypesOnly
|
|
};
|
|
|
|
/// Performs a lookup into the given module and, if necessary, its
|
|
/// reexports, observing proper shadowing rules.
|
|
///
|
|
/// \param module The module that will contain the name.
|
|
/// \param accessPath The import scope on \p module.
|
|
/// \param name The name to look up.
|
|
/// \param[out] decls Any found decls will be added to this vector.
|
|
/// \param lookupKind Whether this lookup is qualified or unqualified.
|
|
/// \param resolutionKind What sort of decl is expected.
|
|
/// \param typeResolver The type resolver for decls that need to be
|
|
/// type-checked. This is needed for shadowing resolution.
|
|
/// \param moduleScopeContext The top-level context from which the lookup is
|
|
/// being performed, for checking access. This must be either a
|
|
/// FileUnit or a Module.
|
|
/// \param extraImports Private imports to include in this search.
|
|
void lookupInModule(ModuleDecl *module, ModuleDecl::AccessPathTy accessPath,
|
|
DeclName name, SmallVectorImpl<ValueDecl *> &decls,
|
|
NLKind lookupKind, ResolutionKind resolutionKind,
|
|
LazyResolver *typeResolver,
|
|
const DeclContext *moduleScopeContext,
|
|
ArrayRef<ModuleDecl::ImportedModule> extraImports = {});
|
|
|
|
template <typename Fn>
|
|
void forAllVisibleModules(const DeclContext *DC, const Fn &fn) {
|
|
DeclContext *moduleScope = DC->getModuleScopeContext();
|
|
if (auto file = dyn_cast<FileUnit>(moduleScope))
|
|
file->forAllVisibleModules(fn);
|
|
else
|
|
cast<ModuleDecl>(moduleScope)
|
|
->forAllVisibleModules(ModuleDecl::AccessPathTy(), fn);
|
|
}
|
|
|
|
/// Once name lookup has gathered a set of results, perform any necessary
|
|
/// steps to prune the result set before returning it to the caller.
|
|
void pruneLookupResultSet(const DeclContext *dc, NLOptions options,
|
|
SmallVectorImpl<ValueDecl *> &decls);
|
|
|
|
/// Do nothing if debugClient is null.
|
|
template <typename Result>
|
|
void filterForDiscriminator(SmallVectorImpl<Result> &results,
|
|
DebuggerClient *debugClient);
|
|
|
|
void recordLookupOfTopLevelName(DeclContext *topLevelContext, DeclName name,
|
|
bool isCascading);
|
|
|
|
} // end namespace namelookup
|
|
|
|
/// Retrieve the set of nominal type declarations that are directly
|
|
/// "inherited" by the given declaration at a particular position in the
|
|
/// list of "inherited" types.
|
|
///
|
|
/// Add anything we find to the \c result vector. If we come across the
|
|
/// AnyObject type, set \c anyObject true.
|
|
void getDirectlyInheritedNominalTypeDecls(
|
|
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
|
|
unsigned i,
|
|
llvm::SmallVectorImpl<std::pair<SourceLoc, NominalTypeDecl *>> &result,
|
|
bool &anyObject);
|
|
|
|
/// Retrieve the set of nominal type declarations that are directly
|
|
/// "inherited" by the given declaration, looking through typealiases
|
|
/// and splitting out the components of compositions.
|
|
///
|
|
/// If we come across the AnyObject type, set \c anyObject true.
|
|
SmallVector<std::pair<SourceLoc, NominalTypeDecl *>, 4>
|
|
getDirectlyInheritedNominalTypeDecls(
|
|
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
|
|
bool &anyObject);
|
|
|
|
/// Retrieve the set of nominal type declarations that appear as the
|
|
/// constraint type of any "Self" constraints in the where clause of the
|
|
/// given protocol or protocol extension.
|
|
SelfBounds getSelfBoundsFromWhereClause(
|
|
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl);
|
|
|
|
namespace namelookup {
|
|
|
|
/// Performs a qualified lookup into the given module and, if necessary, its
|
|
/// reexports, observing proper shadowing rules.
|
|
void
|
|
lookupVisibleDeclsInModule(ModuleDecl *M, ModuleDecl::AccessPathTy accessPath,
|
|
SmallVectorImpl<ValueDecl *> &decls,
|
|
NLKind lookupKind,
|
|
ResolutionKind resolutionKind,
|
|
LazyResolver *typeResolver,
|
|
const DeclContext *moduleScopeContext,
|
|
ArrayRef<ModuleDecl::ImportedModule> extraImports = {});
|
|
|
|
/// Searches through statements and patterns for local variable declarations.
|
|
class FindLocalVal : public StmtVisitor<FindLocalVal> {
|
|
friend class ASTVisitor<FindLocalVal>;
|
|
|
|
const SourceManager &SM;
|
|
SourceLoc Loc;
|
|
VisibleDeclConsumer &Consumer;
|
|
|
|
public:
|
|
FindLocalVal(const SourceManager &SM, SourceLoc Loc,
|
|
VisibleDeclConsumer &Consumer)
|
|
: SM(SM), Loc(Loc), Consumer(Consumer) {}
|
|
|
|
void checkValueDecl(ValueDecl *D, DeclVisibilityKind Reason) {
|
|
Consumer.foundDecl(D, Reason);
|
|
}
|
|
|
|
void checkPattern(const Pattern *Pat, DeclVisibilityKind Reason);
|
|
|
|
void checkParameterList(const ParameterList *params);
|
|
|
|
void checkGenericParams(GenericParamList *Params);
|
|
|
|
void checkSourceFile(const SourceFile &SF);
|
|
|
|
private:
|
|
bool isReferencePointInRange(SourceRange R) {
|
|
return SM.rangeContainsTokenLoc(R, Loc);
|
|
}
|
|
|
|
void visitBreakStmt(BreakStmt *) {}
|
|
void visitContinueStmt(ContinueStmt *) {}
|
|
void visitFallthroughStmt(FallthroughStmt *) {}
|
|
void visitFailStmt(FailStmt *) {}
|
|
void visitReturnStmt(ReturnStmt *) {}
|
|
void visitYieldStmt(YieldStmt *) {}
|
|
void visitThrowStmt(ThrowStmt *) {}
|
|
void visitPoundAssertStmt(PoundAssertStmt *) {}
|
|
void visitDeferStmt(DeferStmt *DS) {
|
|
// Nothing in the defer is visible.
|
|
}
|
|
|
|
void checkStmtCondition(const StmtCondition &Cond);
|
|
|
|
void visitIfStmt(IfStmt *S);
|
|
void visitGuardStmt(GuardStmt *S);
|
|
|
|
void visitWhileStmt(WhileStmt *S);
|
|
void visitRepeatWhileStmt(RepeatWhileStmt *S);
|
|
void visitDoStmt(DoStmt *S);
|
|
|
|
void visitForEachStmt(ForEachStmt *S);
|
|
|
|
void visitBraceStmt(BraceStmt *S, bool isTopLevelCode = false);
|
|
|
|
void visitSwitchStmt(SwitchStmt *S);
|
|
|
|
void visitCaseStmt(CaseStmt *S);
|
|
|
|
void visitDoCatchStmt(DoCatchStmt *S);
|
|
void visitCatchClauses(ArrayRef<CatchStmt*> clauses);
|
|
void visitCatchStmt(CatchStmt *S);
|
|
|
|
};
|
|
|
|
} // end namespace namelookup
|
|
} // end namespace swift
|
|
|
|
#endif
|