Skip to content

Commit aea0b66

Browse files
[ClangImporter] Swift needs to pass -Xclang -fbuiltin-headers-in-system-modules for its module maps that group cstd headers
Swift has some module maps it overlays on Linux and Windows that groups all of the C standard library headers into a single module. This doesn’t allow clang and C++ headers to layer properly with the OS/SDK modules. clang will set -fbuiltin-headers-in-system-modules as necessary for Apple SDKs, but Swift will need to pass that flag itself when required by its module maps.
1 parent d93e65d commit aea0b66

File tree

13 files changed

+119
-25
lines changed

13 files changed

+119
-25
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,10 @@ namespace swift {
961961
/// contains the full option set.
962962
bool ExtraArgsOnly = false;
963963

964+
/// Indicates the ClangImporter added a vfs which requires
965+
/// -fbuiltin-headers-in-system-modules
966+
bool RequiresBuiltinHeadersInSystemModules = false;
967+
964968
/// When building a PCM, rely on the Swift frontend's command-line -Xcc flags
965969
/// to build the Clang module via Clang frontend directly,
966970
/// and completely bypass the Clang driver.

include/swift/ClangImporter/ClangImporter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ struct ClangInvocationFileMapping {
657657
/// to allow using them from Swift.
658658
ClangInvocationFileMapping getClangInvocationFileMapping(
659659
ASTContext &ctx,
660+
bool &requiresBuiltinHeadersInSystemModules,
660661
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = nullptr);
661662

662663
} // end namespace swift

lib/ClangImporter/ClangImporter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,7 @@ ClangImporter::create(ASTContext &ctx,
11781178
}
11791179
}
11801180

1181-
auto fileMapping = getClangInvocationFileMapping(ctx);
1181+
auto fileMapping = getClangInvocationFileMapping(ctx, importerOpts.RequiresBuiltinHeadersInSystemModules);
11821182
// Wrap Swift's FS to allow Clang to override the working directory
11831183
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
11841184
llvm::vfs::RedirectingFileSystem::create(
@@ -1204,6 +1204,11 @@ ClangImporter::create(ASTContext &ctx,
12041204
// Create a new Clang compiler invocation.
12051205
{
12061206
importer->Impl.ClangArgs = getClangArguments(ctx);
1207+
if (importerOpts.RequiresBuiltinHeadersInSystemModules) {
1208+
if (!importerOpts.DirectClangCC1ModuleBuild)
1209+
importer->Impl.ClangArgs.push_back("-Xclang");
1210+
importer->Impl.ClangArgs.push_back("-fbuiltin-headers-in-system-modules");
1211+
}
12071212
ArrayRef<std::string> invocationArgStrs = importer->Impl.ClangArgs;
12081213
if (importerOpts.DumpClangDiagnostics) {
12091214
llvm::errs() << "'";

lib/ClangImporter/ClangIncludePaths.cpp

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,8 @@ GetWindowsAuxiliaryFile(StringRef modulemap, const SearchPathOptions &Options) {
420420

421421
SmallVector<std::pair<std::string, std::string>, 2> GetWindowsFileMappings(
422422
ASTContext &Context,
423-
const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &driverVFS) {
423+
const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> &driverVFS,
424+
bool &requiresBuiltinHeadersInSystemModules) {
424425
const llvm::Triple &Triple = Context.LangOpts.Target;
425426
const SearchPathOptions &SearchPathOpts = Context.SearchPathOpts;
426427
SmallVector<std::pair<std::string, std::string>, 2> Mappings;
@@ -468,8 +469,21 @@ SmallVector<std::pair<std::string, std::string>, 2> GetWindowsFileMappings(
468469
llvm::sys::path::append(UCRTInjection, "module.modulemap");
469470

470471
AuxiliaryFile = GetWindowsAuxiliaryFile("ucrt.modulemap", SearchPathOpts);
471-
if (!AuxiliaryFile.empty())
472+
if (!AuxiliaryFile.empty()) {
473+
// The ucrt module map has the C standard library headers all together.
474+
// That leads to module cycles with the clang _Builtin_ modules. e.g.
475+
// <fenv.h> on ucrt includes <float.h>. The clang builtin <float.h>
476+
// include-nexts <float.h>. When both of those UCRT headers are in the
477+
// ucrt module, there's a module cycle ucrt -> _Builtin_float -> ucrt
478+
// (i.e. fenv.h (ucrt) -> float.h (builtin) -> float.h (ucrt)). Until the
479+
// ucrt module map is updated, the builtin headers need to join the system
480+
// modules. i.e. when the builtin float.h is in the ucrt module too, the
481+
// cycle goes away. Note that -fbuiltin-headers-in-system-modules does
482+
// nothing to fix the same problem with C++ headers, and is generally
483+
// fragile.
472484
Mappings.emplace_back(std::string(UCRTInjection), AuxiliaryFile);
485+
requiresBuiltinHeadersInSystemModules = true;
486+
}
473487
}
474488

475489
struct {
@@ -508,27 +522,44 @@ SmallVector<std::pair<std::string, std::string>, 2> GetWindowsFileMappings(
508522
} // namespace
509523

510524
ClangInvocationFileMapping swift::getClangInvocationFileMapping(
511-
ASTContext &ctx, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
525+
ASTContext &ctx, bool &requiresBuiltinHeadersInSystemModules,
526+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs) {
512527
ClangInvocationFileMapping result;
513528
if (!vfs)
514529
vfs = llvm::vfs::getRealFileSystem();
515530

516531
const llvm::Triple &triple = ctx.LangOpts.Target;
517532

533+
SmallVector<std::pair<std::string, std::string>, 2> libcFileMapping;
518534
if (triple.isOSWASI()) {
519535
// WASI Mappings
520-
result.redirectedFiles.append(
521-
getLibcFileMapping(ctx, "wasi-libc.modulemap", std::nullopt, vfs));
536+
libcFileMapping =
537+
getLibcFileMapping(ctx, "wasi-libc.modulemap", std::nullopt, vfs);
522538
} else {
523539
// Android/BSD/Linux Mappings
524-
result.redirectedFiles.append(getLibcFileMapping(
525-
ctx, "glibc.modulemap", StringRef("SwiftGlibc.h"), vfs));
540+
libcFileMapping = getLibcFileMapping(ctx, "glibc.modulemap",
541+
StringRef("SwiftGlibc.h"), vfs);
526542
}
543+
result.redirectedFiles.append(libcFileMapping);
544+
// Both libc module maps have the C standard library headers all together in a
545+
// SwiftLibc module. That leads to module cycles with the clang _Builtin_
546+
// modules. e.g. <inttypes.h> includes <stdint.h> on these platforms. The
547+
// clang builtin <stdint.h> include-nexts <stdint.h>. When both of those
548+
// platform headers are in the SwiftLibc module, there's a module cycle
549+
// SwiftLibc -> _Builtin_stdint -> SwiftLibc (i.e. inttypes.h (platform) ->
550+
// stdint.h (builtin) -> stdint.h (platform)). Until this can be fixed in
551+
// these module maps, the clang builtin headers need to join the "system"
552+
// modules (SwiftLibc). i.e. when the clang builtin stdint.h is in the
553+
// SwiftLibc module too, the cycle goes away. Note that
554+
// -fbuiltin-headers-in-system-modules does nothing to fix the same problem
555+
// with C++ headers, and is generally fragile.
556+
requiresBuiltinHeadersInSystemModules = !libcFileMapping.empty();
527557

528558
if (ctx.LangOpts.EnableCXXInterop)
529559
getLibStdCxxFileMapping(result, ctx, vfs);
530560

531-
result.redirectedFiles.append(GetWindowsFileMappings(ctx, vfs));
561+
result.redirectedFiles.append(GetWindowsFileMappings(
562+
ctx, vfs, requiresBuiltinHeadersInSystemModules));
532563

533564
return result;
534565
}

lib/DriverTool/sil_opt_main.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ struct SILOptOptions {
193193
"interfaces for all public declarations by "
194194
"default"));
195195

196+
llvm::cl::opt<bool>
197+
StrictImplicitModuleContext = llvm::cl::opt<bool>("strict-implicit-module-context",
198+
llvm::cl::desc("Enable the strict forwarding of compilation "
199+
"context to downstream implicit module dependencies"));
200+
196201
llvm::cl::opt<bool>
197202
DisableSILOwnershipVerifier = llvm::cl::opt<bool>(
198203
"disable = llvm::cl::opt<bool> DisableSILOwnershipVerifier(-sil-ownership-verifier",
@@ -518,6 +523,10 @@ struct SILOptOptions {
518523
swift::UnavailableDeclOptimization::Complete, "complete",
519524
"Eliminate unavailable decls from lowered SIL/IR")),
520525
llvm::cl::init(swift::UnavailableDeclOptimization::None));
526+
527+
llvm::cl::list<std::string> ClangXCC = llvm::cl::list<std::string>(
528+
"Xcc",
529+
llvm::cl::desc("option to pass to clang"));
521530
};
522531

523532
/// Regular expression corresponding to the value given in one of the
@@ -608,6 +617,8 @@ int sil_opt_main(ArrayRef<const char *> argv, void *MainAddr) {
608617
Invocation.setRuntimeResourcePath(options.ResourceDir);
609618
Invocation.getFrontendOptions().EnableLibraryEvolution
610619
= options.EnableLibraryEvolution;
620+
Invocation.getFrontendOptions().StrictImplicitModuleContext
621+
= options.StrictImplicitModuleContext;
611622
// Set the module cache path. If not passed in we use the default swift module
612623
// cache.
613624
Invocation.getClangImporterOptions().ModuleCachePath = options.ModuleCachePath;
@@ -671,6 +682,12 @@ int sil_opt_main(ArrayRef<const char *> argv, void *MainAddr) {
671682
Invocation.getDiagnosticOptions().VerifyMode =
672683
options.VerifyMode ? DiagnosticOptions::Verify : DiagnosticOptions::NoVerify;
673684

685+
ClangImporterOptions &clangImporterOptions =
686+
Invocation.getClangImporterOptions();
687+
for (const auto &xcc : options.ClangXCC) {
688+
clangImporterOptions.ExtraArgs.push_back(xcc);
689+
}
690+
674691
// Setup the SIL Options.
675692
SILOptions &SILOpts = Invocation.getSILOptions();
676693
SILOpts.InlineThreshold = options.SILInlineThreshold;

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,10 +1763,18 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
17631763
LoaderOpts.requestedAction == FrontendOptions::ActionType::ScanDependencies) {
17641764
// Inherit any clang-specific state of the compilation (macros, clang flags, etc.)
17651765
subClangImporterOpts.ExtraArgs = clangImporterOpts.ExtraArgs;
1766-
for (auto arg : subClangImporterOpts.ExtraArgs) {
1767-
GenericArgs.push_back("-Xcc");
1768-
GenericArgs.push_back(ArgSaver.save(arg));
1769-
}
1766+
}
1767+
// If the importer added a vfs that requires -fbuiltin-headers-in-system-modules,
1768+
// set that in ExtraArgs.
1769+
subClangImporterOpts.RequiresBuiltinHeadersInSystemModules = clangImporterOpts.RequiresBuiltinHeadersInSystemModules;
1770+
if (subClangImporterOpts.RequiresBuiltinHeadersInSystemModules) {
1771+
if (!subClangImporterOpts.DirectClangCC1ModuleBuild)
1772+
subClangImporterOpts.ExtraArgs.push_back("-Xclang");
1773+
subClangImporterOpts.ExtraArgs.push_back("-fbuiltin-headers-in-system-modules");
1774+
}
1775+
for (auto arg : subClangImporterOpts.ExtraArgs) {
1776+
GenericArgs.push_back("-Xcc");
1777+
GenericArgs.push_back(ArgSaver.save(arg));
17701778
}
17711779

17721780
subClangImporterOpts.EnableClangSPI = clangImporterOpts.EnableClangSPI;

stdlib/cmake/modules/AddSwiftStdlib.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,7 @@ function(add_swift_target_library_single target name)
929929
-Xcc;-Xclang;-Xcc;-ivfsoverlay;-Xcc;-Xclang;-Xcc;${SWIFTLIB_SINGLE_VFS_OVERLAY})
930930
endif()
931931
list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS
932-
-vfsoverlay;"${SWIFT_WINDOWS_VFS_OVERLAY}")
932+
-vfsoverlay;"${SWIFT_WINDOWS_VFS_OVERLAY}";-strict-implicit-module-context;-Xcc;-Xclang;-Xcc;-fbuiltin-headers-in-system-modules)
933933
if(NOT CMAKE_HOST_SYSTEM MATCHES Windows)
934934
swift_windows_include_for_arch(${SWIFTLIB_SINGLE_ARCHITECTURE} SWIFTLIB_INCLUDE)
935935
foreach(directory ${SWIFTLIB_INCLUDE})
@@ -2659,7 +2659,7 @@ function(_add_swift_target_executable_single name)
26592659

26602660
if(SWIFTEXE_SINGLE_SDK STREQUAL "WINDOWS")
26612661
list(APPEND SWIFTEXE_SINGLE_COMPILE_FLAGS
2662-
-vfsoverlay;"${SWIFT_WINDOWS_VFS_OVERLAY}")
2662+
-vfsoverlay;"${SWIFT_WINDOWS_VFS_OVERLAY}";-strict-implicit-module-context;-Xcc;-Xclang;-Xcc;-fbuiltin-headers-in-system-modules)
26632663
endif()
26642664

26652665
if ("${SWIFTEXE_SINGLE_SDK}" STREQUAL "LINUX")

stdlib/public/Backtracing/SymbolicatedBacktrace.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ import Swift
2323

2424
@_implementationOnly import OS.Libc
2525
@_implementationOnly import Runtime
26+
// Because we've turned off the OS/SDK modules, and we don't have a module for
27+
// stddef.h, and we sometimes build with -fbuiltin-headers-in-system-modules for
28+
// vfs reasons, stddef.h can be absorbed into a random module. Sometimes it's
29+
// SwiftOverlayShims.
30+
@_implementationOnly import SwiftOverlayShims
2631

2732
/// A symbolicated backtrace
2833
public struct SymbolicatedBacktrace: CustomStringConvertible {

test/DebugInfo/C-typedef-Darwin.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,13 @@ let blah = size_t(1024)
99
use(blah)
1010
// CHECK: !DIDerivedType(tag: DW_TAG_typedef,
1111
// CHECK-SAME: scope: ![[DARWIN_MODULE:[0-9]+]],
12-
// CHECK: ![[DARWIN_MODULE]] = !DIModule({{.*}}, name: "stddef"
12+
13+
// size_t is defined in clang, originally by <stddef.h>, and later split out to
14+
// <__stddef_size_t.h>. Depending on the state of clang's builtin headers and
15+
// modules, size_t can be in either Darwin.C.stddef or _Builtin_stddef.size_t.
16+
// size_t is also defined in macOS by <sys/_types/_size_t.h> which is in the
17+
// Darwin.POSIX._types._size_t module. Ideally Apple will remove the duplicate
18+
// declaration and clang will settle to _Builtin_stddef.size_t, but while it's
19+
// all in flux allow all three of those modules.
20+
// Darwin.C.stddef|_Builtin_stddef.size_t|Darwin.POSIX._types._size_t
21+
// CHECK: ![[DARWIN_MODULE]] = !DIModule({{.*}}, name: "{{stddef|size_t|_size_t}}"

test/Interop/lit.local.cfg

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ if get_target_os() in ['windows-msvc']:
3333
# Clang should build object files with link settings equivalent to -libc MD
3434
# when building for the MSVC target.
3535
clang_opt = clang_compile_opt + '-D_MT -D_DLL -Xclang --dependent-lib=msvcrt -Xclang --dependent-lib=oldnames '
36-
config.substitutions.insert(0, ('%target-swift-flags', '-vfsoverlay {}'.format(os.path.join(config.swift_obj_root,
37-
'stdlib',
38-
'windows-vfs-overlay.yaml'))))
36+
# ucrt.modulemap currently requires -fbuiltin-headers-in-system-modules. -strict-implicit-module-context
37+
# is necessary for -Xcc arguments to be passed through ModuleInterfaceLoader.
38+
config.substitutions.insert(0, ('%target-swift-flags', '-vfsoverlay {} -strict-implicit-module-context -Xcc -Xclang -Xcc -fbuiltin-headers-in-system-modules'.format(
39+
os.path.join(config.swift_obj_root,
40+
'stdlib',
41+
'windows-vfs-overlay.yaml'))))
3942
else:
4043
# FIXME(compnerd) do all the targets we currently support use SysV ABI?
4144
config.substitutions.insert(0, ('%target-abi', 'SYSV'))

test/lit.cfg

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,10 +374,12 @@ else:
374374
config.swift_system_overlay_opt = ""
375375
config.clang_system_overlay_opt = ""
376376
if kIsWindows:
377-
config.swift_system_overlay_opt = "-vfsoverlay {}".format(
377+
# ucrt.modulemap currently requires -fbuiltin-headers-in-system-modules. -strict-implicit-module-context
378+
# is necessary for -Xcc arguments to be passed through ModuleInterfaceLoader.
379+
config.swift_system_overlay_opt = "-vfsoverlay {} -strict-implicit-module-context -Xcc -Xclang -Xcc -fbuiltin-headers-in-system-modules".format(
378380
os.path.join(config.swift_obj_root, "stdlib", "windows-vfs-overlay.yaml")
379381
)
380-
config.clang_system_overlay_opt = "-Xcc -ivfsoverlay -Xcc {}".format(
382+
config.clang_system_overlay_opt = "-Xcc -ivfsoverlay -Xcc {} -Xcc -Xclang -Xcc -fbuiltin-headers-in-system-modules".format(
381383
os.path.join(config.swift_obj_root, "stdlib", "windows-vfs-overlay.yaml")
382384
)
383385
stdlib_resource_dir_opt = config.resource_dir_opt
@@ -1593,6 +1595,9 @@ elif run_os in ['windows-msvc']:
15931595
('%r -modulewrap -target %s' % (config.swiftc, config.variant_triple))
15941596
config.target_swift_emit_pcm = \
15951597
('%r -emit-pcm -target %s' % (config.swiftc, config.variant_triple))
1598+
# ('%r -emit-pcm -target %s %s' % (config.swiftc, \
1599+
# config.variant_triple, \
1600+
# config.swift_system_overlay_opt))
15961601

15971602

15981603
elif (run_os in ['linux-gnu', 'linux-gnueabihf', 'freebsd', 'openbsd', 'windows-cygnus', 'windows-gnu'] or

unittests/ClangImporter/ClangImporterTests.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ TEST(ClangImporterTest, libStdCxxInjectionTest) {
204204
{
205205
LibStdCxxInjectionVFS vfs;
206206
vfs.devtoolSet("9").libstdCxxModulemap("\n");
207-
auto paths = swift::getClangInvocationFileMapping(*context, vfs.vfs);
207+
bool requiresBuiltinHeadersInSystemModules;
208+
auto paths = swift::getClangInvocationFileMapping(*context, requiresBuiltinHeadersInSystemModules, vfs.vfs);
208209
ASSERT_TRUE(paths.redirectedFiles.size() == 2);
209210
ASSERT_TRUE(paths.overridenFiles.empty());
210211
EXPECT_EQ(paths.redirectedFiles[0].first,
@@ -215,12 +216,14 @@ TEST(ClangImporterTest, libStdCxxInjectionTest) {
215216
"/opt/rh/devtoolset-9/root/usr/include/c++/9/module.modulemap");
216217
EXPECT_EQ(paths.redirectedFiles[1].second,
217218
"/usr/lib/swift/linux/libstdcxx.modulemap");
219+
EXPECT_FALSE(requiresBuiltinHeadersInSystemModules);
218220
}
219221

220222
{
221223
LibStdCxxInjectionVFS vfs;
222224
vfs.devtoolSet("9").cxxStdlibHeader("string_view").libstdCxxModulemap();
223-
auto paths = swift::getClangInvocationFileMapping(*context, vfs.vfs);
225+
bool requiresBuiltinHeadersInSystemModules;
226+
auto paths = swift::getClangInvocationFileMapping(*context, requiresBuiltinHeadersInSystemModules, vfs.vfs);
224227
ASSERT_TRUE(paths.redirectedFiles.size() == 1);
225228
ASSERT_TRUE(paths.overridenFiles.size() == 1);
226229
EXPECT_EQ(paths.redirectedFiles[0].first,
@@ -232,6 +235,7 @@ TEST(ClangImporterTest, libStdCxxInjectionTest) {
232235
EXPECT_NE(paths.overridenFiles[0].second.find(
233236
"header \"string_view\"\n /// additional headers."),
234237
StringRef::npos);
238+
EXPECT_FALSE(requiresBuiltinHeadersInSystemModules);
235239
}
236240

237241
{
@@ -246,7 +250,8 @@ TEST(ClangImporterTest, libStdCxxInjectionTest) {
246250
.cxxStdlibHeader("charconv")
247251
.cxxStdlibHeader("any")
248252
.libstdCxxModulemap();
249-
auto paths = swift::getClangInvocationFileMapping(*context, vfs.vfs);
253+
bool requiresBuiltinHeadersInSystemModules;
254+
auto paths = swift::getClangInvocationFileMapping(*context, requiresBuiltinHeadersInSystemModules, vfs.vfs);
250255
ASSERT_TRUE(paths.redirectedFiles.size() == 1);
251256
ASSERT_TRUE(paths.overridenFiles.size() == 1);
252257
EXPECT_EQ(paths.redirectedFiles[0].first,
@@ -262,5 +267,6 @@ TEST(ClangImporterTest, libStdCxxInjectionTest) {
262267
"\"optional\"\n header \"string_view\"\n header \"variant\"\n "
263268
"/// additional headers."),
264269
StringRef::npos);
270+
EXPECT_FALSE(requiresBuiltinHeadersInSystemModules);
265271
}
266272
}

utils/build.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ function Build-CMakeProject {
657657

658658
$SwiftArgs += @("-resource-dir", "$SwiftResourceDir")
659659
$SwiftArgs += @("-L", "$SwiftResourceDir\windows")
660-
$SwiftArgs += @("-vfsoverlay", "$RuntimeBinaryCache\stdlib\windows-vfs-overlay.yaml")
660+
$SwiftArgs += @("-vfsoverlay", "$RuntimeBinaryCache\stdlib\windows-vfs-overlay.yaml", "-strict-implicit-module-context", "-Xcc", "-Xclang", "-Xcc", "-fbuiltin-headers-in-system-modules")
661661
}
662662
} else {
663663
$SwiftArgs += @("-sdk", (Get-PinnedToolchainSDK))

0 commit comments

Comments
 (0)