https://gcc.gnu.org/g:e166a0f5ee619f3f97107024ae4671dff9708507
commit r16-5460-ge166a0f5ee619f3f97107024ae4671dff9708507 Author: François Dumont <[email protected]> Date: Thu Nov 20 07:01:53 2025 +0100 libstdc++: [_GLIBCXX_DEBUG] Fix std::erase_if behavior for __gnu_debug::vector 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. Diff: --- libstdc++-v3/include/debug/vector | 26 +++++++++++++++++++ libstdc++-v3/include/std/vector | 17 ++++++------- .../testsuite/23_containers/vector/debug/erase.cc | 28 +++++++++++++++++++++ .../vector/debug/invalidation/erase.cc | 29 ++++++++++++++++++++++ 4 files changed, 90 insertions(+), 10 deletions(-) diff --git a/libstdc++-v3/include/debug/vector b/libstdc++-v3/include/debug/vector index 1b3486b0dc08..f952fe9255b7 100644 --- a/libstdc++-v3/include/debug/vector +++ b/libstdc++-v3/include/debug/vector @@ -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 diff --git a/libstdc++-v3/include/std/vector b/libstdc++-v3/include/std/vector index 920f852f7944..375011fff693 100644 --- a/libstdc++-v3/include/std/vector +++ b/libstdc++-v3/include/std/vector @@ -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 index 000000000000..88ec54794645 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/debug/erase.cc @@ -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 index 000000000000..e7dec568c8ed --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/debug/invalidation/erase.cc @@ -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; +}
