Skip to content

Commit bfe72b4

Browse files
authored
Merge pull request #75589 from swiftlang/egorzhdan/linux-libcxx-interop
[cxx-interop] Allow compiling with libc++ on Linux
2 parents a1eed1f + 059f0f9 commit bfe72b4

31 files changed

+348
-28
lines changed

include/swift/AST/Decl.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
722722
HasAnyUnavailableDuringLoweringValues : 1
723723
);
724724

725-
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1,
725+
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,
726726
/// If the module is compiled as static library.
727727
StaticLibrary : 1,
728728

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

783+
/// Whether this module uses the platform default C++ stdlib, or an
784+
/// overridden C++ stdlib.
785+
CXXStdlibKind : 8,
786+
783787
/// Whether this module has been built with -allow-non-resilient-access.
784788
AllowNonResilientAccess : 1,
785789

include/swift/AST/DiagnosticEngine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ namespace swift {
5050
class ValueDecl;
5151
class SourceFile;
5252

53+
enum class CXXStdlibKind : uint8_t;
5354
enum class DescriptivePatternKind : uint8_t;
5455
enum class SelfAccessKind : uint8_t;
5556
enum class ReferenceOwnership : uint8_t;

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,10 @@ ERROR(need_cxx_interop_to_import_module,none,
942942
NOTE(enable_cxx_interop_docs,none,
943943
"visit https://www.swift.org/documentation/cxx-interop/project-build-setup to learn how to enable C++ interoperability", ())
944944

945+
ERROR(cxx_stdlib_kind_mismatch,none,
946+
"module %0 was built with %1, but current compilation uses %2",
947+
(Identifier, StringRef, StringRef))
948+
945949
ERROR(modularization_issue_decl_moved,Fatal,
946950
"reference to %select{top-level declaration|type}0 %1 broken by a context change; "
947951
"%1 was expected to be in %2, but now a candidate is found only in %3",

include/swift/AST/Module.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "swift/AST/Type.h"
2828
#include "swift/Basic/Assertions.h"
2929
#include "swift/Basic/BasicSourceInfo.h"
30+
#include "swift/Basic/CXXStdlibKind.h"
3031
#include "swift/Basic/Compiler.h"
3132
#include "swift/Basic/Debug.h"
3233
#include "swift/Basic/OptionSet.h"
@@ -705,6 +706,13 @@ class ModuleDecl
705706
Bits.ModuleDecl.HasCxxInteroperability = enabled;
706707
}
707708

709+
CXXStdlibKind getCXXStdlibKind() const {
710+
return static_cast<CXXStdlibKind>(Bits.ModuleDecl.CXXStdlibKind);
711+
}
712+
void setCXXStdlibKind(CXXStdlibKind kind) {
713+
Bits.ModuleDecl.CXXStdlibKind = static_cast<uint8_t>(kind);
714+
}
715+
708716
/// \returns true if this module is a system module; note that the StdLib is
709717
/// considered a system module.
710718
bool isSystemModule() const {

include/swift/AST/ModuleDependencies.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/AST/Import.h"
2222
#include "swift/AST/LinkLibrary.h"
2323
#include "swift/AST/SearchPathOptions.h"
24+
#include "swift/Basic/CXXStdlibKind.h"
2425
#include "swift/Basic/LLVM.h"
2526
#include "clang/CAS/CASOptions.h"
2627
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
@@ -135,7 +136,7 @@ void registerBackDeployLibraries(
135136
std::function<void(const LinkLibrary &)> RegistrationCallback);
136137
void registerCxxInteropLibraries(
137138
const llvm::Triple &Target, StringRef mainModuleName, bool hasStaticCxx,
138-
bool hasStaticCxxStdlib,
139+
bool hasStaticCxxStdlib, CXXStdlibKind cxxStdlibKind,
139140
std::function<void(const LinkLibrary &)> RegistrationCallback);
140141
} // namespace dependencies
141142

include/swift/Basic/CXXStdlibKind.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===--- CXXStdlibKind.h ----------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_BASIC_CXX_STDLIB_KIND_H
14+
#define SWIFT_BASIC_CXX_STDLIB_KIND_H
15+
16+
namespace swift {
17+
18+
enum class CXXStdlibKind : uint8_t {
19+
Unknown = 0,
20+
21+
/// libc++ is the default C++ stdlib on Darwin platforms. It is also supported
22+
/// on Linux when explicitly requested via `-Xcc -stdlib=libc++` flag.
23+
Libcxx = 1,
24+
25+
/// libstdc++ is the default C++ stdlib on most Linux distributions.
26+
Libstdcxx = 2,
27+
28+
/// msvcprt is used when targeting Windows.
29+
Msvcprt = 3,
30+
};
31+
32+
inline std::string to_string(CXXStdlibKind kind) {
33+
switch (kind) {
34+
case CXXStdlibKind::Unknown:
35+
return "unknown C++ stdlib";
36+
case CXXStdlibKind::Libcxx:
37+
return "libc++";
38+
case CXXStdlibKind::Libstdcxx:
39+
return "libstdc++";
40+
case CXXStdlibKind::Msvcprt:
41+
return "msvcprt";
42+
}
43+
llvm_unreachable("unhandled CXXStdlibKind");
44+
}
45+
46+
} // namespace swift
47+
48+
#endif // SWIFT_BASIC_CXX_STDLIB_KIND_H

include/swift/Basic/LangOptions.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define SWIFT_BASIC_LANGOPTIONS_H
2020

2121
#include "swift/AST/DiagnosticsFrontend.h"
22+
#include "swift/Basic/CXXStdlibKind.h"
2223
#include "swift/Basic/Feature.h"
2324
#include "swift/Basic/FixedBitSet.h"
2425
#include "swift/Basic/FunctionBodySkipping.h"
@@ -325,6 +326,16 @@ namespace swift {
325326
void setCxxInteropFromArgs(llvm::opt::ArgList &Args,
326327
swift::DiagnosticEngine &Diags);
327328

329+
/// The C++ standard library used for the current build. This can differ
330+
/// from the default C++ stdlib on a particular platform when `-Xcc
331+
/// -stdlib=xyz` was passed to the compiler.
332+
CXXStdlibKind CXXStdlib = CXXStdlibKind::Unknown;
333+
CXXStdlibKind PlatformDefaultCXXStdlib = CXXStdlibKind::Unknown;
334+
335+
bool isUsingPlatformDefaultCXXStdlib() const {
336+
return CXXStdlib == PlatformDefaultCXXStdlib;
337+
}
338+
328339
bool CForeignReferenceTypes = false;
329340

330341
/// Imports getters and setters as computed properties.

include/swift/ClangImporter/ClangImporter.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ namespace llvm {
2626
class Triple;
2727
class FileCollectorBase;
2828
template<typename Fn> class function_ref;
29+
namespace opt {
30+
class InputArgList;
31+
}
2932
namespace vfs {
3033
class FileSystem;
3134
class OutputBackend;
@@ -51,6 +54,9 @@ namespace clang {
5154
class DeclarationName;
5255
class CompilerInvocation;
5356
class TargetOptions;
57+
namespace driver {
58+
class Driver;
59+
}
5460
namespace tooling {
5561
namespace dependencies {
5662
struct ModuleDeps;
@@ -74,9 +80,11 @@ class EnumDecl;
7480
class FuncDecl;
7581
class ImportDecl;
7682
class IRGenOptions;
83+
class LangOptions;
7784
class ModuleDecl;
7885
struct ModuleDependencyID;
7986
class NominalTypeDecl;
87+
class SearchPathOptions;
8088
class StructDecl;
8189
class SwiftLookupTable;
8290
class TypeDecl;
@@ -196,6 +204,22 @@ class ClangImporter final : public ClangModuleLoader {
196204
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
197205
const std::vector<std::string> &CC1Args);
198206

207+
/// Creates a Clang Driver based on the Swift compiler options.
208+
///
209+
/// \return a pair of the Clang Driver and the diagnostic engine, which needs
210+
/// to be alive during the use of the Driver.
211+
static std::pair<clang::driver::Driver,
212+
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine>>
213+
createClangDriver(
214+
const LangOptions &LangOpts,
215+
const ClangImporterOptions &ClangImporterOpts,
216+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = nullptr);
217+
218+
static llvm::opt::InputArgList
219+
createClangArgs(const ClangImporterOptions &ClangImporterOpts,
220+
const SearchPathOptions &SearchPathOpts,
221+
clang::driver::Driver &clangDriver);
222+
199223
ClangImporter(const ClangImporter &) = delete;
200224
ClangImporter(ClangImporter &&) = delete;
201225
ClangImporter &operator=(const ClangImporter &) = delete;

include/swift/Frontend/Frontend.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ class CompilerInvocation {
256256
/// FIXME: Remove this after all the clients start sending it.
257257
void setDefaultInProcessPluginServerPathIfNecessary();
258258

259+
/// Determine which C++ stdlib should be used for this compilation, and which
260+
/// C++ stdlib is the default for the specified target.
261+
void computeCXXStdlibOptions();
262+
259263
/// Computes the runtime resource path relative to the given Swift
260264
/// executable.
261265
static void computeRuntimeResourcePathFromExecutablePath(

include/swift/Serialization/Validation.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_SERIALIZATION_VALIDATION_H
1515

1616
#include "swift/AST/Identifier.h"
17+
#include "swift/Basic/CXXStdlibKind.h"
1718
#include "swift/Basic/LLVM.h"
1819
#include "swift/Basic/Version.h"
1920
#include "swift/Serialization/SerializationOptions.h"
@@ -128,6 +129,7 @@ class ExtendedValidationInfo {
128129
StringRef ModuleABIName;
129130
StringRef ModulePackageName;
130131
StringRef ExportAsName;
132+
CXXStdlibKind CXXStdlib;
131133
struct {
132134
unsigned ArePrivateImportsEnabled : 1;
133135
unsigned IsSIB : 1;
@@ -241,6 +243,9 @@ class ExtendedValidationInfo {
241243
void setHasCxxInteroperability(bool val) {
242244
Bits.HasCxxInteroperability = val;
243245
}
246+
247+
CXXStdlibKind getCXXStdlibKind() const { return CXXStdlib; }
248+
void setCXXStdlibKind(CXXStdlibKind kind) { CXXStdlib = kind; }
244249
};
245250

246251
struct SearchPath {

lib/AST/Module.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx,
725725
Bits.ModuleDecl.IsConcurrencyChecked = 0;
726726
Bits.ModuleDecl.ObjCNameLookupCachePopulated = 0;
727727
Bits.ModuleDecl.HasCxxInteroperability = 0;
728+
Bits.ModuleDecl.CXXStdlibKind = 0;
728729
Bits.ModuleDecl.AllowNonResilientAccess = 0;
729730
Bits.ModuleDecl.SerializePackageEnabled = 0;
730731
}

lib/AST/ModuleDependencies.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,11 @@ void
541541
swift::dependencies::registerCxxInteropLibraries(
542542
const llvm::Triple &Target,
543543
StringRef mainModuleName,
544-
bool hasStaticCxx, bool hasStaticCxxStdlib,
544+
bool hasStaticCxx, bool hasStaticCxxStdlib, CXXStdlibKind cxxStdlibKind,
545545
std::function<void(const LinkLibrary&)> RegistrationCallback) {
546-
if (Target.isOSDarwin())
546+
if (cxxStdlibKind == CXXStdlibKind::Libcxx)
547547
RegistrationCallback(LinkLibrary("c++", LibraryKind::Library));
548-
else if (Target.isOSLinux())
548+
else if (cxxStdlibKind == CXXStdlibKind::Libstdcxx)
549549
RegistrationCallback(LinkLibrary("stdc++", LibraryKind::Library));
550550

551551
// Do not try to link Cxx with itself.

lib/AST/ModuleLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ void ModuleLoader::findOverlayFiles(SourceLoc diagLoc, ModuleDecl *module,
172172
using namespace llvm::sys;
173173
using namespace file_types;
174174

175+
// If an overlay for CxxStdlib was requested, only proceed if compiling with
176+
// the platform-default C++ stdlib.
177+
if (module->getName() == module->getASTContext().Id_CxxStdlib &&
178+
!module->getASTContext().LangOpts.isUsingPlatformDefaultCXXStdlib())
179+
return;
180+
175181
// If cross import information is passed on command-line, prefer use that.
176182
auto &crossImports = module->getASTContext().SearchPathOpts.CrossImportInfo;
177183
auto overlays = crossImports.find(module->getNameStr());

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4296,8 +4296,10 @@ ModuleDecl *ClangModuleUnit::getOverlayModule() const {
42964296
}
42974297
// If this Clang module is a part of the C++ stdlib, and we haven't loaded
42984298
// the overlay for it so far, it is a split libc++ module (e.g. std_vector).
4299-
// Load the CxxStdlib overlay explicitly.
4300-
if (!overlay && importer::isCxxStdModule(clangModule)) {
4299+
// Load the CxxStdlib overlay explicitly, if building with the
4300+
// platform-default C++ stdlib.
4301+
if (!overlay && importer::isCxxStdModule(clangModule) &&
4302+
Ctx.LangOpts.isUsingPlatformDefaultCXXStdlib()) {
43014303
ImportPath::Module::Builder builder(Ctx.Id_CxxStdlib);
43024304
overlay = owner.loadModule(SourceLoc(), std::move(builder).get());
43034305
}

lib/ClangImporter/ClangIncludePaths.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,18 @@ parseClangDriverArgs(const clang::driver::Driver &clangDriver,
112112
return clangDriver.getOpts().ParseArgs(args, unused1, unused2);
113113
}
114114

115-
static clang::driver::Driver
116-
createClangDriver(const ASTContext &ctx,
117-
const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &vfs) {
115+
std::pair<clang::driver::Driver,
116+
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine>>
117+
ClangImporter::createClangDriver(
118+
const LangOptions &LangOpts, const ClangImporterOptions &ClangImporterOpts,
119+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
120+
auto *silentDiagConsumer = new clang::DiagnosticConsumer();
118121
auto clangDiags = clang::CompilerInstance::createDiagnostics(
119-
new clang::DiagnosticOptions());
120-
clang::driver::Driver clangDriver(ctx.ClangImporterOpts.clangPath,
121-
ctx.LangOpts.Target.str(), *clangDiags,
122+
new clang::DiagnosticOptions(), silentDiagConsumer);
123+
clang::driver::Driver clangDriver(ClangImporterOpts.clangPath,
124+
LangOpts.Target.str(), *clangDiags,
122125
"clang LLVM compiler", vfs);
123-
return clangDriver;
126+
return {std::move(clangDriver), clangDiags};
124127
}
125128

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

165-
static llvm::opt::InputArgList
166-
createClangArgs(const ASTContext &ctx, clang::driver::Driver &clangDriver) {
168+
llvm::opt::InputArgList
169+
ClangImporter::createClangArgs(const ClangImporterOptions &ClangImporterOpts,
170+
const SearchPathOptions &SearchPathOpts,
171+
clang::driver::Driver &clangDriver) {
167172
// Flags passed to Swift with `-Xcc` might affect include paths.
168173
std::vector<const char *> clangArgs;
169-
for (const auto &each : ctx.ClangImporterOpts.ExtraArgs) {
174+
for (const auto &each : ClangImporterOpts.ExtraArgs) {
170175
clangArgs.push_back(each.c_str());
171176
}
172177
llvm::opt::InputArgList clangDriverArgs =
173178
parseClangDriverArgs(clangDriver, clangArgs);
174179
// If an SDK path was explicitly passed to Swift, make sure to pass it to
175180
// Clang driver as well. It affects the resulting include paths.
176-
auto sdkPath = ctx.SearchPathOpts.getSDKPath();
181+
auto sdkPath = SearchPathOpts.getSDKPath();
177182
if (!sdkPath.empty())
178183
clangDriver.SysRoot = sdkPath.str();
179-
if (auto sysroot = ctx.SearchPathOpts.getSysRoot())
184+
if (auto sysroot = SearchPathOpts.getSysRoot())
180185
clangDriver.SysRoot = sysroot->str();
181186
return clangDriverArgs;
182187
}
@@ -188,8 +193,10 @@ getLibcFileMapping(ASTContext &ctx, StringRef modulemapFileName,
188193
const llvm::Triple &triple = ctx.LangOpts.Target;
189194

190195
// Extract the libc path from Clang driver.
191-
auto clangDriver = createClangDriver(ctx, vfs);
192-
auto clangDriverArgs = createClangArgs(ctx, clangDriver);
196+
auto [clangDriver, clangDiagEngine] = ClangImporter::createClangDriver(
197+
ctx.LangOpts, ctx.ClangImporterOpts, vfs);
198+
auto clangDriverArgs = ClangImporter::createClangArgs(
199+
ctx.ClangImporterOpts, ctx.SearchPathOpts, clangDriver);
193200

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

260271
// Extract the libstdc++ installation path from Clang driver.
261-
auto clangDriver = createClangDriver(ctx, vfs);
262-
auto clangDriverArgs = createClangArgs(ctx, clangDriver);
272+
auto [clangDriver, clangDiagEngine] = ClangImporter::createClangDriver(
273+
ctx.LangOpts, ctx.ClangImporterOpts, vfs);
274+
auto clangDriverArgs = ClangImporter::createClangArgs(
275+
ctx.ClangImporterOpts, ctx.SearchPathOpts, clangDriver);
263276

264277
llvm::opt::ArgStringList stdlibArgStrings;
265278
const auto &clangToolchain =
@@ -430,8 +443,10 @@ SmallVector<std::pair<std::string, std::string>, 2> GetWindowsFileMappings(
430443
if (!Triple.isWindowsMSVCEnvironment())
431444
return Mappings;
432445

433-
clang::driver::Driver Driver = createClangDriver(Context, driverVFS);
434-
const llvm::opt::InputArgList Args = createClangArgs(Context, Driver);
446+
auto [Driver, clangDiagEngine] = ClangImporter::createClangDriver(
447+
Context.LangOpts, Context.ClangImporterOpts, driverVFS);
448+
const llvm::opt::InputArgList Args = ClangImporter::createClangArgs(
449+
Context.ClangImporterOpts, Context.SearchPathOpts, Driver);
435450
const clang::driver::ToolChain &ToolChain = Driver.getToolChain(Args, Triple);
436451
llvm::vfs::FileSystem &VFS = ToolChain.getVFS();
437452

0 commit comments

Comments
 (0)