diff --git a/.github/workflows/libcxx-build-and-test.yaml b/.github/workflows/libcxx-build-and-test.yaml index 3551fc150e59b..4d3992c45e1df 100644 --- a/.github/workflows/libcxx-build-and-test.yaml +++ b/.github/workflows/libcxx-build-and-test.yaml @@ -44,6 +44,7 @@ jobs: matrix: config: [ 'frozen-cxx03-headers', + 'generic-abi-unstable', 'generic-cxx03', 'generic-cxx26', 'generic-modules' @@ -54,6 +55,9 @@ jobs: - config: 'generic-gcc' cc: 'gcc-14' cxx: 'g++-14' + - config: 'generic-gcc-abi-unstable' + cc: 'gcc-14' + cxx: 'g++-14' steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: ${{ matrix.config }}.${{ matrix.cxx }} @@ -239,7 +243,7 @@ jobs: windows: runs-on: windows-2022 - needs: [ stage2 ] + #needs: [ stage2 ] strategy: fail-fast: false matrix: @@ -263,10 +267,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 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 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 void c::g() {} +template int c::v = 0; +// #1 template class __declspec(dllexport) c; -// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIiE1fEv - +// #2 extern template class __declspec(dllexport) c; template class c; -// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcE1fEv - +// #3 extern template class c; -template class __declspec(dllexport) c; +template class __declspec(dllexport) c; // expected-warning {{ 'dllexport' attribute ignored on explicit instantiation definition }} -// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN1cIdE1fEv template 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 void outer::f() {} -template void outer::inner::f() {} +template void outer::g() {} +template void outer::inner::g() {} +template int outer::v = 0; +template int outer::inner::v = 0; -template class __declspec(dllexport) outer; +// #4 +template struct __declspec(dllexport) outer; -// CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1fEv -// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1fEv - -extern template class __declspec(dllimport) outer; +// #5 +extern template struct __declspec(dllimport) outer; USEMEMFUNC(outer, f) +USEMEMFUNC(outer, g) +USEMEMFUNC(outer, u) +USEMEMFUNC(outer, v) USEMEMFUNC(outer::inner, f) +USEMEMFUNC(outer::inner, g) +USEMEMFUNC(outer::inner, u) +USEMEMFUNC(outer::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 diff --git a/libcxx/include/__config b/libcxx/include/__config index 110450f6e9c51..e523aad711fbf 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 _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..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 sentry(basic_ostream<_CharT, _Traits>& __os); - ~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 95340c739c118..e229cac3bf7ef 100644 --- a/libcxx/include/istream +++ b/libcxx/include/istream @@ -309,7 +309,8 @@ class basic_istream<_CharT, _Traits>::sentry { bool __ok_; public: - explicit 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_; } diff --git a/libcxx/test/libcxx/containers/sequences/deque/incomplete.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/incomplete.pass.cpp index ebf5869078331..a7e4ce48d1157 100644 --- a/libcxx/test/libcxx/containers/sequences/deque/incomplete.pass.cpp +++ b/libcxx/test/libcxx/containers/sequences/deque/incomplete.pass.cpp @@ -11,7 +11,7 @@ // deque() // deque::iterator() -// ADDITIONAL_COMPILE_FLAGS: -Wno-macro-redefined -D_LIBCPP_ABI_INCOMPLETE_TYPES_IN_DEQUE +// ADDITIONAL_COMPILE_FLAGS: -Wno-error -Wno-macro-redefined -D_LIBCPP_ABI_INCOMPLETE_TYPES_IN_DEQUE #include #include diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot index d8b23be9a0323..721313bf46ba9 100755 --- a/libcxx/utils/ci/run-buildbot +++ b/libcxx/utils/ci/run-buildbot @@ -353,6 +353,14 @@ generic-gcc-cxx11) -DLIBUNWIND_ENABLE_WERROR=NO check-runtimes ;; +generic-gcc-abi-unstable) + clean + generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-abi-unstable.cmake" \ + -DLIBCXX_ENABLE_WERROR=NO \ + -DLIBCXXABI_ENABLE_WERROR=NO \ + -DLIBUNWIND_ENABLE_WERROR=NO + check-runtimes +;; # # Sanitizers #