Skip to content

[6.0🍒][Explicit Module Builds] C++ Interoperability mode fixes #73767

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions include/swift/AST/ModuleDependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,9 @@ class ClangModuleDependencyStorage : public ModuleDependencyInfoStorageBase {
/// CASID for the Root of ClangIncludeTree. Empty if not used.
std::string CASClangIncludeTreeRootID;

/// Whether this is a "system" module.
bool IsSystem;

ClangModuleDependencyStorage(const std::string &pcmOutputPath,
const std::string &mappedPCMPath,
const std::string &moduleMapFile,
Expand All @@ -412,15 +415,16 @@ class ClangModuleDependencyStorage : public ModuleDependencyInfoStorageBase {
const std::vector<std::string> &capturedPCMArgs,
const std::string &CASFileSystemRootID,
const std::string &clangIncludeTreeRoot,
const std::string &moduleCacheKey)
const std::string &moduleCacheKey,
bool IsSystem)
: ModuleDependencyInfoStorageBase(ModuleDependencyKind::Clang,
moduleCacheKey),
pcmOutputPath(pcmOutputPath), mappedPCMPath(mappedPCMPath),
moduleMapFile(moduleMapFile), contextHash(contextHash),
buildCommandLine(buildCommandLine), fileDependencies(fileDependencies),
capturedPCMArgs(capturedPCMArgs),
CASFileSystemRootID(CASFileSystemRootID),
CASClangIncludeTreeRootID(clangIncludeTreeRoot) {}
CASClangIncludeTreeRootID(clangIncludeTreeRoot), IsSystem(IsSystem) {}

ModuleDependencyInfoStorageBase *clone() const override {
return new ClangModuleDependencyStorage(*this);
Expand Down Expand Up @@ -549,11 +553,12 @@ class ModuleDependencyInfo {
const std::vector<std::string> &capturedPCMArgs,
const std::string &CASFileSystemRootID,
const std::string &clangIncludeTreeRoot,
const std::string &moduleCacheKey) {
const std::string &moduleCacheKey,
bool IsSystem) {
return ModuleDependencyInfo(std::make_unique<ClangModuleDependencyStorage>(
pcmOutputPath, mappedPCMPath, moduleMapFile, contextHash,
nonPathCommandLine, fileDependencies, capturedPCMArgs,
CASFileSystemRootID, clangIncludeTreeRoot, moduleCacheKey));
CASFileSystemRootID, clangIncludeTreeRoot, moduleCacheKey, IsSystem));
}

/// Describe a placeholder dependency swift module.
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,9 @@ namespace swift {
bool DisableImplicitBacktracingModuleImport =
!SWIFT_IMPLICIT_BACKTRACING_IMPORT;

/// Disable the implicit import of the Cxx module.
bool DisableImplicitCxxModuleImport = false;

// Whether to use checked continuations when making an async call from
// Swift into ObjC. If false, will use unchecked continuations instead.
bool UseCheckedAsyncObjCBridging = false;
Expand Down
5 changes: 5 additions & 0 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,11 @@ bool requiresCPlusPlus(const clang::Module *module);
/// (std_vector, std_iosfwd, etc).
bool isCxxStdModule(const clang::Module *module);

/// Returns true if the given module is one of the C++ standard library modules.
/// This could be the top-level std module, or any of the libc++ split modules
/// (std_vector, std_iosfwd, etc).
bool isCxxStdModule(StringRef moduleName, bool IsSystem);

/// Returns the pointee type if the given type is a C++ `const`
/// reference type, `None` otherwise.
std::optional<clang::QualType>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ using llvm::BCVBR;
/// Every .moddepcache file begins with these 4 bytes, for easy identification.
const unsigned char MODULE_DEPENDENCY_CACHE_FORMAT_SIGNATURE[] = {'I', 'M', 'D','C'};
const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MAJOR =
6; // mappedPCMPath
7; // isSystem
/// Increment this on every change.
const unsigned MODULE_DEPENDENCY_CACHE_FORMAT_VERSION_MINOR = 1;

Expand All @@ -53,6 +53,8 @@ using ContextHashIDField = IdentifierIDField;

/// A bit that indicates whether or not a module is a framework
using IsFrameworkField = BCFixed<1>;
/// A bit that indicates whether or not a module is a system module
using IsSystemField = BCFixed<1>;

/// Arrays of various identifiers, distinguished for readability
using IdentifierIDArryField = llvm::BCArray<IdentifierIDField>;
Expand Down Expand Up @@ -193,7 +195,8 @@ using ClangModuleDetailsLayout =
FlagIDArrayIDField, // capturedPCMArgs
IdentifierIDField, // CASFileSystemRootID
IdentifierIDField, // clangIncludeTreeRoot
IdentifierIDField // moduleCacheKey
IdentifierIDField, // moduleCacheKey
IsSystemField // isSystem
>;
} // namespace graph_block

Expand Down
6 changes: 6 additions & 0 deletions include/swift/Frontend/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ class CompilerInvocation {
/// imported.
bool shouldImportSwiftBacktracing() const;

/// Whether the CXX module should be implicitly imported.
bool shouldImportCxx() const;

/// Performs input setup common to these tools:
/// sil-opt, sil-func-extractor, sil-llvm-gen, and sil-nm.
/// Return value includes the buffer so caller can keep it alive.
Expand Down Expand Up @@ -660,6 +663,9 @@ class CompilerInstance {
/// i.e. if it can be found.
bool canImportSwiftBacktracing() const;

/// Whether the Cxx library can be imported
bool canImportCxx() const;

/// Whether the CxxShim library can be imported
/// i.e. if it can be found.
bool canImportCxxShim() const;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,10 @@ def disable_implicit_backtracing_module_import : Flag<["-"],
"disable-implicit-backtracing-module-import">,
HelpText<"Disable the implicit import of the _Backtracing module.">;

def disable_implicit_cxx_module_import : Flag<["-"],
"disable-implicit-cxx-module-import">,
HelpText<"Disable the implicit import of the C++ Standard Library module.">;

def disable_arc_opts : Flag<["-"], "disable-arc-opts">,
HelpText<"Don't run SIL ARC optimization passes.">;
def disable_ossa_opts : Flag<["-"], "disable-ossa-opts">,
Expand Down
4 changes: 2 additions & 2 deletions include/swift/Serialization/SerializedModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ class SerializedModuleLoaderBase : public ModuleLoader {
std::string headerImport;
};

static BinaryModuleImports
getImportsOfModule(const ModuleFileSharedCore &loadedModule,
llvm::ErrorOr<BinaryModuleImports>
getImportsOfModule(const ModuleFileSharedCore &loadedModuleFile,
ModuleLoadingBehavior transitiveBehavior,
StringRef packageName, bool isTestableImport);

Expand Down
2 changes: 2 additions & 0 deletions include/swift/Strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ constexpr static const StringLiteral SWIFT_BACKTRACING_NAME = "_Backtracing";
constexpr static const StringLiteral SWIFT_SHIMS_NAME = "SwiftShims";
/// The name of the CxxShim module, which contains a cxx casting utility.
constexpr static const StringLiteral CXX_SHIM_NAME = "CxxShim";
/// The name of the Cxx module, which contains C++ interop helper protocols.
constexpr static const StringLiteral CXX_MODULE_NAME = "Cxx";
/// The name of the Builtin module, which contains Builtin functions.
constexpr static const StringLiteral BUILTIN_NAME = "Builtin";
/// The name of the clang imported header module.
Expand Down
14 changes: 10 additions & 4 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7875,14 +7875,20 @@ bool importer::requiresCPlusPlus(const clang::Module *module) {
}

bool importer::isCxxStdModule(const clang::Module *module) {
if (module->getTopLevelModuleName() == "std")
return isCxxStdModule(module->getTopLevelModuleName(),
module->getTopLevelModule()->IsSystem);
}

bool importer::isCxxStdModule(StringRef moduleName, bool IsSystem) {
if (moduleName == "std")
return true;
// In recent libc++ versions the module is split into multiple top-level
// modules (std_vector, std_utility, etc).
if (module->getTopLevelModule()->IsSystem &&
module->getTopLevelModuleName().starts_with("std_"))
if (IsSystem && moduleName.starts_with("std_")) {
if (moduleName == "std_errno_h")
return false;
return true;

}
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/ClangImporter/ClangModuleDependencyScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ ModuleDependencyVector ClangImporter::bridgeClangModuleDependencies(
auto dependencies = ModuleDependencyInfo::forClangModule(
pcmPath, mappedPCMPath, clangModuleDep.ClangModuleMapFile,
clangModuleDep.ID.ContextHash, swiftArgs, fileDeps, capturedPCMArgs,
RootID, IncludeTree, /*module-cache-key*/ "");
RootID, IncludeTree, /*module-cache-key*/ "", clangModuleDep.IsSystem);
for (const auto &moduleName : clangModuleDep.ClangModuleDeps) {
dependencies.addModuleImport(moduleName.ModuleName, &alreadyAddedModules);
// It is safe to assume that all dependencies of a Clang module are Clang modules.
Expand Down
8 changes: 4 additions & 4 deletions lib/DependencyScan/ModuleDependencyCacheSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,12 +574,12 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
cache.configureForContextHash(getContextHash());
unsigned pcmOutputPathID, mappedPCMPathID, moduleMapPathID, contextHashID,
commandLineArrayID, fileDependenciesArrayID, capturedPCMArgsArrayID,
CASFileSystemRootID, clangIncludeTreeRootID, moduleCacheKeyID;
CASFileSystemRootID, clangIncludeTreeRootID, moduleCacheKeyID, isSystem;
ClangModuleDetailsLayout::readRecord(
Scratch, pcmOutputPathID, mappedPCMPathID, moduleMapPathID,
contextHashID, commandLineArrayID, fileDependenciesArrayID,
capturedPCMArgsArrayID, CASFileSystemRootID, clangIncludeTreeRootID,
moduleCacheKeyID);
moduleCacheKeyID, isSystem);
auto pcmOutputPath = getIdentifier(pcmOutputPathID);
if (!pcmOutputPath)
llvm::report_fatal_error("Bad pcm output path");
Expand Down Expand Up @@ -615,7 +615,7 @@ bool ModuleDependenciesCacheDeserializer::readGraph(SwiftDependencyScanningServi
auto moduleDep = ModuleDependencyInfo::forClangModule(
*pcmOutputPath, *mappedPCMPath, *moduleMapPath, *contextHash,
*commandLineArgs, *fileDependencies, *capturedPCMArgs,
*rootFileSystemID, *clangIncludeTreeRoot, *moduleCacheKey);
*rootFileSystemID, *clangIncludeTreeRoot, *moduleCacheKey, isSystem);

// Add dependencies of this module
for (const auto &moduleName : *currentModuleImports)
Expand Down Expand Up @@ -1064,7 +1064,7 @@ void ModuleDependenciesCacheSerializer::writeModuleInfo(
getArrayID(moduleID, ModuleIdentifierArrayKind::CapturedPCMArgs),
getIdentifier(clangDeps->CASFileSystemRootID),
getIdentifier(clangDeps->CASClangIncludeTreeRootID),
getIdentifier(clangDeps->moduleCacheKey));
getIdentifier(clangDeps->moduleCacheKey), clangDeps->IsSystem);

break;
}
Expand Down
35 changes: 29 additions & 6 deletions lib/DependencyScan/ModuleDependencyScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/FileTypes.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/DependencyScan/ModuleDependencyScanner.h"
#include "swift/Frontend/ModuleInterfaceLoader.h"
#include "swift/Serialization/SerializedModuleLoader.h"
Expand Down Expand Up @@ -763,24 +764,46 @@ void ModuleDependencyScanner::resolveSwiftOverlayDependencies(
// FIXME: Once all clients know to fetch these dependencies from
// `swiftOverlayDependencies`, the goal is to no longer have them in
// `directDependencies` so the following will need to go away.
directDependencies.insert({moduleName, cachedInfo->getKind()});
directDependencies.insert({moduleName, cachedInfo->getKind()});
} else {
// Cache discovered module dependencies.
cache.recordDependencies(lookupResult.value());
if (!lookupResult.value().empty()) {
swiftOverlayDependencies.insert(
{moduleName, lookupResult.value()[0].first.Kind});
swiftOverlayDependencies.insert({moduleName, lookupResult.value()[0].first.Kind});
// FIXME: Once all clients know to fetch these dependencies from
// `swiftOverlayDependencies`, the goal is to no longer have them in
// `directDependencies` so the following will need to go away.
directDependencies.insert(
{moduleName, lookupResult.value()[0].first.Kind});
}
directDependencies.insert({moduleName, lookupResult.value()[0].first.Kind});
}
}
}
};
for (const auto &clangDep : clangDependencies)
recordResult(clangDep);

// C++ Interop requires additional handling
if (ScanCompilerInvocation.getLangOptions().EnableCXXInterop) {
for (const auto &clangDepName : clangDependencies) {
// If this Clang module is a part of the C++ stdlib, and we haven't loaded
// the overlay for it so far, it is a split libc++ module (e.g.
// std_vector). Load the CxxStdlib overlay explicitly.
const auto &clangDepInfo =
cache.findDependency(clangDepName, ModuleDependencyKind::Clang)
.value()
->getAsClangModule();
if (importer::isCxxStdModule(clangDepName, clangDepInfo->IsSystem) &&
!swiftOverlayDependencies.contains(
{clangDepName, ModuleDependencyKind::SwiftInterface}) &&
!swiftOverlayDependencies.contains(
{clangDepName, ModuleDependencyKind::SwiftBinary})) {
ScanningThreadPool.async(
scanForSwiftDependency,
getModuleImportIdentifier(ScanASTContext.Id_CxxStdlib.str()));
ScanningThreadPool.wait();
recordResult(ScanASTContext.Id_CxxStdlib.str().str());
}
}
}
}

void ModuleDependencyScanner::discoverCrossImportOverlayDependencies(
Expand Down
6 changes: 6 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,9 @@ validateCxxInteropCompatibilityMode(StringRef mode) {
// Swift 5 is the default language version.
if (mode == "swift-5.9")
return {CxxCompatMode::enabled, version::Version({5})};
// Note: If this is updated, corresponding code in
// InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl needs
// to be updated also.
return {CxxCompatMode::invalid, {}};
}

Expand Down Expand Up @@ -639,6 +642,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.DisableImplicitStringProcessingModuleImport |=
Args.hasArg(OPT_disable_implicit_string_processing_module_import);

Opts.DisableImplicitCxxModuleImport |=
Args.hasArg(OPT_disable_implicit_cxx_module_import);

Opts.DisableImplicitBacktracingModuleImport =
Args.hasFlag(OPT_disable_implicit_backtracing_module_import,
OPT_enable_implicit_backtracing_module_import,
Expand Down
34 changes: 32 additions & 2 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,26 @@ bool CompilerInvocation::shouldImportSwiftBacktracing() const {
FrontendOptions::ParseInputMode::SwiftModuleInterface;
}

bool CompilerInvocation::shouldImportCxx() const {
// C++ Interop is disabled
if (!getLangOptions().EnableCXXInterop)
return false;
// Avoid C++ stdlib when building Swift stdlib
if (getImplicitStdlibKind() == ImplicitStdlibKind::Builtin)
return false;
// Avoid importing Cxx when building Cxx itself
if (getFrontendOptions().ModuleName == CXX_MODULE_NAME)
return false;
// Cxx cannot be imported when Library evolution is enabled
if (getFrontendOptions().EnableLibraryEvolution)
return false;
// Implicit import of Cxx is disabled
if (getLangOptions().DisableImplicitCxxModuleImport)
return false;

return true;
}

/// Implicitly import the SwiftOnoneSupport module in non-optimized
/// builds. This allows for use of popular specialized functions
/// from the standard library, which makes the non-optimized builds
Expand Down Expand Up @@ -1165,6 +1185,13 @@ bool CompilerInstance::canImportSwiftBacktracing() const {
return getASTContext().testImportModule(modulePath);
}

bool CompilerInstance::canImportCxx() const {
ImportPath::Module::Builder builder(
getASTContext().getIdentifier(CXX_MODULE_NAME));
auto modulePath = builder.get();
return getASTContext().testImportModule(modulePath);
}

bool CompilerInstance::canImportCxxShim() const {
ImportPath::Module::Builder builder(
getASTContext().getIdentifier(CXX_SHIM_NAME));
Expand Down Expand Up @@ -1268,8 +1295,11 @@ ImplicitImportInfo CompilerInstance::getImplicitImportInfo() const {
}
}

if (Invocation.getLangOptions().EnableCXXInterop && canImportCxxShim()) {
pushImport(CXX_SHIM_NAME, {ImportFlags::ImplementationOnly});
if (Invocation.getLangOptions().EnableCXXInterop) {
if (Invocation.shouldImportCxx() && canImportCxx())
pushImport(CXX_MODULE_NAME);
if (canImportCxxShim())
pushImport(CXX_SHIM_NAME, {ImportFlags::ImplementationOnly});
}

imports.ShouldImportUnderlyingModule = frontendOpts.ImportUnderlyingModule;
Expand Down
27 changes: 27 additions & 0 deletions lib/Frontend/ModuleInterfaceLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1969,6 +1969,33 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
GenericArgs.push_back("-blocklist-file");
GenericArgs.push_back(blocklist);
}

// For now, we only inherit the C++ interoperability mode in
// Explicit Module Builds.
if (langOpts.EnableCXXInterop &&
(frontendOpts.DisableImplicitModules ||
LoaderOpts.requestedAction ==
FrontendOptions::ActionType::ScanDependencies)) {
// Modelled after a reverse of validateCxxInteropCompatibilityMode
genericSubInvocation.getLangOptions().EnableCXXInterop = true;
genericSubInvocation.getLangOptions().cxxInteropCompatVersion =
langOpts.cxxInteropCompatVersion;
std::string compatVersion;
if (langOpts.cxxInteropCompatVersion.empty())
compatVersion = "default";
else if (langOpts.cxxInteropCompatVersion[0] == 5)
compatVersion = "swift-5.9";
else if (langOpts.cxxInteropCompatVersion[0] == 6)
compatVersion = "swift-6";
else if (langOpts.cxxInteropCompatVersion[0] ==
version::getUpcomingCxxInteropCompatVersion())
compatVersion = "upcoming-swift";
else // TODO: This may need to be updated once more versions are added
compatVersion = "default";

GenericArgs.push_back(
ArgSaver.save("-cxx-interoperability-mode=" + compatVersion));
}
}

/// Calculate an output filename in \p genericSubInvocation's cache path that
Expand Down
Loading