Skip to content

Commit 0555764

Browse files
committed
[Dependency Scanning] Refine cross-import overlay detection algorithm
The algorithm already performs pairwise checks on module dependencies brought into compilation per-source-file. Previously, the algorithm considered the entire sub-graph of a given source file. Actual source compiles do not consider the full transitive module dependency set for cross-import-overlay lookup, but rather only directly-imported modules in a given source file, and '@_exported import' Swift transitive dependencies. This change adds tracking of whether a given import statement is 'exported' to the dependency scanner and then refines the cross-import overlay lookup logic to only consider transitive modules that are exported by directly-imported dependencies.
1 parent 809dbf0 commit 0555764

9 files changed

+62
-26
lines changed

include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ using IsFrameworkField = BCFixed<1>;
5656
using IsSystemField = BCFixed<1>;
5757
/// A bit that indicates whether or not a module is that of a static archive
5858
using IsStaticField = BCFixed<1>;
59-
/// A bit taht indicates whether or not a link library is a force-load one
59+
/// A bit that indicates whether or not a link library is a force-load one
6060
using IsForceLoadField = BCFixed<1>;
61-
/// A bit taht indicates whether or not an import statement is optional
61+
/// A bit that indicates whether or not an import statement is optional
6262
using IsOptionalImport = BCFixed<1>;
63-
/// A bit taht indicates whether or not an import statement is @_exported
63+
/// A bit that indicates whether or not an import statement is @_exported
6464
using IsExportedImport = BCFixed<1>;
6565

6666
/// Source location fields

lib/AST/ModuleDependencies.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void ModuleDependencyInfo::addModuleImport(
126126
StringRef module, bool isExported, llvm::StringSet<> *alreadyAddedModules,
127127
const SourceManager *sourceManager, SourceLoc sourceLocation) {
128128
auto scannerImportLocToDiagnosticLocInfo =
129-
[&sourceManager, isExported](SourceLoc sourceLocation) {
129+
[&sourceManager](SourceLoc sourceLocation) {
130130
auto lineAndColumnNumbers =
131131
sourceManager->getLineAndColumnInBuffer(sourceLocation);
132132
return ScannerImportStatementInfo::ImportDiagnosticLocationInfo(

lib/ClangImporter/ClangModuleDependencyScanner.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,10 @@ ModuleDependencyVector ClangImporter::bridgeClangModuleDependencies(
281281

282282
std::vector<ModuleDependencyID> directDependencyIDs;
283283
for (const auto &moduleName : clangModuleDep.ClangModuleDeps) {
284-
dependencies.addModuleImport(moduleName.ModuleName, &alreadyAddedModules);
284+
// FIXME: This assumes, conservatively, that all Clang module imports
285+
// are exported. We need to fix this once the clang scanner gains the appropriate
286+
// API to query this.
287+
dependencies.addModuleImport(moduleName.ModuleName, /* isExported */ true, &alreadyAddedModules);
285288
// It is safe to assume that all dependencies of a Clang module are Clang modules.
286289
directDependencyIDs.push_back({moduleName.ModuleName, ModuleDependencyKind::Clang});
287290
}

lib/DependencyScan/ModuleDependencyScanner.cpp

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -590,18 +590,19 @@ static void discoverCrossImportOverlayFiles(
590590
mainModuleName.str(), ModuleDependencyKind::SwiftSource});
591591

592592
llvm::StringMap<ModuleDependencyIDSet> perSourceFileDependencies;
593-
const ModuleDependencyIDSet directSwiftDepsSet{
593+
const ModuleDependencyIDSet mainModuleDirectSwiftDepsSet{
594594
mainModuleInfo.getImportedSwiftDependencies().begin(),
595595
mainModuleInfo.getImportedSwiftDependencies().end()};
596-
const ModuleDependencyIDSet directClangDepsSet{
596+
const ModuleDependencyIDSet mainModuleDirectClangDepsSet{
597597
mainModuleInfo.getImportedClangDependencies().begin(),
598598
mainModuleInfo.getImportedClangDependencies().end()};
599599

600600
// A utility to map an import identifier to one of the
601601
// known resolved module dependencies
602602
auto getModuleIDForImportIdentifier =
603-
[directSwiftDepsSet, directClangDepsSet](
604-
const std::string &importIdentifierStr) -> ModuleDependencyID {
603+
[](const std::string &importIdentifierStr,
604+
const ModuleDependencyIDSet &directSwiftDepsSet,
605+
const ModuleDependencyIDSet &directClangDepsSet) -> ModuleDependencyID {
605606
if (auto textualDepIt = directSwiftDepsSet.find(
606607
{importIdentifierStr, ModuleDependencyKind::SwiftInterface});
607608
textualDepIt != directSwiftDepsSet.end())
@@ -621,8 +622,9 @@ static void discoverCrossImportOverlayFiles(
621622
// Collect the set of directly-imported module dependencies
622623
// for each source file in the source module under scan.
623624
for (const auto &import : mainModuleInfo.getModuleImports()) {
624-
auto importResolvedModuleID =
625-
getModuleIDForImportIdentifier(import.importIdentifier);
625+
auto importResolvedModuleID = getModuleIDForImportIdentifier(
626+
import.importIdentifier, mainModuleDirectSwiftDepsSet,
627+
mainModuleDirectClangDepsSet);
626628
for (const auto &importLocation : import.importLocations)
627629
perSourceFileDependencies[importLocation.bufferIdentifier].insert(
628630
importResolvedModuleID);
@@ -640,19 +642,29 @@ static void discoverCrossImportOverlayFiles(
640642
auto moduleID = worklist.pop_back_val();
641643
perSourceFileDependencies[bufferIdentifier].insert(moduleID);
642644
if (isSwiftDependencyKind(moduleID.Kind)) {
643-
for (const auto &directSwiftDepID :
644-
cache.getImportedSwiftDependencies(moduleID)) {
645-
if (perSourceFileDependencies[bufferIdentifier].count(directSwiftDepID))
646-
continue;
647-
worklist.push_back(directSwiftDepID);
645+
auto moduleInfo = cache.findKnownDependency(moduleID);
646+
if (llvm::any_of(moduleInfo.getModuleImports(),
647+
[](const ScannerImportStatementInfo &importInfo) {
648+
return importInfo.isExported;
649+
})) {
650+
const ModuleDependencyIDSet directSwiftDepsSet{
651+
moduleInfo.getImportedSwiftDependencies().begin(),
652+
moduleInfo.getImportedSwiftDependencies().end()};
653+
const ModuleDependencyIDSet directClangDepsSet{
654+
moduleInfo.getImportedClangDependencies().begin(),
655+
moduleInfo.getImportedClangDependencies().end()};
656+
for (const auto &import : moduleInfo.getModuleImports()) {
657+
if (import.isExported) {
658+
auto importResolvedDepID = getModuleIDForImportIdentifier(
659+
import.importIdentifier, directSwiftDepsSet,
660+
directClangDepsSet);
661+
if (!perSourceFileDependencies[bufferIdentifier].count(
662+
importResolvedDepID))
663+
worklist.push_back(importResolvedDepID);
664+
}
665+
}
648666
}
649667
}
650-
for (const auto &directClangDepID :
651-
cache.getImportedClangDependencies(moduleID)) {
652-
if (perSourceFileDependencies[bufferIdentifier].count(directClangDepID))
653-
continue;
654-
worklist.push_back(directClangDepID);
655-
}
656668
}
657669
}
658670

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// swift-interface-format-version: 1.0
22
// swift-module-flags: -module-name EWrapper
33
import Swift
4-
import E
4+
@_exported import E
55
public func funcEWrapper() { }
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -module-name EWrapperNonExported
3+
import Swift
4+
import E
5+
public func funcEWrapperNonExported() { }
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// swift-interface-format-version: 1.0
22
// swift-module-flags: -module-name SubEWrapper
33
import Swift
4-
import SubE
4+
@_exported import SubE
55
public func funcSubEWrapper() { }

test/ScanDependencies/module_deps_cross_import_overlay.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// RUN: %empty-directory(%t)
22
// RUN: mkdir -p %t/clang-module-cache
3-
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %S/Inputs/CHeaders/ExtraCModules -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -module-name CrossImportTestModule -enable-cross-import-overlays
3+
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %S/Inputs/CHeaders/ExtraCModules -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -module-name CrossImportTestModule -enable-cross-import-overlays
44
// Check the contents of the JSON output
55
// RUN: %validate-json %t/deps.json | %FileCheck %s
66

77
// Ensure that round-trip serialization does not affect result
8-
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -test-dependency-scan-cache-serialization -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %S/Inputs/CHeaders/ExtraCModules -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -module-name CrossImportTestModule -enable-cross-import-overlays
8+
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -test-dependency-scan-cache-serialization -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %S/Inputs/CHeaders/ExtraCModules -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 -module-name CrossImportTestModule -enable-cross-import-overlays
99
// RUN: %validate-json %t/deps.json | %FileCheck %s
1010

1111
// REQUIRES: executable_test
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// REQUIRES: objc_interop
2+
// RUN: %empty-directory(%t)
3+
// RUN: %empty-directory(%t/clang-module-cache)
4+
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %S/Inputs/CHeaders/ExtraCModules -module-name CrossImportTestModule -enable-cross-import-overlays
5+
// Check the contents of the JSON output
6+
// RUN: %validate-json %t/deps.json | %FileCheck %s
7+
8+
// Ensure that round-trip serialization does not affect result
9+
// RUN: %target-swift-frontend -scan-dependencies -module-load-mode prefer-interface -test-dependency-scan-cache-serialization -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %S/Inputs/CHeaders/ExtraCModules -module-name CrossImportTestModule -enable-cross-import-overlays
10+
// RUN: %validate-json %t/deps.json | %FileCheck %s
11+
12+
// Ensure that if one of the two components of a cross-import overlay is a transitive (non-exported) Swift dependency, then no overlay is required to be brought in.
13+
import EWrapperNonExported
14+
import SubEWrapper
15+
16+
// CHECK-NOT: "swift": "_cross_import_E"

0 commit comments

Comments
 (0)