| //===--- UnqualifiedLookup.cpp - Swift Name Lookup Routines ---------------===// |
| // |
| // 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 the construction of an UnqualifiedLookup, which entails |
| /// performing the lookup. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/ASTVisitor.h" |
| #include "swift/AST/ClangModuleLoader.h" |
| #include "swift/AST/DebuggerClient.h" |
| #include "swift/AST/ExistentialLayout.h" |
| #include "swift/AST/ImportCache.h" |
| #include "swift/AST/Initializer.h" |
| #include "swift/AST/LazyResolver.h" |
| #include "swift/AST/ModuleNameLookup.h" |
| #include "swift/AST/NameLookup.h" |
| #include "swift/AST/NameLookupRequests.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/ReferencedNameTracker.h" |
| #include "swift/AST/SourceFile.h" |
| #include "swift/Basic/STLExtras.h" |
| #include "swift/Basic/SourceManager.h" |
| #include "swift/Basic/Statistic.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/TinyPtrVector.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #define DEBUG_TYPE "namelookup" |
| |
| using namespace swift; |
| using namespace swift::namelookup; |
| |
| |
| namespace { |
| |
| /// Determine whether unqualified lookup should look at the members of the |
| /// given nominal type or extension, vs. only looking at type parameters. |
| template <typename D> bool shouldLookupMembers(D *decl, SourceLoc loc) { |
| // Only look at members of this type (or its inherited types) when |
| // inside the body or a protocol's top-level 'where' clause. (Why the |
| // 'where' clause? Because that's where you put constraints on |
| // inherited associated types.) |
| |
| // When we have no source-location information, we have to perform member |
| // lookup. |
| if (loc.isInvalid() || decl->getBraces().isInvalid()) |
| return true; |
| |
| // Within the braces, always look for members. |
| auto &ctx = decl->getASTContext(); |
| auto braces = decl->getBraces(); |
| if (braces.Start != braces.End && |
| ctx.SourceMgr.rangeContainsTokenLoc(braces, loc)) |
| return true; |
| |
| // Within 'where' clause, we can also look for members. |
| if (auto *whereClause = decl->getTrailingWhereClause()) { |
| SourceRange whereClauseRange = whereClause->getSourceRange(); |
| if (whereClauseRange.isValid() && |
| ctx.SourceMgr.rangeContainsTokenLoc(whereClauseRange, loc)) { |
| return true; |
| } |
| } |
| |
| // Don't look at the members. |
| return false; |
| } |
| } // end anonymous namespace |
| |
| namespace { |
| /// Because UnqualifiedLookup does all of its work in the constructor, |
| /// a factory class is needed to hold all of the inputs and outputs so |
| /// that the construction code can be decomposed into bite-sized pieces. |
| |
| class UnqualifiedLookupFactory { |
| |
| friend class ASTScopeDeclConsumerForUnqualifiedLookup; |
| |
| public: |
| using Flags = UnqualifiedLookup::Flags; |
| using Options = UnqualifiedLookup::Options; |
| using ResultsVector = UnqualifiedLookup::ResultsVector; |
| |
| private: |
| struct ContextAndResolvedIsCascadingUse { |
| DeclContext *const DC; |
| const bool isCascadingUse; |
| }; |
| |
| /// Finds lookup results based on the types that self conforms to. |
| /// For instance, self always conforms to a struct, enum or class. |
| /// But in addition, self could conform to any number of protocols. |
| /// For example, when there's a protocol extension, e.g. extension P where |
| /// self: P2, self also conforms to P2 so P2 must be searched. |
| class ResultFinderForTypeContext { |
| UnqualifiedLookupFactory *const factory; |
| /// Nontypes are formally members of the base type, i.e. the dynamic type |
| /// of the activation record. |
| DeclContext *const dynamicContext; |
| /// Types are formally members of the metatype, i.e. the static type of the |
| /// activation record. |
| DeclContext *const staticContext; |
| using SelfBounds = SmallVector<NominalTypeDecl *, 2>; |
| SelfBounds selfBounds; |
| |
| public: |
| /// \p staticContext is also the context from which to derive the self types |
| ResultFinderForTypeContext(UnqualifiedLookupFactory *factory, |
| DeclContext *dynamicContext, |
| DeclContext *staticContext); |
| |
| void dump() const; |
| |
| private: |
| SelfBounds findSelfBounds(DeclContext *dc); |
| |
| // Classify this declaration. |
| // Types are formally members of the metatype. |
| DeclContext *whereValueIsMember(const ValueDecl *const member) const { |
| return isa<TypeDecl>(member) ? staticContext : dynamicContext; |
| } |
| |
| public: |
| /// Do the lookups and add matches to results. |
| void findResults(const DeclName &Name, bool isCascadingUse, |
| NLOptions baseNLOptions, DeclContext *contextForLookup, |
| SmallVectorImpl<LookupResultEntry> &results) const; |
| }; |
| |
| enum class AddGenericParameters { Yes, No }; |
| |
| #ifndef NDEBUG |
| /// A consumer for debugging that lets the UnqualifiedLookupFactory know when |
| /// finding something. |
| class InstrumentedNamedDeclConsumer : public NamedDeclConsumer { |
| virtual void anchor() override; |
| UnqualifiedLookupFactory *factory; |
| |
| public: |
| InstrumentedNamedDeclConsumer(UnqualifiedLookupFactory *factory, |
| DeclName name, |
| SmallVectorImpl<LookupResultEntry> &results, |
| bool isTypeLookup) |
| : NamedDeclConsumer(name, results, isTypeLookup), factory(factory) {} |
| |
| virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo = {}) override { |
| unsigned before = results.size(); |
| NamedDeclConsumer::foundDecl(VD, Reason, dynamicLookupInfo); |
| unsigned after = results.size(); |
| if (after > before) |
| factory->addedResult(results.back()); |
| } |
| }; |
| #endif |
| // Inputs |
| const DeclName Name; |
| DeclContext *const DC; |
| ModuleDecl &M; |
| const ASTContext &Ctx; |
| const SourceLoc Loc; |
| const SourceManager &SM; |
| |
| /// Used to find the file-local names. |
| DebuggerClient *const DebugClient; |
| |
| const Options options; |
| const bool isOriginallyTypeLookup; |
| const NLOptions baseNLOptions; |
| // Transputs |
| #ifndef NDEBUG |
| InstrumentedNamedDeclConsumer Consumer; |
| #else |
| NamedDeclConsumer Consumer; |
| #endif |
| // Outputs |
| SmallVectorImpl<LookupResultEntry> &Results; |
| size_t &IndexOfFirstOuterResult; |
| ResultsVector UnavailableInnerResults; |
| |
| #ifndef NDEBUG |
| static unsigned lookupCounter; |
| static const unsigned targetLookup; |
| #endif |
| |
| public: // for exp debugging |
| SourceFile const *recordedSF = nullptr; |
| bool recordedIsCascadingUse = false; |
| unsigned resultsSizeBeforeLocalsPass = ~0; |
| |
| public: |
| // clang-format off |
| UnqualifiedLookupFactory(DeclName Name, |
| DeclContext *const DC, |
| SourceLoc Loc, |
| Options options, |
| UnqualifiedLookup &lookupToBeCreated); |
| |
| UnqualifiedLookupFactory(DeclName Name, |
| DeclContext *const DC, |
| SourceLoc Loc, |
| Options options, |
| SmallVectorImpl<LookupResultEntry> &Results, |
| size_t &IndexOfFirstOuterResult); |
| // clang-format on |
| |
| void performUnqualifiedLookup(); |
| |
| private: |
| struct ContextAndUnresolvedIsCascadingUse { |
| DeclContext *whereToLook; |
| Optional<bool> isCascadingUse; |
| ContextAndResolvedIsCascadingUse resolve(const bool resolution) const { |
| return ContextAndResolvedIsCascadingUse{ |
| whereToLook, isCascadingUse.getValueOr(resolution)}; |
| } |
| }; |
| |
| bool useASTScopesForLookup() const; |
| |
| /// For testing, assume this lookup is enabled: |
| bool useASTScopesForLookupIfEnabled() const; |
| |
| void lookUpTopLevelNamesInModuleScopeContext(DeclContext *); |
| |
| void lookInASTScopes(); |
| |
| /// Can lookup stop searching for results, assuming hasn't looked for outer |
| /// results yet? |
| bool isFirstResultEnough() const; |
| |
| /// Every time lookup finishes searching a scope, call me |
| /// to record the dividing line between results from first fruitful scope and |
| /// the result. |
| void recordCompletionOfAScope(); |
| |
| template <typename Fn> void ifNotDoneYet(Fn fn) { |
| recordCompletionOfAScope(); |
| if (!isFirstResultEnough()) |
| fn(); |
| } |
| |
| template <typename Fn1, typename Fn2> void ifNotDoneYet(Fn1 fn1, Fn2 fn2) { |
| ifNotDoneYet(fn1); |
| ifNotDoneYet(fn2); |
| } |
| |
| #pragma mark context-based lookup declarations |
| |
| void lookupOperatorInDeclContexts(ContextAndUnresolvedIsCascadingUse); |
| |
| void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse); |
| |
| void finishLookingInContext( |
| AddGenericParameters addGenericParameters, |
| DeclContext *lookupContextForThisContext, |
| Optional<ResultFinderForTypeContext> &&resultFinderForTypeContext, |
| Optional<bool> isCascadingUse); |
| |
| void lookupInModuleScopeContext(DeclContext *, Optional<bool> isCascadingUse); |
| |
| void lookupNamesIntroducedByPatternBindingInitializer( |
| PatternBindingInitializer *PBI, Optional<bool> isCascadingUse); |
| |
| void |
| lookupNamesIntroducedByLazyVariableInitializer(PatternBindingInitializer *PBI, |
| ParamDecl *selfParam, |
| Optional<bool> isCascadingUse); |
| |
| void lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( |
| PatternBindingInitializer *PBI, Optional<bool> isCascadingUse); |
| |
| /// An initializer of a global name, or a function-likelocal name. |
| void lookupNamesIntroducedByInitializerOfGlobalOrLocal( |
| PatternBindingInitializer *PBI, Optional<bool> isCascadingUse); |
| |
| void lookupNamesIntroducedByFunctionDecl(AbstractFunctionDecl *AFD, |
| Optional<bool> isCascadingUse); |
| |
| void lookupNamesIntroducedByMemberFunction(AbstractFunctionDecl *AFD, |
| bool isCascadingUse); |
| |
| void lookupNamesIntroducedByPureFunction(AbstractFunctionDecl *AFD, |
| bool isCascadingUse); |
| |
| void lookupNamesIntroducedByClosure(AbstractClosureExpr *ACE, |
| Optional<bool> isCascadingUse); |
| |
| template <typename NominalTypeDeclOrExtensionDecl> |
| void lookupNamesIntroducedByNominalTypeOrExtension( |
| NominalTypeDeclOrExtensionDecl *D, Optional<bool> isCascadingUse); |
| |
| void lookupNamesIntroducedByDefaultArgumentInitializer( |
| DefaultArgumentInitializer *I, Optional<bool> isCascadingUse); |
| |
| void lookupNamesIntroducedByMiscContext(DeclContext *dc, |
| Optional<bool> isCascadingUse); |
| |
| void lookForLocalVariablesIn(AbstractFunctionDecl *AFD, |
| Optional<bool> isCascadingUse); |
| void lookForLocalVariablesIn(ClosureExpr *); |
| void lookForLocalVariablesIn(SourceFile *); |
| |
| bool isOutsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const; |
| |
| void addGenericParametersForContext(DeclContext *dc); |
| void addGenericParametersForContext(GenericParamList *); |
| |
| /// Consume generic parameters |
| void addGenericParametersForFunction(AbstractFunctionDecl *AFD); |
| |
| static GenericParamList *getGenericParams(const DeclContext *const dc); |
| |
| /// For diagnostic purposes, move aside the unavailables, and put |
| /// them back as a last-ditch effort. |
| /// Could be cleaner someday with a richer interface to UnqualifiedLookup. |
| void setAsideUnavailableResults(size_t firstPossiblyUnavailableResult); |
| |
| void recordDependencyOnTopLevelName(DeclContext *topLevelContext, |
| DeclName name, bool isCascadingUse); |
| |
| void addImportedResults(DeclContext *const dc); |
| |
| void addNamesKnownToDebugClient(DeclContext *dc); |
| |
| void addUnavailableInnerResults(); |
| |
| void lookForAModuleWithTheGivenName(DeclContext *const dc); |
| |
| #pragma mark common helper declarations |
| static NLOptions |
| computeBaseNLOptions(const UnqualifiedLookup::Options options, |
| const bool isOriginallyTypeLookup); |
| |
| Optional<bool> getInitialIsCascadingUse() const { |
| return options.contains(Flags::KnownPrivate) ? Optional<bool>(false) |
| : None; |
| } |
| |
| static bool resolveIsCascadingUse(const DeclContext *const dc, |
| Optional<bool> isCascadingUse, |
| bool onlyCareAboutFunctionBody) { |
| return isCascadingUse.getValueOr(dc->isCascadingContextForLookup( |
| /*functionsAreNonCascading=*/onlyCareAboutFunctionBody)); |
| } |
| |
| static bool resolveIsCascadingUse(ContextAndUnresolvedIsCascadingUse x, |
| bool onlyCareAboutFunctionBody) { |
| return resolveIsCascadingUse(x.whereToLook, x.isCascadingUse, |
| onlyCareAboutFunctionBody); |
| } |
| |
| void findResultsAndSaveUnavailables( |
| DeclContext *lookupContextForThisContext, |
| ResultFinderForTypeContext &&resultFinderForTypeContext, |
| bool isCascadingUse, NLOptions baseNLOptions); |
| |
| public: |
| void dump() const; |
| void dumpScopes() const; |
| void print(raw_ostream &OS) const; |
| |
| void dumpResults() const; |
| |
| bool verifyEqualTo(const UnqualifiedLookupFactory &&, const char *thisLabel, |
| const char *otherLabel) const; |
| |
| /// Legacy lookup is wrong here; we should NOT find this symbol. |
| bool shouldDiffer() const; |
| StringRef getSourceFileName() const; |
| |
| #ifndef NDEBUG |
| bool isTargetLookup() const; |
| void stopForDebuggingIfStartingTargetLookup(bool isASTScopeLookup) const; |
| void stopForDebuggingIfDuringTargetLookup(bool isASTScopeLookup) const; |
| void |
| stopForDebuggingIfAddingTargetLookupResult(const LookupResultEntry &) const; |
| void addedResult(const LookupResultEntry &) const; |
| #endif |
| }; |
| |
| } // namespace |
| |
| namespace { |
| /// Used to gather lookup results |
| class ASTScopeDeclConsumerForUnqualifiedLookup |
| : public AbstractASTScopeDeclConsumer { |
| UnqualifiedLookupFactory &factory; |
| |
| public: |
| ASTScopeDeclConsumerForUnqualifiedLookup(UnqualifiedLookupFactory &factory) |
| : factory(factory) {} |
| |
| virtual ~ASTScopeDeclConsumerForUnqualifiedLookup() = default; |
| |
| bool consume(ArrayRef<ValueDecl *> values, DeclVisibilityKind vis, |
| NullablePtr<DeclContext> baseDC = nullptr) override; |
| |
| /// returns true if finished and new value for isCascadingUse |
| bool lookInMembers(NullablePtr<DeclContext> selfDC, |
| DeclContext *const scopeDC, NominalTypeDecl *const nominal, |
| function_ref<bool(Optional<bool>)>) override; |
| |
| #ifndef NDEBUG |
| void startingNextLookupStep() override { |
| factory.stopForDebuggingIfDuringTargetLookup(true); |
| } |
| bool isTargetLookup() const override { return factory.isTargetLookup(); } |
| |
| void finishingLookup(std::string msg) const override { |
| if (isTargetLookup()) |
| llvm::errs() << "Finishing lookup: " << msg << "\n"; |
| } |
| #endif |
| }; |
| } // namespace |
| |
| #pragma mark UnqualifiedLookupFactory functions |
| |
| // clang-format off |
| UnqualifiedLookupFactory::UnqualifiedLookupFactory( |
| DeclName Name, |
| DeclContext *const DC, |
| SourceLoc Loc, |
| Options options, |
| UnqualifiedLookup &lookupToBeCreated) |
| : UnqualifiedLookupFactory(Name, DC, Loc, options, |
| lookupToBeCreated.Results, |
| lookupToBeCreated.IndexOfFirstOuterResult) |
| |
| {} |
| |
| UnqualifiedLookupFactory::UnqualifiedLookupFactory( |
| DeclName Name, |
| DeclContext *const DC, |
| SourceLoc Loc, |
| Options options, |
| SmallVectorImpl<LookupResultEntry> &Results, |
| size_t &IndexOfFirstOuterResult) |
| : |
| Name(Name), |
| DC(DC), |
| M(*DC->getParentModule()), |
| Ctx(M.getASTContext()), |
| Loc(Loc), |
| SM(Ctx.SourceMgr), |
| DebugClient(M.getDebugClient()), |
| options(options), |
| isOriginallyTypeLookup(options.contains(Flags::TypeLookup)), |
| baseNLOptions(computeBaseNLOptions(options, isOriginallyTypeLookup)), |
| #ifdef NDEBUG |
| Consumer(Name, Results, isOriginallyTypeLookup), |
| #else |
| Consumer(this, Name, Results, isOriginallyTypeLookup), |
| #endif |
| Results(Results), |
| IndexOfFirstOuterResult(IndexOfFirstOuterResult) |
| {} |
| // clang-format on |
| |
| void UnqualifiedLookupFactory::performUnqualifiedLookup() { |
| #ifndef NDEBUG |
| ++lookupCounter; |
| auto localCounter = lookupCounter; |
| (void)localCounter; // for debugging |
| #endif |
| FrontendStatsTracer StatsTracer(Ctx.Stats, "performUnqualifedLookup", |
| DC->getParentSourceFile()); |
| |
| const Optional<bool> initialIsCascadingUse = getInitialIsCascadingUse(); |
| |
| ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUse{ |
| DC, initialIsCascadingUse}; |
| const bool crosscheckUnqualifiedLookup = |
| Ctx.LangOpts.CrosscheckUnqualifiedLookup; |
| if (useASTScopesForLookup()) { |
| static bool haveWarned = false; |
| if (!haveWarned && Ctx.LangOpts.WarnIfASTScopeLookup) { |
| haveWarned = true; |
| llvm::errs() << "WARNING: TRYING Scope exclusively\n"; |
| } |
| lookInASTScopes(); |
| } else { |
| #ifndef NDEBUG |
| stopForDebuggingIfStartingTargetLookup(false); |
| #endif |
| |
| if (Name.isOperator()) |
| lookupOperatorInDeclContexts(contextAndIsCascadingUse); |
| else |
| lookupNamesIntroducedBy(contextAndIsCascadingUse); |
| } |
| |
| if (crosscheckUnqualifiedLookup && useASTScopesForLookupIfEnabled()) { |
| ResultsVector results; |
| size_t indexOfFirstOuterResult = 0; |
| UnqualifiedLookupFactory altLookup(Name, DC, Loc, options, results, |
| indexOfFirstOuterResult); |
| if (!useASTScopesForLookup()) |
| altLookup.lookInASTScopes(); |
| else if (Name.isOperator()) |
| altLookup.lookupOperatorInDeclContexts(contextAndIsCascadingUse); |
| else |
| altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse); |
| |
| assert( |
| verifyEqualTo(std::move(altLookup), "main lookup", "alternate lookup")); |
| } |
| } |
| |
| void UnqualifiedLookupFactory::lookUpTopLevelNamesInModuleScopeContext( |
| DeclContext *DC) { |
| // TODO: Does the debugger client care about compound names? |
| if (Name.isSimpleName() && !Name.isSpecial() && DebugClient && |
| DebugClient->lookupOverrides(Name.getBaseName(), DC, Loc, |
| isOriginallyTypeLookup, Results)) |
| return; |
| |
| addImportedResults(DC); |
| addNamesKnownToDebugClient(DC); |
| if (Results.empty()) { |
| // If we still haven't found anything, but we do have some |
| // declarations that are "unavailable in the current Swift", drop |
| // those in. |
| addUnavailableInnerResults(); |
| if (Results.empty()) |
| lookForAModuleWithTheGivenName(DC); |
| } |
| recordCompletionOfAScope(); |
| } |
| |
| bool UnqualifiedLookupFactory::useASTScopesForLookup() const { |
| return Ctx.LangOpts.EnableASTScopeLookup && useASTScopesForLookupIfEnabled(); |
| } |
| |
| bool UnqualifiedLookupFactory::useASTScopesForLookupIfEnabled() const { |
| if (!Loc.isValid()) |
| return false; |
| const auto *const SF = DC->getParentSourceFile(); |
| return SF && SF->isSuitableForASTScopes(); |
| } |
| |
| #pragma mark context-based lookup definitions |
| |
| void UnqualifiedLookupFactory::lookupOperatorInDeclContexts( |
| const ContextAndUnresolvedIsCascadingUse contextAndUseArg) { |
| ContextAndResolvedIsCascadingUse contextAndResolvedIsCascadingUse{ |
| // Operators are global |
| contextAndUseArg.whereToLook->getModuleScopeContext(), |
| resolveIsCascadingUse(contextAndUseArg, |
| /*onlyCareAboutFunctionBody*/ true)}; |
| lookupInModuleScopeContext(contextAndResolvedIsCascadingUse.DC, |
| contextAndResolvedIsCascadingUse.isCascadingUse); |
| } |
| |
| // TODO: Unify with LookupVisibleDecls.cpp::lookupVisibleDeclsImpl |
| void UnqualifiedLookupFactory::lookupNamesIntroducedBy( |
| const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg) { |
| #ifndef NDEBUG |
| stopForDebuggingIfDuringTargetLookup(false); |
| #endif |
| DeclContext *const dc = contextAndIsCascadingUseArg.whereToLook; |
| const auto isCascadingUseSoFar = contextAndIsCascadingUseArg.isCascadingUse; |
| if (dc->isModuleScopeContext()) |
| lookupInModuleScopeContext(dc, isCascadingUseSoFar); |
| else if (auto *PBI = dyn_cast<PatternBindingInitializer>(dc)) |
| lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar); |
| else if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc)) |
| lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar); |
| else if (auto *ACE = dyn_cast<AbstractClosureExpr>(dc)) |
| lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar); |
| else if (auto *ED = dyn_cast<ExtensionDecl>(dc)) |
| lookupNamesIntroducedByNominalTypeOrExtension(ED, isCascadingUseSoFar); |
| else if (auto *ND = dyn_cast<NominalTypeDecl>(dc)) |
| lookupNamesIntroducedByNominalTypeOrExtension(ND, isCascadingUseSoFar); |
| else if (auto I = dyn_cast<DefaultArgumentInitializer>(dc)) |
| lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar); |
| else |
| lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar); |
| } |
| |
| void UnqualifiedLookupFactory::lookupInModuleScopeContext( |
| DeclContext *dc, Optional<bool> isCascadingUse) { |
| if (auto SF = dyn_cast<SourceFile>(dc)) { |
| resultsSizeBeforeLocalsPass = Results.size(); |
| lookForLocalVariablesIn(SF); |
| } |
| ifNotDoneYet([&] { |
| // If no result has been found yet, the dependency must be on a top-level |
| // name, since up to now, the search has been for non-top-level names. |
| recordDependencyOnTopLevelName(dc, Name, isCascadingUse.getValueOr(true)); |
| lookUpTopLevelNamesInModuleScopeContext(dc); |
| }); |
| } |
| |
| void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer( |
| PatternBindingInitializer *PBI, Optional<bool> isCascadingUse) { |
| // Lazy variable initializer contexts have a 'self' parameter for |
| // instance member lookup. |
| if (auto *selfParam = PBI->getImplicitSelfDecl()) |
| lookupNamesIntroducedByLazyVariableInitializer(PBI, selfParam, |
| isCascadingUse); |
| else if (PBI->getParent()->isTypeContext()) |
| lookupNamesIntroducedByInitializerOfStoredPropertyOfAType(PBI, |
| isCascadingUse); |
| else |
| lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse); |
| } |
| |
| void UnqualifiedLookupFactory::lookupNamesIntroducedByLazyVariableInitializer( |
| PatternBindingInitializer *PBI, ParamDecl *selfParam, |
| Optional<bool> isCascadingUse) { |
| Consumer.foundDecl(selfParam, DeclVisibilityKind::FunctionParameter); |
| ifNotDoneYet([&] { |
| DeclContext *const patternContainer = PBI->getParent(); |
| // clang-format off |
| finishLookingInContext( |
| AddGenericParameters::Yes, |
| patternContainer, |
| ResultFinderForTypeContext(this, PBI, patternContainer), |
| resolveIsCascadingUse(PBI, isCascadingUse, |
| /*onlyCareAboutFunctionBody=*/false)); |
| // clang-format on |
| }); |
| } |
| |
| void UnqualifiedLookupFactory:: |
| lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( |
| PatternBindingInitializer *PBI, Optional<bool> isCascadingUse) { |
| // Initializers for stored properties of types perform static |
| // lookup into the surrounding context. |
| DeclContext *const storedPropertyContainer = PBI->getParent(); |
| // clang-format off |
| finishLookingInContext( |
| AddGenericParameters::Yes, |
| storedPropertyContainer, |
| ResultFinderForTypeContext( |
| this, storedPropertyContainer, storedPropertyContainer), |
| resolveIsCascadingUse(storedPropertyContainer, None, |
| /*onlyCareAboutFunctionBody=*/false)); |
| // clang-format on |
| } |
| |
| void UnqualifiedLookupFactory:: |
| lookupNamesIntroducedByInitializerOfGlobalOrLocal( |
| PatternBindingInitializer *PBI, Optional<bool> isCascadingUse) { |
| // There's not much to find here, we'll keep going up to a parent |
| // context. |
| // clang-format off |
| finishLookingInContext( |
| AddGenericParameters::Yes, |
| PBI, |
| None, // not looking in the partic type |
| resolveIsCascadingUse(PBI, isCascadingUse, |
| /*onlyCareAboutFunctionBody=*/false)); |
| // clang-format on |
| } |
| |
| void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl( |
| AbstractFunctionDecl *AFD, Optional<bool> isCascadingUseArg) { |
| |
| // DOUG: how does this differ from isOutsideBodyOfFunction below? |
| const bool isCascadingUse = |
| AFD->isCascadingContextForLookup(false) && |
| (isCascadingUseArg.getValueOr( |
| Loc.isInvalid() || AFD->getBodySourceRange().isInvalid() || |
| !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc))); |
| |
| if (AFD->getDeclContext()->isTypeContext()) |
| lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse); |
| else |
| lookupNamesIntroducedByPureFunction(AFD, isCascadingUse); |
| } |
| |
| void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( |
| AbstractFunctionDecl *AFD, bool isCascadingUse) { |
| lookForLocalVariablesIn(AFD, isCascadingUse); |
| ifNotDoneYet( |
| [&] { |
| // If we're inside a function context, we're about to move to |
| // the parent DC, so we have to check the function's generic |
| // parameters first. |
| // Cannot start here in finishLookingInContext because AFD's |
| // getOuterParameters may be null even when AFD's parent has generics. |
| addGenericParametersForFunction(AFD); |
| }, |
| [&] { |
| DeclContext *const fnDeclContext = AFD->getDeclContext(); |
| // If we're not in the body of the function (for example, we |
| // might be type checking a default argument expression and |
| // performing name lookup from there), the base declaration |
| // is the nominal type, not 'self'. |
| DeclContext *const BaseDC = |
| isOutsideBodyOfFunction(AFD) ? fnDeclContext : AFD; |
| // If we are inside of a method, check to see if there are any ivars in |
| // scope, and if so, whether this is a reference to one of them. |
| // FIXME: We should persist this information between lookups. |
| // clang-format off |
| finishLookingInContext( |
| AddGenericParameters::Yes, |
| AFD->getParent(), |
| ResultFinderForTypeContext(this, BaseDC, fnDeclContext), |
| isCascadingUse); |
| // clang-format on |
| }); |
| } |
| |
| void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction( |
| AbstractFunctionDecl *AFD, bool isCascadingUse) { |
| lookForLocalVariablesIn(AFD, isCascadingUse); |
| ifNotDoneYet([&] { |
| // clang-format off |
| finishLookingInContext( |
| AddGenericParameters::Yes, |
| AFD, |
| None, |
| isCascadingUse); |
| }); |
| } |
| |
| |
| void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure( |
| AbstractClosureExpr *ACE, Optional<bool> isCascadingUse) { |
| if (auto *CE = dyn_cast<ClosureExpr>(ACE)) |
| lookForLocalVariablesIn(CE); |
| ifNotDoneYet([&] { |
| // clang-format off |
| finishLookingInContext( |
| AddGenericParameters::Yes, |
| ACE, |
| None, |
| resolveIsCascadingUse(ACE, isCascadingUse, |
| /*onlyCareAboutFunctionBody=*/false)); |
| // clang-format on |
| }); |
| } |
| |
| template <typename NominalTypeDeclOrExtensionDecl> |
| void UnqualifiedLookupFactory::lookupNamesIntroducedByNominalTypeOrExtension( |
| NominalTypeDeclOrExtensionDecl *D, Optional<bool> isCascadingUse) { |
| // clang-format off |
| finishLookingInContext( |
| AddGenericParameters::Yes, |
| D, |
| shouldLookupMembers(D, Loc) |
| ? Optional<ResultFinderForTypeContext>( |
| ResultFinderForTypeContext(this, D, D)) |
| : None, |
| resolveIsCascadingUse(D, isCascadingUse, |
| /*onlyCareAboutFunctionBody=*/false)); |
| |
| // clang-format on |
| } |
| |
| void UnqualifiedLookupFactory:: |
| lookupNamesIntroducedByDefaultArgumentInitializer( |
| DefaultArgumentInitializer *I, Optional<bool> isCascadingUse) { |
| // In a default argument, skip immediately out of both the |
| // initializer and the function. |
| finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false); |
| } |
| |
| void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext( |
| DeclContext *dc, Optional<bool> isCascadingUse) { |
| // clang-format off |
| assert(isa<TopLevelCodeDecl>(dc) || |
| isa<Initializer>(dc) || |
| isa<TypeAliasDecl>(dc) || |
| isa<SubscriptDecl>(dc)); |
| finishLookingInContext( |
| AddGenericParameters::Yes, |
| dc, |
| None, |
| resolveIsCascadingUse(DC, isCascadingUse, |
| /*onlyCareAboutFunctionBody=*/false)); |
| // clang-format on |
| } |
| |
| |
| void UnqualifiedLookupFactory::finishLookingInContext( |
| const AddGenericParameters addGenericParameters, |
| DeclContext *const lookupContextForThisContext, |
| Optional<ResultFinderForTypeContext> &&resultFinderForTypeContext, |
| const Optional<bool> isCascadingUse) { |
| #ifndef NDEBUG |
| stopForDebuggingIfDuringTargetLookup(false); |
| #endif |
| // When a generic has the same name as a member, Swift prioritizes the generic |
| // because the member could still be named by qualifying it. But there is no |
| // corresponding way to qualify a generic parameter. |
| // So, look for generics first. |
| if (addGenericParameters == AddGenericParameters::Yes) |
| addGenericParametersForContext(lookupContextForThisContext); |
| |
| ifNotDoneYet( |
| [&] { |
| if (resultFinderForTypeContext) |
| findResultsAndSaveUnavailables(lookupContextForThisContext, |
| std::move(*resultFinderForTypeContext), |
| *isCascadingUse, baseNLOptions); |
| }, |
| // Recurse into the next context. |
| [&] { |
| lookupNamesIntroducedBy(ContextAndUnresolvedIsCascadingUse{ |
| lookupContextForThisContext->getParentForLookup(), isCascadingUse}); |
| }); |
| } |
| |
| |
| void UnqualifiedLookupFactory::lookForLocalVariablesIn( |
| AbstractFunctionDecl *AFD, Optional<bool> isCascadingUse) { |
| // Look for local variables; normally, the parser resolves these |
| // for us, but it can't do the right thing inside local types. |
| // FIXME: when we can parse and typecheck the function body partially |
| // for code completion, AFD->getBody() check can be removed. |
| |
| if (Loc.isInvalid() || AFD->getBodySourceRange().isInvalid() || |
| !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc) || |
| !AFD->getBody()) { |
| return; |
| } |
| |
| namelookup::FindLocalVal localVal(SM, Loc, Consumer); |
| localVal.visit(AFD->getBody()); |
| |
| ifNotDoneYet([&] { |
| if (auto *P = AFD->getImplicitSelfDecl()) |
| localVal.checkValueDecl(P, DeclVisibilityKind::FunctionParameter); |
| localVal.checkParameterList(AFD->getParameters()); |
| }); |
| } |
| |
| void UnqualifiedLookupFactory::lookForLocalVariablesIn(ClosureExpr *CE) { |
| // Look for local variables; normally, the parser resolves these |
| // for us, but it can't do the right thing inside local types. |
| if (Loc.isInvalid()) |
| return; |
| namelookup::FindLocalVal localVal(SM, Loc, Consumer); |
| if (auto body = CE->getBody()) |
| localVal.visit(body); |
| ifNotDoneYet([&] { |
| if (auto params = CE->getParameters()) |
| localVal.checkParameterList(params); |
| }); |
| } |
| |
| void UnqualifiedLookupFactory::lookForLocalVariablesIn(SourceFile *SF) { |
| if (Loc.isInvalid()) |
| return; |
| // Look for local variables in top-level code; normally, the parser |
| // resolves these for us, but it can't do the right thing for |
| // local types. |
| namelookup::FindLocalVal localVal(SM, Loc, Consumer); |
| localVal.checkSourceFile(*SF); |
| } |
| |
| bool UnqualifiedLookupFactory::isOutsideBodyOfFunction( |
| const AbstractFunctionDecl *const AFD) const { |
| return !AFD->isImplicit() && Loc.isValid() && |
| AFD->getBodySourceRange().isValid() && |
| !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc); |
| } |
| |
| GenericParamList * |
| UnqualifiedLookupFactory::getGenericParams(const DeclContext *const dc) { |
| if (auto nominal = dyn_cast<NominalTypeDecl>(dc)) |
| return nominal->getGenericParams(); |
| if (auto ext = dyn_cast<ExtensionDecl>(dc)) |
| return ext->getGenericParams(); |
| if (auto subscript = dyn_cast<SubscriptDecl>(dc)) |
| return subscript->getGenericParams(); |
| if (auto func = dyn_cast<AbstractFunctionDecl>(dc)) |
| return func->getGenericParams(); |
| return nullptr; |
| } |
| |
| void UnqualifiedLookupFactory::addGenericParametersForContext( |
| DeclContext *dc) { |
| // Generics can be nested, so visit the generic list, innermost first. |
| // Cannot use DeclContext::forEachGenericContext because this code breaks out |
| // if it finds a match and isFirstResultEnough() |
| addGenericParametersForContext(getGenericParams(dc)); |
| } |
| |
| void UnqualifiedLookupFactory::addGenericParametersForContext( |
| GenericParamList *dcGenericParams) { |
| if (!dcGenericParams) |
| return; |
| namelookup::FindLocalVal localVal(SM, Loc, Consumer); |
| localVal.checkGenericParams(dcGenericParams); |
| ifNotDoneYet([&] { |
| addGenericParametersForContext( |
| dcGenericParams->getOuterParameters()); |
| }); |
| } |
| |
| void UnqualifiedLookupFactory::addGenericParametersForFunction( |
| AbstractFunctionDecl *AFD) { |
| GenericParamList *GenericParams = AFD->getGenericParams(); |
| if (GenericParams) { |
| namelookup::FindLocalVal localVal(SM, Loc, Consumer); |
| localVal.checkGenericParams(GenericParams); |
| } |
| } |
| |
| void UnqualifiedLookupFactory::ResultFinderForTypeContext::findResults( |
| const DeclName &Name, bool isCascadingUse, NLOptions baseNLOptions, |
| DeclContext *contextForLookup, |
| SmallVectorImpl<LookupResultEntry> &results) const { |
| // An optimization: |
| if (selfBounds.empty()) |
| return; |
| const NLOptions options = |
| baseNLOptions | (isCascadingUse ? NL_KnownCascadingDependency |
| : NL_KnownNonCascadingDependency); |
| |
| SmallVector<ValueDecl *, 4> Lookup; |
| contextForLookup->lookupQualified(selfBounds, Name, options, Lookup); |
| for (auto Result : Lookup) { |
| results.push_back(LookupResultEntry(whereValueIsMember(Result), Result)); |
| #ifndef NDEBUG |
| factory->addedResult(results.back()); |
| #endif |
| } |
| } |
| |
| // TODO (someday): Instead of adding unavailable entries to Results, |
| // then later shunting them aside, just put them in the right place |
| // to begin with. |
| |
| void UnqualifiedLookupFactory::setAsideUnavailableResults( |
| const size_t firstPossiblyUnavailableResult) { |
| // An optimization: |
| assert(Results.size() >= firstPossiblyUnavailableResult); |
| if (Results.size() == firstPossiblyUnavailableResult) |
| return; |
| // Predicate that determines whether a lookup result should |
| // be unavailable except as a last-ditch effort. |
| auto unavailableLookupResult = [&](const LookupResultEntry &result) { |
| auto &effectiveVersion = Ctx.LangOpts.EffectiveLanguageVersion; |
| return result.getValueDecl()->getAttrs().isUnavailableInSwiftVersion( |
| effectiveVersion); |
| }; |
| |
| // If all of the results we found are unavailable, keep looking. |
| auto begin = Results.begin() + firstPossiblyUnavailableResult; |
| if (std::all_of(begin, Results.end(), unavailableLookupResult)) { |
| // better to have more structure in results |
| UnavailableInnerResults.append(begin, Results.end()); |
| Results.erase(begin, Results.end()); |
| return; |
| } |
| // The debugger may have a different private discriminator |
| // in order to support lookup relative to the place where |
| // execution is suspended. |
| filterForDiscriminator(Results, DebugClient); |
| } |
| |
| |
| void UnqualifiedLookupFactory::recordDependencyOnTopLevelName( |
| DeclContext *topLevelContext, DeclName name, bool isCascadingUse) { |
| recordLookupOfTopLevelName(topLevelContext, Name, isCascadingUse); |
| recordedSF = dyn_cast<SourceFile>(topLevelContext); |
| recordedIsCascadingUse = isCascadingUse; |
| } |
| |
| void UnqualifiedLookupFactory::addImportedResults(DeclContext *const dc) { |
| using namespace namelookup; |
| SmallVector<ValueDecl *, 8> CurModuleResults; |
| auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly |
| : ResolutionKind::Overloadable; |
| lookupInModule(dc, Name, CurModuleResults, NLKind::UnqualifiedLookup, |
| resolutionKind, dc); |
| |
| // Always perform name shadowing for type lookup. |
| if (options.contains(Flags::TypeLookup)) { |
| removeShadowedDecls(CurModuleResults, dc); |
| } |
| |
| for (auto VD : CurModuleResults) { |
| Results.push_back(LookupResultEntry(VD)); |
| #ifndef NDEBUG |
| addedResult(Results.back()); |
| #endif |
| } |
| |
| filterForDiscriminator(Results, DebugClient); |
| } |
| |
| void UnqualifiedLookupFactory::addNamesKnownToDebugClient(DeclContext *dc) { |
| if (Name.isSimpleName() && DebugClient) |
| DebugClient->lookupAdditions(Name.getBaseName(), dc, Loc, |
| isOriginallyTypeLookup, Results); |
| } |
| |
| void UnqualifiedLookupFactory::addUnavailableInnerResults() { |
| Results = std::move(UnavailableInnerResults); |
| } |
| |
| void UnqualifiedLookupFactory::lookForAModuleWithTheGivenName( |
| DeclContext *const dc) { |
| using namespace namelookup; |
| if (!Name.isSimpleName() || Name.isSpecial()) |
| return; |
| |
| // Look for a module with the given name. |
| if (Name.isSimpleName(M.getName())) { |
| Results.push_back(LookupResultEntry(&M)); |
| #ifndef NDEBUG |
| addedResult(Results.back()); |
| #endif |
| return; |
| } |
| ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier()); |
| if (!desiredModule && Name == Ctx.TheBuiltinModule->getName()) |
| desiredModule = Ctx.TheBuiltinModule; |
| if (desiredModule) { |
| // Make sure the desired module is actually visible from the current |
| // context. |
| if (Ctx.getImportCache().isImportedBy(desiredModule, dc)) { |
| Results.push_back(LookupResultEntry(desiredModule)); |
| #ifndef NDEBUG |
| addedResult(Results.back()); |
| #endif |
| } |
| } |
| } |
| |
| #pragma mark common helper definitions |
| |
| |
| void UnqualifiedLookupFactory::findResultsAndSaveUnavailables( |
| DeclContext *lookupContextForThisContext, |
| ResultFinderForTypeContext &&resultFinderForTypeContext, |
| bool isCascadingUse, NLOptions baseNLOptions) { |
| auto firstPossiblyUnavailableResult = Results.size(); |
| resultFinderForTypeContext.findResults(Name, isCascadingUse, baseNLOptions, |
| lookupContextForThisContext, Results); |
| setAsideUnavailableResults(firstPossiblyUnavailableResult); |
| } |
| |
| |
| NLOptions UnqualifiedLookupFactory::computeBaseNLOptions( |
| const UnqualifiedLookup::Options options, |
| const bool isOriginallyTypeLookup) { |
| NLOptions baseNLOptions = NL_UnqualifiedDefault; |
| if (options.contains(Flags::AllowProtocolMembers)) |
| baseNLOptions |= NL_ProtocolMembers; |
| if (isOriginallyTypeLookup) |
| baseNLOptions |= NL_OnlyTypes; |
| if (options.contains(Flags::IgnoreAccessControl)) |
| baseNLOptions |= NL_IgnoreAccessControl; |
| return baseNLOptions; |
| } |
| |
| bool UnqualifiedLookupFactory::isFirstResultEnough() |
| const { |
| return !Results.empty() && !options.contains(Flags::IncludeOuterResults); |
| } |
| |
| void UnqualifiedLookupFactory::recordCompletionOfAScope() { |
| // OK to call (NOOP) if there are more inner results and Results is empty |
| if (IndexOfFirstOuterResult == 0) |
| IndexOfFirstOuterResult = Results.size(); |
| } |
| |
| UnqualifiedLookupFactory::ResultFinderForTypeContext:: |
| ResultFinderForTypeContext(UnqualifiedLookupFactory *factory, |
| DeclContext *dynamicContext, |
| DeclContext *staticContext) |
| : factory(factory), |
| dynamicContext(dynamicContext), staticContext(staticContext), |
| selfBounds(findSelfBounds(staticContext)) {} |
| |
| UnqualifiedLookupFactory::ResultFinderForTypeContext::SelfBounds |
| UnqualifiedLookupFactory::ResultFinderForTypeContext::findSelfBounds( |
| DeclContext *dc) { |
| auto nominal = dc->getSelfNominalTypeDecl(); |
| if (!nominal) |
| return {}; |
| |
| SelfBounds selfBounds; |
| selfBounds.push_back(nominal); |
| |
| // For a protocol extension, check whether there are additional "Self" |
| // constraints that can affect name lookup. |
| if (dc->getExtendedProtocolDecl()) { |
| auto ext = cast<ExtensionDecl>(dc); |
| auto bounds = getSelfBoundsFromWhereClause(ext); |
| for (auto bound : bounds.decls) |
| selfBounds.push_back(bound); |
| } |
| return selfBounds; |
| } |
| |
| #pragma mark ASTScopeImpl support |
| |
| void UnqualifiedLookupFactory::lookInASTScopes() { |
| |
| ASTScopeDeclConsumerForUnqualifiedLookup consumer(*this); |
| |
| #ifndef NDEBUG |
| stopForDebuggingIfStartingTargetLookup(true); |
| #endif |
| |
| const auto history = ASTScope::unqualifiedLookup(DC->getParentSourceFile(), |
| Name, Loc, DC, consumer); |
| |
| ifNotDoneYet([&] { |
| // Copied from lookupInModuleScopeContext |
| // If no result has been found yet, the dependency must be on a top-level |
| // name, since up to now, the search has been for non-top-level names. |
| auto *const moduleScopeContext = DC->getParentSourceFile(); |
| |
| const Optional<bool> isCascadingUseAtStartOfLookup = |
| !Name.isOperator() |
| ? getInitialIsCascadingUse() |
| : resolveIsCascadingUse(DC, getInitialIsCascadingUse(), |
| /*onlyCareAboutFunctionBody*/ true); |
| |
| const Optional<bool> isCascadingUseAfterLookup = |
| ASTScope::computeIsCascadingUse(history, isCascadingUseAtStartOfLookup); |
| |
| recordDependencyOnTopLevelName(moduleScopeContext, Name, |
| isCascadingUseAfterLookup.getValueOr(true)); |
| lookUpTopLevelNamesInModuleScopeContext(moduleScopeContext); |
| }); |
| } |
| |
| bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( |
| ArrayRef<ValueDecl *> values, DeclVisibilityKind vis, |
| NullablePtr<DeclContext> baseDC) { |
| for (auto *value: values) { |
| if (factory.isOriginallyTypeLookup && !isa<TypeDecl>(value)) |
| continue; |
| if (!value->getFullName().matchesRef(factory.Name)) |
| continue; |
| |
| // In order to preserve the behavior of the existing context-based lookup, |
| // which finds all results for non-local variables at the top level instead |
| // of stopping at the first one, ignore results at the top level that are |
| // not local variables. The caller \c lookInASTScopes will |
| // then do the appropriate work when the scope lookup fails. In |
| // FindLocalVal::visitBraceStmt, it sees PatternBindingDecls, not VarDecls, |
| // so a VarDecl at top level would not be found by the context-based lookup. |
| if (isa<SourceFile>(value->getDeclContext()) && |
| (vis != DeclVisibilityKind::LocalVariable || isa<VarDecl>(value))) |
| return false; |
| |
| factory.Results.push_back(LookupResultEntry(value)); |
| #ifndef NDEBUG |
| factory.stopForDebuggingIfAddingTargetLookupResult(factory.Results.back()); |
| #endif |
| } |
| factory.recordCompletionOfAScope(); |
| return factory.isFirstResultEnough(); |
| } |
| |
| bool ASTScopeDeclGatherer::consume(ArrayRef<ValueDecl *> valuesArg, |
| DeclVisibilityKind, |
| NullablePtr<DeclContext>) { |
| for (auto *v: valuesArg) |
| values.push_back(v); |
| return false; |
| } |
| |
| // TODO: in future, migrate this functionality into ASTScopes |
| bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers( |
| NullablePtr<DeclContext> selfDC, DeclContext *const scopeDC, |
| NominalTypeDecl *const nominal, |
| function_ref<bool(Optional<bool>)> calculateIsCascadingUse) { |
| if (selfDC) { |
| if (auto *d = selfDC.get()->getAsDecl()) { |
| if (auto *afd = dyn_cast<AbstractFunctionDecl>(d)) |
| assert(!factory.isOutsideBodyOfFunction(afd) && "Should be inside"); |
| } |
| } |
| auto resultFinder = UnqualifiedLookupFactory::ResultFinderForTypeContext( |
| &factory, selfDC ? selfDC.get() : scopeDC, scopeDC); |
| const bool isCascadingUse = |
| calculateIsCascadingUse(factory.getInitialIsCascadingUse()); |
| factory.findResultsAndSaveUnavailables(scopeDC, std::move(resultFinder), |
| isCascadingUse, factory.baseNLOptions); |
| factory.recordCompletionOfAScope(); |
| return factory.isFirstResultEnough(); |
| } |
| |
| |
| #pragma mark UnqualifiedLookup functions |
| |
| // clang-format off |
| UnqualifiedLookup::UnqualifiedLookup(DeclName Name, |
| DeclContext *const DC, |
| SourceLoc Loc, |
| Options options) |
| // clang-format on |
| : IndexOfFirstOuterResult(0) { |
| |
| auto *stats = DC->getASTContext().Stats; |
| if (stats) |
| stats->getFrontendCounters().NumUnqualifiedLookup++; |
| |
| UnqualifiedLookupFactory factory(Name, DC, Loc, options, *this); |
| factory.performUnqualifiedLookup(); |
| } |
| |
| TypeDecl *UnqualifiedLookup::getSingleTypeResult() const { |
| if (Results.size() != 1) |
| return nullptr; |
| return dyn_cast<TypeDecl>(Results.back().getValueDecl()); |
| } |
| |
| #pragma mark debugging |
| #ifndef NDEBUG |
| void UnqualifiedLookupFactory::InstrumentedNamedDeclConsumer::anchor() {} |
| #endif |
| |
| void UnqualifiedLookupFactory::ResultFinderForTypeContext::dump() const { |
| (void)factory; |
| llvm::errs() << "dynamicContext: "; |
| dynamicContext->dumpContext(); |
| llvm::errs() << "staticContext: "; |
| staticContext->dumpContext(); |
| llvm::errs() << "selfBounds: "; |
| for (const auto *D : selfBounds) |
| D->dump(llvm::errs(), 1); |
| llvm::errs() << "\n"; |
| } |
| |
| void UnqualifiedLookupFactory::dumpScopes() const { |
| llvm::errs() << "\n\nScopes:\n"; |
| DC->getParentSourceFile()->getScope().print(llvm::errs()); |
| llvm::errs() << "\n"; |
| } |
| |
| void UnqualifiedLookupFactory::dump() const { print(llvm::errs()); } |
| |
| void UnqualifiedLookupFactory::dumpResults() const { |
| auto &out = llvm::errs(); |
| for (auto i : indices(Results)) { |
| if (i == resultsSizeBeforeLocalsPass) |
| out << "============== next pass ============\n"; |
| out << i << ": "; |
| Results[i].print(out); |
| out << "\n"; |
| } |
| if (resultsSizeBeforeLocalsPass == Results.size()) |
| out << "============== next pass ============\n"; |
| if (resultsSizeBeforeLocalsPass == ~0u) |
| out << "never tried locals\n\n"; |
| } |
| |
| void UnqualifiedLookupFactory::print(raw_ostream &OS) const { |
| OS << "Look up"; |
| #ifndef NDEBUG |
| OS << " (" << lookupCounter << ")"; |
| #endif |
| OS << " '" << Name << "' at: "; |
| Loc.print(OS, DC->getASTContext().SourceMgr); |
| OS << "\nStarting in: "; |
| DC->printContext(OS); |
| OS << "\n"; |
| } |
| |
| #pragma mark debugging: output utilities for grepping |
| |
| static void writeLine(std::string s) { |
| llvm::errs() << "\n+-+-+-+- " << s << "\n"; |
| } |
| |
| StringRef UnqualifiedLookupFactory::getSourceFileName() const { |
| return DC->getParentSourceFile()->getFilename(); |
| } |
| |
| static void writeFirstLine(const UnqualifiedLookupFactory &ul, StringRef s) { |
| std::string line = |
| std::string("In file: ") + ul.getSourceFileName().str() + ", " + s.str(); |
| writeLine(line); |
| } |
| |
| static void writeInconsistent(const UnqualifiedLookupFactory &me, |
| const char *thisLabel, |
| const UnqualifiedLookupFactory &other, |
| const char *otherLabel, StringRef s) { |
| writeFirstLine(me, s); |
| other.dump(); |
| llvm::errs() << "\n" << thisLabel << " Results:\n"; |
| me.dumpResults(); |
| llvm::errs() << "\n" << otherLabel << " Results:\n"; |
| other.dumpResults(); |
| me.dumpScopes(); |
| } |
| |
| #pragma mark comparing results |
| |
| bool UnqualifiedLookupFactory::verifyEqualTo( |
| const UnqualifiedLookupFactory &&other, const char *thisLabel, |
| const char *otherLabel) const { |
| if (shouldDiffer()) { |
| return true; |
| } |
| auto writeErr = [&](StringRef s) { |
| writeInconsistent(*this, thisLabel, other, otherLabel, s); |
| }; |
| if (Results.size() != other.Results.size()) { |
| const bool tooMany = Results.size() < other.Results.size(); |
| writeErr(std::string(tooMany ? "Found too many: " : "Found too few: ") + |
| std::to_string(Results.size()) + " vs " + |
| std::to_string(other.Results.size())); |
| if (tooMany) |
| assert(false && "ASTScopeImpl found too many"); |
| else |
| assert(false && "ASTScopeImpl found too few"); |
| } |
| for (size_t i : indices(Results)) { |
| const auto &e = Results[i]; |
| const auto &oe = other.Results[i]; |
| if (e.getValueDecl() != oe.getValueDecl()) { |
| // print_ast_tc_function_bodies.swift generic from subscript vs get fn |
| std::string a; llvm::raw_string_ostream as(a); |
| std::string b; llvm::raw_string_ostream bs(b); |
| e.getValueDecl()->print(as); |
| oe.getValueDecl()->print(bs); |
| if (a == b) |
| llvm::errs() << "ValueDecls differ but print same\n"; |
| else { |
| writeErr(std::string( "ValueDecls differ at ") + std::to_string(i)); |
| assert(false && "ASTScopeImpl found different Decl"); |
| } |
| } |
| if (e.getDeclContext() != oe.getDeclContext()) { |
| writeErr((std::string("Contexts differ at ")) + std::to_string(i)); |
| assert(false && "ASTScopeImpl found different context"); |
| } |
| // unsigned printContext(llvm::raw_ostream &OS, unsigned indent = 0, |
| // bool onlyAPartialLine = false) const; |
| } |
| if (IndexOfFirstOuterResult != other.IndexOfFirstOuterResult) { |
| writeErr( std::string("IndexOfFirstOuterResult differs, should be: ") |
| + std::to_string(IndexOfFirstOuterResult) |
| + std::string( ", is: ") |
| + std::to_string(other.IndexOfFirstOuterResult)); |
| assert(false && "ASTScopeImpl IndexOfFirstOuterResult differs"); |
| } |
| if (recordedSF != other.recordedSF) { |
| writeErr(std::string("recordedSF differs: shouldBe: ") + |
| (recordedSF ? recordedSF->getFilename().str() |
| : std::string("<no name>")) + |
| std::string(" is: ") + |
| (other.recordedSF ? other.recordedSF->getFilename().str() |
| : std::string("<no name>"))); |
| assert(false && "ASTScopeImpl recordedSF differs"); |
| } |
| if (recordedSF && recordedIsCascadingUse != other.recordedIsCascadingUse) { |
| writeErr(std::string("recordedIsCascadingUse differs: shouldBe: ") + |
| std::to_string(recordedIsCascadingUse) + std::string(" is: ") + |
| std::to_string(other.recordedIsCascadingUse)); |
| assert(false && "ASTScopeImpl recordedIsCascadingUse differs"); |
| } |
| return true; |
| } |
| |
| bool UnqualifiedLookupFactory::shouldDiffer() const { |
| auto *SF = dyn_cast<SourceFile>(DC->getModuleScopeContext()); |
| if (!SF) |
| return false; |
| |
| static std::vector<const char*> testsThatShouldDiffer { |
| "swift/test/Constraints/diagnostics.swift", |
| "swift/test/Constraints/enum_cases.swift", |
| "swift/test/Constraints/rdar39401774.swift", |
| "swift/test/Constraints/rdar39401774-astscope.swift", |
| "swift/test/Interpreter/repl.swift", |
| "swift/test/Sema/diag_defer_captures.swift", |
| "swift/test/Sema/diag_use_before_declaration.swift", |
| "swift/test/SourceKit/CodeFormat/indent-closure.swift", |
| "swift/test/TypeCoercion/overload_noncall.swift", |
| "swift/test/expr/capture/nested_class.swift", |
| "swift/test/expr/capture/order.swift", |
| "swift/test/NameBinding/name-binding.swift" |
| }; |
| StringRef fileName = SF->getFilename(); |
| return llvm::any_of(testsThatShouldDiffer, [&](const char *testFile) { |
| return fileName.endswith(testFile); |
| }); |
| } |
| |
| #pragma mark breakpointing |
| #ifndef NDEBUG |
| |
| bool UnqualifiedLookupFactory::isTargetLookup() const { |
| return lookupCounter == targetLookup; |
| } |
| |
| void UnqualifiedLookupFactory::stopForDebuggingIfStartingTargetLookup( |
| const bool isASTScopeLookup) const { |
| if (!isTargetLookup()) |
| return; |
| if (isASTScopeLookup) |
| llvm::errs() << "starting target ASTScopeImpl lookup\n"; |
| else |
| llvm::errs() << "starting target context-based lookup\n"; |
| } |
| |
| void UnqualifiedLookupFactory::stopForDebuggingIfDuringTargetLookup( |
| const bool isASTScopeLookup) const { |
| if (!isTargetLookup()) |
| return; |
| if (isASTScopeLookup) |
| llvm::errs() << "during target ASTScopeImpl lookup\n"; |
| else |
| llvm::errs() << "during target context-based lookup\n"; |
| } |
| |
| void UnqualifiedLookupFactory::stopForDebuggingIfAddingTargetLookupResult( |
| const LookupResultEntry &e) const { |
| if (!isTargetLookup()) |
| return; |
| auto &out = llvm::errs(); |
| out << "\nresult for Target lookup:\n"; |
| e.print(out); |
| out << "\n"; |
| } |
| |
| void UnqualifiedLookupFactory::addedResult(const LookupResultEntry &e) const { |
| stopForDebuggingIfAddingTargetLookupResult(e); |
| } |
| |
| unsigned UnqualifiedLookupFactory::lookupCounter = 0; |
| |
| // set to ~0 when not debugging |
| const unsigned UnqualifiedLookupFactory::targetLookup = ~0; |
| |
| #endif // NDEBUG |