-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[libc++] Introduce a new attribute keyword for Clang improves compatibility with Mingw-GCC #141040
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
base: main
Are you sure you want to change the base?
[libc++] Introduce a new attribute keyword for Clang improves compatibility with Mingw-GCC #141040
Conversation
✅ With the latest revision this PR passed the C/C++ code formatter. |
4581bdb
to
5994875
Compare
…bility 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.
5994875
to
4209e6c
Compare
I didn't quite see this bit answered here; in which way would that break the ABI, if we'd fix this incompatibility? If we'd make libc++.dll export more symbols than we did before, that wouldn't break anything for preexisting callers of the DLL. New binaries built would obviously require the new libc++.dll though, but that's generally the rule anyway.
This is indeed self-inconsistent, and is the issue that I reported at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89087. Now as libstdc++ doesn't use explicit dllexports, but just uses the default fallback behaviour of exporting all symbols, they're not really hit by this issue, so there's not much pressure to fix it on their side. (We could also do the same in libc++, by explicitly passing Can you elaborate on what would break if we'd try to fix this on the Clang side (making Clang do similarly to what GCC does, but self-consistently by making it export the nested class, like the dllimport behaviour seems to expect)? |
I agree that exporting additional symbols wouldn't break existing binaries at the symbol usage level. However, I guess, from user's perspective, needing to upgrade the DLL might be treated as ABI change. Saying it "breaks ABI" may have been too strong but not completely wrong, as seeing manner of ELF world, exporting new symbols will cause change SOVERSION.
I think that makes a new 'dialect' to be avoided (but I understand should not to emulate GCC's defect, too). As real harm, reverse of #135910 might occur but I don't have any test. Need a time to clarify that harms or not. Regardless to approach of fixing Clang incompatibility with MinGW-GCC, this new keyword would be viable to able to work libc++ with MinGW-GCC. |
I believe we established that dllexport and dllimport are handled the same between Clang and GCC, and it is the more mundane |
A new ABI annotation keyword
_LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1
needs to be introduced and attached toostream::sentry::sentry
,ostream::sentry::~sentry
andistream::sentry
to improve binary compatibility on MinGW platform.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 we need to introduce the 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
andbasic_istream::sentry::sentry
. Other entities won't be affected by patching Clang so doesn't need to be annotate.Background
Clang (targeting MinGW a.k.a. windows-gnu, slightly different from windows-msvc) handles template instantiation:
extern template __declspec(dllexport) class TheTemplateClass<T>;
allows exporting the outer template instantiation, but not its nested types (e.g., InnerClass).
extern template __declspec(dllimport) class TheTemplateClass<T>;
try to import the outer template instantiation, but not its nested types - they will be instantiated in client object.
But MinGW-GCC handles template instantiation differently:
extern template __declspec(dllexport) class TheTemplateClass<T>;
allows exporting the outer template instantiation, but not its nested types (e.g., InnerClass).
extern template __declspec(dllimport) class TheTemplateClass<T>;
causes MinGW-GCC to also try importing nested types such as TheTemplateClass::InnerClass,
even if they were never exported. This leads to linker errors like:
undefined reference to TheTemplateClass<T>::InnerClass::...
This difference causes link-time problems ( duplicated symbol or undefined reference ) or run-time problems ( illegal memory access, crash or other strange errors ) as reported in #135910 , so we are trying to align the behavior of Clang to MinGW-GCC.
But modifying Clang breaks libc++:
so we need to fix symbol visibility annotation in libc++ prior to patch Clang.
Effects
What attaching
_LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1
toostream::sentry::sentry
s does:Expanded to
_LIBCPP_HIDE_FROM_ABI
.Virtually no-op while Clang and MinGW-GCC doesn't export them from a past.
Forces instantiate in client code and prohibits trying to import from DLL.
This is same to what former Clang does and gains compatibility with patched Clang and MinGW-GCC.
Expanded to
_LIBCPP_HIDE_FROM_ABI_V1
. Keeps V1 ABI stable.Thus, this change introduces no significant side-effects except for need to add
inline
together.Other options that were not adopted
Modify GCC's behavior:
@mstorsjo reported to GCC as defect over 5 years ago but there is no response yet: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89087 .
There seems to be no chance of fixing it.
Attaching
_LIBCPP_EXPORTED_FROM_ABI
like:This is simply wrong. Breaks ABI and doesn't work with template argument other than char or wchar_t.
Declaring
ostream::sentry
s with__attribute__((exclude_from_explicit_instantiation))
if__MINGW32__
and when client-side with a new keyword like (empty if in other conditions):and
This works well but leaves GCC incompatible, so this idea is not perfect.
Instantiate inner class explicitly
This works for both of Clang and GCC but scattering
# if
blocks is not a good style.Similar to this proposal but expand to nothing for non-MinGW platforms like this:
This works fine. If keeping non-inline is preferred to keeping consistency of presence or absence of
inline
between platforms, this is an option.Conclusion
Due to incompatible behavior on MinGW platform, Clang needs to be modified. But patching Clang breaks libc++ so adjusting visibility of some symbols is required. Any keyword already exist can't be suitable so we're going to introduce a new keyword named
_LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1
.