From 0750a607b8eca61a4c4a26a271e8e6a2a1fdfa3a Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 25 Nov 2024 00:40:00 +0800 Subject: [PATCH 1/5] Implement LWG-4169 --- stl/inc/atomic | 1 + .../Dev11_0863628_atomic_compare_exchange/test.cpp | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/stl/inc/atomic b/stl/inc/atomic index b9b48316567..fe5cbdce254 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -2123,6 +2123,7 @@ public: using _Base::_Base; + template , int> = 0> constexpr atomic() noexcept(is_nothrow_default_constructible_v<_Ty>) : _Base() {} atomic(const atomic&) = delete; diff --git a/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp b/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp index 52958c67c01..5d74ec0cce8 100644 --- a/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp +++ b/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp @@ -12,11 +12,11 @@ #include #include #include +#include #include #include #include - using namespace std; #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) @@ -438,6 +438,17 @@ STATIC_ASSERT(atomic::is_always_lock_free); STATIC_ASSERT(atomic::is_always_lock_free); #endif // _HAS_CXX17 +// Also test LWG-4169 std::atomic's default constructor should be constrained +// (backported to C++14/17 modes as we backported P0883R2) +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(is_default_constructible_v>); +STATIC_ASSERT(!is_default_constructible_v>>); +STATIC_ASSERT(!is_default_constructible_v>>); +STATIC_ASSERT(!is_default_constructible_v>>); + // Also test P0418R2 atomic compare_exchange memory_order Requirements void test_compare_exchange_relaxed_memory_orders() { From e682b08c3677381ff536705a03d27b96006213b1 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 25 Nov 2024 01:47:28 +0800 Subject: [PATCH 2/5] Workaround for EDG --- stl/inc/atomic | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/stl/inc/atomic b/stl/inc/atomic index fe5cbdce254..a07dc817457 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -2804,9 +2804,17 @@ _EXPORT_STD struct atomic_flag { // flag with test-and-set semantics #endif // _HAS_CXX20 #if 1 // TRANSITION, ABI - atomic _Storage; + atomic _Storage +#if !_HAS_CXX20 && defined(__EDG__) + {0L} +#endif // ^^^ workaround ^^^ + ; #else // ^^^ don't break ABI / break ABI vvv - atomic _Storage; + atomic _Storage +#if !_HAS_CXX20 && defined(__EDG__) + {false} +#endif // ^^^ workaround ^^^ + ; #endif // ^^^ break ABI ^^^ }; From b726bc1f6b9b176a549ea759457dde70b792999e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 25 Nov 2024 09:58:43 +0800 Subject: [PATCH 3/5] Revert "Workaround for EDG" Also avoid inheriting constructors and additional copy. This reverts commit e682b08c3677381ff536705a03d27b96006213b1. --- stl/inc/atomic | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/stl/inc/atomic b/stl/inc/atomic index a07dc817457..33e28011ad8 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -586,7 +586,7 @@ struct _Atomic_storage { _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept + /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept : _Storage(_Value) { // non-atomically initialize this atomic } @@ -719,7 +719,7 @@ struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept + /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -822,7 +822,7 @@ struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept + /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -924,7 +924,7 @@ struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept + /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -1026,7 +1026,7 @@ struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal> _Value) noexcept + /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -1148,7 +1148,7 @@ struct _Atomic_storage<_Ty&, 16> { // lock-free using 16-byte intrinsics _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty&, const _TVal> _Value) noexcept + /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty&, const _TVal&> _Value) noexcept : _Storage{_Value} {} // non-atomically initialize this atomic void store(const _TVal _Value) noexcept { // store with sequential consistency @@ -2121,11 +2121,11 @@ public: using value_type = _Ty; - using _Base::_Base; - template , int> = 0> constexpr atomic() noexcept(is_nothrow_default_constructible_v<_Ty>) : _Base() {} + /* implicit */ constexpr atomic(const _Ty _Value) noexcept : _Base(_Value) {} + atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; atomic& operator=(const atomic&) volatile = delete; @@ -2804,17 +2804,9 @@ _EXPORT_STD struct atomic_flag { // flag with test-and-set semantics #endif // _HAS_CXX20 #if 1 // TRANSITION, ABI - atomic _Storage -#if !_HAS_CXX20 && defined(__EDG__) - {0L} -#endif // ^^^ workaround ^^^ - ; + atomic _Storage; #else // ^^^ don't break ABI / break ABI vvv - atomic _Storage -#if !_HAS_CXX20 && defined(__EDG__) - {false} -#endif // ^^^ workaround ^^^ - ; + atomic _Storage; #endif // ^^^ break ABI ^^^ }; From ca9beb8386d5756e9de21a7d01e8f5a3b5315255 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 27 Nov 2024 18:22:14 -0800 Subject: [PATCH 4/5] Simplify `_Atomic_storage` converting constructors --- stl/inc/atomic | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/stl/inc/atomic b/stl/inc/atomic index 33e28011ad8..c6f7656235b 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -580,14 +580,14 @@ template struct _Atomic_storage { // Provides operations common to all specializations of std::atomic, load, store, exchange, and CAS. // Locking version used when hardware has no atomic operations for sizeof(_Ty). + _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); using _TVal = remove_reference_t<_Ty>; using _Guard = _Atomic_lock_guard::_Spinlock>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept - : _Storage(_Value) { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage(_Value) { // non-atomically initialize this atomic } @@ -714,13 +714,13 @@ public: template struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics + _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept - : _Storage{_Value} { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -817,13 +817,13 @@ struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics template struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics + _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept - : _Storage{_Value} { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -919,13 +919,13 @@ struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics template struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics + _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept - : _Storage{_Value} { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -1021,13 +1021,13 @@ struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics template struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics + _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty, const _TVal&> _Value) noexcept - : _Storage{_Value} { + /* implicit */ constexpr _Atomic_storage(const _Ty& _Value) noexcept : _Storage{_Value} { // non-atomically initialize this atomic } @@ -1143,12 +1143,15 @@ struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics #ifdef _WIN64 template struct _Atomic_storage<_Ty&, 16> { // lock-free using 16-byte intrinsics + _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); + // TRANSITION, ABI: replace '_Ty&' with '_Ty' in this specialization using _TVal = remove_reference_t<_Ty&>; _Atomic_storage() = default; - /* implicit */ constexpr _Atomic_storage(conditional_t, _Ty&, const _TVal&> _Value) noexcept + // TRANSITION, ABI: replace _this_ occurrence of '_Ty&' with 'const _Ty&' + /* implicit */ constexpr _Atomic_storage(_Ty& _Value) noexcept : _Storage{_Value} {} // non-atomically initialize this atomic void store(const _TVal _Value) noexcept { // store with sequential consistency From 576324e8719f42e09e05ef7d2f64d2a2caef9633 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 27 Nov 2024 18:31:54 -0800 Subject: [PATCH 5/5] Remove paranoia `static_assert`s These are overkill. The constructors are ill-formed if `_Ty` is an rvalue reference type, and they were ill-formed before the latest change to the parameter types. --- stl/inc/atomic | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/stl/inc/atomic b/stl/inc/atomic index c6f7656235b..9f4da0a47e3 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -580,7 +580,6 @@ template struct _Atomic_storage { // Provides operations common to all specializations of std::atomic, load, store, exchange, and CAS. // Locking version used when hardware has no atomic operations for sizeof(_Ty). - _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); using _TVal = remove_reference_t<_Ty>; using _Guard = _Atomic_lock_guard::_Spinlock>; @@ -714,8 +713,6 @@ public: template struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics - _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); - using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; @@ -817,8 +814,6 @@ struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics template struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics - _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); - using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; @@ -919,8 +914,6 @@ struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics template struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics - _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); - using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; @@ -1021,8 +1014,6 @@ struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics template struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics - _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); - using _TVal = remove_reference_t<_Ty>; _Atomic_storage() = default; @@ -1143,8 +1134,6 @@ struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics #ifdef _WIN64 template struct _Atomic_storage<_Ty&, 16> { // lock-free using 16-byte intrinsics - _STL_INTERNAL_STATIC_ASSERT(!is_rvalue_reference_v<_Ty>); - // TRANSITION, ABI: replace '_Ty&' with '_Ty' in this specialization using _TVal = remove_reference_t<_Ty&>;