https://github.com/kikairoya updated https://github.com/llvm/llvm-project/pull/140145
>From 4f0eab77111d15515082c82d288f25ca88424a9e Mon Sep 17 00:00:00 2001 From: kikairoya <kikair...@gmail.com> Date: Mon, 21 Apr 2025 23:30:13 +0900 Subject: [PATCH 1/2] [Cygwin][MinGW] Internal class in explicitly-instantiation-declarated template should be instantiated In-code comment says "explicit instantiation decl of the outer class doesn't affect the inner class" but this behavior seems MSVC specific, MinGW-GCC and Cygwin-GCC does not. Clang should honor gcc's behavior. This change fixes std::string compatibilty and resolves strange link error (statically linked), strange crash (dynamically linked) using libstdc++ on Cygwin. --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 1 + .../CodeGenCXX/mingw-template-dllexport.cpp | 109 +++++++++++++++--- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index d028eea4f8f3e..b7c27b3795f5d 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -4247,6 +4247,7 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation, continue; if (Context.getTargetInfo().getTriple().isOSWindows() && + !Context.getTargetInfo().getTriple().isOSCygMing() && TSK == TSK_ExplicitInstantiationDeclaration) { // On Windows, explicit instantiation decl of the outer class doesn't // affect the inner class. Typically extern template declarations are diff --git a/clang/test/CodeGenCXX/mingw-template-dllexport.cpp b/clang/test/CodeGenCXX/mingw-template-dllexport.cpp index de112d6da53db..a6047b5955e96 100644 --- a/clang/test/CodeGenCXX/mingw-template-dllexport.cpp +++ b/clang/test/CodeGenCXX/mingw-template-dllexport.cpp @@ -6,46 +6,121 @@ #define JOIN2(x, y) x##y #define JOIN(x, y) JOIN2(x, y) #define UNIQ(name) JOIN(name, __LINE__) -#define USEMEMFUNC(class, func) void (class::*UNIQ(use)())() { return &class::func; } +#define USEMEMFUNC(class, func) auto UNIQ(use) = &class::func; template <class T> class c { + // MinGW-GCC does not apply 'dllexport' to inline member function in dll-exported template but clang does from long ago. void f() {} + void g(); + inline static int u = 0; + static int v; }; +template <class T> void c<T>::g() {} +template <class T> int c<T>::v = 0; +// #1 template class __declspec(dllexport) c<int>; -// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIiE1fEv - +// #2 extern template class __declspec(dllexport) c<char>; template class c<char>; -// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcE1fEv - +// #3 extern template class c<double>; -template class __declspec(dllexport) c<double>; +template class __declspec(dllexport) c<double>; // expected-warning {{ 'dllexport' attribute ignored on explicit instantiation definition }} -// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN1cIdE1fEv template <class T> struct outer { - void f(); + void f() {} + void g(); + inline static int u = 0; + static int v; + // MinGW-GCC and Clang does not apply 'dllexport' to inner type and its sub-elements in template class. struct inner { - void f(); + void f() {} + void g(); + inline static int u = 0; + static int v; }; }; -template <class T> void outer<T>::f() {} -template <class T> void outer<T>::inner::f() {} +template <class T> void outer<T>::g() {} +template <class T> void outer<T>::inner::g() {} +template <class T> int outer<T>::v = 0; +template <class T> int outer<T>::inner::v = 0; -template class __declspec(dllexport) outer<int>; +// #4 +template struct __declspec(dllexport) outer<int>; -// CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1fEv -// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1fEv - -extern template class __declspec(dllimport) outer<char>; +// #5 +extern template struct __declspec(dllimport) outer<char>; USEMEMFUNC(outer<char>, f) +USEMEMFUNC(outer<char>, g) +USEMEMFUNC(outer<char>, u) +USEMEMFUNC(outer<char>, v) USEMEMFUNC(outer<char>::inner, f) +USEMEMFUNC(outer<char>::inner, g) +USEMEMFUNC(outer<char>::inner, u) +USEMEMFUNC(outer<char>::inner, v) + + +// #1 variables +// CHECK: @_ZN1cIiE1uE = {{.*}} dllexport {{.*}} +// CHECK: @_ZN1cIiE1vE = {{.*}} dllexport {{.*}} + +// #2 variables +// CHECK: @_ZN1cIcE1uE = {{.*}} dllexport {{.*}} +// CHECK: @_ZN1cIcE1vE = {{.*}} dllexport {{.*}} + +// #3 variables +// CHECK: @_ZN1cIdE1uE = {{.*}} +// CHECK-NOT: @_ZN1cIcE1uE = {{.*}} dllexport {{.*}} +// CHECK: @_ZN1cIdE1vE = {{.*}} +// CHECK-NOT: @_ZN1cIcE1vE = {{.*}} dllexport {{.*}} + +// #4 variables +// CHECK: @_ZN5outerIiE1uE = {{.*}} dllexport {{.*}} +// CHECK: @_ZN5outerIiE1vE = {{.*}} dllexport {{.*}} +// CHECK: @_ZN5outerIiE5inner1uE = {{.*}} +// CHECK-NOT: @_ZN5outerIiE5inner1uE = {{.*}} dllexport {{.*}} +// CHECK: @_ZN5outerIiE5inner1vE = {{.*}} +// CHECK-NOT: @_ZN5outerIiE5inner1vE = {{.*}} dllexport {{.*}} + +// #5 variables +// CHECK: @_ZN5outerIcE1uE = external dllimport {{.*}} +// CHECK: @_ZN5outerIcE1vE = external dllimport {{.*}} +// CHECK-NOT: @_ZN5outerIcE5inner1uE = dllimport {{.*}} +// CHECK-NOT: @_ZN5outerIcE5inner1vE = dllimport {{.*}} +// CHECK: @_ZN5outerIcE5inner1uE = external {{.*}} +// CHECK: @_ZN5outerIcE5inner1vE = external {{.*}} + + +// #1 functions +// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIiE1fEv +// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIiE1gEv + +// #2 functions +// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcE1fEv +// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcE1gEv + +// #3 functions +// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN1cIdE1fEv +// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN1cIdE1gEv + +// #4 functions +// CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1fEv +// CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1gEv +// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1fEv +// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1gEv +// #5 functions // CHECK: declare dllimport {{.*}} @_ZN5outerIcE1fEv -// CHECK: define {{.*}} @_ZN5outerIcE5inner1fEv +// CHECK: declare dllimport {{.*}} @_ZN5outerIcE1gEv +// CHECK-NOT: declare dllimport {{.*}} @_ZN5outerIcE5inner1fEv +// CHECK-NOT: declare dllimport {{.*}} @_ZN5outerIcE5inner1gEv +// CHECK-NOT: define {{.*}} @_ZN5outerIcE1fEv +// CHECK-NOT: define {{.*}} @_ZN5outerIcE5inner1fEv +// CHECK-NOT: define {{.*}} @_ZN5outerIcE1gEv +// CHECK-NOT: define {{.*}} @_ZN5outerIcE5inner1gEv >From 92a3b90dbc4affe081eb5f6ebb8413d415ecc079 Mon Sep 17 00:00:00 2001 From: kikairoya <kikair...@gmail.com> Date: Sat, 17 May 2025 16:22:42 +0900 Subject: [PATCH 2/2] [libc++] std::ostream::sentry should excluded from explicit instantiation on client-side --- libcxx/docs/DesignDocs/VisibilityMacros.rst | 5 +++++ libcxx/include/__config | 8 ++++++++ libcxx/include/__ostream/basic_ostream.h | 2 +- libcxx/include/istream | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/libcxx/docs/DesignDocs/VisibilityMacros.rst b/libcxx/docs/DesignDocs/VisibilityMacros.rst index e9f88118b7ef3..b56ddf53dbfdc 100644 --- a/libcxx/docs/DesignDocs/VisibilityMacros.rst +++ b/libcxx/docs/DesignDocs/VisibilityMacros.rst @@ -93,6 +93,11 @@ Visibility Macros the extern template declaration) as exported on Windows, as discussed above. On all other platforms, this macro has an empty definition. +**_LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS** + To keep binary compatibility purpose only of a class not marked as `dllexport` + that is defined in a class template. Not to add this for new class, and this + can be replaced to `_LIBCPP_EXPORTED_FROM_ABI` at a timing of ABI change occurs. + Links ===== diff --git a/libcxx/include/__config b/libcxx/include/__config index 110450f6e9c51..91b75860c84ef 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -365,6 +365,11 @@ typedef __char32_t char32_t; # define _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS # define _LIBCPP_OVERRIDABLE_FUNC_VIS # define _LIBCPP_EXPORTED_FROM_ABI +# if defined(_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) || !__has_attribute(exclude_from_explicit_instantiation) +# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS +# else +# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS __attribute__((__exclude_from_explicit_instantiation__)) +# endif # elif defined(_LIBCPP_BUILDING_LIBRARY) # if defined(__MINGW32__) # define _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __declspec(dllexport) @@ -375,11 +380,13 @@ typedef __char32_t char32_t; # endif # define _LIBCPP_OVERRIDABLE_FUNC_VIS __declspec(dllexport) # define _LIBCPP_EXPORTED_FROM_ABI __declspec(dllexport) +# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS # else # define _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __declspec(dllimport) # define _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS # define _LIBCPP_OVERRIDABLE_FUNC_VIS # define _LIBCPP_EXPORTED_FROM_ABI __declspec(dllimport) +# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS # endif # define _LIBCPP_HIDDEN @@ -399,6 +406,7 @@ typedef __char32_t char32_t; # define _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_VISIBILITY("default") # define _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS _LIBCPP_VISIBILITY("default") # define _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS +# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS // TODO: Make this a proper customization point or remove the option to override it. # ifndef _LIBCPP_OVERRIDABLE_FUNC_VIS diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h index f7473a36d8ccc..a72350501ccda 100644 --- a/libcxx/include/__ostream/basic_ostream.h +++ b/libcxx/include/__ostream/basic_ostream.h @@ -71,7 +71,7 @@ class basic_ostream : virtual public basic_ios<_CharT, _Traits> { public: // 27.7.2.4 Prefix/suffix: - class sentry; + class _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS sentry; // 27.7.2.6 Formatted output: inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 basic_ostream& operator<<(basic_ostream& (*__pf)(basic_ostream&)) { diff --git a/libcxx/include/istream b/libcxx/include/istream index 95340c739c118..b41dd59060f78 100644 --- a/libcxx/include/istream +++ b/libcxx/include/istream @@ -228,7 +228,7 @@ public: basic_istream& operator=(const basic_istream& __rhs) = delete; // 27.7.1.1.3 Prefix/suffix: - class sentry; + class _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS sentry; // 27.7.1.2 Formatted input: inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 basic_istream& operator>>(basic_istream& (*__pf)(basic_istream&)) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits