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/3] [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 d20a1f60ce8af58043f19aa9be41e0ef1031fb6c Mon Sep 17 00:00:00 2001 From: Jeremy Drake <git...@jdrake.com> Date: Mon, 19 May 2025 16:01:57 -0700 Subject: [PATCH 2/3] test _LIBCPP_HIDE_FROM_ABI on sentry methods --- libcxx/include/__ostream/basic_ostream.h | 50 ++++++++++++------------ libcxx/include/istream | 46 +++++++++++----------- 2 files changed, 50 insertions(+), 46 deletions(-) diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h index f7473a36d8ccc..2a2433903c38e 100644 --- a/libcxx/include/__ostream/basic_ostream.h +++ b/libcxx/include/__ostream/basic_ostream.h @@ -180,43 +180,45 @@ class basic_ostream : virtual public basic_ios<_CharT, _Traits> { _LIBCPP_HIDE_FROM_ABI basic_ostream() {} // extension, intentially does not initialize }; +#if defined(__MINGW32__) || defined(__CYGWIN__) +# define _LIBCPP_HIDE_INNER_CLASS_METHOD _LIBCPP_HIDE_FROM_ABI +#else +# define _LIBCPP_HIDE_INNER_CLASS_METHOD _LIBCPP_HIDE_FROM_ABI_AFTER_V1 +#endif + template <class _CharT, class _Traits> class basic_ostream<_CharT, _Traits>::sentry { bool __ok_; basic_ostream<_CharT, _Traits>& __os_; public: - explicit sentry(basic_ostream<_CharT, _Traits>& __os); - ~sentry(); - sentry(const sentry&) = delete; - sentry& operator=(const sentry&) = delete; - - _LIBCPP_HIDE_FROM_ABI explicit operator bool() const { return __ok_; } -}; - -template <class _CharT, class _Traits> -basic_ostream<_CharT, _Traits>::sentry::sentry(basic_ostream<_CharT, _Traits>& __os) : __ok_(false), __os_(__os) { - if (__os.good()) { - if (__os.tie()) - __os.tie()->flush(); - __ok_ = true; + _LIBCPP_HIDE_INNER_CLASS_METHOD explicit sentry(basic_ostream<_CharT, _Traits>& __os) : __ok_(false), __os_(__os) { + if (__os.good()) { + if (__os.tie()) + __os.tie()->flush(); + __ok_ = true; + } } -} -template <class _CharT, class _Traits> -basic_ostream<_CharT, _Traits>::sentry::~sentry() { - if (__os_.rdbuf() && __os_.good() && (__os_.flags() & ios_base::unitbuf) && uncaught_exceptions() == 0) { + _LIBCPP_HIDE_INNER_CLASS_METHOD ~sentry() { + if (__os_.rdbuf() && __os_.good() && (__os_.flags() & ios_base::unitbuf) && uncaught_exceptions() == 0) { # if _LIBCPP_HAS_EXCEPTIONS - try { + try { # endif // _LIBCPP_HAS_EXCEPTIONS - if (__os_.rdbuf()->pubsync() == -1) - __os_.setstate(ios_base::badbit); + if (__os_.rdbuf()->pubsync() == -1) + __os_.setstate(ios_base::badbit); # if _LIBCPP_HAS_EXCEPTIONS - } catch (...) { - } + } catch (...) { + } # endif // _LIBCPP_HAS_EXCEPTIONS + } } -} + + sentry(const sentry&) = delete; + sentry& operator=(const sentry&) = delete; + + _LIBCPP_HIDE_FROM_ABI explicit operator bool() const { return __ok_; } +}; template <class _CharT, class _Traits> basic_ostream<_CharT, _Traits>::basic_ostream(basic_ostream&& __rhs) { diff --git a/libcxx/include/istream b/libcxx/include/istream index 95340c739c118..cb841703a2244 100644 --- a/libcxx/include/istream +++ b/libcxx/include/istream @@ -309,7 +309,30 @@ class basic_istream<_CharT, _Traits>::sentry { bool __ok_; public: - explicit sentry(basic_istream<_CharT, _Traits>& __is, bool __noskipws = false); +#if defined(__MINGW32__) || defined(__CYGWIN__) + _LIBCPP_HIDE_FROM_ABI +#else + _LIBCPP_HIDE_FROM_ABI_AFTER_V1 +#endif + explicit sentry(basic_istream<_CharT, _Traits>& __is, bool __noskipws = false) : __ok_(false) { + if (__is.good()) { + if (__is.tie()) + __is.tie()->flush(); + if (!__noskipws && (__is.flags() & ios_base::skipws)) { + typedef istreambuf_iterator<_CharT, _Traits> _Ip; + const ctype<_CharT>& __ct = std::use_facet<ctype<_CharT> >(__is.getloc()); + _Ip __i(__is); + _Ip __eof; + for (; __i != __eof; ++__i) + if (!__ct.is(__ct.space, *__i)) + break; + if (__i == __eof) + __is.setstate(ios_base::failbit | ios_base::eofbit); + } + __ok_ = __is.good(); + } else + __is.setstate(ios_base::failbit); + } // ~sentry() = default; _LIBCPP_HIDE_FROM_ABI explicit operator bool() const { return __ok_; } @@ -318,27 +341,6 @@ public: sentry& operator=(const sentry&) = delete; }; -template <class _CharT, class _Traits> -basic_istream<_CharT, _Traits>::sentry::sentry(basic_istream<_CharT, _Traits>& __is, bool __noskipws) : __ok_(false) { - if (__is.good()) { - if (__is.tie()) - __is.tie()->flush(); - if (!__noskipws && (__is.flags() & ios_base::skipws)) { - typedef istreambuf_iterator<_CharT, _Traits> _Ip; - const ctype<_CharT>& __ct = std::use_facet<ctype<_CharT> >(__is.getloc()); - _Ip __i(__is); - _Ip __eof; - for (; __i != __eof; ++__i) - if (!__ct.is(__ct.space, *__i)) - break; - if (__i == __eof) - __is.setstate(ios_base::failbit | ios_base::eofbit); - } - __ok_ = __is.good(); - } else - __is.setstate(ios_base::failbit); -} - template <class _CharT, class _Traits> basic_istream<_CharT, _Traits>::basic_istream(basic_istream&& __rhs) : __gc_(__rhs.__gc_) { __rhs.__gc_ = 0; >From 5035da0532008bb274f92f8ebf06ce5a0c9d385f Mon Sep 17 00:00:00 2001 From: Jeremy Drake <git...@jdrake.com> Date: Mon, 19 May 2025 16:31:42 -0700 Subject: [PATCH 3/3] 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits