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