https://gcc.gnu.org/g:55c330d029e2e6b20a62ab208262b34c6cd78acb
commit r17-1912-g55c330d029e2e6b20a62ab208262b34c6cd78acb Author: Tomasz Kamiński <[email protected]> Date: Thu Jun 25 13:28:04 2026 +0200 libstdc++: Make bitset::reference a proper range proxy. This implements remaining changes from P3612R1 Harmonize proxy-reference operations (LWG 3638 and 4187). As the const-assignment operator in bitset::reference (from LWG4187) is used by range concepts (std::indirectly_writable) to recognize iterator returning it by value as proxy iterator (see range_proxy.cc) test, this change is backported to C++23. This puts it inline with vector<bool> reference and other proxy type, for which the corresponding assignment operator where added by P2321 in C++23 and implemented in r13-2159-g72886f. Similary, a swap overloads accepting bitset::reference by value and bool&, are necessary for ranges::swap and ranges::iter_swap to work correctly, so they should also be backported. This patch follows the approach from resolution of LWG3638 from r12-5311-g59434931fb658f, and backports the overloads to C++11. This harmonizes the operations on this two proxy types (per paper title). Corresponding test are added for both classes. The deprecation warning for static vector<bool>::swap is added since C++26. The text in _GLIBCXX26_DEPRECATED_SUGGEST leads to following message: 'static constexpr void std::vector<bool, _Alloc>::swap(reference, reference) [with... ]' is deprecated: use 'swap' invoked via ADL or 'std::ranges::swap' instead libstdc++-v3/ChangeLog: * include/bits/stl_bvector.h: Add _GLIBCXX_RESOLVE_LIB_DEFECTS comment for 3638. (vector<bool, _Alloc>::operator=(bool) const) [__glibcxx_ranges_zip]: Adjust guard. (vector<bool, _Alloc>::swap): Add deprected attribute via _GLIBCXX26_DEPRECATED_SUGGEST. * include/std/bitset (bitset::operator=(bool) const) [__glibcxx_ranges_zip]: Define. (bistset::reference::swap) [__cplusplus >= 201103L]: Define. * testsuite/20_util/bitset/access/range_proxy.cc: New test. * testsuite/20_util/bitset/access/ref_swap.cc: New test. * testsuite/23_containers/vector/bool/element_access/range_proxy.cc: New test. * testsuite/23_containers/vector/bool/element_access/ref_swap.cc: New test. * testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc: Check for deprecated warning on static swap. Reviewed-by: Jonathan Wakely <[email protected]> Signed-off-by: Tomasz Kamiński <[email protected]> Diff: --- libstdc++-v3/include/bits/stl_bvector.h | 6 +- libstdc++-v3/include/std/bitset | 43 ++++++++++ .../testsuite/20_util/bitset/access/range_proxy.cc | 95 ++++++++++++++++++++++ .../testsuite/20_util/bitset/access/ref_swap.cc | 28 +++++++ .../vector/bool/element_access/range_proxy.cc | 63 ++++++++++++++ .../vector/bool/element_access/ref_swap.cc | 40 +++++++++ .../vector/bool/modifiers/swap/constexpr.cc | 2 +- 7 files changed, 275 insertions(+), 2 deletions(-) diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h index f190d1be5cec..d792f7077860 100644 --- a/libstdc++-v3/include/bits/stl_bvector.h +++ b/libstdc++-v3/include/bits/stl_bvector.h @@ -116,7 +116,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER return *this; } -#if __cplusplus > 202002L +#if __glibcxx_ranges_zip // >= C++23 constexpr const _Bit_reference& operator=(bool __x) const noexcept { @@ -149,6 +149,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { *_M_p ^= _M_mask; } #if __cplusplus >= 201103L + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3638. vector<bool>::swap(reference, reference) is useless + _GLIBCXX20_CONSTEXPR friend void swap(_Bit_reference __x, _Bit_reference __y) noexcept @@ -1266,6 +1269,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER } // [23.2.5]/1, third-to-last entry in synopsis listing + _GLIBCXX26_DEPRECATED_SUGGEST("swap' invoked via ADL or 'std::ranges::swap") _GLIBCXX20_CONSTEXPR static void swap(reference __x, reference __y) _GLIBCXX_NOEXCEPT diff --git a/libstdc++-v3/include/std/bitset b/libstdc++-v3/include/std/bitset index a8cb337f4b15..d581606e7dde 100644 --- a/libstdc++-v3/include/std/bitset +++ b/libstdc++-v3/include/std/bitset @@ -908,6 +908,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER return *this; } +#if __glibcxx_ranges_zip // >= C++23 + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4187. bitset::reference should be const-assignable + constexpr const reference& + operator=(bool __x) const noexcept + { + if (__x) + *_M_wp |= _Base::_S_maskbit(_M_bpos); + else + *_M_wp &= ~_Base::_S_maskbit(_M_bpos); + return *this; + } +#endif // C++23 + // For b[i] = b[__j]; _GLIBCXX23_CONSTEXPR reference& @@ -939,6 +953,35 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER *_M_wp ^= _Base::_S_maskbit(_M_bpos); return *this; } + +#if __cplusplus >= 201103L + _GLIBCXX23_CONSTEXPR + friend void + swap(reference __x, reference __y) noexcept + { + bool __tmp = __x; + __x = __y; + __y = __tmp; + } + + _GLIBCXX23_CONSTEXPR + friend void + swap(reference __x, bool& __y) noexcept + { + bool __tmp = __x; + __x = __y; + __y = __tmp; + } + + _GLIBCXX23_CONSTEXPR + friend void + swap(bool& __x, reference __y) noexcept + { + bool __tmp = __x; + __x = __y; + __y = __tmp; + } +#endif }; friend class reference; diff --git a/libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc b/libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc new file mode 100644 index 000000000000..b485a50bef8c --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc @@ -0,0 +1,95 @@ +// { dg-do run { target c++20 } } + +#include <bitset> +#include <concepts> +#include <iterator> +#include <version> +#include <testsuite_hooks.h> + +void +test_swap() +{ + std::bitset<2> v("01"); + std::ranges::swap(v[0], v[1]); + VERIFY( v[0] == false ); + VERIFY( v[1] == true ); + + bool b = true; + std::ranges::swap(v[0], b); + VERIFY( v[0] == true ); + VERIFY( b == false ); + + std::ranges::swap(b, v[1]); + VERIFY( v[1] == false ); + VERIFY( b == true ); +} + +template<size_t N> +struct bitset_iterator +{ + using value_type = bool; + using reference = std::bitset<2>::reference; + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + + bitset_iterator() = default; + bitset_iterator(std::bitset<N>& c) noexcept : cont(&c) {} + + reference operator*() const noexcept + { return (*cont)[pos]; } + + bitset_iterator& operator++() { + ++pos; + return *this; + } + + bitset_iterator operator++(int) { + auto tmp = *this; + ++pos; + return *this; + }; + + friend bool operator==(bitset_iterator, bitset_iterator) = default; + +private: + std::bitset<N>* cont = nullptr; + size_t pos = 0; +}; + +void +test_iter_swap() +{ + std::bitset<2> v("01"); + auto it1 = bitset_iterator<2>(v); + auto it2 = std::ranges::next(it1); + std::ranges::iter_swap(it1, it2); + VERIFY( v[0] == false ); + VERIFY( v[1] == true ); + + bool b = true, *p = &b; + std::ranges::iter_swap(it1, p); + VERIFY( v[0] == true ); + VERIFY( b == false ); + + std::ranges::iter_swap(p, it2); + VERIFY( v[1] == false ); + VERIFY( b == true ); +} + +#if __cpp_lib_ranges_zip +static_assert( std::indirectly_writable<bitset_iterator<2>, bool> ); +static_assert( std::indirectly_writable<bitset_iterator<2>, + std::bitset<2>::reference> ); + +#else +static_assert( !std::indirectly_writable<bitset_iterator<2>, bool> ); +static_assert( !std::indirectly_writable<bitset_iterator<2>, + std::bitset<2>::reference> ); +#endif + +int +main() +{ + test_swap(); + test_iter_swap(); +} diff --git a/libstdc++-v3/testsuite/20_util/bitset/access/ref_swap.cc b/libstdc++-v3/testsuite/20_util/bitset/access/ref_swap.cc new file mode 100644 index 000000000000..1d8e3c1bf490 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bitset/access/ref_swap.cc @@ -0,0 +1,28 @@ +// { dg-do run { target c++11 } } + +#include <bitset> +#include <testsuite_hooks.h> + +void +test_friend() +{ + std::bitset<2> v("01"); + swap(v[0], v[1]); + VERIFY( v[0] == false ); + VERIFY( v[1] == true ); + + bool b = true; + swap(v[0], b); + VERIFY( v[0] == true ); + VERIFY( b == false ); + + swap(b, v[1]); + VERIFY( v[1] == false ); + VERIFY( b == true ); +} + +int +main() +{ + test_friend(); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc new file mode 100644 index 000000000000..2d59fbfdaa67 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc @@ -0,0 +1,63 @@ +// { dg-do run { target c++20 } } + +#include <vector> +#include <concepts> +#include <iterator> +#include <version> +#include <testsuite_hooks.h> + +void +test_swap() +{ + std::vector<bool> v{true, false}; + std::ranges::swap(v[0], v[1]); + VERIFY( v[0] == false ); + VERIFY( v[1] == true ); + + bool b = true; + std::ranges::swap(v[0], b); + VERIFY( v[0] == true ); + VERIFY( b == false ); + + std::ranges::swap(b, v[1]); + VERIFY( v[1] == false ); + VERIFY( b == true ); +} + +void +test_iter_swap() +{ + std::vector<bool> v{true, false}; + auto it1 = v.begin(); + auto it2 = std::ranges::next(it1); + std::ranges::iter_swap(it1, it2); + VERIFY( v[0] == false ); + VERIFY( v[1] == true ); + + bool b = true, *p = &b; + std::ranges::iter_swap(it1, p); + VERIFY( v[0] == true ); + VERIFY( b == false ); + + std::ranges::iter_swap(p, it2); + VERIFY( v[1] == false ); + VERIFY( b == true ); +} + +#if __cpp_lib_ranges_zip +static_assert( std::indirectly_writable<std::vector<bool>::iterator, bool> ); +static_assert( std::indirectly_writable<std::vector<bool>::iterator, + std::vector<bool>::reference> ); + +#else +static_assert( !std::indirectly_writable<std::vector<bool>::iterator, bool> ); +static_assert( !std::indirectly_writable<std::vector<bool>::iterator, + std::vector<bool>::reference> ); +#endif + +int +main() +{ + test_swap(); + test_iter_swap(); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc new file mode 100644 index 000000000000..5c250cb2bbd2 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc @@ -0,0 +1,40 @@ +// { dg-do run { target c++11 } } + +#include <vector> +#include <testsuite_hooks.h> + +void +test_friend() +{ + std::vector<bool> v{true, false}; + swap(v[0], v[1]); + VERIFY( v[0] == false ); + VERIFY( v[1] == true ); + + bool b = true; + swap(v[0], b); + VERIFY( v[0] == true ); + VERIFY( b == false ); + + swap(b, v[1]); + VERIFY( v[1] == false ); + VERIFY( b == true ); +} + +void +test_static() +{ +#ifndef _GLIBCXX_DEBUG + std::vector<bool> v{true, false}; + std::vector<bool>::swap(v[0], v[1]); // { dg-warning "deprecated" "::swap" { target { c++26 && { ! debug_mode } } } } + VERIFY( v[0] == false ); + VERIFY( v[1] == true ); +#endif +} + +int +main() +{ + test_friend(); + test_static(); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc index d2a0218a1097..b269b16249bc 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/swap/constexpr.cc @@ -53,7 +53,7 @@ constexpr bool test_reference_swap() { std::vector<bool> v1 = {true, false, true}; - std::vector<bool>::swap(v1[0], v1[1]); + std::vector<bool>::swap(v1[0], v1[1]); // { dg-warning "deprecated" "::swap" { target { c++26 && { ! debug_mode } } } } VERIFY(v1[0] == false); VERIFY(v1[1] == true);
