[libcxx-commits] [libcxx] [libc++] Introduce a new attribute keyword for Clang improves compatibility with Mingw-GCC (PR #141040)

Tomohiro Kashiwada via libcxx-commits libcxx-commits at lists.llvm.org
Thu May 22 04:24:15 PDT 2025


https://github.com/kikairoya updated https://github.com/llvm/llvm-project/pull/141040

>From 59948754f51304a1ea02ed50e17b3eb25ee0a817 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Thu, 22 May 2025 06:42:34 +0900
Subject: [PATCH] [libc++] Introduce a new attribute keyword for Clang improves
 compatibility with Mingw-GCC

The new ABI annotation keyword _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1
is introduced.

In MinGW environment, Clang handles dllexport attribute of internal
class that defined in class template in different way from GCC.
This incompatibility should be fixed but breaks ABI of libc++, so
introduce a new keyword to keep ABI in MinGW environment with
old and patched Clang and to stay ABI compatible on other platforms.

This attribute is attached only for basic_ostream::sentry::sentry,
basic_ostream::sentry::~sentry and basic_istream::sentry::sentry.
Other entities won't be affected by patching Clang so doesn't need
to be annotate.

At a time to introduce a new (static or non-static) member function
is added to an inner class within a class as a non-template, all of
such member functions needs to be attached _LIBCPP_HIDE_FROM_ABI
Otherwise, that member functions contained in DLL will be
inaccessible on MinGW environment.
---
 libcxx/include/__config                  | 42 ++++++++++++++++++++++++
 libcxx/include/__ostream/basic_ostream.h |  4 +--
 libcxx/include/istream                   |  2 +-
 3 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 57223e4f1db18..118f97cc77d7d 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -530,6 +530,48 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_HIDE_FROM_ABI_AFTER_V1 _LIBCPP_HIDE_FROM_ABI
 #  endif
 
+// _LIBCPP_HIDE_FROM_ABI is required for member functions defined within an inner class of a class template
+// (e.g., std::basic_ostream<...>::sentry::sentry(...)), due to inconsistent behavior in MinGW-GCC (and
+// Cygwin as well, in all relevant cases) regarding template instantiation and symbol visibility when combined
+// with __declspec(dllexport/dllimport).
+//
+// Previous versions of Clang did not exhibit this issue, but upcoming versions are expected to align with
+// GCC's behavior for compatibility. This is particularly important because some of libstdc++ packages
+// compiled with --with-default-libstdcxx-abi=gcc4-compatible are incompatible with Clang, resulting in linking
+// errors or runtime crushes.
+//
+// A few such member functions already exist (here are ostream::sentry::sentry, ostream::~sentry and
+// istream::sentry::sentry) were not previously marked with _LIBCPP_HIDE_FROM_ABI but should be to avoid symbol
+// visibility issues. However, adding the macro unconditionally would break the ABI on other platforms.
+//
+// Therefore, a dedicated macro _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 is introduced. This macro expands to
+// _LIBCPP_HIDE_FROM_ABI only when targeting MinGW, and to _LIBCPP_HIDE_FROM_ABI_AFTER_V1 on all other platforms.
+//
+// Going forward, whenever a new (static or non-static) member function is added to an inner class within a
+// class template, it must be annotated with _LIBCPP_HIDE_FROM_ABI to ensure proper symbol visibility when
+// targeting MinGW. Otherwise, the resulting DLL will be unusable due to missing symbols.
+//
+// The underlying issue arises from how MinGW-GCC handles template instantiation:
+//
+//   - When exporting: 'extern template __declspec(dllexport) class TheTemplateClass<T>;'
+//     allows exporting the outer template instantiation, but not its nested types (e.g., InnerClass).
+//
+//   - When importing: 'extern template __declspec(dllimport) class TheTemplateClass<T>;'
+//     causes MinGW-GCC to also try importing nested types such as TheTemplateClass<T>::InnerClass,
+//     even if they were never exported. This leads to linker errors like:
+//     'undefined reference to TheTemplateClass<T>::InnerClass::...'
+//
+// This differs from Clang's historical behavior, which did not import nested classes implicitly.
+// However, to ensure ABI compatibility with the MinGW-GCC toolchain (commonly used in the MinGW ecosystem),
+// Clang will adopt this behavior as well.
+//
+// Note: As of this writing, Clang does not yet implement this behavior, since doing so would break libc++.
+#  if defined(__MINGW32__) || defined(__CYGWIN__)
+#    define _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 _LIBCPP_HIDE_FROM_ABI
+#  else
+#    define _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 _LIBCPP_HIDE_FROM_ABI_AFTER_V1
+#  endif
+
 // TODO: Remove this workaround once we drop support for Clang 16
 #  if __has_warning("-Wc++23-extensions")
 #    define _LIBCPP_CLANG_DIAGNOSTIC_IGNORED_CXX23_EXTENSION _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wc++23-extensions")
diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index 09ad401a48d60..8c146840becf2 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -187,8 +187,8 @@ class basic_ostream<_CharT, _Traits>::sentry {
   basic_ostream<_CharT, _Traits>& __os_;
 
 public:
-  explicit sentry(basic_ostream<_CharT, _Traits>& __os);
-  ~sentry();
+  explicit inline _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 sentry(basic_ostream<_CharT, _Traits>& __os);
+  inline _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 ~sentry();
   sentry(const sentry&)            = delete;
   sentry& operator=(const sentry&) = delete;
 
diff --git a/libcxx/include/istream b/libcxx/include/istream
index 4596eebf5ed22..6bbeccaa2de55 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -310,7 +310,7 @@ class basic_istream<_CharT, _Traits>::sentry {
   bool __ok_;
 
 public:
-  explicit sentry(basic_istream<_CharT, _Traits>& __is, bool __noskipws = false);
+  explicit inline _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 sentry(basic_istream<_CharT, _Traits>& __is, bool __noskipws = false);
   //    ~sentry() = default;
 
   _LIBCPP_HIDE_FROM_ABI explicit operator bool() const { return __ok_; }



More information about the libcxx-commits mailing list