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

>From 37af003a4cc9afb74a05cddd5118ed0695cec632 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 9349b45cdb162d7a1ccd79aa0e9d4cb87cac3505 Mon Sep 17 00:00:00 2001
From: kikairoya <kikair...@gmail.com>
Date: Tue, 20 May 2025 19:13:33 +0900
Subject: [PATCH 2/2] [libc++] Introduce a new attribute keyword for Clang
 improves compatibility with Mingw-GCC

This is affected to only Clang for MinGW target.

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 ensure nothing affects to other
environment/platforms.

The new attribute keyword _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS does
nothing in almost all situations except if included from client
(not in building libc++ itself) by clang (not by GCC or others).

If clang does include libc++, the keyword will be expanded to
__attribute__((__exclude_from_explicit_instantiation__)), results
attached class will be away from explicit instantiation declaration
so will be instanciated implicitly as formar Clang does. Thus,
it will no-op for old Clang that has incompatibility with MinGW-GCC
or emulate old behavior for patched Clang.

This attribute is attached only for std::basic_ostream::sentry and
std::basic_istream::sentry. Other entities won't be affected by
patching Clang so doesn't need to be annotate. Notably, at a time
to introduce a new class as a non-template inner type of a class
template, that class also needs to be attached
_LIBCPP_INNER_CLASS_ININ_TEMPLATE_VIS.
---
 libcxx/docs/DesignDocs/VisibilityMacros.rst | 7 +++++++
 libcxx/include/__config                     | 8 ++++++++
 libcxx/include/__ostream/basic_ostream.h    | 2 +-
 libcxx/include/istream                      | 2 +-
 4 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/libcxx/docs/DesignDocs/VisibilityMacros.rst 
b/libcxx/docs/DesignDocs/VisibilityMacros.rst
index e9f88118b7ef3..68e5430bd4a33 100644
--- a/libcxx/docs/DesignDocs/VisibilityMacros.rst
+++ b/libcxx/docs/DesignDocs/VisibilityMacros.rst
@@ -93,6 +93,13 @@ 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 let dll-importing and dll-exporting consitent on windows-gnu environment,
+  this annotation must be attached to a non-template class that isn't marked
+  as `dllexport` defined in a class template. Currentry here are 
ostream::sentry
+  and istream::sentry only but if a time to such a new class be introduced,
+  the class has to be attached this annotation.
+
 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

Reply via email to