//===--- ModuleNameLookup.cpp - Name lookup within a module ----*- c++ -*--===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "ModuleNameLookup.h" #include "swift/AST/LazyResolver.h" #include "llvm/Support/raw_ostream.h" using namespace swift; using namespace namelookup; namespace { /// A cache used by lookupInModule(). class ModuleLookupCache { public: using MapTy = llvm::SmallDenseMap, 32>; MapTy Map; bool SearchedClangModule = false; }; class SortCanType { public: bool operator()(CanType lhs, CanType rhs) const { return std::less()(lhs.getPointer(), rhs.getPointer()); } }; using CanTypeSet = llvm::SmallSet; using NamedCanTypeSet = llvm::DenseMap>; static_assert(ResolutionKind() == ResolutionKind::Overloadable, "Entries in NamedCanTypeSet should be overloadable initially"); } // end anonymous namespace /// Returns true if this particular ValueDecl is overloadable. static bool isOverloadable(const ValueDecl *VD) { return isa(VD) || isa(VD) || isa(VD); } static bool isValidOverload(CanTypeSet &overloads, const ValueDecl *VD) { if (!isOverloadable(VD)) return overloads.empty(); if (overloads.count(VD->getType()->getCanonicalType())) return false; return true; } static bool isValidOverload(NamedCanTypeSet &overloads, const ValueDecl *VD) { auto &entry = overloads[VD->getName()]; if (entry.first != ResolutionKind::Overloadable) return false; return isValidOverload(entry.second, VD); } /// Updates \p overloads with the types of the given decls. /// /// \returns true if all of the given decls are overloadable, false if not. static bool updateOverloadSet(CanTypeSet &overloads, ArrayRef decls) { for (auto result : decls) { if (!isOverloadable(result)) return false; if (!result->hasType()) continue; overloads.insert(result->getType()->getCanonicalType()); } return true; } /// Updates \p overloads with the types of the given decls. /// /// \returns true, since there can always be more overloadable decls. static bool updateOverloadSet(NamedCanTypeSet &overloads, ArrayRef decls) { for (auto result : decls) { auto &entry = overloads[result->getName()]; if (!isOverloadable(result)) entry.first = ResolutionKind::Exact; else if (!result->hasType()) continue; else entry.second.insert(result->getType()->getCanonicalType()); } return true; } /// After finding decls by name lookup, filter based on the given /// resolution kind and existing overload set and add them to \p results. template static ResolutionKind recordImportDecls(LazyResolver *typeResolver, SmallVectorImpl &results, ArrayRef newDecls, OverloadSetTy &overloads, ResolutionKind resolutionKind) { switch (resolutionKind) { case ResolutionKind::Overloadable: { // Add new decls if they provide a new overload. Note that the new decls // may be ambiguous with respect to each other, just not any existing decls. std::copy_if(newDecls.begin(), newDecls.end(), std::back_inserter(results), [&](ValueDecl *result) -> bool { if (!result->hasType()) { if (typeResolver) typeResolver->resolveDeclSignature(result); else return true; } return isValidOverload(overloads, result); }); // Update the overload set. bool stillOverloadable = updateOverloadSet(overloads, newDecls); return stillOverloadable ? ResolutionKind::Overloadable : ResolutionKind::Exact; } case ResolutionKind::Exact: // Add all decls. If they're ambiguous, they're ambiguous. results.append(newDecls.begin(), newDecls.end()); return ResolutionKind::Exact; case ResolutionKind::TypesOnly: // Add type decls only. If they're ambiguous, they're ambiguous. std::copy_if(newDecls.begin(), newDecls.end(), std::back_inserter(results), [](const ValueDecl *VD) { return isa(VD); }); return ResolutionKind::TypesOnly; } } /// Performs a qualified lookup into the given module and, if necessary, its /// reexports, observing proper shadowing rules. template static void lookupInModule(Module *module, Module::AccessPathTy accessPath, SmallVectorImpl &decls, ResolutionKind resolutionKind, bool canReturnEarly, LazyResolver *typeResolver, ModuleLookupCache &cache, bool topLevel, CallbackTy callback) { ModuleLookupCache::MapTy::iterator iter; bool isNew; std::tie(iter, isNew) = cache.Map.insert({{accessPath, module}, {}}); if (!isNew) { decls.append(iter->second.begin(), iter->second.end()); return; } size_t initialCount = decls.size(); // Only perform unscoped searches once in Clang modules. // FIXME: This is a weird hack. ClangImporter should just filter the results // for us. bool isClangModule = false; if (accessPath.empty()) isClangModule = module->getKind() == ModuleKind::Clang; SmallVector localDecls; if (!isClangModule || !cache.SearchedClangModule) { callback(module, accessPath, localDecls); if (isClangModule) cache.SearchedClangModule = true; } OverloadSetTy overloads; // FIXME: Pass TypeResolver down instead of getting it from the AST. resolutionKind = recordImportDecls(typeResolver, decls, localDecls, overloads, resolutionKind); bool foundDecls = decls.size() > initialCount; if (!foundDecls || !canReturnEarly || resolutionKind == ResolutionKind::Overloadable) { SmallVector reexports; module->getImportedModules(reexports, topLevel); // Prefer scoped imports (import func swift.max) to whole-module imports. SmallVector unscopedValues; SmallVector scopedValues; for (auto next : reexports) { // Filter any whole-module imports, and skip specific-decl imports if the // import path doesn't match exactly. Module::AccessPathTy combinedAccessPath; if (accessPath.empty()) { combinedAccessPath = next.first; } else if (!next.first.empty() && !Module::isSameAccessPath(next.first, accessPath)) { // If we ever allow importing non-top-level decls, it's possible the // rule above isn't what we want. assert(next.first.size() == 1 && "import of non-top-level decl"); continue; } else { combinedAccessPath = accessPath; } auto &resultSet = next.first.empty() ? unscopedValues : scopedValues; lookupInModule(next.second, combinedAccessPath, resultSet, resolutionKind, canReturnEarly, typeResolver, cache, false, callback); } // Add the results from scoped imports. resolutionKind = recordImportDecls(typeResolver, decls, scopedValues, overloads, resolutionKind); // Add the results from unscoped imports. foundDecls = decls.size() > initialCount; if (!foundDecls || !canReturnEarly || resolutionKind == ResolutionKind::Overloadable) { resolutionKind = recordImportDecls(typeResolver, decls, unscopedValues, overloads, resolutionKind); } } std::sort(decls.begin() + initialCount, decls.end()); auto afterUnique = std::unique(decls.begin() + initialCount, decls.end()); decls.erase(afterUnique, decls.end()); auto &cachedValues = cache.Map[{accessPath, module}]; cachedValues.insert(cachedValues.end(), decls.begin() + initialCount, decls.end()); } void namelookup::lookupInModule(Module *startModule, Module::AccessPathTy topAccessPath, Identifier name, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, LazyResolver *typeResolver, bool topLevel) { ModuleLookupCache cache; ::lookupInModule(startModule, topAccessPath, decls, resolutionKind, /*canReturnEarly=*/true, typeResolver, cache, topLevel, [=](Module *module, Module::AccessPathTy path, SmallVectorImpl &localDecls) { module->lookupValue(path, name, lookupKind, localDecls); } ); } // FIXME: Should Module::lookupVisibleDecls be refactored to take a vector // directly? namespace { class VectorDeclConsumer : public VisibleDeclConsumer { public: SmallVectorImpl &results; explicit VectorDeclConsumer(SmallVectorImpl &decls) : results(decls) {} virtual void foundDecl(ValueDecl *decl) { results.push_back(decl); } }; } void namelookup::lookupVisibleDeclsInModule(Module *topLevel, Module::AccessPathTy topAccessPath, SmallVectorImpl &decls, NLKind lookupKind, ResolutionKind resolutionKind, LazyResolver *typeResolver) { ModuleLookupCache cache; ::lookupInModule(topLevel, topAccessPath, decls, resolutionKind, /*canReturnEarly=*/false, typeResolver, cache, /*topLevel=*/true, [=](Module *module, Module::AccessPathTy path, SmallVectorImpl &localDecls) { VectorDeclConsumer consumer(localDecls); module->lookupVisibleDecls(path, consumer, lookupKind); } ); }