From: François Dumont Date: Thu, 20 Nov 2025 06:15:30 +0000 (+0100) Subject: libstdc++: Fix std::erase_if behavior for std::__debug::deque X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bbe0599297c4aa098b1751bc8c518d8c4471ac42;p=thirdparty%2Fgcc.git 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 --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 + 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 + 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 - 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 - 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 +#include + +void test01() +{ + std::deque 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 +#include + +using __gnu_debug::deque; + +void test01() +{ + deque 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; +}