Skip to content

libcxx: std::ostream::sentry should be exported #140169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions .github/workflows/libcxx-build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
matrix:
config: [
'frozen-cxx03-headers',
'generic-abi-unstable',
'generic-cxx03',
'generic-cxx26',
'generic-modules'
Expand All @@ -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 }}
Expand Down Expand Up @@ -239,7 +243,7 @@ jobs:

windows:
runs-on: windows-2022
needs: [ stage2 ]
#needs: [ stage2 ]
strategy:
fail-fast: false
matrix:
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
109 changes: 92 additions & 17 deletions clang/test/CodeGenCXX/mingw-template-dllexport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
20 changes: 20 additions & 0 deletions libcxx/include/__config
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__ostream/basic_ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion libcxx/include/istream
Original file line number Diff line number Diff line change
Expand Up @@ -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_; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <deque>
#include <cassert>
Expand Down
8 changes: 8 additions & 0 deletions libcxx/utils/ci/run-buildbot
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down
Loading