From 77a61a242fb552eae15cac5513c2c901a827159b Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 4 Aug 2025 17:34:50 -0700 Subject: [PATCH] [Dependency Scanning][C++ Interop] Avoid 'CxxStdlib' overlay lookup for binary Swift dependencies which were not built with C++ interop In addition to skipping it on textual Swift module dependencies which were built without C++ interop enabled, also skip it over similarly on binary Swift dependencies --- include/swift/AST/ModuleDependencies.h | 15 +++-- .../SerializedModuleDependencyCacheFormat.h | 3 + .../ModuleDependencyCacheSerialization.cpp | 10 ++-- .../ModuleDependencyScanner.cpp | 31 ++++++---- lib/Serialization/ModuleFileSharedCore.h | 5 ++ lib/Serialization/ScanningLoaders.cpp | 1 + ...xx-overlay-for-non-interop-interface.swift | 58 +++++++++++++------ 7 files changed, 87 insertions(+), 36 deletions(-) diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 3739d7465ea..e6d09c3af23 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -393,8 +393,8 @@ public: ArrayRef linkLibraries, ArrayRef serializedSearchPaths, StringRef headerImport, StringRef definingModuleInterface, - bool isFramework, bool isStatic, StringRef moduleCacheKey, - StringRef userModuleVersion) + bool isFramework, bool isStatic, bool isBuiltWithCxxInterop, + StringRef moduleCacheKey, StringRef userModuleVersion) : ModuleDependencyInfoStorageBase(ModuleDependencyKind::SwiftBinary, moduleImports, optionalModuleImports, linkLibraries, moduleCacheKey), @@ -403,6 +403,7 @@ public: definingModuleInterfacePath(definingModuleInterface), serializedSearchPaths(serializedSearchPaths), isFramework(isFramework), isStatic(isStatic), + isBuiltWithCxxInterop(isBuiltWithCxxInterop), userModuleVersion(userModuleVersion) {} ModuleDependencyInfoStorageBase *clone() const override { @@ -440,6 +441,10 @@ public: /// A flag that indicates this dependency is associated with a static archive const bool isStatic; + /// A flag that indicates this dependency module was built + /// with C++ interop enabled + const bool isBuiltWithCxxInterop; + /// The user module version of this binary module. const std::string userModuleVersion; @@ -573,14 +578,14 @@ public: ArrayRef linkLibraries, ArrayRef serializedSearchPaths, StringRef headerImport, StringRef definingModuleInterface, - bool isFramework, bool isStatic, StringRef moduleCacheKey, - StringRef userModuleVer) { + bool isFramework, bool isStatic, bool isBuiltWithCxxInterop, + StringRef moduleCacheKey, StringRef userModuleVer) { return ModuleDependencyInfo( std::make_unique( compiledModulePath, moduleDocPath, sourceInfoPath, moduleImports, optionalModuleImports, linkLibraries, serializedSearchPaths, headerImport, definingModuleInterface,isFramework, isStatic, - moduleCacheKey, userModuleVer)); + isBuiltWithCxxInterop, moduleCacheKey, userModuleVer)); } /// Describe the main Swift module. diff --git a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h index 91ba72ae112..c61cb26cd81 100644 --- a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h +++ b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h @@ -56,6 +56,8 @@ using IsFrameworkField = BCFixed<1>; using IsSystemField = BCFixed<1>; /// A bit that indicates whether or not a module is that of a static archive using IsStaticField = BCFixed<1>; +/// A bit that indicates whether or not a module is built with C++ interop +using IsBuiltWithCxxInteropField = BCFixed<1>; /// A bit that indicates whether or not a link library is a force-load one using IsForceLoadField = BCFixed<1>; /// A bit that indicates whether or not an import statement is optional @@ -267,6 +269,7 @@ using SwiftBinaryModuleDetailsLayout = SearchPathArrayIDField, // serializedSearchPaths IsFrameworkField, // isFramework IsStaticField, // isStatic + IsBuiltWithCxxInteropField, // IsBuiltWithCxxInterop IdentifierIDField, // moduleCacheKey IdentifierIDField // UserModuleVersion >; diff --git a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp index 276cb6a309c..4ab832fb962 100644 --- a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp +++ b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp @@ -679,13 +679,14 @@ bool ModuleDependenciesCacheDeserializer::readGraph( unsigned compiledModulePathID, moduleDocPathID, moduleSourceInfoPathID, headerImportID, definingInterfacePathID, searchPathArrayID, headerModuleDependenciesArrayID, headerImportsSourceFilesArrayID, - isFramework, isStatic, moduleCacheKeyID, userModuleVersionID; + isFramework, isStatic, isBuiltWithCxxInterop, moduleCacheKeyID, + userModuleVersionID; SwiftBinaryModuleDetailsLayout::readRecord( Scratch, compiledModulePathID, moduleDocPathID, moduleSourceInfoPathID, headerImportID, definingInterfacePathID, headerModuleDependenciesArrayID, headerImportsSourceFilesArrayID, - searchPathArrayID, isFramework, isStatic, moduleCacheKeyID, - userModuleVersionID); + searchPathArrayID, isFramework, isStatic, isBuiltWithCxxInterop, + moduleCacheKeyID, userModuleVersionID); auto compiledModulePath = getIdentifier(compiledModulePathID); if (!compiledModulePath) @@ -720,7 +721,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph( *compiledModulePath, *moduleDocPath, *moduleSourceInfoPath, importStatements, optionalImportStatements, linkLibraries, *searchPaths, *headerImport, *definingInterfacePath, isFramework, - isStatic, *moduleCacheKey, *userModuleVersion); + isStatic, isBuiltWithCxxInterop, *moduleCacheKey, *userModuleVersion); addCommonDependencyInfo(moduleDep); addSwiftCommonDependencyInfo(moduleDep); @@ -1629,6 +1630,7 @@ void ModuleDependenciesCacheSerializer::writeModuleInfo( ModuleIdentifierArrayKind::HeaderInputDependencySourceFiles), getSearchPathArrayID(moduleID), swiftBinDeps->isFramework, swiftBinDeps->isStatic, + swiftBinDeps->isBuiltWithCxxInterop, getIdentifier(swiftBinDeps->moduleCacheKey), getIdentifier(swiftBinDeps->userModuleVersion)); break; diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 3617a73294e..2e86a245919 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -1525,23 +1525,34 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependenciesForModule( recordResult(clangDep.getKey().str()); // C++ Interop requires additional handling - bool lookupCxxStdLibOverlay = ScanCompilerInvocation.getLangOptions().EnableCXXInterop; - if (lookupCxxStdLibOverlay && moduleID.Kind == ModuleDependencyKind::SwiftInterface) { + bool lookupCxxStdLibOverlay = + ScanCompilerInvocation.getLangOptions().EnableCXXInterop; + if (lookupCxxStdLibOverlay && + moduleID.Kind == ModuleDependencyKind::SwiftInterface) { const auto &moduleInfo = cache.findKnownDependency(moduleID); const auto commandLine = moduleInfo.getCommandline(); // If the textual interface was built without C++ interop, do not query // the C++ Standard Library Swift overlay for its compilation. - // - // FIXME: We always declare the 'Darwin' module as formally having been built - // without C++Interop, for compatibility with prior versions. Once we are certain - // that we are only building against modules built with support of - // '-formal-cxx-interoperability-mode', this hard-coded check should be removed. - if (moduleID.ModuleName == "Darwin" || - llvm::find(commandLine, "-formal-cxx-interoperability-mode=off") != - commandLine.end()) + if (llvm::find(commandLine, "-formal-cxx-interoperability-mode=off") != + commandLine.end()) + lookupCxxStdLibOverlay = false; + } else if (lookupCxxStdLibOverlay && + moduleID.Kind == ModuleDependencyKind::SwiftBinary) { + const auto &moduleDetails = + cache.findKnownDependency(moduleID).getAsSwiftBinaryModule(); + // If the binary module was built without C++ interop, do not query + // the C++ Standard Library Swift overlay. + if (!moduleDetails->isBuiltWithCxxInterop) lookupCxxStdLibOverlay = false; } + // FIXME: We always declare the 'Darwin' module as formally having been built + // without C++Interop, for compatibility with prior versions. Once we are certain + // that we are only building against modules built with support of + // '-formal-cxx-interoperability-mode', this hard-coded check should be removed. + if (lookupCxxStdLibOverlay && moduleID.ModuleName == "Darwin") + lookupCxxStdLibOverlay = false; + if (lookupCxxStdLibOverlay) { for (const auto &clangDepNameEntry : visibleClangDependencies) { auto clangDepName = clangDepNameEntry.getKey().str(); diff --git a/lib/Serialization/ModuleFileSharedCore.h b/lib/Serialization/ModuleFileSharedCore.h index 418a8634c69..bbe5298c39c 100644 --- a/lib/Serialization/ModuleFileSharedCore.h +++ b/lib/Serialization/ModuleFileSharedCore.h @@ -653,6 +653,11 @@ public: return Bits.IsStaticLibrary; } + /// Was this module built with C++ interop enabled. + bool isBuiltWithCxxInterop() const { + return Bits.HasCxxInteroperability; + } + llvm::VersionTuple getUserModuleVersion() const { return UserModuleVersion; } diff --git a/lib/Serialization/ScanningLoaders.cpp b/lib/Serialization/ScanningLoaders.cpp index 53e55966c74..070a915bb33 100644 --- a/lib/Serialization/ScanningLoaders.cpp +++ b/lib/Serialization/ScanningLoaders.cpp @@ -340,6 +340,7 @@ llvm::ErrorOr SwiftModuleScanner::scanBinaryModuleFile( binaryModuleOptionalImports->moduleImports, linkLibraries, serializedSearchPaths, binaryModuleImports->headerImport, definingModulePath, isFramework, loadedModuleFile->isStaticLibrary(), + loadedModuleFile->isBuiltWithCxxInterop(), /*module-cache-key*/ "", userModuleVer); for (auto ¯o : loadedModuleFile->getExternalMacros()) { diff --git a/test/ScanDependencies/no-cxx-overlay-for-non-interop-interface.swift b/test/ScanDependencies/no-cxx-overlay-for-non-interop-interface.swift index fbbf31a5594..ce9c29fbde6 100644 --- a/test/ScanDependencies/no-cxx-overlay-for-non-interop-interface.swift +++ b/test/ScanDependencies/no-cxx-overlay-for-non-interop-interface.swift @@ -1,3 +1,4 @@ + // RUN: %empty-directory(%t) // RUN: %empty-directory(%t/deps) // RUN: split-file %s %t @@ -8,17 +9,29 @@ // RUN: %target-swift-frontend -scan-dependencies -o %t/deps_no_interop_dep.json %t/clientNoInteropDep.swift -I %t/deps -cxx-interoperability-mode=default -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -verify // RUN: cat %t/deps_no_interop_dep.json | %FileCheck %s -check-prefix=DISABLE-CHECK +// RUN: %target-swift-frontend -emit-module %t/BinaryDepNoInterop.swift -emit-module-path %t/deps/BinaryDepNoInterop.swiftmodule -module-name BinaryDepNoInterop -I %t/deps -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import +// RUN: %target-swift-frontend -scan-dependencies -o %t/deps_no_interop_binary_dep.json %t/clientNoInteropBinaryDep.swift -I %t/deps -cxx-interoperability-mode=default -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -verify +// RUN: cat %t/deps_no_interop_binary_dep.json | %FileCheck %s -check-prefix=DISABLE-BINARY-CHECK + // RUN: %target-swift-frontend -scan-dependencies -o %t/deps_darwin_dep.json %t/clientDarwin.swift -I %t/deps -cxx-interoperability-mode=default -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -verify // RUN: cat %t/deps_darwin_dep.json | %FileCheck %s -check-prefix=DARWIN-CHECK //--- deps/bar.h void bar(void); +//--- deps/baz.h +#include "bar.h" +void baz(void); + //--- deps/module.modulemap module std_Bar [system] { header "bar.h" export * } +module normal { + header "baz.h" + export * +} //--- deps/Foo.swiftinterface // swift-interface-format-version: 1.0 @@ -39,12 +52,19 @@ public struct Foo2 {} import std_Bar public struct Foo3 {} +//--- BinaryDepNoInterop.swift +import normal +public struct Foo6 {} + //--- clientWithInteropDep.swift import Foo //--- clientNoInteropDep.swift import FooNoInterop +//--- clientNoInteropBinaryDep.swift +import BinaryDepNoInterop + //--- clientDarwin.swift import Darwin @@ -87,21 +107,25 @@ import Darwin // DISABLE-CHECK: } // DISABLE-CHECK: ], -// Ensure that the the 'Darwin' dependency does not get the C++ standard library overlay for its 'std_*' dependencies -// -// 'Darwin' as it appears in direct deps -// DARWIN-CHECK: "swift": "Darwin" -// 'Darwin' as it appears in source-import deps -// DARWIN-CHECK: "swift": "Darwin" -// Actual dependency info node -// DARWIN-CHECK: "swift": "Darwin" +// DISABLE-BINARY-CHECK: "modulePath": "{{.*}}{{/|\\}}BinaryDepNoInterop.swiftmodule" +// DISABLE-BINARY-CHECK: "directDependencies": [ +// DISABLE-BINARY-CHECK-NEXT: { +// DISABLE-BINARY-CHECK-NEXT: "swift": "Swift" +// DISABLE-BINARY-CHECK-NEXT: }, +// DISABLE-BINARY-CHECK-NEXT: { +// DISABLE-BINARY-CHECK-NEXT: "swift": "SwiftOnoneSupport" +// DISABLE-BINARY-CHECK-NEXT: }, +// DISABLE-BINARY-CHECK-NEXT: { +// DISABLE-BINARY-CHECK-NEXT: "clang": "normal" +// DISABLE-BINARY-CHECK-NEXT: } +// DISABLE-BINARY-CHECK-NEXT: ], + +// DARWIN-CHECK: "modulePath": "{{.*}}{{/|\\}}Darwin-{{.*}}.swiftmodule" // DARWIN-CHECK: "directDependencies": [ -// DARWIN-CHECK: { -// DARWIN-CHECK: "swift": "SwiftOnoneSupport" -// DARWIN-CHECK: }, -// DARWIN-CHECK: { -// DARWIN-CHECK: "clang": "std_Bar" -// DARWIN-CHECK: } -// DARWIN-CHECK: ], - - +// DARWIN-CHECK-NEXT: { +// DARWIN-CHECK-NEXT: "swift": "SwiftOnoneSupport" +// DARWIN-CHECK-NEXT: }, +// DARWIN-CHECK-NEXT: { +// DARWIN-CHECK-NEXT: "clang": "std_Bar" +// DARWIN-CHECK-NEXT: } +// DARWIN-CHECK-NEXT: ],