libstdc++: Implement DR 526 on [forward_]list remove_if/unique [PR 91620]

Respect DR 526 in implementation of std::[forward_]list remove/remove_if/unique.
[forward_]list::remove was already implementing it but the implementation has
been modified to generalize the following pattern. All nodes to remove are
collected in an intermediate [forward_]list which purpose is just to be
detroyed once out of scope.

libstdc++-v3/ChangeLog:

	PR libstdc++/91620
	* include/bits/forward_list.tcc (forward_list<>::remove): Collect nodes
	to destroy in an intermediate forward_list.
	(forward_list<>::remove_if, forward_list<>::unique): Likewise.
	* include/bits/list.tcc (list<>::remove, list<>::unique): Likewise.
	(list<>::remove_if): Likewise.
	* include/debug/forward_list (forward_list<>::_M_erase_after): Remove.
	(forward_list<>::erase_after): Adapt.
	(forward_list<>::remove, forward_list<>::remove_if): Collect nodes to
	destroy in an intermediate forward_list.
	(forward_list<>::unique): Likewise.
	* include/debug/list (list<>::remove, list<>::unique): Likewise.
	(list<>::remove_if): Likewise.
	* testsuite/23_containers/forward_list/operations/91620.cc: New test.
	* testsuite/23_containers/list/operations/91620.cc: New test.
This commit is contained in:
François Dumont 2020-08-08 22:22:00 +02:00
parent 8f9fd3c4ee
commit 8b7af071b0
6 changed files with 382 additions and 124 deletions

View file

@ -290,30 +290,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
remove(const _Tp& __val) -> __remove_return_type
{
size_type __removed __attribute__((__unused__)) = 0;
_Node_base* __curr = &this->_M_impl._M_head;
_Node_base* __extra = nullptr;
forward_list __to_destroy(get_allocator());
while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next))
{
if (*__tmp->_M_valptr() == __val)
{
if (__tmp->_M_valptr() != std::__addressof(__val))
{
this->_M_erase_after(__curr);
_GLIBCXX20_ONLY( __removed++ );
continue;
}
else
__extra = __curr;
}
__curr = __curr->_M_next;
}
auto __prev_it = cbefore_begin();
while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next))
if (*__tmp->_M_valptr() == __val)
{
__to_destroy.splice_after(__to_destroy.cbefore_begin(),
*this, __prev_it);
_GLIBCXX20_ONLY( __removed++ );
}
else
++__prev_it;
if (__extra)
{
this->_M_erase_after(__extra);
_GLIBCXX20_ONLY( __removed++ );
}
return _GLIBCXX20_ONLY( __removed );
}
@ -324,17 +313,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
remove_if(_Pred __pred) -> __remove_return_type
{
size_type __removed __attribute__((__unused__)) = 0;
_Node_base* __curr = &this->_M_impl._M_head;
while (_Node* __tmp = static_cast<_Node*>(__curr->_M_next))
{
if (__pred(*__tmp->_M_valptr()))
{
this->_M_erase_after(__curr);
_GLIBCXX20_ONLY( __removed++ );
}
else
__curr = __curr->_M_next;
}
forward_list __to_destroy(get_allocator());
auto __prev_it = cbefore_begin();
while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next))
if (__pred(*__tmp->_M_valptr()))
{
__to_destroy.splice_after(__to_destroy.cbefore_begin(),
*this, __prev_it);
_GLIBCXX20_ONLY( __removed++ );
}
else
++__prev_it;
return _GLIBCXX20_ONLY( __removed );
}
@ -348,20 +339,24 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __last = end();
if (__first == __last)
return _GLIBCXX20_ONLY(0);
forward_list __to_destroy(get_allocator());
size_type __removed __attribute__((__unused__)) = 0;
iterator __next = __first;
while (++__next != __last)
{
if (__binary_pred(*__first, *__next))
{
erase_after(__first);
__to_destroy.splice_after(__to_destroy.cbefore_begin(),
*this, __first);
_GLIBCXX20_ONLY( __removed++ );
}
else
__first = __next;
__next = __first;
}
return _GLIBCXX20_ONLY( __removed );
return _GLIBCXX20_ONLY( __removed );
}
#undef _GLIBCXX20_ONLY

View file

@ -331,10 +331,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
list<_Tp, _Alloc>::
remove(const value_type& __value)
{
#if !_GLIBCXX_USE_CXX11_ABI
size_type __removed __attribute__((__unused__)) = 0;
#endif
list __to_destroy(get_allocator());
iterator __first = begin();
iterator __last = end();
iterator __extra = __last;
while (__first != __last)
{
iterator __next = __first;
@ -344,22 +346,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 526. Is it undefined if a function in the standard changes
// in parameters?
if (std::__addressof(*__first) != std::__addressof(__value))
{
_M_erase(__first);
_GLIBCXX20_ONLY( __removed++ );
}
else
__extra = __first;
__to_destroy.splice(__to_destroy.begin(), *this, __first);
#if !_GLIBCXX_USE_CXX11_ABI
_GLIBCXX20_ONLY( __removed++ );
#endif
}
__first = __next;
}
if (__extra != __last)
{
_M_erase(__extra);
_GLIBCXX20_ONLY( __removed++ );
}
return _GLIBCXX20_ONLY( __removed );
#if !_GLIBCXX_USE_CXX11_ABI
return _GLIBCXX20_ONLY( __removed );
#else
return _GLIBCXX20_ONLY( __to_destroy.size() );
#endif
}
template<typename _Tp, typename _Alloc>
@ -371,20 +371,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __last = end();
if (__first == __last)
return _GLIBCXX20_ONLY( 0 );
#if !_GLIBCXX_USE_CXX11_ABI
size_type __removed __attribute__((__unused__)) = 0;
#endif
list __to_destroy(get_allocator());
iterator __next = __first;
while (++__next != __last)
{
if (*__first == *__next)
{
_M_erase(__next);
__to_destroy.splice(__to_destroy.begin(), *this, __next);
#if !_GLIBCXX_USE_CXX11_ABI
_GLIBCXX20_ONLY( __removed++ );
#endif
}
else
__first = __next;
__next = __first;
}
#if !_GLIBCXX_USE_CXX11_ABI
return _GLIBCXX20_ONLY( __removed );
#else
return _GLIBCXX20_ONLY( __to_destroy.size() );
#endif
}
template<typename _Tp, typename _Alloc>
@ -533,21 +543,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
list<_Tp, _Alloc>::
remove_if(_Predicate __pred)
{
#if !_GLIBCXX_USE_CXX11_ABI
size_type __removed __attribute__((__unused__)) = 0;
iterator __first = begin();
iterator __last = end();
while (__first != __last)
#endif
list __to_destroy(get_allocator());
iterator __first = begin();
iterator __last = end();
while (__first != __last)
{
iterator __next = __first;
++__next;
if (__pred(*__first))
{
_M_erase(__first);
__to_destroy.splice(__to_destroy.begin(), *this, __first);
#if !_GLIBCXX_USE_CXX11_ABI
_GLIBCXX20_ONLY( __removed++ );
#endif
}
__first = __next;
}
#if !_GLIBCXX_USE_CXX11_ABI
return _GLIBCXX20_ONLY( __removed );
#else
return _GLIBCXX20_ONLY( __to_destroy.size() );
#endif
}
template<typename _Tp, typename _Alloc>
@ -560,20 +580,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator __last = end();
if (__first == __last)
return _GLIBCXX20_ONLY(0);
#if !_GLIBCXX_USE_CXX11_ABI
size_type __removed __attribute__((__unused__)) = 0;
#endif
list __to_destroy(get_allocator());
iterator __next = __first;
while (++__next != __last)
{
if (__binary_pred(*__first, *__next))
{
_M_erase(__next);
__to_destroy.splice(__to_destroy.begin(), *this, __next);
#if !_GLIBCXX_USE_CXX11_ABI
_GLIBCXX20_ONLY( __removed++ );
#endif
}
else
__first = __next;
__next = __first;
}
#if !_GLIBCXX_USE_CXX11_ABI
return _GLIBCXX20_ONLY( __removed );
#else
return _GLIBCXX20_ONLY( __to_destroy.size() );
#endif
}
#undef _GLIBCXX20_ONLY

View file

@ -452,21 +452,15 @@ namespace __debug
return { _Base::insert_after(__pos.base(), __il), this };
}
private:
_Base_iterator
_M_erase_after(_Base_const_iterator __pos)
{
_Base_const_iterator __next = std::next(__pos);
this->_M_invalidate_if([__next](_Base_const_iterator __it)
{ return __it == __next; });
return _Base::erase_after(__pos);
}
public:
iterator
erase_after(const_iterator __pos)
{
__glibcxx_check_erase_after(__pos);
return { _M_erase_after(__pos.base()), this };
_Base_const_iterator __next = std::next(__pos.base());
this->_M_invalidate_if([__next](_Base_const_iterator __it)
{ return __it == __next; });
return { _Base::erase_after(__pos.base()), this };
}
iterator
@ -691,29 +685,23 @@ namespace __debug
return _Base::remove(__val);
size_type __removed __attribute__((__unused__)) = 0;
_Base_iterator __x = _Base::before_begin();
_Base_iterator __old = __x++;
_Base_iterator __extra = _Base::end();
while (__x != _Base::end())
_Base __to_destroy(get_allocator());
_Base_const_iterator __x = _Base::cbefore_begin();
_Base_const_iterator __old = __x++;
while (__x != _Base::cend())
{
if (*__x == __val)
{
if (std::__addressof(*__x) != std::__addressof(__val))
{
__x = _M_erase_after(__old);
_GLIBCXX20_ONLY( __removed++ );
continue;
}
else
__extra = __old;
_Base_const_iterator __next = std::next(__old);
this->_M_invalidate_if([__next](_Base_const_iterator __it)
{ return __it == __next; });
__to_destroy.splice_after(__to_destroy.cbefore_begin(),
_M_base(), __old);
__x = __old;
_GLIBCXX20_ONLY( __removed++ );
}
__old = __x++;
}
if (__extra != _Base::end())
{
this->_M_erase_after(__extra);
_GLIBCXX20_ONLY( __removed++ );
__old = __x++;
}
return _GLIBCXX20_ONLY( __removed );
@ -727,16 +715,23 @@ namespace __debug
return _Base::remove_if(__pred);
size_type __removed __attribute__((__unused__)) = 0;
_Base __to_destroy(get_allocator());
_Base_iterator __x = _Base::before_begin();
_Base_iterator __old = __x++;
while (__x != _Base::end())
if (__pred(*__x))
{
__x = _M_erase_after(__old);
_GLIBCXX20_ONLY( __removed++ );
}
else
{
if (__pred(*__x))
{
this->_M_invalidate_if([__x](_Base_const_iterator __it)
{ return __it == __x; });
__to_destroy.splice_after(__to_destroy.cbefore_begin(),
_M_base(), __old);
__x = __old;
_GLIBCXX20_ONLY( __removed++ );
}
__old = __x++;
}
return _GLIBCXX20_ONLY( __removed );
}
@ -753,22 +748,27 @@ namespace __debug
if (!this->_M_iterators && !this->_M_const_iterators)
return _Base::unique(__binary_pred);
_Base_iterator __first = _Base::begin();
_Base_iterator __last = _Base::end();
_Base_const_iterator __first = _Base::cbegin();
_Base_const_iterator __last = _Base::cend();
if (__first == __last)
return _GLIBCXX20_ONLY(0);
size_type __removed __attribute__((__unused__)) = 0;
_Base_iterator __next = std::next(__first);
_Base __to_destroy(get_allocator());
_Base_const_iterator __next = std::next(__first);
while (__next != __last)
{
if (__binary_pred(*__first, *__next))
{
__next = _M_erase_after(__first);
this->_M_invalidate_if([__next](_Base_const_iterator __it)
{ return __it == __next; });
__to_destroy.splice_after(__to_destroy.cbefore_begin(),
_M_base(), __first);
__next = __first;
_GLIBCXX20_ONLY( __removed++ );
}
else
__first = __next++;
__first = __next++;
}
return _GLIBCXX20_ONLY( __removed );

View file

@ -681,36 +681,36 @@ namespace __debug
if (!this->_M_iterators && !this->_M_const_iterators)
return _Base::remove(__value);
#if !_GLIBCXX_USE_CXX11_ABI
size_type __removed __attribute__((__unused__)) = 0;
#endif
_Base __to_destroy(get_allocator());
_Base_iterator __first = _Base::begin();
_Base_iterator __last = _Base::end();
_Base_iterator __extra = __last;
while (__first != __last)
{
_Base_iterator __next = __first;
++__next;
if (*__first == __value)
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 526. Is it undefined if a function in the standard changes
// in parameters?
if (std::__addressof(*__first) != std::__addressof(__value))
{
__first = _M_erase(__first);
_GLIBCXX20_ONLY( __removed++ );
}
else
{
__extra = __first;
++__first;
}
else
++__first;
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 526. Is it undefined if a function in the standard changes
// in parameters?
this->_M_invalidate_if(_Equal(__first));
__to_destroy.splice(__to_destroy.begin(), _M_base(), __first);
#if !_GLIBCXX_USE_CXX11_ABI
_GLIBCXX20_ONLY( __removed++ );
#endif
}
__first = __next;
}
if (__extra != __last)
{
_M_erase(__extra);
_GLIBCXX20_ONLY( __removed++ );
}
#if !_GLIBCXX_USE_CXX11_ABI
return _GLIBCXX20_ONLY( __removed );
#else
return _GLIBCXX20_ONLY( __to_destroy.size() );
#endif
}
template<class _Predicate>
@ -720,17 +720,31 @@ namespace __debug
if (!this->_M_iterators && !this->_M_const_iterators)
return _Base::remove_if(__pred);
#if !_GLIBCXX_USE_CXX11_ABI
size_type __removed __attribute__((__unused__)) = 0;
#endif
_Base __to_destroy(get_allocator());
for (_Base_iterator __x = _Base::begin(); __x != _Base::end(); )
{
_Base_iterator __next = __x;
++__next;
if (__pred(*__x))
{
__x = _M_erase(__x);
this->_M_invalidate_if(_Equal(__x));
__to_destroy.splice(__to_destroy.begin(), _M_base(), __x);
#if !_GLIBCXX_USE_CXX11_ABI
_GLIBCXX20_ONLY( __removed++ );
#endif
}
else
++__x;
__x = __next;
}
#if !_GLIBCXX_USE_CXX11_ABI
return _GLIBCXX20_ONLY( __removed );
#else
return _GLIBCXX20_ONLY( __to_destroy.size() );
#endif
}
_GLIBCXX_LIST_REMOVE_RETURN_TYPE_TAG
@ -743,21 +757,31 @@ namespace __debug
if (empty())
return _GLIBCXX20_ONLY(0);
#if !_GLIBCXX_USE_CXX11_ABI
size_type __removed __attribute__((__unused__)) = 0;
#endif
_Base __to_destroy(get_allocator());
_Base_iterator __first = _Base::begin();
_Base_iterator __last = _Base::end();
_Base_iterator __next = __first;
while (++__next != __last)
if (*__first == *__next)
{
_M_erase(__next);
this->_M_invalidate_if(_Equal(__next));
__to_destroy.splice(__to_destroy.begin(), _M_base(), __next);
__next = __first;
#if !_GLIBCXX_USE_CXX11_ABI
_GLIBCXX20_ONLY( __removed++ );
#endif
}
else
__first = __next;
#if !_GLIBCXX_USE_CXX11_ABI
return _GLIBCXX20_ONLY( __removed );
#else
return _GLIBCXX20_ONLY( __to_destroy.size() );
#endif
}
template<class _BinaryPredicate>
@ -770,21 +794,32 @@ namespace __debug
if (empty())
return _GLIBCXX20_ONLY(0);
#if !_GLIBCXX_USE_CXX11_ABI
size_type __removed __attribute__((__unused__)) = 0;
#endif
_Base __to_destroy(get_allocator());
_Base_iterator __first = _Base::begin();
_Base_iterator __last = _Base::end();
_Base_iterator __next = __first;;
_Base_iterator __next = __first;
while (++__next != __last)
if (__binary_pred(*__first, *__next))
{
_M_erase(__next);
this->_M_invalidate_if(_Equal(__next));
__to_destroy.splice(__to_destroy.begin(), _M_base(), __next);
__next = __first;
#if !_GLIBCXX_USE_CXX11_ABI
_GLIBCXX20_ONLY( __removed++ );
#endif
}
else
__first = __next;
return _GLIBCXX20_ONLY( __removed );
#if !_GLIBCXX_USE_CXX11_ABI
return _GLIBCXX20_ONLY( __removed );
#else
return _GLIBCXX20_ONLY( __to_destroy.size() );
#endif
}
#undef _GLIBCXX_LIST_REMOVE_RETURN_TYPE_TAG

View file

@ -0,0 +1,88 @@
// { dg-do run { target c++11 } }
// { dg-options "-g -O0" }
//
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <forward_list>
#include <memory>
#include <testsuite_hooks.h>
struct PredLWG526
{
PredLWG526(int i) : i_(i) {};
~PredLWG526() { i_ = -32767; }
bool
operator() (const PredLWG526& p) const { return p.i_ == i_; }
bool
operator==(int i) const { return i == i_; }
bool
operator() (const PredLWG526& lhs, const PredLWG526& rhs) const
{
VERIFY( i_ != -32767 );
return lhs.i_ == rhs.i_;
}
int i_;
};
void test01()
{
int a1[] = {1, 2, 1, 3, 5, 8, 11};
int a2[] = {2, 3, 5, 8, 11};
std::forward_list<PredLWG526> fl(a1, a1 + 7);
VERIFY( std::distance(fl.begin(), fl.end()) == 7 );
fl.remove_if(std::cref(fl.front()));
VERIFY( std::distance(fl.begin(), fl.end()) == 5 );
for (size_t i = 0; !fl.empty(); ++i)
{
VERIFY( fl.front() == a2[i] );
fl.pop_front();
}
}
void test02()
{
int a1[] = {1, 1, 1, 2, 3, 5, 8, 11};
int a2[] = {1, 2, 3, 5, 8, 11};
std::forward_list<PredLWG526> fl(a1, a1 + 8);
VERIFY( std::distance(fl.begin(), fl.end()) == 8 );
auto it = fl.begin();
++it;
fl.unique(std::cref(*it));
VERIFY( std::distance(fl.begin(), fl.end()) == 6 );
for (size_t i = 0; !fl.empty(); ++i)
{
VERIFY( fl.front() == a2[i] );
fl.pop_front();
}
}
int main()
{
test01();
test02();
return 0;
}

View file

@ -0,0 +1,110 @@
// { dg-do run { target c++11 } }
// { dg-options "-g -O0" }
//
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <list>
#include <memory>
#include <testsuite_hooks.h>
struct PredLWG526
{
PredLWG526(int i) : i_(i) {};
~PredLWG526() { i_ = -32767; }
bool
operator() (const PredLWG526& p) const { return p.i_ == i_; }
bool
operator==(int i) const { return i == i_; }
bool
operator()(const PredLWG526& lhs, const PredLWG526& rhs) const
{
VERIFY( i_ != -32767 );
return lhs.i_ == rhs.i_;
}
friend bool
operator==(const PredLWG526& lhs, const PredLWG526& rhs)
{ return lhs.i_ == rhs.i_; }
int i_;
};
void test01()
{
int a1[] = {1, 2, 1, 3, 5, 8, 11};
int a2[] = {2, 3, 5, 8, 11};
std::list<PredLWG526> l(a1, a1 + 7);
VERIFY( std::distance(l.begin(), l.end()) == 7 );
l.remove(l.front());
VERIFY( std::distance(l.begin(), l.end()) == 5 );
for (size_t i = 0; !l.empty(); ++i)
{
VERIFY( l.front() == a2[i] );
l.pop_front();
}
}
void test02()
{
int a1[] = {1, 2, 1, 3, 5, 8, 11};
int a2[] = {2, 3, 5, 8, 11};
std::list<PredLWG526> l(a1, a1 + 7);
VERIFY( std::distance(l.begin(), l.end()) == 7 );
l.remove_if(std::cref(l.front()));
VERIFY( std::distance(l.begin(), l.end()) == 5 );
for (size_t i = 0; !l.empty(); ++i)
{
VERIFY( l.front() == a2[i] );
l.pop_front();
}
}
void test03()
{
int a1[] = {1, 1, 1, 2, 3, 5, 8, 11};
int a2[] = {1, 2, 3, 5, 8, 11};
std::list<PredLWG526> l(a1, a1 + 8);
VERIFY( std::distance(l.begin(), l.end()) == 8 );
auto it = l.begin();
++it;
l.unique(std::cref(*it));
VERIFY( std::distance(l.begin(), l.end()) == 6 );
for (size_t i = 0; !l.empty(); ++i)
{
VERIFY( l.front() == a2[i] );
l.pop_front();
}
}
int main()
{
test01();
test02();
test03();
return 0;
}