Skip to content

[cxx-interop] Allow compiling with libc++ on Linux #75589

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 1 commit into from
Aug 9, 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
6 changes: 5 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
HasAnyUnavailableDuringLoweringValues : 1
);

SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
/// If the module is compiled as static library.
StaticLibrary : 1,

Expand Down Expand Up @@ -780,6 +780,10 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
/// Whether this module has been built with C++ interoperability enabled.
HasCxxInteroperability : 1,

/// Whether this module uses the platform default C++ stdlib, or an
/// overridden C++ stdlib.
CXXStdlibKind : 8,

/// Whether this module has been built with -allow-non-resilient-access.
AllowNonResilientAccess : 1,

Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/DiagnosticEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ namespace swift {
class ValueDecl;
class SourceFile;

enum class CXXStdlibKind : uint8_t;
enum class DescriptivePatternKind : uint8_t;
enum class SelfAccessKind : uint8_t;
enum class ReferenceOwnership : uint8_t;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,10 @@ ERROR(need_cxx_interop_to_import_module,none,
NOTE(enable_cxx_interop_docs,none,
"visit https://www.swift.org/documentation/cxx-interop/project-build-setup to learn how to enable C++ interoperability", ())

ERROR(cxx_stdlib_kind_mismatch,none,
"module %0 was built with %1, but current compilation uses %2",
(Identifier, StringRef, StringRef))

ERROR(modularization_issue_decl_moved,Fatal,
"reference to %select{top-level declaration|type}0 %1 broken by a context change; "
"%1 was expected to be in %2, but now a candidate is found only in %3",
Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "swift/AST/Type.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/BasicSourceInfo.h"
#include "swift/Basic/CXXStdlibKind.h"
#include "swift/Basic/Compiler.h"
#include "swift/Basic/Debug.h"
#include "swift/Basic/OptionSet.h"
Expand Down Expand Up @@ -705,6 +706,13 @@ class ModuleDecl
Bits.ModuleDecl.HasCxxInteroperability = enabled;
}

CXXStdlibKind getCXXStdlibKind() const {
return static_cast<CXXStdlibKind>(Bits.ModuleDecl.CXXStdlibKind);
}
void setCXXStdlibKind(CXXStdlibKind kind) {
Bits.ModuleDecl.CXXStdlibKind = static_cast<uint8_t>(kind);
}

/// \returns true if this module is a system module; note that the StdLib is
/// considered a system module.
bool isSystemModule() const {
Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/ModuleDependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/AST/Import.h"
#include "swift/AST/LinkLibrary.h"
#include "swift/AST/SearchPathOptions.h"
#include "swift/Basic/CXXStdlibKind.h"
#include "swift/Basic/LLVM.h"
#include "clang/CAS/CASOptions.h"
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
Expand Down Expand Up @@ -135,7 +136,7 @@ void registerBackDeployLibraries(
std::function<void(const LinkLibrary &)> RegistrationCallback);
void registerCxxInteropLibraries(
const llvm::Triple &Target, StringRef mainModuleName, bool hasStaticCxx,
bool hasStaticCxxStdlib,
bool hasStaticCxxStdlib, CXXStdlibKind cxxStdlibKind,
std::function<void(const LinkLibrary &)> RegistrationCallback);
} // namespace dependencies

Expand Down
48 changes: 48 additions & 0 deletions include/swift/Basic/CXXStdlibKind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===--- CXXStdlibKind.h ----------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_CXX_STDLIB_KIND_H
#define SWIFT_BASIC_CXX_STDLIB_KIND_H

namespace swift {

enum class CXXStdlibKind : uint8_t {
Unknown = 0,

/// libc++ is the default C++ stdlib on Darwin platforms. It is also supported
/// on Linux when explicitly requested via `-Xcc -stdlib=libc++` flag.
Libcxx = 1,

/// libstdc++ is the default C++ stdlib on most Linux distributions.
Libstdcxx = 2,

/// msvcprt is used when targeting Windows.
Msvcprt = 3,
};

inline std::string to_string(CXXStdlibKind kind) {
switch (kind) {
case CXXStdlibKind::Unknown:
return "unknown C++ stdlib";
case CXXStdlibKind::Libcxx:
return "libc++";
case CXXStdlibKind::Libstdcxx:
return "libstdc++";
case CXXStdlibKind::Msvcprt:
return "msvcprt";
}
llvm_unreachable("unhandled CXXStdlibKind");
}

} // namespace swift

#endif // SWIFT_BASIC_CXX_STDLIB_KIND_H
11 changes: 11 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define SWIFT_BASIC_LANGOPTIONS_H

#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/CXXStdlibKind.h"
#include "swift/Basic/Feature.h"
#include "swift/Basic/FixedBitSet.h"
#include "swift/Basic/FunctionBodySkipping.h"
Expand Down Expand Up @@ -321,6 +322,16 @@ namespace swift {
void setCxxInteropFromArgs(llvm::opt::ArgList &Args,
swift::DiagnosticEngine &Diags);

/// The C++ standard library used for the current build. This can differ
/// from the default C++ stdlib on a particular platform when `-Xcc
/// -stdlib=xyz` was passed to the compiler.
CXXStdlibKind CXXStdlib = CXXStdlibKind::Unknown;
CXXStdlibKind PlatformDefaultCXXStdlib = CXXStdlibKind::Unknown;

bool isUsingPlatformDefaultCXXStdlib() const {
return CXXStdlib == PlatformDefaultCXXStdlib;
}

bool CForeignReferenceTypes = false;

/// Imports getters and setters as computed properties.
Expand Down
24 changes: 24 additions & 0 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ namespace llvm {
class Triple;
class FileCollectorBase;
template<typename Fn> class function_ref;
namespace opt {
class InputArgList;
}
namespace vfs {
class FileSystem;
class OutputBackend;
Expand All @@ -51,6 +54,9 @@ namespace clang {
class DeclarationName;
class CompilerInvocation;
class TargetOptions;
namespace driver {
class Driver;
}
namespace tooling {
namespace dependencies {
struct ModuleDeps;
Expand All @@ -74,9 +80,11 @@ class EnumDecl;
class FuncDecl;
class ImportDecl;
class IRGenOptions;
class LangOptions;
class ModuleDecl;
struct ModuleDependencyID;
class NominalTypeDecl;
class SearchPathOptions;
class StructDecl;
class SwiftLookupTable;
class TypeDecl;
Expand Down Expand Up @@ -196,6 +204,22 @@ class ClangImporter final : public ClangModuleLoader {
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
const std::vector<std::string> &CC1Args);

/// Creates a Clang Driver based on the Swift compiler options.
///
/// \return a pair of the Clang Driver and the diagnostic engine, which needs
/// to be alive during the use of the Driver.
static std::pair<clang::driver::Driver,
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine>>
createClangDriver(
const LangOptions &LangOpts,
const ClangImporterOptions &ClangImporterOpts,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = nullptr);

static llvm::opt::InputArgList
createClangArgs(const ClangImporterOptions &ClangImporterOpts,
const SearchPathOptions &SearchPathOpts,
clang::driver::Driver &clangDriver);

ClangImporter(const ClangImporter &) = delete;
ClangImporter(ClangImporter &&) = delete;
ClangImporter &operator=(const ClangImporter &) = delete;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Frontend/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ class CompilerInvocation {
/// FIXME: Remove this after all the clients start sending it.
void setDefaultInProcessPluginServerPathIfNecessary();

/// Determine which C++ stdlib should be used for this compilation, and which
/// C++ stdlib is the default for the specified target.
void computeCXXStdlibOptions();

/// Computes the runtime resource path relative to the given Swift
/// executable.
static void computeRuntimeResourcePathFromExecutablePath(
Expand Down
5 changes: 5 additions & 0 deletions include/swift/Serialization/Validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#define SWIFT_SERIALIZATION_VALIDATION_H

#include "swift/AST/Identifier.h"
#include "swift/Basic/CXXStdlibKind.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Version.h"
#include "swift/Serialization/SerializationOptions.h"
Expand Down Expand Up @@ -128,6 +129,7 @@ class ExtendedValidationInfo {
StringRef ModuleABIName;
StringRef ModulePackageName;
StringRef ExportAsName;
CXXStdlibKind CXXStdlib;
struct {
unsigned ArePrivateImportsEnabled : 1;
unsigned IsSIB : 1;
Expand Down Expand Up @@ -241,6 +243,9 @@ class ExtendedValidationInfo {
void setHasCxxInteroperability(bool val) {
Bits.HasCxxInteroperability = val;
}

CXXStdlibKind getCXXStdlibKind() const { return CXXStdlib; }
void setCXXStdlibKind(CXXStdlibKind kind) { CXXStdlib = kind; }
};

struct SearchPath {
Expand Down
1 change: 1 addition & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx,
Bits.ModuleDecl.IsConcurrencyChecked = 0;
Bits.ModuleDecl.ObjCNameLookupCachePopulated = 0;
Bits.ModuleDecl.HasCxxInteroperability = 0;
Bits.ModuleDecl.CXXStdlibKind = 0;
Bits.ModuleDecl.AllowNonResilientAccess = 0;
Bits.ModuleDecl.SerializePackageEnabled = 0;
}
Expand Down
6 changes: 3 additions & 3 deletions lib/AST/ModuleDependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,11 +540,11 @@ void
swift::dependencies::registerCxxInteropLibraries(
const llvm::Triple &Target,
StringRef mainModuleName,
bool hasStaticCxx, bool hasStaticCxxStdlib,
bool hasStaticCxx, bool hasStaticCxxStdlib, CXXStdlibKind cxxStdlibKind,
std::function<void(const LinkLibrary&)> RegistrationCallback) {
if (Target.isOSDarwin())
if (cxxStdlibKind == CXXStdlibKind::Libcxx)
RegistrationCallback(LinkLibrary("c++", LibraryKind::Library));
else if (Target.isOSLinux())
else if (cxxStdlibKind == CXXStdlibKind::Libstdcxx)
RegistrationCallback(LinkLibrary("stdc++", LibraryKind::Library));

// Do not try to link Cxx with itself.
Expand Down
6 changes: 6 additions & 0 deletions lib/AST/ModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ void ModuleLoader::findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module,
using namespace llvm::sys;
using namespace file_types;

// If an overlay for CxxStdlib was requested, only proceed if compiling with
// the platform-default C++ stdlib.
if (module->getName() == module->getASTContext().Id_CxxStdlib &&
!module->getASTContext().LangOpts.isUsingPlatformDefaultCXXStdlib())
return;

// If cross import information is passed on command-line, prefer use that.
auto &crossImports = module->getASTContext().SearchPathOpts.CrossImportInfo;
auto overlays = crossImports.find(module->getNameStr());
Expand Down
6 changes: 4 additions & 2 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4298,8 +4298,10 @@ ModuleDecl *ClangModuleUnit::getOverlayModule() const {
}
// 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.
if (!overlay && importer::isCxxStdModule(clangModule)) {
// Load the CxxStdlib overlay explicitly, if building with the
// platform-default C++ stdlib.
if (!overlay && importer::isCxxStdModule(clangModule) &&
Ctx.LangOpts.isUsingPlatformDefaultCXXStdlib()) {
ImportPath::Module::Builder builder(Ctx.Id_CxxStdlib);
overlay = owner.loadModule(SourceLoc(), std::move(builder).get());
}
Expand Down
51 changes: 33 additions & 18 deletions lib/ClangImporter/ClangIncludePaths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,18 @@ parseClangDriverArgs(const clang::driver::Driver &clangDriver,
return clangDriver.getOpts().ParseArgs(args, unused1, unused2);
}

static clang::driver::Driver
createClangDriver(const ASTContext &ctx,
const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &vfs) {
std::pair<clang::driver::Driver,
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine>>
ClangImporter::createClangDriver(
const LangOptions &LangOpts, const ClangImporterOptions &ClangImporterOpts,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
auto *silentDiagConsumer = new clang::DiagnosticConsumer();
auto clangDiags = clang::CompilerInstance::createDiagnostics(
new clang::DiagnosticOptions());
clang::driver::Driver clangDriver(ctx.ClangImporterOpts.clangPath,
ctx.LangOpts.Target.str(), *clangDiags,
new clang::DiagnosticOptions(), silentDiagConsumer);
clang::driver::Driver clangDriver(ClangImporterOpts.clangPath,
LangOpts.Target.str(), *clangDiags,
"clang LLVM compiler", vfs);
return clangDriver;
return {std::move(clangDriver), clangDiags};
}

/// Given a list of include paths and a list of file names, finds the first
Expand Down Expand Up @@ -162,21 +165,23 @@ static std::optional<Path> findFirstIncludeDir(
return std::nullopt;
}

static llvm::opt::InputArgList
createClangArgs(const ASTContext &ctx, clang::driver::Driver &clangDriver) {
llvm::opt::InputArgList
ClangImporter::createClangArgs(const ClangImporterOptions &ClangImporterOpts,
const SearchPathOptions &SearchPathOpts,
clang::driver::Driver &clangDriver) {
// Flags passed to Swift with `-Xcc` might affect include paths.
std::vector<const char *> clangArgs;
for (const auto &each : ctx.ClangImporterOpts.ExtraArgs) {
for (const auto &each : ClangImporterOpts.ExtraArgs) {
clangArgs.push_back(each.c_str());
}
llvm::opt::InputArgList clangDriverArgs =
parseClangDriverArgs(clangDriver, clangArgs);
// If an SDK path was explicitly passed to Swift, make sure to pass it to
// Clang driver as well. It affects the resulting include paths.
auto sdkPath = ctx.SearchPathOpts.getSDKPath();
auto sdkPath = SearchPathOpts.getSDKPath();
if (!sdkPath.empty())
clangDriver.SysRoot = sdkPath.str();
if (auto sysroot = ctx.SearchPathOpts.getSysRoot())
if (auto sysroot = SearchPathOpts.getSysRoot())
clangDriver.SysRoot = sysroot->str();
return clangDriverArgs;
}
Expand All @@ -188,8 +193,10 @@ getLibcFileMapping(ASTContext &ctx, StringRef modulemapFileName,
const llvm::Triple &triple = ctx.LangOpts.Target;

// Extract the libc path from Clang driver.
auto clangDriver = createClangDriver(ctx, vfs);
auto clangDriverArgs = createClangArgs(ctx, clangDriver);
auto [clangDriver, clangDiagEngine] = ClangImporter::createClangDriver(
ctx.LangOpts, ctx.ClangImporterOpts, vfs);
auto clangDriverArgs = ClangImporter::createClangArgs(
ctx.ClangImporterOpts, ctx.SearchPathOpts, clangDriver);

llvm::opt::ArgStringList includeArgStrings;
const auto &clangToolchain =
Expand Down Expand Up @@ -256,10 +263,16 @@ static void getLibStdCxxFileMapping(
if (triple.isAndroid()
|| (triple.isMusl() && triple.getVendor() == llvm::Triple::Swift))
return;
// Make sure we are building with libstdc++. On platforms where libstdc++ is
// the default C++ stdlib, users can still compile with `-Xcc -stdlib=libc++`.
if (ctx.LangOpts.CXXStdlib != CXXStdlibKind::Libstdcxx)
return;

// Extract the libstdc++ installation path from Clang driver.
auto clangDriver = createClangDriver(ctx, vfs);
auto clangDriverArgs = createClangArgs(ctx, clangDriver);
auto [clangDriver, clangDiagEngine] = ClangImporter::createClangDriver(
ctx.LangOpts, ctx.ClangImporterOpts, vfs);
auto clangDriverArgs = ClangImporter::createClangArgs(
ctx.ClangImporterOpts, ctx.SearchPathOpts, clangDriver);

llvm::opt::ArgStringList stdlibArgStrings;
const auto &clangToolchain =
Expand Down Expand Up @@ -453,8 +466,10 @@ SmallVector<std::pair<std::string, std::string>, 2> GetWindowsFileMappings(
if (!Triple.isWindowsMSVCEnvironment())
return Mappings;

clang::driver::Driver Driver = createClangDriver(Context, driverVFS);
const llvm::opt::InputArgList Args = createClangArgs(Context, Driver);
auto [Driver, clangDiagEngine] = ClangImporter::createClangDriver(
Context.LangOpts, Context.ClangImporterOpts, driverVFS);
const llvm::opt::InputArgList Args = ClangImporter::createClangArgs(
Context.ClangImporterOpts, Context.SearchPathOpts, Driver);
const clang::driver::ToolChain &ToolChain = Driver.getToolChain(Args, Triple);
llvm::vfs::FileSystem &VFS = ToolChain.getVFS();

Expand Down
Loading