]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: [_GLIBCXX_DEBUG] Fix std::erase_if behavior for __gnu_debug::vector
authorFrançois Dumont <frs.dumont@gmail.com>
Thu, 20 Nov 2025 06:01:53 +0000 (07:01 +0100)
committerFrançois Dumont <fdumont@gcc.gnu.org>
Thu, 20 Nov 2025 18:10:20 +0000 (19:10 +0100)
When using directly __gnu_debug::vector the std::erase_if is called with a
reference to the std::vector base class and so is missing the invalidation
of the iterators implied by this operation.

To fix this provide a std::erase_if overload dedicated to __gnu_debug::vector.
Doing so we can cleanup the implementation dedicated to std::vector from any
_GLIBCXX_DEBUG consideration.

libstdc++-v3/ChangeLog:

* include/debug/vector (std::erase_if, std::erase): New overloads for
std::__debug::vector instances.
* include/std/vector (std::erase_if, std::erase): Make overloads specific
to normal std::vector implementation.
* testsuite/23_containers/vector/debug/erase.cc: New test case.
* testsuite/23_containers/vector/debug/invalidation/erase.cc: New test case.

libstdc++-v3/include/debug/vector
libstdc++-v3/include/std/vector
libstdc++-v3/testsuite/23_containers/vector/debug/erase.cc [new file with mode: 0644]
libstdc++-v3/testsuite/23_containers/vector/debug/invalidation/erase.cc [new file with mode: 0644]

index 1b3486b0dc08ded11b87d01b729d1f5dfb4bf9ed..f952fe9255b76b79ecead0ce31edf134cb1845dd 100644 (file)
@@ -1038,6 +1038,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   }  // namespace __detail::__variant
 #endif // C++17
 
+#ifdef __glibcxx_erase_if // C++ >= 20 && HOSTED
+  template<typename _Tp, typename _Alloc, typename _Predicate>
+    constexpr typename __debug::vector<_Tp, _Alloc>::size_type
+    erase_if(__debug::vector<_Tp, _Alloc>& __cont, _Predicate __pred)
+    {
+      _GLIBCXX_STD_C::vector<_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>
+    constexpr typename __debug::vector<_Tp, _Alloc>::size_type
+    erase(__debug::vector<_Tp, _Alloc>& __cont, const _Up& __value)
+    { return std::erase_if(__cont, __gnu_cxx::__ops::__equal_to(__value)); }
+#endif // __glibcxx_erase_if
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
index 920f852f79448fe1c434285fc8aca51e80462614..375011fff693ce3a23134d47a9d84e524501b21d 100644 (file)
@@ -112,19 +112,16 @@ namespace std _GLIBCXX_VISIBILITY(default)
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Tp, typename _Alloc, typename _Predicate>
-    constexpr typename vector<_Tp, _Alloc>::size_type
-    erase_if(vector<_Tp, _Alloc>& __cont, _Predicate __pred)
+    constexpr typename _GLIBCXX_STD_C::vector<_Tp, _Alloc>::size_type
+    erase_if(_GLIBCXX_STD_C::vector<_Tp, _Alloc>& __cont, _Predicate __pred)
     {
-      using namespace __gnu_cxx;
-      _GLIBCXX_STD_C::vector<_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();
        }
 
@@ -133,8 +130,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Tp, typename _Alloc,
           typename _Up _GLIBCXX26_DEF_VAL_T(_Tp)>
-    constexpr typename vector<_Tp, _Alloc>::size_type
-    erase(vector<_Tp, _Alloc>& __cont, const _Up& __value)
+    constexpr typename _GLIBCXX_STD_C::vector<_Tp, _Alloc>::size_type
+    erase(_GLIBCXX_STD_C::vector<_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/vector/debug/erase.cc b/libstdc++-v3/testsuite/23_containers/vector/debug/erase.cc
new file mode 100644 (file)
index 0000000..88ec547
--- /dev/null
@@ -0,0 +1,28 @@
+// { dg-do run { target c++20 } }
+// { dg-add-options no_pch }
+// { dg-require-debug-mode "" }
+
+#include <vector>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+  std::vector<int> v;
+
+  for (int i = 0; i != 10; ++i)
+    v.push_back(i);
+
+  auto before = v.begin() + 4;
+  auto last = v.end() - 1;
+
+  VERIFY( std::erase(v, 6) == 1 );
+
+  VERIFY(before._M_dereferenceable());
+  VERIFY(last._M_singular());
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/23_containers/vector/debug/invalidation/erase.cc b/libstdc++-v3/testsuite/23_containers/vector/debug/invalidation/erase.cc
new file mode 100644 (file)
index 0000000..e7dec56
--- /dev/null
@@ -0,0 +1,29 @@
+// { dg-do run { target c++20 } }
+// { dg-add-options no_pch }
+
+#include <debug/vector>
+#include <testsuite_hooks.h>
+
+using __gnu_debug::vector;
+
+void test01()
+{
+  vector<int> v;
+
+  for (int i = 0; i != 10; ++i)
+    v.push_back(i);
+
+  auto before = v.begin() + 4;
+  auto last = v.end() -1;
+
+  VERIFY( std::erase(v, 6) == 1 );
+
+  VERIFY(before._M_dereferenceable());
+  VERIFY(last._M_singular());
+}
+
+int main()
+{
+  test01();
+  return 0;
+}