https://gcc.gnu.org/g:427911622fcae352f086dd543b21c16071f7e4ab
commit r16-5215-g427911622fcae352f086dd543b21c16071f7e4ab Author: Tomasz Kamiński <[email protected]> Date: Wed Nov 12 11:16:58 2025 +0100 libtdc++: Test atomic_ref<volatile T> only if operations are lock-free [PR122584] 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 PR libstdc++/122584 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. Reviewed-by: Jonathan Wakely <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- .../testsuite/29_atomics/atomic_ref/address.cc | 7 +- .../testsuite/29_atomics/atomic_ref/bool.cc | 10 ++- .../testsuite/29_atomics/atomic_ref/deduction.cc | 7 +- .../testsuite/29_atomics/atomic_ref/float.cc | 34 ++++----- .../testsuite/29_atomics/atomic_ref/generic.cc | 9 ++- .../testsuite/29_atomics/atomic_ref/integral.cc | 9 ++- .../testsuite/29_atomics/atomic_ref/op_support.cc | 84 +++++++++++++--------- .../testsuite/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 42a080af55e2..e2eda506efa2 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 c73319010ee8..76ba9fdbc632 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 01dbfce23751..bfc2080e566a 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 c69f3a711d34..5736668aeee1 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 079ec1b1a785..7bdecdd9be8d 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 310434cefb54..010b40b62a0d 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 93c65dce2636..3afa0bb6a8d2 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 8db45c797c8d..c503b330f9d6 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 8617661f8e13..5d67c77daa8d 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
