https://github.com/jeremyd2019 updated 
https://github.com/llvm/llvm-project/pull/140169

>From 47f81a804a36a5b685f130f22d2ab5d330170861 Mon Sep 17 00:00:00 2001
From: kikairoya <kikair...@gmail.com>
Date: Mon, 21 Apr 2025 23:30:13 +0900
Subject: [PATCH 1/4] [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 e4f98b8be1b53fa0da22ce9461e73e39e80875d7 Mon Sep 17 00:00:00 2001
From: Jeremy Drake <git...@jdrake.com>
Date: Mon, 19 May 2025 16:31:42 -0700
Subject: [PATCH 2/4] CI: run windows jobs without waiting for stage2

use test llvm-mingw toolchain
---
 .github/workflows/libcxx-build-and-test.yaml | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/libcxx-build-and-test.yaml 
b/.github/workflows/libcxx-build-and-test.yaml
index 3551fc150e59b..27e5bd6450495 100644
--- a/.github/workflows/libcxx-build-and-test.yaml
+++ b/.github/workflows/libcxx-build-and-test.yaml
@@ -239,7 +239,7 @@ jobs:
 
   windows:
     runs-on: windows-2022
-    needs: [ stage2 ]
+    #needs: [ stage2 ]
     strategy:
       fail-fast: false
       matrix:
@@ -263,10 +263,24 @@ jobs:
         if: ${{ matrix.mingw != true }}
         run: |
           choco install -y llvm --version=19.1.7 --allow-downgrade
+      - name: Download test llvm-mingw
+        if: ${{ matrix.mingw == true }}
+        shell: bash
+        run: |
+          
ARTIFACT_URL=https://github.com/jeremyd2019/llvm-mingw/actions/runs/15080594305/artifacts/3143907630
+          case "$ARTIFACT_URL" in
+          https://github.com/*/actions/runs/[0-9]*/artifacts/[0-9]*)
+            ARTIFACT_URL="$(echo "$ARTIFACT_URL" |
+              sed 
's|^\(https://\)\(github.com/\)\(.*/actions/\)runs/[0-9]*/\(artifacts/[0-9]*\)$|\1api.\2repos/\3\4/zip|')"
+              ;;
+          esac
+          curl -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" \
+              -fLo artifact.zip "$ARTIFACT_URL"
+          powershell Expand-Archive artifact.zip -DestinationPath .
+          rm -f artifact.zip
       - name: Install llvm-mingw
         if: ${{ matrix.mingw == true }}
         run: |
-          curl -LO 
https://github.com/mstorsjo/llvm-mingw/releases/download/20250114/llvm-mingw-20250114-ucrt-x86_64.zip
           powershell Expand-Archive llvm-mingw*.zip -DestinationPath .
           del llvm-mingw*.zip
           mv llvm-mingw* c:\llvm-mingw

>From da769f16fec7596fab07749597dfdf23c135629b Mon Sep 17 00:00:00 2001
From: kikairoya <kikair...@gmail.com>
Date: Tue, 20 May 2025 22:16:23 +0900
Subject: [PATCH 3/4] [libc++] Introduce a new attribute keyword for Clang
 improves compatibility with Mingw-GCC

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 class as a non-template inner type of
a class template, all non-inline member functions of that class
also needs to be attached _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1
or _LIBCPP_HIDE_FROM_ABI, not _LIBCPP_HIDE_FROM_ABI_AFTER_V1.
Otherwise, that member functions contained in DLL will be
inaccessible on MinGW environment.

For time to increase ABI version, _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1
can be simply replaced to _LIBCPP_HIDE_FROM_ABI_AFTER_V1.
---
 libcxx/include/__config                  | 20 ++++++++++++++++++++
 libcxx/include/__ostream/basic_ostream.h |  4 ++--
 libcxx/include/istream                   |  2 +-
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index 110450f6e9c51..c0974cb65fed8 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -530,6 +530,26 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_HIDE_FROM_ABI_AFTER_V1 _LIBCPP_HIDE_FROM_ABI
 #  endif
 
+//// ** in-progress: this description block should be here or merged block 
above? **
+// _LIBCPP_HIDE_FROM_ABI(_AFTER_V1) is mandatory for member functions in a 
inner class in a class template
+// (e.g. std::basic_ostream<...>::sentry::sentry(...) ) due to strange 
behavior of MinGW-GCC about
+// instantiation combined with dllimport/dllexport described below. Former 
clang didn't do this buggy behavior
+// but will be aligned to GCC for platform compatibility (in particluar, 
required by some of libstdc++ package).
+// Thus, such member functions exist from a past (they are, 
ostream::sentry::sentry, ostream::ostream::~sentry
+// and istream::sentry::sentry only) need to be HIDE_FROM_ABI but simply 
attaching simply ABI on other platforms.
+// So it's needed a dedicated keyword named 
_LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 to affects only for MinGW
+// target (and, Cygwin too). For other platforms, that is expanded to 
_LIBCPP_HIDE_FROM_ABI_AFTER_V1 because at
+// a time to fix V2 ABI comes, _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 can be 
simply replaced into
+// _LIBCPP_HIDE_FROM_ABI_AFTER_V1 and be completely removed.
+// If time to a new class in a class template comes, all non-inline member 
functions of that new inner class must
+// be declared with _LIBCPP_HIDE_FROM_ABI_AFTER_V1 otherwise they build to DLL 
will be inaccessible by MinGW-GCC.
+//// ** in-progress: describe peculiar behavior of MinGW-GCC **
+#  if defined(__MINGW32__) || defined(__CYGWIN__)
+#    define _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 inline 
_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 f7473a36d8ccc..dc655a4f93dcb 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -186,8 +186,8 @@ class basic_ostream<_CharT, _Traits>::sentry {
   basic_ostream<_CharT, _Traits>& __os_;
 
 public:
-  explicit sentry(basic_ostream<_CharT, _Traits>& __os);
-  ~sentry();
+  explicit _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 
sentry(basic_ostream<_CharT, _Traits>& __os);
+  _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 95340c739c118..e9fdbbebb2eca 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -309,7 +309,7 @@ class basic_istream<_CharT, _Traits>::sentry {
   bool __ok_;
 
 public:
-  explicit sentry(basic_istream<_CharT, _Traits>& __is, bool __noskipws = 
false);
+  explicit _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_; }

>From 54221fd09c3cb0de345ec579daea617c28b3c820 Mon Sep 17 00:00:00 2001
From: Jeremy Drake <git...@jdrake.com>
Date: Tue, 20 May 2025 10:22:10 -0700
Subject: [PATCH 4/4] move inline keyword

---
 libcxx/include/__config                  | 2 +-
 libcxx/include/__ostream/basic_ostream.h | 4 ++--
 libcxx/include/istream                   | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index c0974cb65fed8..e523aad711fbf 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -545,7 +545,7 @@ typedef __char32_t char32_t;
 // be declared with _LIBCPP_HIDE_FROM_ABI_AFTER_V1 otherwise they build to DLL 
will be inaccessible by MinGW-GCC.
 //// ** in-progress: describe peculiar behavior of MinGW-GCC **
 #  if defined(__MINGW32__) || defined(__CYGWIN__)
-#    define _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 inline 
_LIBCPP_HIDE_FROM_ABI
+#    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
diff --git a/libcxx/include/__ostream/basic_ostream.h 
b/libcxx/include/__ostream/basic_ostream.h
index dc655a4f93dcb..9ae905f689b6a 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -186,8 +186,8 @@ class basic_ostream<_CharT, _Traits>::sentry {
   basic_ostream<_CharT, _Traits>& __os_;
 
 public:
-  explicit _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 
sentry(basic_ostream<_CharT, _Traits>& __os);
-  _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 ~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 e9fdbbebb2eca..0f6294dda1b76 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -309,7 +309,7 @@ class basic_istream<_CharT, _Traits>::sentry {
   bool __ok_;
 
 public:
-  explicit _LIBCPP_HIDE_FROM_ABI_MINGW_OR_AFTER_V1 
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_; }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to