[clang] [libcxx] libcxx: std::ostream::sentry should be exported (PR #140169)
via cfe-commits
cfe-commits at lists.llvm.org
Mon May 19 16:27:04 PDT 2025
https://github.com/jeremyd2019 updated https://github.com/llvm/llvm-project/pull/140169
>From 47f81a804a36a5b685f130f22d2ab5d330170861 Mon Sep 17 00:00:00 2001
From: kikairoya <kikairoya at gmail.com>
Date: Mon, 21 Apr 2025 23:30:13 +0900
Subject: [PATCH 1/2] [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 9275c093b51b0dae0b883e0d42c14507f8fc9870 Mon Sep 17 00:00:00 2001
From: Jeremy Drake <github at jdrake.com>
Date: Mon, 19 May 2025 16:01:57 -0700
Subject: [PATCH 2/2] test _LIBCPP_HIDE_FROM_ABI on sentry methods
---
libcxx/include/__ostream/basic_ostream.h | 44 ++++++++++--------------
libcxx/include/istream | 41 ++++++++++------------
2 files changed, 38 insertions(+), 47 deletions(-)
diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index f7473a36d8ccc..9a3fb4ec1313d 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -186,37 +186,31 @@ class basic_ostream<_CharT, _Traits>::sentry {
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_FROM_ABI 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_FROM_ABI ~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
+ }
}
-}
+ _LIBCPP_HIDE_FROM_ABI sentry(const sentry&) = delete;
+ _LIBCPP_HIDE_FROM_ABI 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..3cd4482f6cfd9 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -309,7 +309,25 @@ class basic_istream<_CharT, _Traits>::sentry {
bool __ok_;
public:
- explicit sentry(basic_istream<_CharT, _Traits>& __is, bool __noskipws = false);
+ _LIBCPP_HIDE_FROM_ABI 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 +336,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;
More information about the cfe-commits
mailing list