The addressed regression is https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122584.
On Wed, Nov 12, 2025 at 11:21 AM Tomasz Kamiński <[email protected]> wrote: > For non-templated tests, a volatile_<T> alias is used. This alias expands > to > volatile T if std::atomic_ref<T>::is_always_lock_free is true, and to T > otherwise. For templated functions, testing is controlled using if > constexpr. > > PR libstdc++/115402 > > libstdc++-v3/ChangeLog: > > * testsuite/29_atomics/atomic_ref/address.cc: Guard test for > volatile with if constexpr. > * testsuite/29_atomics/atomic_ref/deduction.cc: Likewise. > * testsuite/29_atomics/atomic_ref/op_support.cc: Likewise. > * testsuite/29_atomics/atomic_ref/requirements.cc: Likewise. > * testsuite/29_atomics/atomic_ref/bool.cc: Use volatile_t alias. > * testsuite/29_atomics/atomic_ref/generic.cc: Likewise. > * testsuite/29_atomics/atomic_ref/integral.cc: Likewise. > * testsuite/29_atomics/atomic_ref/pointer.cc: Likewise. > * testsuite/29_atomics/atomic_ref/float.cc: Likewise, and remove > not discarding if constexpr. > --- > Tested on x86_64-linux. All *atomic_ref* test additionaly separately > tested with: > * all standard modes, > * atomic_ref<T>::is_always_lock free modified to always be false, and > * atomic_ref<T> ctor being ill-formed, to confirm the checks are triggered. > > OK for trunk? > > .../29_atomics/atomic_ref/address.cc | 7 +- > .../testsuite/29_atomics/atomic_ref/bool.cc | 10 ++- > .../29_atomics/atomic_ref/deduction.cc | 7 +- > .../testsuite/29_atomics/atomic_ref/float.cc | 34 ++++---- > .../29_atomics/atomic_ref/generic.cc | 9 +- > .../29_atomics/atomic_ref/integral.cc | 9 +- > .../29_atomics/atomic_ref/op_support.cc | 84 +++++++++++-------- > .../29_atomics/atomic_ref/pointer.cc | 9 +- > .../29_atomics/atomic_ref/requirements.cc | 83 ++++++++++-------- > 9 files changed, 155 insertions(+), 97 deletions(-) > > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/address.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/address.cc > index 42a080af55e..e2eda506efa 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/address.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/address.cc > @@ -24,8 +24,11 @@ void testAtomicRefAddressForCV() > { > testAtomicRefAddress<T>(); > testAtomicRefAddress<const T>(); > - testAtomicRefAddress<volatile T>(); > - testAtomicRefAddress<const volatile T>(); > + if constexpr (std::atomic_ref<T>::is_always_lock_free) > + { > + testAtomicRefAddress<volatile T>(); > + testAtomicRefAddress<const volatile T>(); > + } > } > > int > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc > index c73319010ee..76ba9fdbc63 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc > @@ -21,12 +21,16 @@ > > #include <atomic> > #include <testsuite_hooks.h> > +#include <type_traits> > + > +template<typename T> > +using volatile_ > + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile > T, T>; > > void > test01() > { > bool value; > - > { > const auto mo = std::memory_order_relaxed; > std::atomic_ref<bool> a(value); > @@ -66,8 +70,8 @@ test02() > std::atomic_ref<bool> a0(b); > std::atomic_ref<bool> a1(b); > std::atomic_ref<const bool> a1c(b); > - std::atomic_ref<volatile bool> a1v(b); > - std::atomic_ref<const volatile bool> a1cv(b); > + std::atomic_ref<volatile_<bool>> a1v(b); > + std::atomic_ref<volatile_<const bool>> a1cv(b); > std::atomic_ref<bool> a2(a0); > b = true; > VERIFY( a1.load() ); > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc > index 01dbfce2375..bfc2080e566 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc > @@ -33,8 +33,11 @@ test(T v) > { > test_impl<T>(v); > test_impl<const T>(v); > - test_impl<volatile T>(v); > - test_impl<const volatile T>(v); > + if constexpr (std::atomic_ref<T>::is_always_lock_free) > + { > + test_impl<volatile T>(v); > + test_impl<const volatile T>(v); > + } > } > > int main() > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc > index c69f3a711d3..5736668aeee 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc > @@ -19,6 +19,11 @@ > > #include <atomic> > #include <testsuite_hooks.h> > +#include <type_traits> > + > +template<typename T> > +using volatile_ > + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile > T, T>; > > void > test01() > @@ -297,22 +302,19 @@ test03() > void > test04() > { > - if constexpr (std::atomic_ref<float>::is_always_lock_free) > - { > - float i = 0.0f; > - std::atomic_ref<float> a0(i); > - std::atomic_ref<float> a1(i); > - std::atomic_ref<const float> a1c(i); > - std::atomic_ref<volatile float> a1v(i); > - std::atomic_ref<const volatile float> a1cv(i); > - std::atomic_ref<float> a2(a0); > - a0 = 1.0f; > - VERIFY( a1 == 1.0f ); > - VERIFY( a1c == 1.0f ); > - VERIFY( a1v == 1.0f ); > - VERIFY( a1cv == 1.0f ); > - VERIFY( a2 == 1.0f ); > - } > + float i = 0.0f; > + std::atomic_ref<float> a0(i); > + std::atomic_ref<float> a1(i); > + std::atomic_ref<const float> a1c(i); > + std::atomic_ref<volatile_<float>> a1v(i); > + std::atomic_ref<volatile_<const float>> a1cv(i); > + std::atomic_ref<float> a2(a0); > + a0 = 1.0f; > + VERIFY( a1 == 1.0f ); > + VERIFY( a1c == 1.0f ); > + VERIFY( a1v == 1.0f ); > + VERIFY( a1cv == 1.0f ); > + VERIFY( a2 == 1.0f ); > } > > int > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc > index 079ec1b1a78..7bdecdd9be8 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc > @@ -22,6 +22,11 @@ > #include <atomic> > #include <limits.h> > #include <testsuite_hooks.h> > +#include <type_traits> > + > +template<typename T> > +using volatile_ > + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile > T, T>; > > struct X > { > @@ -109,8 +114,8 @@ test02() > std::atomic_ref<X> a0(i); > std::atomic_ref<X> a1(i); > std::atomic_ref<const X> a1c(i); > - std::atomic_ref<volatile X> a1v(i); > - std::atomic_ref<const volatile X> a1cv(i); > + std::atomic_ref<volatile_<X>> a1v(i); > + std::atomic_ref<volatile_<const X>> a1cv(i); > std::atomic_ref<X> a2(a0); > a0 = 42; > VERIFY( a1.load() == 42 ); > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc > index 310434cefb5..010b40b62a0 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc > @@ -22,6 +22,11 @@ > #include <atomic> > #include <limits.h> > #include <testsuite_hooks.h> > +#include <type_traits> > + > +template<typename T> > +using volatile_ > + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile > T, T>; > > void > test01() > @@ -303,8 +308,8 @@ test03() > std::atomic_ref<int> a0(i); > std::atomic_ref<int> a1(i); > std::atomic_ref<const int> a1c(i); > - std::atomic_ref<volatile int> a1v(i); > - std::atomic_ref<const volatile int> a1cv(i); > + std::atomic_ref<volatile_<int>> a1v(i); > + std::atomic_ref<volatile_<const int>> a1cv(i); > std::atomic_ref<int> a2(a0); > a0 = 42; > VERIFY( a1 == 42 ); > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc > index 93c65dce263..38bf0603d0b 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc > @@ -2,6 +2,11 @@ > > #include <atomic> > > +template<typename T> > +concept is_supported > + = !std::is_volatile_v<T> > + || std::atomic_ref<std::remove_cv_t<T>>::is_always_lock_free; > + > template<class T> concept has_and = requires (T& a) { a &= false; }; > template<class T> concept has_or = requires (T& a) { a |= false; }; > template<class T> concept has_xor = requires (T& a) { a ^= false; }; > @@ -16,53 +21,62 @@ template<typename T> > void > no_stores() > { > - static_assert( !HAS(a = t) ); > - static_assert( !HAS(a.store(t)) ); > - static_assert( !HAS(a.store(t, mo)) ); > - static_assert( !HAS(a.exchange(t)) ); > - static_assert( !HAS(a.exchange(t, mo)) ); > - > - static_assert( !HAS(a.compare_exchange_weak(t, t)) ); > - static_assert( !HAS(a.compare_exchange_weak(t, t, mo)) ); > - static_assert( !HAS(a.compare_exchange_weak(t, t, mo, mo)) ); > - > - static_assert( !HAS(a.compare_exchange_strong(t, t)) ); > - static_assert( !HAS(a.compare_exchange_strong(t, t, mo)) ); > - static_assert( !HAS(a.compare_exchange_strong(t, t, mo, mo)) ); > + if constexpr (is_supported<T>) > + { > + static_assert( !HAS(a = t) ); > + static_assert( !HAS(a.store(t)) ); > + static_assert( !HAS(a.store(t, mo)) ); > + static_assert( !HAS(a.exchange(t)) ); > + static_assert( !HAS(a.exchange(t, mo)) ); > + > + static_assert( !HAS(a.compare_exchange_weak(t, t)) ); > + static_assert( !HAS(a.compare_exchange_weak(t, t, mo)) ); > + static_assert( !HAS(a.compare_exchange_weak(t, t, mo, mo)) ); > + > + static_assert( !HAS(a.compare_exchange_strong(t, t)) ); > + static_assert( !HAS(a.compare_exchange_strong(t, t, mo)) ); > + static_assert( !HAS(a.compare_exchange_strong(t, t, mo, mo)) ); > + } > } > > template<typename T> > void > no_additions() > { > - static_assert( !HAS(a++) ); > - static_assert( !HAS(++a) ); > - static_assert( !HAS(a += t) ); > - static_assert( !HAS(a.fetch_add(t)) ); > - static_assert( !HAS(a.fetch_add(t, mo)) ); > - > - static_assert( !HAS(a--) ); > - static_assert( !HAS(--a) ); > - static_assert( !HAS(a -= t) ); > - static_assert( !HAS(a.fetch_sub(t)) ); > - static_assert( !HAS(a.fetch_sub(t, mo)) ); > + if constexpr (is_supported<T>) > + { > + static_assert( !HAS(a++) ); > + static_assert( !HAS(++a) ); > + static_assert( !HAS(a += t) ); > + static_assert( !HAS(a.fetch_add(t)) ); > + static_assert( !HAS(a.fetch_add(t, mo)) ); > + > + static_assert( !HAS(a--) ); > + static_assert( !HAS(--a) ); > + static_assert( !HAS(a -= t) ); > + static_assert( !HAS(a.fetch_sub(t)) ); > + static_assert( !HAS(a.fetch_sub(t, mo)) ); > + } > } > > template<typename T> > void > no_bitops() > { > - static_assert( !HAS(a &= t) ); > - static_assert( !HAS(a.fetch_and(t)) ); > - static_assert( !HAS(a.fetch_and(t, mo)) ); > - > - static_assert( !HAS(a |= t) ); > - static_assert( !HAS(a.fetch_or(t)) ); > - static_assert( !HAS(a.fetch_or(t, mo)) ); > - > - static_assert( !HAS(a ^= t) ); > - static_assert( !HAS(a.fetch_xor(t)) ); > - static_assert( !HAS(a.fetch_xor(t, mo)) ); > + if constexpr (is_supported<T>) > + { > + static_assert( !HAS(a &= t) ); > + static_assert( !HAS(a.fetch_and(t)) ); > + static_assert( !HAS(a.fetch_and(t, mo)) ); > + > + static_assert( !HAS(a |= t) ); > + static_assert( !HAS(a.fetch_or(t)) ); > + static_assert( !HAS(a.fetch_or(t, mo)) ); > + > + static_assert( !HAS(a ^= t) ); > + static_assert( !HAS(a.fetch_xor(t)) ); > + static_assert( !HAS(a.fetch_xor(t, mo)) ); > + } > } > > template<typename T> > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc > index 8db45c797c8..c503b330f9d 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc > @@ -21,6 +21,11 @@ > > #include <atomic> > #include <testsuite_hooks.h> > +#include <type_traits> > + > +template<typename T> > +using volatile_ > + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile > T, T>; > > void > test01() > @@ -211,8 +216,8 @@ test03() > std::atomic_ref<int*> a0(ptr); > std::atomic_ref<int*> a1(ptr); > std::atomic_ref<int* const> a1c(ptr); > - std::atomic_ref<int* volatile> a1v(ptr); > - std::atomic_ref<int* const volatile> a1cv(ptr); > + std::atomic_ref<volatile_<int*>> a1v(ptr); > + std::atomic_ref<volatile_<int* const>> a1cv(ptr); > std::atomic_ref<int*> a2(a0); > a0 = &i; > VERIFY( a1 == &i ); > diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc > b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc > index 8617661f8e1..7f5272f61b7 100644 > --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc > +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc > @@ -20,61 +20,78 @@ > #include <atomic> > #include <type_traits> > > +template<typename T> > +concept is_supported > + = !std::is_volatile_v<T> > + || std::atomic_ref<std::remove_cv_t<T>>::is_always_lock_free; > + > template <class T> > void > test_generic() > { > - using A = std::atomic_ref<T>; > - static_assert( std::is_standard_layout_v<A> ); > - static_assert( std::is_nothrow_copy_constructible_v<A> ); > - static_assert( std::is_trivially_destructible_v<A> ); > - static_assert( std::is_same_v<typename A::value_type, > std::remove_cv_t<T>> ); > - static_assert( !requires { typename A::difference_type; } ); > - static_assert( !std::is_copy_assignable_v<A> ); > - static_assert( !std::is_move_assignable_v<A> ); > + if constexpr (is_supported<T>) > + { > + using A = std::atomic_ref<T>; > + static_assert( std::is_standard_layout_v<A> ); > + static_assert( std::is_nothrow_copy_constructible_v<A> ); > + static_assert( std::is_trivially_destructible_v<A> ); > + static_assert( std::is_same_v<typename A::value_type, > std::remove_cv_t<T>> ); > + static_assert( !requires { typename A::difference_type; } ); > + static_assert( !std::is_copy_assignable_v<A> ); > + static_assert( !std::is_move_assignable_v<A> ); > + } > } > > template <class T> > void > test_integral() > { > - using A = std::atomic_ref<T>; > - static_assert( std::is_standard_layout_v<A> ); > - static_assert( std::is_nothrow_copy_constructible_v<A> ); > - static_assert( std::is_trivially_destructible_v<A> ); > - static_assert( std::is_same_v<typename A::value_type, > std::remove_cv_t<T>> ); > - static_assert( std::is_same_v<typename A::difference_type, typename > A::value_type> ); > - static_assert( !std::is_copy_assignable_v<A> ); > - static_assert( !std::is_move_assignable_v<A> ); > + if constexpr (is_supported<T>) > + { > + using A = std::atomic_ref<T>; > + static_assert( std::is_standard_layout_v<A> ); > + static_assert( std::is_nothrow_copy_constructible_v<A> ); > + static_assert( std::is_trivially_destructible_v<A> ); > + static_assert( std::is_same_v<typename A::value_type, > std::remove_cv_t<T>> ); > + static_assert( std::is_same_v<typename A::difference_type, typename > A::value_type> ); > + static_assert( !std::is_copy_assignable_v<A> ); > + static_assert( !std::is_move_assignable_v<A> ); > + } > } > > template <class T> > void > test_floating_point() > { > - using A = std::atomic_ref<T>; > - static_assert( std::is_standard_layout_v<A> ); > - static_assert( std::is_nothrow_copy_constructible_v<A> ); > - static_assert( std::is_trivially_destructible_v<A> ); > - static_assert( std::is_same_v<typename A::value_type, > std::remove_cv_t<T>> ); > - static_assert( std::is_same_v<typename A::difference_type, typename > A::value_type> ); > - static_assert( !std::is_copy_assignable_v<A> ); > - static_assert( !std::is_move_assignable_v<A> ); > + if constexpr (is_supported<T>) > + { > + using A = std::atomic_ref<T>; > + static_assert( std::is_standard_layout_v<A> ); > + static_assert( std::is_nothrow_copy_constructible_v<A> ); > + static_assert( std::is_trivially_destructible_v<A> ); > + static_assert( std::is_same_v<typename A::value_type, > std::remove_cv_t<T>> ); > + static_assert( std::is_same_v<typename A::difference_type, typename > A::value_type> ); > + static_assert( !std::is_copy_assignable_v<A> ); > + static_assert( !std::is_move_assignable_v<A> ); > + } > } > > template <class T> > void > test_pointer() > { > - using A = std::atomic_ref<T>; > - static_assert( std::is_standard_layout_v<A> ); > - static_assert( std::is_nothrow_copy_constructible_v<A> ); > - static_assert( std::is_trivially_destructible_v<A> ); > - static_assert( std::is_same_v<typename A::value_type, > std::remove_cv_t<T>> ); > - static_assert( std::is_same_v<typename A::difference_type, > std::ptrdiff_t> ); > - static_assert( std::is_nothrow_copy_constructible_v<A> ); > - static_assert( !std::is_copy_assignable_v<A> ); > - static_assert( !std::is_move_assignable_v<A> ); > + if constexpr (is_supported<T>) > + { > + using A = std::atomic_ref<T>; > + static_assert( std::is_standard_layout_v<A> ); > + static_assert( std::is_nothrow_copy_constructible_v<A> ); > + static_assert( std::is_trivially_destructible_v<A> ); > + static_assert( std::is_same_v<typename A::value_type, > std::remove_cv_t<T>> ); > + static_assert( std::is_same_v<typename A::difference_type, > std::ptrdiff_t> ); > + static_assert( std::is_nothrow_copy_constructible_v<A> ); > + static_assert( !std::is_copy_assignable_v<A> ); > + static_assert( !std::is_move_assignable_v<A> ); > + } > } > > int > -- > 2.51.0 > >
