On Wed, 12 Nov 2025 at 10:21, 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
Please add a second bugzilla reference here, so that a comment is
added to both PRs:
PR libstdc++/122584
OK with that change, thanks.
>
> 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
>