This implements remaining changes from P3612R1 Harmonize proxy-reference
operations (LWG 3638 and 4187).

As the const-assigment 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 coresponding assigment
operator where added by P2321 in C++20 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 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 to both classes.

The deprecation warning for static vector<bool>::swap is added sinc 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>::swap): Add deprected attribute via
        _GLIBCXX26_DEPRECATED_SUGGEST.
        * include/std/bitset (bitset::operator=(bool) const)
        [__cplusplus > 202002]: 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:
        Checkfor deprecated warning on static swap.
---
Do you agree with my backport choice for swap? I think the minimum is
since C++23.

Testing on x86_64-linux. *bitset* and *vector/bool* test already passed
in all standard modes. OK for trunk?

 libstdc++-v3/include/bits/stl_bvector.h       |  4 +
 libstdc++-v3/include/std/bitset               | 43 +++++++++
 .../20_util/bitset/access/range_proxy.cc      | 94 +++++++++++++++++++
 .../20_util/bitset/access/ref_swap.cc         | 28 ++++++
 .../vector/bool/element_access/range_proxy.cc | 62 ++++++++++++
 .../vector/bool/element_access/ref_swap.cc    | 38 ++++++++
 .../vector/bool/modifiers/swap/constexpr.cc   |  2 +-
 7 files changed, 270 insertions(+), 1 deletion(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/bitset/access/ref_swap.cc
 create mode 100644 
libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc
 create mode 100644 
libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc

diff --git a/libstdc++-v3/include/bits/stl_bvector.h 
b/libstdc++-v3/include/bits/stl_bvector.h
index f190d1be5ce..0637e687cbb 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -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 a8cb337f4b1..a5822319b90 100644
--- a/libstdc++-v3/include/std/bitset
+++ b/libstdc++-v3/include/std/bitset
@@ -908,6 +908,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
          return *this;
        }
 
+#if __cplusplus > 202002L
+       // _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 00000000000..9eac384ce44
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/bitset/access/range_proxy.cc
@@ -0,0 +1,94 @@
+// { dg-do run { target c++20 } }
+
+#include <bitset>
+#include <concepts>
+#include <iterator>
+#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 __cplusplus > 202002L
+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 00000000000..1d8e3c1bf49
--- /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 00000000000..075132034f1
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/range_proxy.cc
@@ -0,0 +1,62 @@
+// { dg-do run { target c++20 } }
+
+#include <vector>
+#include <concepts>
+#include <iterator>
+#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 __cplusplus > 202002L
+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 00000000000..2221e3b03f0
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/ref_swap.cc
@@ -0,0 +1,38 @@
+// { 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()
+{
+  std::vector<bool> v{true, false};
+  std::vector<bool>::swap(v[0], v[1]); // { dg-warning "deprecated" "::swap" { 
target c++26 } }
+  VERIFY( v[0] == false );
+  VERIFY( v[1] == true );
+}
+
+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 d2a0218a109..1eb526800f7 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 } }
 
   VERIFY(v1[0] == false);
   VERIFY(v1[1] == true);
-- 
2.54.0

Reply via email to