[gcc r16-5892] libstdc++: Fix std::erase_if behavior for std::__debug::deque
Francois Dumont
fdumont@gcc.gnu.org
Thu Dec 4 21:04:47 GMT 2025
https://gcc.gnu.org/g:bbe0599297c4aa098b1751bc8c518d8c4471ac42
commit r16-5892-gbbe0599297c4aa098b1751bc8c518d8c4471ac42
Author: François Dumont <frs.dumont@gmail.com>
Date: Thu Nov 20 07:15:30 2025 +0100
libstdc++: Fix std::erase_if behavior for std::__debug::deque
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.
Diff:
---
libstdc++-v3/include/debug/deque | 28 ++++++++++++++++++++++
libstdc++-v3/include/std/deque | 17 ++++++-------
.../testsuite/23_containers/deque/debug/erase.cc | 27 +++++++++++++++++++++
.../deque/debug/invalidation/erase.cc | 28 ++++++++++++++++++++++
4 files changed, 90 insertions(+), 10 deletions(-)
diff --git a/libstdc++-v3/include/debug/deque b/libstdc++-v3/include/debug/deque
index ed69eb842e2b..b2e5dd327176 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 c82f9dff2869..600f607ac7d3 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 000000000000..d8c36bb11e6c
--- /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 000000000000..c18a5ff4080a
--- /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;
+}
More information about the Libstdc++-cvs
mailing list