std::erase and std::erase_if are broken for users directly referencing
    __gnu_debug::deque in their code that is to say without activating the
    _GLIBCXX_DEBUG mode. The iterators potentially invalidated by the erase
    operations are not detected by the __gnu_debug::deque container and so
    won't be reported as invalidated.

    We need explicit std::erase and std::erase_if implementations for
    std::__debug::deque which will work also when _GLIBCXX_DEBUG mode is
    activated.

    libstdc++-v3/ChangeLog:

            * include/debug/deque
(std::erase_if<>(std::__debug::deque<>&, _Pred): New.
            (std::erase<>(std::__debug::deque<>&, const _Up&): New.
            * include/std/deque (std::erase_if(std::deque<>&, _Pred)): Remove
            _GLIBCXX_DEBUG code.
            * testsuite/23_containers/deque/debug/erase.cc: New test case.
            * testsuite/23_containers/deque/debug/invalidation/erase.cc: New test case.

23_containers/deque tests run normal and _GLIBCXX_DEBUG under Linux x86_64.

PR https://forge.sourceware.org/gcc/gcc-TEST/pulls/128

Is PR CI state fine as proof of tests ?

This is why I did a PR for such a trivial patch. My 10 years old laptop is definitely too old to run the full testsuite.

Ok to commit ?

François

diff --git a/libstdc++-v3/include/debug/deque b/libstdc++-v3/include/debug/deque
index ed69eb842e2..b2e5dd32717 100644
--- a/libstdc++-v3/include/debug/deque
+++ b/libstdc++-v3/include/debug/deque
@@ -771,6 +771,34 @@ namespace __debug
     { __lhs.swap(__rhs); }
 
 } // namespace __debug
+
+#ifdef __glibcxx_erase_if // C++ >= 20 && HOSTED
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  template<typename _Tp, typename _Alloc, typename _Predicate>
+    inline typename __debug::deque<_Tp, _Alloc>::size_type
+    erase_if(__debug::deque<_Tp, _Alloc>& __cont, _Predicate __pred)
+    {
+      _GLIBCXX_STD_C::deque<_Tp, _Alloc>& __unsafe_cont = __cont;
+      const auto __osz = __cont.size();
+      const auto __end = __unsafe_cont.end();
+      auto __removed = std::__remove_if(__unsafe_cont.begin(), __end,
+                                       std::move(__pred));
+      if (__removed != __end)
+       {
+         __cont.erase(__niter_wrap(__cont.begin(), __removed),
+                      __cont.end());
+         return __osz - __cont.size();
+       }
+
+      return 0;
+    }
+
+  template<typename _Tp, typename _Alloc, typename _Up = _Tp>
+    inline typename __debug::deque<_Tp, _Alloc>::size_type
+    erase(__debug::deque<_Tp, _Alloc>& __cont, const _Up& __value)
+    { return std::erase_if(__cont, __gnu_cxx::__ops::__equal_to(__value)); }
+_GLIBCXX_END_NAMESPACE_VERSION
+#endif // __cpp_lib_erase_if
 } // namespace std
 
 #endif
diff --git a/libstdc++-v3/include/std/deque b/libstdc++-v3/include/std/deque
index c82f9dff286..600f607ac7d 100644
--- a/libstdc++-v3/include/std/deque
+++ b/libstdc++-v3/include/std/deque
@@ -100,19 +100,16 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Tp, typename _Alloc, typename _Predicate>
-    inline typename deque<_Tp, _Alloc>::size_type
-    erase_if(deque<_Tp, _Alloc>& __cont, _Predicate __pred)
+    inline typename _GLIBCXX_STD_C::deque<_Tp, _Alloc>::size_type
+    erase_if(_GLIBCXX_STD_C::deque<_Tp, _Alloc>& __cont, _Predicate __pred)
     {
-      using namespace __gnu_cxx;
-      _GLIBCXX_STD_C::deque<_Tp, _Alloc>& __ucont = __cont;
       const auto __osz = __cont.size();
-      const auto __end = __ucont.end();
-      auto __removed = std::__remove_if(__ucont.begin(), __end,
+      const auto __end = __cont.end();
+      auto __removed = std::__remove_if(__cont.begin(), __end,
                                        std::move(__pred));
       if (__removed != __end)
        {
-         __cont.erase(__niter_wrap(__cont.begin(), __removed),
-                      __cont.end());
+         __cont.erase(__removed, __end);
          return __osz - __cont.size();
        }
 
@@ -121,8 +118,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Tp, typename _Alloc,
           typename _Up _GLIBCXX26_DEF_VAL_T(_Tp)>
-    inline typename deque<_Tp, _Alloc>::size_type
-    erase(deque<_Tp, _Alloc>& __cont, const _Up& __value)
+    inline typename _GLIBCXX_STD_C::deque<_Tp, _Alloc>::size_type
+    erase(_GLIBCXX_STD_C::deque<_Tp, _Alloc>& __cont, const _Up& __value)
     { return std::erase_if(__cont, __gnu_cxx::__ops::__equal_to(__value)); }
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/23_containers/deque/debug/erase.cc 
b/libstdc++-v3/testsuite/23_containers/deque/debug/erase.cc
new file mode 100644
index 00000000000..d8c36bb11e6
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/deque/debug/erase.cc
@@ -0,0 +1,27 @@
+// { dg-do run { target c++20 } }
+// { dg-require-debug-mode "" }
+
+#include <deque>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  std::deque<int> d;
+
+  for (int i = 0; i != 10; ++i)
+    d.push_back(i);
+
+  auto before = d.begin() + 4;
+  auto last = d.end() - 1;
+
+  VERIFY( std::erase(d, 6) == 1 );
+
+  VERIFY(before._M_dereferenceable());
+  VERIFY(last._M_singular());
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/deque/debug/invalidation/erase.cc 
b/libstdc++-v3/testsuite/23_containers/deque/debug/invalidation/erase.cc
new file mode 100644
index 00000000000..c18a5ff4080
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/deque/debug/invalidation/erase.cc
@@ -0,0 +1,28 @@
+// { dg-do run { target c++20 } }
+
+#include <debug/deque>
+#include <testsuite_hooks.h>
+
+using __gnu_debug::deque;
+
+void test01()
+{
+  deque<int> d;
+
+  for (int i = 0; i != 10; ++i)
+    d.push_back(i);
+
+  auto before = d.begin() + 4;
+  auto last = d.end() -1;
+
+  VERIFY( std::erase(d, 6) == 1 );
+
+  VERIFY(before._M_dereferenceable());
+  VERIFY(last._M_singular());
+}
+
+int main()
+{
+  test01();
+  return 0;
+}

Reply via email to