//===--- ModuleNameLookup.cpp - Name lookup within a module ---------------===// // // 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 // //===----------------------------------------------------------------------===// #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ImportCache.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "llvm/Support/raw_ostream.h" using namespace swift; using namespace namelookup; namespace { /// Encapsulates the work done for a recursive qualified lookup into a module. /// /// The \p LookupStrategy handles the non-recursive part of the lookup. It /// must be a subclass of ModuleNameLookup. template class ModuleNameLookup { ASTContext &ctx; const ResolutionKind resolutionKind; const bool respectAccessControl; LookupStrategy *getDerived() { static_assert(std::is_base_of, LookupStrategy>::value, "ModuleNameLookup is a CRTP class"); return static_cast(this); } public: ModuleNameLookup(ASTContext &ctx, ResolutionKind resolutionKind) : ctx(ctx), resolutionKind(resolutionKind), respectAccessControl(!ctx.isAccessControlDisabled()) {} /// Performs a qualified lookup into the given module and, if necessary, its /// reexports. /// /// The results are appended to \p decls. void lookupInModule(SmallVectorImpl &decls, const DeclContext *moduleOrFile, ModuleDecl::AccessPathTy accessPath, const DeclContext *moduleScopeContext); }; /// Encapsulates the work done for a recursive qualified lookup into a module /// by full name. class LookupByName : public ModuleNameLookup { using Super = ModuleNameLookup; friend Super; const DeclName name; const NLKind lookupKind; public: LookupByName(ASTContext &ctx, ResolutionKind resolutionKind, DeclName name, NLKind lookupKind) : Super(ctx, resolutionKind), name(name), lookupKind(lookupKind) {} private: /// Returns whether it's okay to stop recursively searching imports, given  /// that we found something non-overloadable. static bool canReturnEarly() { return true; } void doLocalLookup(ModuleDecl *module, ModuleDecl::AccessPathTy path, SmallVectorImpl &localDecls) { // If this import is specific to some named decl ("import Swift.Int") // then filter out any lookups that don't match. if (!ModuleDecl::matchesAccessPath(path, name)) return; module->lookupValue(name, lookupKind, localDecls); } }; /// Encapsulates the work done for a recursive qualified lookup into a module /// to find all visible decls. class LookupVisibleDecls : public ModuleNameLookup { using Super = ModuleNameLookup; friend Super; const NLKind lookupKind; public: LookupVisibleDecls(ASTContext &ctx, ResolutionKind resolutionKind, NLKind lookupKind) : ModuleNameLookup(ctx, resolutionKind), lookupKind(lookupKind) {} private: /// Returns whether it's okay to stop recursively searching imports, given /// that we found something non-overloadable. static bool canReturnEarly() { return false; } void doLocalLookup(ModuleDecl *module, ModuleDecl::AccessPathTy path, SmallVectorImpl &localDecls) { VectorDeclConsumer consumer(localDecls); module->lookupVisibleDecls(path, consumer, lookupKind); } }; } // end anonymous namespace template void ModuleNameLookup::lookupInModule( SmallVectorImpl &decls, const DeclContext *moduleOrFile, ModuleDecl::AccessPathTy accessPath, const DeclContext *moduleScopeContext) { assert(moduleOrFile->isModuleScopeContext()); // Does the module scope have any separately-imported overlays shadowing // the module we're looking into? SmallVector overlays; moduleScopeContext->getSeparatelyImportedOverlays( moduleOrFile->getParentModule(), overlays); if (!overlays.empty()) { // If so, look in each of those overlays. for (auto overlay : overlays) lookupInModule(decls, overlay, accessPath, moduleScopeContext); // FIXME: This may not work gracefully if more than one of these lookups // finds something. return; } const size_t initialCount = decls.size(); size_t currentCount = decls.size(); auto updateNewDecls = [&](const DeclContext *moduleScopeContext) { if (decls.size() == currentCount) return; auto newEnd = std::remove_if( decls.begin() + currentCount, decls.end(), [&](ValueDecl *VD) { if (resolutionKind == ResolutionKind::TypesOnly && !isa(VD)) return true; if (respectAccessControl && !VD->isAccessibleFrom(moduleScopeContext)) return true; return false; }); decls.erase(newEnd, decls.end()); currentCount = decls.size(); }; // Do the lookup into the current module. auto *module = moduleOrFile->getParentModule(); getDerived()->doLocalLookup(module, accessPath, decls); updateNewDecls(moduleScopeContext); bool canReturnEarly = (initialCount != decls.size() && getDerived()->canReturnEarly()); if (canReturnEarly && resolutionKind == ResolutionKind::Overloadable) { // If we only found top-level functions, keep looking, since we may // find additional overloads. if (std::all_of(decls.begin() + initialCount, decls.end(), [](ValueDecl *VD) { return isa(VD); })) canReturnEarly = false; } // If needed, search for decls in re-exported modules as well. if (!canReturnEarly) { auto &imports = ctx.getImportCache().getImportSet(moduleOrFile); auto visitImport = [&](ModuleDecl::ImportedModule import, const DeclContext *moduleScopeContext) { if (import.first.empty()) import.first = accessPath; else if (!accessPath.empty() && !ModuleDecl::isSameAccessPath(import.first, accessPath)) return; getDerived()->doLocalLookup(import.second, import.first, decls); updateNewDecls(moduleScopeContext); }; // If the ClangImporter's special header import module appears in the // import set, we must visit it first. ModuleDecl *headerImportModule = nullptr; if (imports.hasHeaderImportModule()) { if (auto *loader = ctx.getClangModuleLoader()) { headerImportModule = loader->getImportedHeaderModule(); if (headerImportModule) { ModuleDecl::ImportedModule import({}, headerImportModule); visitImport(import, nullptr); } } } for (auto import : imports.getTopLevelImports()) { // A module appears in its own top-level import list; since we checked // it already, skip it. if (import.second == module) continue; // Skip the special import set module; we've already visited it. if (import.second == headerImportModule) continue; visitImport(import, moduleScopeContext); } for (auto import : imports.getTransitiveImports()) { // Skip the special import set module; we've already visited it. if (import.second == headerImportModule) continue; visitImport(import, nullptr); } } // Nothing more to do if we don't have ambiguous results. if (decls.size() - initialCount <= 1) return; // Remove duplicated declarations, which can happen when the same module is // imported with multiple access paths. llvm::SmallPtrSet knownDecls; decls.erase(std::remove_if(decls.begin() + initialCount, decls.end(), [&](ValueDecl *d) -> bool { return !knownDecls.insert(d).second; }), decls.end()); } llvm::Expected LookupInModuleRequest::evaluate( Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name, NLKind lookupKind, ResolutionKind resolutionKind, const DeclContext *moduleScopeContext) const { assert(moduleScopeContext->isModuleScopeContext()); auto &ctx = moduleOrFile->getASTContext(); FrontendStatsTracer tracer(ctx.Stats, "lookup-in-module"); QualifiedLookupResult decls; LookupByName lookup(ctx, resolutionKind, name, lookupKind); lookup.lookupInModule(decls, moduleOrFile, {}, moduleScopeContext); return decls; } void namelookup::lookupInModule(const DeclContext *moduleOrFile, DeclName name, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, const DeclContext *moduleScopeContext) { auto &ctx = moduleOrFile->getASTContext(); LookupInModuleRequest req(moduleOrFile, name, lookupKind, resolutionKind, moduleScopeContext); auto results = evaluateOrDefault(ctx.evaluator, req, {}); decls.append(results.begin(), results.end()); } void namelookup::lookupVisibleDeclsInModule( const DeclContext *moduleOrFile, ModuleDecl::AccessPathTy accessPath, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, const DeclContext *moduleScopeContext) { assert(moduleScopeContext->isModuleScopeContext()); auto &ctx = moduleOrFile->getASTContext(); LookupVisibleDecls lookup(ctx, resolutionKind, lookupKind); lookup.lookupInModule(decls, moduleOrFile, accessPath, moduleScopeContext); } void namelookup::simple_display(llvm::raw_ostream &out, ResolutionKind kind) { switch (kind) { case ResolutionKind::Overloadable: out << "Overloadable"; return; case ResolutionKind::TypesOnly: out << "TypesOnly"; return; } llvm_unreachable("Unhandled case in switch"); }