Skip to content

Commit 4627431

Browse files
committed
Link against the C++ standard library when C++ interop is enabled.
This doesn't yet allow including C++ headers on platforms where libc++ isn't the default; see comments in UnixToolChains.cpp for details. However, it does, for example, allow throwing and catching exceptions in C++ code used through interop, unblocking https://github.com/apple/swift/pull/30674/files.
1 parent 4265835 commit 4627431

File tree

6 files changed

+74
-34
lines changed

6 files changed

+74
-34
lines changed

include/swift/Driver/ToolChain.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ class ToolChain {
105105

106106
const char *computeFrontendModeForCompile() const;
107107

108+
bool cxxInteropEnabled() const;
109+
108110
void addFrontendInputAndOutputArguments(
109111
llvm::opt::ArgStringList &Arguments,
110112
std::vector<FilelistInfo> &FilelistInfos) const;

lib/Driver/DarwinToolChains.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,11 @@ toolchains::Darwin::constructInvocation(const DynamicLinkJobAction &job,
712712
Arguments.push_back("-arch");
713713
Arguments.push_back(context.Args.MakeArgString(getTriple().getArchName()));
714714

715+
if (context.cxxInteropEnabled()) {
716+
// We only support libc++ on Darwin.
717+
Arguments.push_back("-lc++");
718+
}
719+
715720
addArgsToLinkStdlib(Arguments, job, context);
716721

717722
addProfileGenerationArgs(Arguments, context);

lib/Driver/ToolChain.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@ ToolChain::JobContext::getTemporaryFilePath(const llvm::Twine &name,
6767
return C.getArgs().MakeArgString(buffer.str());
6868
}
6969

70+
bool
71+
ToolChain::JobContext::cxxInteropEnabled() const {
72+
// TODO: Eventually, we'll want to have a driver flag to control C++ interop,
73+
// but for the time being, we just query the frontend flag.
74+
for (const Arg *A : Args.filtered(options::OPT_Xfrontend)) {
75+
if (A->containsValue("-enable-cxx-interop")) {
76+
return true;
77+
}
78+
}
79+
80+
return false;
81+
}
82+
7083
Optional<Job::ResponseFileInfo>
7184
ToolChain::getResponseFileInfo(const Compilation &C, const char *executablePath,
7285
const ToolChain::InvocationInfo &invocationInfo,

lib/Driver/UnixToolChains.cpp

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -182,27 +182,30 @@ toolchains::GenericUnix::constructInvocation(const DynamicLinkJobAction &job,
182182

183183
// Configure the toolchain.
184184
//
185-
// By default use the system `clang` to perform the link. We use `clang` for
186-
// the driver here because we do not wish to select a particular C++ runtime.
187-
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
188-
// C++ code from pure Swift code. If linked libraries are C++ based, they
189-
// should properly link C++. In the case of static linking, the user can
190-
// explicitly specify the C++ runtime to link against. This is particularly
191-
// important for platforms like android where as it is a Linux platform, the
192-
// default C++ runtime is `libstdc++` which is unsupported on the target but
193-
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
194-
// be present. This results in linking the wrong version of libstdc++
195-
// generating invalid binaries. It is also possible to use different C++
196-
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
197-
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
198-
// surface this via a driver flag. For now, opt for the simpler approach of
199-
// just using `clang` and avoid a dependency on the C++ runtime.
200-
const char *Clang = "clang";
185+
// We use `clang++` if C++ interop is enabled, `clang` otherwise.
186+
//
187+
// We don't use `clang++` unconditionally because we want to avoid pulling in
188+
// a C++ standard library if it's not needed, in particular because the
189+
// standard library that `clang++` selects by default may not be the one that
190+
// is desired.
191+
//
192+
// TODO: In principle, it should be possible to use a different C++ standard
193+
// library than the one configured by default by passing a `-stdlib` option
194+
// to `-Xcc` and `-Xclang-linker`, e.g.
195+
// `-Xcc -stdlib=libc++ -Xclang-linker -stdlib=libc++`.
196+
// Once there is a driver flag for C++ interop, we will probably also want to
197+
// add a driver flag for selecting the C++ standard library.
198+
//
199+
// In practice, using libc++ on Linux, for example, does not work because the
200+
// SwiftGlibc module definition is incompatible with libc++'s header layout.
201+
// We probably need to ensure that we use libc++'s own module map instead of
202+
// the SwiftGlibc module map.
203+
const char *Clang = context.cxxInteropEnabled()? "clang++" : "clang";
201204
if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) {
202205
StringRef toolchainPath(A->getValue());
203206

204207
// If there is a clang in the toolchain folder, use that instead.
205-
if (auto tool = llvm::sys::findProgramByName("clang", {toolchainPath})) {
208+
if (auto tool = llvm::sys::findProgramByName(Clang, {toolchainPath})) {
206209
Clang = context.Args.MakeArgString(tool.get());
207210
}
208211

lib/Driver/WindowsToolChains.cpp

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,27 +86,13 @@ toolchains::Windows::constructInvocation(const DynamicLinkJobAction &job,
8686

8787
// Configure the toolchain.
8888
//
89-
// By default use the system `clang` to perform the link. We use `clang` for
90-
// the driver here because we do not wish to select a particular C++ runtime.
91-
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
92-
// C++ code from pure Swift code. If linked libraries are C++ based, they
93-
// should properly link C++. In the case of static linking, the user can
94-
// explicitly specify the C++ runtime to link against. This is particularly
95-
// important for platforms like android where as it is a Linux platform, the
96-
// default C++ runtime is `libstdc++` which is unsupported on the target but
97-
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
98-
// be present. This results in linking the wrong version of libstdc++
99-
// generating invalid binaries. It is also possible to use different C++
100-
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
101-
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
102-
// surface this via a driver flag. For now, opt for the simpler approach of
103-
// just using `clang` and avoid a dependency on the C++ runtime.
104-
const char *Clang = "clang";
89+
// We use `clang++` if C++ interop is enabled, `clang` otherwise.
90+
const char *Clang = context.cxxInteropEnabled()? "clang++" : "clang";
10591
if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) {
10692
StringRef toolchainPath(A->getValue());
10793

10894
// If there is a clang in the toolchain folder, use that instead.
109-
if (auto tool = llvm::sys::findProgramByName("clang", {toolchainPath}))
95+
if (auto tool = llvm::sys::findProgramByName(Clang, {toolchainPath}))
11096
Clang = context.Args.MakeArgString(tool.get());
11197
}
11298

test/Driver/linker.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@
101101
// INFERRED_NAMED_DARWIN tests above: 'libLINKER.dylib'.
102102
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -emit-library %s -o libLINKER.dylib | %FileCheck -check-prefix INFERRED_NAME_DARWIN %s
103103

104+
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-ios7.1 -Xfrontend -enable-cxx-interop %s 2>&1 | %FileCheck -check-prefix IOS-cxx-interop %s
105+
106+
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-linux-gnu -Xfrontend -enable-cxx-interop %s 2>&1 | %FileCheck -check-prefix LINUX-cxx-interop %s
107+
108+
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-unknown-windows-msvc -Xfrontend -enable-cxx-interop %s 2>&1 | %FileCheck -check-prefix WINDOWS-cxx-interop %s
109+
104110
// Check reading the SDKSettings.json from an SDK
105111
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -sdk %S/Inputs/MacOSX10.15.versioned.sdk %s 2>&1 | %FileCheck -check-prefix MACOS_10_15 %s
106112
// RUN: %swiftc_driver -driver-print-jobs -target x86_64-apple-macosx10.9 -sdk %S/Inputs/MacOSX10.15.4.versioned.sdk %s 2>&1 | %FileCheck -check-prefix MACOS_10_15_4 %s
@@ -408,6 +414,31 @@
408414
// INFERRED_NAME_WINDOWS: -o LINKER.dll
409415
// INFERRED_NAME_WASI: -o libLINKER.so
410416

417+
// IOS-cxx-interop: swift
418+
// IOS-cxx-interop-DAG: -enable-cxx-interop
419+
// IOS-cxx-interop-DAG: -o [[OBJECTFILE:.*]]
420+
421+
// IOS-cxx-interop: {{(bin/)?}}ld{{"? }}
422+
// IOS-cxx-interop-DAG: [[OBJECTFILE]]
423+
// IOS-cxx-interop-DAG: -lc++
424+
// IOS-cxx-interop: -o linker
425+
426+
// LINUX-cxx-interop: swift
427+
// LINUX-cxx-interop-DAG: -enable-cxx-interop
428+
// LINUX-cxx-interop-DAG: -o [[OBJECTFILE:.*]]
429+
430+
// LINUX-cxx-interop: clang++{{(\.exe)?"? }}
431+
// LINUX-cxx-interop: [[OBJECTFILE]]
432+
// LINUX-cxx-interop: -o linker
433+
434+
// WINDOWS-cxx-interop: swift
435+
// WINDOWS-cxx-interop-DAG: -enable-cxx-interop
436+
// WINDOWS-cxx-interop-DAG: -o [[OBJECTFILE:.*]]
437+
438+
// WINDOWS-cxx-interop: clang++{{(\.exe)?"? }}
439+
// WINDOWS-cxx-interop: [[OBJECTFILE]]
440+
// WINDOWS-cxx-interop: -o linker
441+
411442
// Test ld detection. We use hard links to make sure
412443
// the Swift driver really thinks it's been moved.
413444

0 commit comments

Comments
 (0)