From 8335a038bf1ef3729c52d9ef6e11e2338052c1c5 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 15 Oct 2025 14:00:28 -0700 Subject: [PATCH] [Dependency Scanning] Refactor batch clang dependency query to preserve only partial results Previously, with the change to bridge Clang dependency scanning results on-demand, the scanner would execute Clang dependency scanning queries for each unresolved import, in parallel, and aggregate all of the results to post-process (including on-demand bridging) later. As a consequence of that change, all of the Clang scanner queries' results ('TranslationUnitDeps') got aggregated during a scan and had their lifetimes extended until a later point when they got processed and added to the scanner's cache. This change refactors the Clang dependency scanner invocation to, upon query completion, accumulate only the 'ModuleDeps' nodes which have not been registered by a prior scan, discarding the rest of the 'TranslationUnitDeps' graph. The arrgegated 'ModuleDeps' objects are still bridged on-demand downstream. This change further splits up the 'resolveAllClangModuleDependencies' method's functionality to improve readability and maintainability, into: - 'gatherUnresolvedImports' method which collects all of collected Swift dependents' imports which did not get resolved to Swift dependencies - 'performParallelClangModuleLookup' which actually executes the parallel queries and includes the new logic described above - 'cacheComputedClangModuleLookupResults' method which takes the result of the parallel Clang scanner query and records in in the Swift scanner cache - 'reQueryMissedModulesFromCache' method which covers the scenario where Clang scanner query returned no result because either the dependency can only be found transitively, or the query is for a dependency previously-queried. --- include/swift/AST/ModuleDependencies.h | 6 + .../DependencyScan/ModuleDependencyScanner.h | 55 ++++ lib/AST/ModuleDependencies.cpp | 117 +++---- .../ModuleDependencyScanner.cpp | 297 +++++++++++------- 4 files changed, 306 insertions(+), 169 deletions(-) diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 71955cd8ea927..cfc47bfda95f8 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -1187,6 +1187,12 @@ class ModuleDependenciesCache { DiagnosticEngine &diags, BridgeClangDependencyCallback bridgeClangModule); + /// Record Clang module dependency. + void recordClangDependency( + const clang::tooling::dependencies::ModuleDeps &dependency, + DiagnosticEngine &diags, + BridgeClangDependencyCallback bridgeClangModule); + /// Update stored dependencies for the given module. void updateDependency(ModuleDependencyID moduleID, ModuleDependencyInfo dependencyInfo); diff --git a/include/swift/DependencyScan/ModuleDependencyScanner.h b/include/swift/DependencyScan/ModuleDependencyScanner.h index 49062e3f5525b..529eba5d18cda 100644 --- a/include/swift/DependencyScan/ModuleDependencyScanner.h +++ b/include/swift/DependencyScan/ModuleDependencyScanner.h @@ -32,6 +32,11 @@ using LookupModuleOutputCallback = llvm::function_ref; using RemapPathCallback = llvm::function_ref; +/// A map from a module id to a collection of import statement infos. +using ImportStatementInfoMap = + std::unordered_map>; + /// A dependency scanning worker which performs filesystem lookup /// of a named module dependency. class ModuleDependencyScanningWorker { @@ -329,6 +334,56 @@ class ModuleDependencyScanner { /// for a given module name. Identifier getModuleImportIdentifier(StringRef moduleName); +private: + struct BatchClangModuleLookupResult { + llvm::StringMap + discoveredDependencyInfos; + llvm::StringMap> visibleModules; + }; + + /// For the provided collection of unresolved imports + /// belonging to identified Swift dependnecies, execute a parallel + /// query to the Clang dependency scanner for each import's module identifier. + void performParallelClangModuleLookup( + const ImportStatementInfoMap &unresolvedImportsMap, + const ImportStatementInfoMap &unresolvedOptionalImportsMap, + ModuleDependenciesCache &cache, + BatchClangModuleLookupResult &result); + + /// Given a result of a batch Clang module dependency lookup, + /// record its results in the cache: + /// 1. Record all discovered Clang module dependency infos + /// in the \c cache. + /// 1. Update the set of visible Clang modules from each Swift module + /// in the \c cache. + /// 2. Update the total collection of all disovered clang modules + /// in \c allDiscoveredClangModules. + /// 3. Record all import identifiers which the scan failed to resolve + /// in \c failedToResolveImports. + /// 4. Update the set of resolved Clang dependencies for each Swift + /// module dependency in \c resolvedClangDependenciesMap. + void cacheComputedClangModuleLookupResults( + const BatchClangModuleLookupResult &lookupResult, + const ImportStatementInfoMap &unresolvedImportsMap, + const ImportStatementInfoMap &unresolvedOptionalImportsMap, + ArrayRef swiftModuleDependents, + ModuleDependenciesCache &cache, + ModuleDependencyIDSetVector &allDiscoveredClangModules, + std::vector> + &failedToResolveImports, + std::unordered_map + &resolvedClangDependenciesMap); + + /// Re-query some failed-to-resolve Clang imports from cache + /// in chance they were brought in as transitive dependencies. + void reQueryMissedModulesFromCache( + ModuleDependenciesCache &cache, + const std::vector< + std::pair> + &failedToResolveImports, + std::unordered_map + &resolvedClangDependenciesMap); + /// Assuming the \c `moduleImport` failed to resolve, /// iterate over all binary Swift module dependencies with serialized /// search paths and attempt to diagnose if the failed-to-resolve module diff --git a/lib/AST/ModuleDependencies.cpp b/lib/AST/ModuleDependencies.cpp index 1dda454da76bd..0a65c1f99dc6e 100644 --- a/lib/AST/ModuleDependencies.cpp +++ b/lib/AST/ModuleDependencies.cpp @@ -788,61 +788,68 @@ void ModuleDependenciesCache::recordClangDependencies( const clang::tooling::dependencies::ModuleDepsGraph &dependencies, DiagnosticEngine &diags, BridgeClangDependencyCallback bridgeClangModule) { - for (const auto &dep : dependencies) { - auto depID = - ModuleDependencyID{dep.ID.ModuleName, ModuleDependencyKind::Clang}; - if (hasDependency(depID)) { - auto priorClangModuleDetails = - findKnownDependency(depID).getAsClangModule(); - DEBUG_ASSERT(priorClangModuleDetails); - auto priorContextHash = priorClangModuleDetails->contextHash; - auto newContextHash = dep.ID.ContextHash; - if (priorContextHash != newContextHash) { - // This situation means that within the same scanning action, Clang - // Dependency Scanner has produced two different variants of the same - // module. This is not supposed to happen, but we are currently - // hunting down the rare cases where it does, seemingly due to - // differences in Clang Scanner direct by-name queries and transitive - // header lookup queries. - // - // Emit a failure diagnostic here that is hopefully more actionable - // for the time being. - diags.diagnose(SourceLoc(), - diag::dependency_scan_unexpected_variant, - dep.ID.ModuleName); - diags.diagnose( - SourceLoc(), - diag::dependency_scan_unexpected_variant_context_hash_note, - priorContextHash, newContextHash); - diags.diagnose( - SourceLoc(), - diag::dependency_scan_unexpected_variant_module_map_note, - priorClangModuleDetails->moduleMapFile, dep.ClangModuleMapFile); - - auto newClangModuleDetails = bridgeClangModule(dep).getAsClangModule(); - auto diagnoseExtraCommandLineFlags = - [&diags](const ClangModuleDependencyStorage *checkModuleDetails, - const ClangModuleDependencyStorage *baseModuleDetails, - bool isNewlyDiscovered) -> void { - std::unordered_set baseCommandLineSet( - baseModuleDetails->buildCommandLine.begin(), - baseModuleDetails->buildCommandLine.end()); - for (const auto &checkArg : checkModuleDetails->buildCommandLine) - if (baseCommandLineSet.find(checkArg) == baseCommandLineSet.end()) - diags.diagnose( - SourceLoc(), - diag::dependency_scan_unexpected_variant_extra_arg_note, - isNewlyDiscovered, checkArg); - }; - diagnoseExtraCommandLineFlags(priorClangModuleDetails, - newClangModuleDetails, true); - diagnoseExtraCommandLineFlags(newClangModuleDetails, - priorClangModuleDetails, false); - } - } else { - recordDependency(dep.ID.ModuleName, bridgeClangModule(dep)); - addSeenClangModule(dep.ID); - } + for (const auto &dep : dependencies) + recordClangDependency(dep, diags, bridgeClangModule); +} + +void ModuleDependenciesCache::recordClangDependency( + const clang::tooling::dependencies::ModuleDeps &dependency, + DiagnosticEngine &diags, + BridgeClangDependencyCallback bridgeClangModule) { + auto depID = + ModuleDependencyID{dependency.ID.ModuleName, ModuleDependencyKind::Clang}; + if (!hasDependency(depID)) { + recordDependency(dependency.ID.ModuleName, bridgeClangModule(dependency)); + addSeenClangModule(dependency.ID); + return; + } + + auto priorClangModuleDetails = + findKnownDependency(depID).getAsClangModule(); + DEBUG_ASSERT(priorClangModuleDetails); + auto priorContextHash = priorClangModuleDetails->contextHash; + auto newContextHash = dependency.ID.ContextHash; + if (priorContextHash != newContextHash) { + // This situation means that within the same scanning action, Clang + // Dependency Scanner has produced two different variants of the same + // module. This is not supposed to happen, but we are currently + // hunting down the rare cases where it does, seemingly due to + // differences in Clang Scanner direct by-name queries and transitive + // header lookup queries. + // + // Emit a failure diagnostic here that is hopefully more actionable + // for the time being. + diags.diagnose(SourceLoc(), + diag::dependency_scan_unexpected_variant, + dependency.ID.ModuleName); + diags.diagnose( + SourceLoc(), + diag::dependency_scan_unexpected_variant_context_hash_note, + priorContextHash, newContextHash); + diags.diagnose( + SourceLoc(), + diag::dependency_scan_unexpected_variant_module_map_note, + priorClangModuleDetails->moduleMapFile, dependency.ClangModuleMapFile); + + auto newClangModuleDetails = bridgeClangModule(dependency).getAsClangModule(); + auto diagnoseExtraCommandLineFlags = + [&diags](const ClangModuleDependencyStorage *checkModuleDetails, + const ClangModuleDependencyStorage *baseModuleDetails, + bool isNewlyDiscovered) -> void { + std::unordered_set baseCommandLineSet( + baseModuleDetails->buildCommandLine.begin(), + baseModuleDetails->buildCommandLine.end()); + for (const auto &checkArg : checkModuleDetails->buildCommandLine) + if (baseCommandLineSet.find(checkArg) == baseCommandLineSet.end()) + diags.diagnose( + SourceLoc(), + diag::dependency_scan_unexpected_variant_extra_arg_note, + isNewlyDiscovered, checkArg); + }; + diagnoseExtraCommandLineFlags(priorClangModuleDetails, + newClangModuleDetails, true); + diagnoseExtraCommandLineFlags(newClangModuleDetails, + priorClangModuleDetails, false); } } diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 4e1f13817d109..676f10c1e5388 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -973,21 +973,13 @@ void ModuleDependencyScanner::resolveSwiftModuleDependencies( return; } -void ModuleDependencyScanner::resolveAllClangModuleDependencies( - ArrayRef swiftModuleDependents, - ModuleDependenciesCache &cache, - ModuleDependencyIDSetVector &allDiscoveredClangModules) { - // Gather all unresolved imports which must correspond to - // Clang modules (since no Swift module for them was found). - llvm::StringSet<> unresolvedImportIdentifiers; - llvm::StringSet<> unresolvedOptionalImportIdentifiers; - std::unordered_map> - unresolvedImportsMap; - std::unordered_map> - unresolvedOptionalImportsMap; - +static void +gatherUnresolvedImports(ModuleDependenciesCache &cache, + ASTContext &scanASTContext, + ArrayRef swiftModuleDependents, + ModuleDependencyIDSetVector &allDiscoveredClangModules, + ImportStatementInfoMap &unresolvedImportsMap, + ImportStatementInfoMap &unresolvedOptionalImportsMap) { for (const auto &moduleID : swiftModuleDependents) { auto moduleDependencyInfo = cache.findKnownDependency(moduleID); auto unresolvedImports = @@ -1030,43 +1022,89 @@ void ModuleDependencyScanner::resolveAllClangModuleDependencies( // When querying a *clang* module 'CxxStdlib' we must // instead expect a module called 'std'... auto addCanonicalClangModuleImport = - [this](const ScannerImportStatementInfo &importInfo, - std::vector &unresolvedImports, - llvm::StringSet<> &unresolvedImportIdentifiers) { + [&scanASTContext]( + const ScannerImportStatementInfo &importInfo, + std::vector &unresolvedImports) { if (importInfo.importIdentifier == - ScanASTContext.Id_CxxStdlib.str()) { + scanASTContext.Id_CxxStdlib.str()) { auto canonicalImportInfo = ScannerImportStatementInfo( "std", importInfo.isExported, importInfo.accessLevel, importInfo.importLocations); unresolvedImports.push_back(canonicalImportInfo); - unresolvedImportIdentifiers.insert( - canonicalImportInfo.importIdentifier); } else { unresolvedImports.push_back(importInfo); - unresolvedImportIdentifiers.insert(importInfo.importIdentifier); } }; for (const auto &depImport : moduleDependencyInfo.getModuleImports()) if (!resolvedImportIdentifiers.contains(depImport.importIdentifier)) - addCanonicalClangModuleImport(depImport, *unresolvedImports, - unresolvedImportIdentifiers); + addCanonicalClangModuleImport(depImport, *unresolvedImports); for (const auto &depImport : moduleDependencyInfo.getOptionalModuleImports()) if (!resolvedImportIdentifiers.contains(depImport.importIdentifier)) - addCanonicalClangModuleImport(depImport, *unresolvedOptionalImports, - unresolvedOptionalImportIdentifiers); + addCanonicalClangModuleImport(depImport, *unresolvedOptionalImports); + } + } +} + +void ModuleDependencyScanner::reQueryMissedModulesFromCache( + ModuleDependenciesCache &cache, + const std::vector> + &failedToResolveImports, + std::unordered_map + &resolvedClangDependenciesMap) { + // It is possible that a specific import resolution failed because we are + // attempting to resolve a module which can only be brought in via a + // modulemap of a different Clang module dependency which is not otherwise + // on the current search paths. For example, suppose we are scanning a + // `.swiftinterface` for module `Foo`, which contains: + // ----- + // @_exported import Foo + // import Bar + // ... + // ----- + // Where `Foo` is the underlying Framework clang module whose .modulemap + // defines an auxiliary module `Bar`. Because Foo is a framework, its + // modulemap is under + // `/Foo.framework/Modules/module.modulemap`. + // Which means that lookup of `Bar` alone from Swift will not be able to + // locate the module in it. However, the lookup of Foo will itself bring in + // the auxiliary module becuase the Clang scanner instance scanning for + // clang module Foo will be able to find it in the corresponding framework + // module's modulemap and register it as a dependency which means it will be + // registered with the scanner's cache in the step above. To handle such + // cases, we first add all successfully-resolved modules and (for Clang + // modules) their transitive dependencies to the cache, and then attempt to + // re-query imports for which resolution originally failed from the cache. + // If this fails, then the scanner genuinely failed to resolve this + // dependency. + for (const auto &unresolvedImport : failedToResolveImports) { + auto unresolvedModuleID = ModuleDependencyID{ + unresolvedImport.second.importIdentifier, ModuleDependencyKind::Clang}; + auto optionalCachedModuleInfo = cache.findDependency(unresolvedModuleID); + if (optionalCachedModuleInfo) { + resolvedClangDependenciesMap[unresolvedImport.first].insert( + unresolvedModuleID); + cache.addVisibleClangModules(unresolvedImport.first, + {unresolvedImport.second.importIdentifier}); + } else { + // Failed to resolve module dependency. + IssueReporter.diagnoseModuleNotFoundFailure( + unresolvedImport.second, cache, unresolvedImport.first, + attemptToFindResolvingSerializedSearchPath(unresolvedImport.second, + cache)); } } +} - // Module lookup result collection - llvm::StringMap< - std::optional> - moduleLookupResult; +void ModuleDependencyScanner::performParallelClangModuleLookup( + const ImportStatementInfoMap &unresolvedImportsMap, + const ImportStatementInfoMap &unresolvedOptionalImportsMap, + ModuleDependenciesCache &cache, BatchClangModuleLookupResult &result) { auto seenClangModules = cache.getAlreadySeenClangModules(); std::mutex resultAccessLock; - auto scanForClangModuleDependency = [this, &moduleLookupResult, - &resultAccessLock, &seenClangModules]( + auto scanForClangModuleDependency = [this, &result, &resultAccessLock, + &seenClangModules]( Identifier moduleIdentifier) { auto scanResult = withDependencyScanningWorker( [&](ModuleDependencyScanningWorker *ScanningWorker) { @@ -1078,112 +1116,143 @@ void ModuleDependencyScanner::resolveAllClangModuleDependencies( }); { std::lock_guard guard(resultAccessLock); - moduleLookupResult.insert_or_assign(moduleIdentifier.str(), scanResult); + if (scanResult) { + llvm::for_each(scanResult->ModuleGraph, [&result](const auto &dep) { + result.discoveredDependencyInfos.try_emplace(dep.ID.ModuleName, dep); + }); + result.visibleModules.insert_or_assign(moduleIdentifier.str(), + scanResult->VisibleModules); + } } }; // Enque asynchronous lookup tasks - for (const auto &unresolvedIdentifier : unresolvedImportIdentifiers) - ScanningThreadPool.async( - scanForClangModuleDependency, - getModuleImportIdentifier(unresolvedIdentifier.getKey())); - for (const auto &unresolvedIdentifier : unresolvedOptionalImportIdentifiers) - ScanningThreadPool.async( - scanForClangModuleDependency, - getModuleImportIdentifier(unresolvedIdentifier.getKey())); + for (const auto &unresolvedImports : unresolvedImportsMap) + for (const auto &unresolvedImportInfo : unresolvedImports.second) + ScanningThreadPool.async( + scanForClangModuleDependency, + getModuleImportIdentifier(unresolvedImportInfo.importIdentifier)); + + for (const auto &unresolvedImports : unresolvedOptionalImportsMap) + for (const auto &unresolvedImportInfo : unresolvedImports.second) + ScanningThreadPool.async( + scanForClangModuleDependency, + getModuleImportIdentifier(unresolvedImportInfo.importIdentifier)); + ScanningThreadPool.wait(); +} - // Use the computed scan results to update the dependency info +void ModuleDependencyScanner::cacheComputedClangModuleLookupResults( + const BatchClangModuleLookupResult &lookupResult, + const ImportStatementInfoMap &unresolvedImportsMap, + const ImportStatementInfoMap &unresolvedOptionalImportsMap, + ArrayRef swiftModuleDependents, + ModuleDependenciesCache &cache, + ModuleDependencyIDSetVector &allDiscoveredClangModules, + std::vector> + &failedToResolveImports, + std::unordered_map + &resolvedClangDependenciesMap) { for (const auto &moduleID : swiftModuleDependents) { - std::vector failedToResolveImports; - ModuleDependencyIDSetVector importedClangDependencies; auto recordResolvedClangModuleImport = - [this, &moduleLookupResult, &importedClangDependencies, &cache, + [this, &lookupResult, &cache, &resolvedClangDependenciesMap, &allDiscoveredClangModules, moduleID, &failedToResolveImports]( const ScannerImportStatementInfo &moduleImport, bool optionalImport) { - ASSERT(moduleLookupResult.contains(moduleImport.importIdentifier)); - const auto &lookupResult = - moduleLookupResult.at(moduleImport.importIdentifier); - if (lookupResult.has_value()) { - cache.recordClangDependencies( - lookupResult->ModuleGraph, ScanASTContext.Diags, - [this](auto &clangDep) { + auto &moduleIdentifier = moduleImport.importIdentifier; + auto dependencyID = + ModuleDependencyID{moduleIdentifier, ModuleDependencyKind::Clang}; + + // Add visible Clang modules for this query to the depending + // Swift module + if (lookupResult.visibleModules.contains(moduleIdentifier)) + cache.addVisibleClangModules( + moduleID, lookupResult.visibleModules.at(moduleIdentifier)); + + // Add the resolved dependency ID + if (lookupResult.discoveredDependencyInfos.contains( + moduleIdentifier)) { + auto dependencyInfo = lookupResult.discoveredDependencyInfos.at( + moduleImport.importIdentifier); + allDiscoveredClangModules.insert(dependencyID); + cache.recordClangDependency( + dependencyInfo, ScanASTContext.Diags, [this](auto &clangDep) { return bridgeClangModuleDependency(clangDep); }); - - // Add the full transitive dependency set - for (const auto &dep : lookupResult->ModuleGraph) - allDiscoveredClangModules.insert( - {dep.ID.ModuleName, ModuleDependencyKind::Clang}); - - importedClangDependencies.insert( - {moduleImport.importIdentifier, ModuleDependencyKind::Clang}); - - // Add visible Clang modules for this query to the depending - // Swift module - cache.addVisibleClangModules(moduleID, - lookupResult->VisibleModules); + resolvedClangDependenciesMap[moduleID].insert(dependencyID); } else if (!optionalImport) { // Otherwise, we failed to resolve this dependency. We will try // again using the cache after all other imports have been // resolved. If that fails too, a scanning failure will be // diagnosed. - failedToResolveImports.push_back(moduleImport); + failedToResolveImports.push_back( + std::make_pair(moduleID, moduleImport)); } }; - for (const auto &unresolvedImport : unresolvedImportsMap[moduleID]) + for (const auto &unresolvedImport : unresolvedImportsMap.at(moduleID)) recordResolvedClangModuleImport(unresolvedImport, false); - for (const auto &unresolvedImport : unresolvedOptionalImportsMap[moduleID]) + for (const auto &unresolvedImport : + unresolvedOptionalImportsMap.at(moduleID)) recordResolvedClangModuleImport(unresolvedImport, true); + } - // It is possible that a specific import resolution failed because we are - // attempting to resolve a module which can only be brought in via a - // modulemap of a different Clang module dependency which is not otherwise - // on the current search paths. For example, suppose we are scanning a - // `.swiftinterface` for module `Foo`, which contains: - // ----- - // @_exported import Foo - // import Bar - // ... - // ----- - // Where `Foo` is the underlying Framework clang module whose .modulemap - // defines an auxiliary module `Bar`. Because Foo is a framework, its - // modulemap is under - // `/Foo.framework/Modules/module.modulemap`. - // Which means that lookup of `Bar` alone from Swift will not be able to - // locate the module in it. However, the lookup of Foo will itself bring in - // the auxiliary module becuase the Clang scanner instance scanning for - // clang module Foo will be able to find it in the corresponding framework - // module's modulemap and register it as a dependency which means it will be - // registered with the scanner's cache in the step above. To handle such - // cases, we first add all successfully-resolved modules and (for Clang - // modules) their transitive dependencies to the cache, and then attempt to - // re-query imports for which resolution originally failed from the cache. - // If this fails, then the scanner genuinely failed to resolve this - // dependency. - for (const auto &unresolvedImport : failedToResolveImports) { - auto unresolvedModuleID = ModuleDependencyID{ - unresolvedImport.importIdentifier, ModuleDependencyKind::Clang}; - auto optionalCachedModuleInfo = cache.findDependency(unresolvedModuleID); - if (optionalCachedModuleInfo.has_value()) - importedClangDependencies.insert(unresolvedModuleID); - else { - // Failed to resolve module dependency. - IssueReporter.diagnoseModuleNotFoundFailure( - unresolvedImport, cache, moduleID, - attemptToFindResolvingSerializedSearchPath(unresolvedImport, - cache)); - } + // Use the computed scan results to record all transitive clang module + // dependencies + for (const auto &dep : lookupResult.discoveredDependencyInfos) { + auto depID = + ModuleDependencyID{dep.getKey().str(), ModuleDependencyKind::Clang}; + if (!allDiscoveredClangModules.contains(depID)) { + cache.recordClangDependency( + dep.second, ScanASTContext.Diags, [this](auto &clangDep) { + return bridgeClangModuleDependency(clangDep); + }); + allDiscoveredClangModules.insert(depID); } - - if (!importedClangDependencies.empty()) - cache.setImportedClangDependencies( - moduleID, importedClangDependencies.takeVector()); } +} - return; +void ModuleDependencyScanner::resolveAllClangModuleDependencies( + ArrayRef swiftModuleDependents, + ModuleDependenciesCache &cache, + ModuleDependencyIDSetVector &allDiscoveredClangModules) { + // Gather all unresolved imports which must correspond to + // Clang modules (since no Swift module for them was found). + ImportStatementInfoMap unresolvedImportsMap; + ImportStatementInfoMap unresolvedOptionalImportsMap; + gatherUnresolvedImports(cache, ScanASTContext, swiftModuleDependents, + allDiscoveredClangModules, unresolvedImportsMap, + unresolvedOptionalImportsMap); + + // Execute parallel lookup of all unresolved import + // identifiers as Clang modules. + BatchClangModuleLookupResult lookupResult; + performParallelClangModuleLookup( + unresolvedImportsMap, unresolvedOptionalImportsMap, cache, lookupResult); + + // Use the computed scan results to record directly-queried clang module + // dependencies. + std::vector> + failedToResolveImports; + std::unordered_map + resolvedClangDependenciesMap; + cacheComputedClangModuleLookupResults( + lookupResult, unresolvedImportsMap, unresolvedOptionalImportsMap, + swiftModuleDependents, cache, allDiscoveredClangModules, + failedToResolveImports, resolvedClangDependenciesMap); + + // Re-query some failed-to-resolve Clang imports from cache + // in chance they were brought in as transitive dependencies. + reQueryMissedModulesFromCache(cache, failedToResolveImports, + resolvedClangDependenciesMap); + + // Record the computed result for each Swift module in the graph. + for (const auto &moduleID : swiftModuleDependents) { + auto resolvedDependencies = + resolvedClangDependenciesMap[moduleID].getArrayRef(); + if (!resolvedDependencies.empty()) + cache.setImportedClangDependencies(moduleID, resolvedDependencies); + } } void ModuleDependencyScanner::resolveHeaderDependencies( @@ -1231,7 +1300,7 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependencies( // in case it itself has unresolved module dependencies. for (const auto &overlayDepID : discoveredSwiftOverlays) { ModuleDependencyIDSetVector allNewModules = - resolveImportedModuleDependencies(overlayDepID, cache); + resolveImportedModuleDependencies(overlayDepID, cache); allDiscoveredDependencies.insert(allNewModules.begin(), allNewModules.end()); } @@ -1604,8 +1673,8 @@ void ModuleDependencyScanner::resolveCrossImportOverlayDependencies( auto actualMainID = ModuleDependencyID{mainModuleName.str(), ModuleDependencyKind::SwiftSource}; auto dummyMainDependencies = ModuleDependencyInfo::forSwiftSourceModule(); - std::for_each( - newOverlays.begin(), newOverlays.end(), [&](Identifier modName) { + llvm::for_each( + newOverlays, [&](Identifier modName) { dummyMainDependencies.addModuleImport( modName.str(), /* isExported */ false,