Fix PR52822 (stable_partition move-assigns object to itself) by scanning for...

Fix PR52822 (stable_partition move-assigns object to itself) by
scanning for the first value that doesn't match the predicate before
starting to rearrange values.

2012-04-03   Jeffrey Yasskin  <jyasskin@google.com>

	PR libstdc++/52822
	* include/bits/stl_algo.h (__find_if_not): Expose in
	C++98 mode.
	(__find_if_not_n): Like __find_if_not, but works on and updates a
	counted range instead of a bounded range.
	(stable_partition): Guarantee !__pred(*__first) in call to
	__stable_partition_adaptive() or __inplace_stable_partition().
	(__stable_partition_adaptive): Use new precondition to avoid
	moving/copying objects onto themselves.  Guarantee new
	precondition to recursive calls.
	(__inplace_stable_partition): Use new precondition to simplify
	base case, remove __last parameter.  Guarantee new precondition to
	recursive calls.
	* testsuite/25_algorithms/stable_partition/moveable.cc (test02):
	Test a sequence that starts with a value matching the predicate.
	* testsuite/25_algorithms/stable_partition/pr52822.cc:
	Test vectors, which have a destructive self-move-assignment.

From-SVN: r186391
This commit is contained in:
Jeffrey Yasskin 2012-04-12 20:59:09 +00:00 committed by Jeffrey Yasskin
parent a2547fd0d6
commit 2fc9b37dd0
4 changed files with 152 additions and 27 deletions

View File

@ -1,3 +1,22 @@
2012-04-12 Jeffrey Yasskin <jyasskin@google.com>
PR libstdc++/52822
* include/bits/stl_algo.h (__find_if_not): Expose in C++98 mode.
(__find_if_not_n): Like __find_if_not, but works on and updates a
counted range instead of a bounded range.
(stable_partition): Guarantee !__pred(*__first) in call to
__stable_partition_adaptive() or __inplace_stable_partition().
(__stable_partition_adaptive): Use new precondition to avoid
moving/copying objects onto themselves. Guarantee new
precondition to recursive calls.
(__inplace_stable_partition): Use new precondition to simplify
base case, remove __last parameter. Guarantee new precondition to
recursive calls.
* testsuite/25_algorithms/stable_partition/moveable.cc (test02):
Test a sequence that starts with a value matching the predicate.
* testsuite/25_algorithms/stable_partition/pr52822.cc: Test
vectors, which have a destructive self-move-assignment.
2012-04-12 Andreas Schwab <schwab@linux-m68k.org>
* testsuite/Makefile.am (check_DEJAGNUnormal0): Run

View File

@ -244,7 +244,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
}
#ifdef __GXX_EXPERIMENTAL_CXX0X__
/// This is an overload used by find_if_not() for the Input Iterator case.
template<typename _InputIterator, typename _Predicate>
inline _InputIterator
@ -303,7 +302,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __last;
}
}
#endif
/// Provided for stable_partition to use.
template<typename _InputIterator, typename _Predicate>
inline _InputIterator
__find_if_not(_InputIterator __first, _InputIterator __last,
_Predicate __pred)
{
return std::__find_if_not(__first, __last, __pred,
std::__iterator_category(__first));
}
/// Like find_if_not(), but uses and updates a count of the
/// remaining range length instead of comparing against an end
/// iterator.
template<typename _InputIterator, typename _Predicate, typename _Distance>
_InputIterator
__find_if_not_n(_InputIterator __first, _Distance& __len, _Predicate __pred)
{
for (; __len; --__len, ++__first)
if (!bool(__pred(*__first)))
break;
return __first;
}
// set_difference
// set_intersection
@ -789,8 +810,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__glibcxx_function_requires(_UnaryPredicateConcept<_Predicate,
typename iterator_traits<_InputIterator>::value_type>)
__glibcxx_requires_valid_range(__first, __last);
return std::__find_if_not(__first, __last, __pred,
std::__iterator_category(__first));
return std::__find_if_not(__first, __last, __pred);
}
/**
@ -1784,30 +1804,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// partition
/// This is a helper function...
/// Requires __len != 0 and !__pred(*__first),
/// same as __stable_partition_adaptive.
template<typename _ForwardIterator, typename _Predicate, typename _Distance>
_ForwardIterator
__inplace_stable_partition(_ForwardIterator __first,
_ForwardIterator __last,
_Predicate __pred, _Distance __len)
{
if (__len == 1)
return __pred(*__first) ? __last : __first;
return __first;
_ForwardIterator __middle = __first;
std::advance(__middle, __len / 2);
_ForwardIterator __begin = std::__inplace_stable_partition(__first,
__middle,
__pred,
__len / 2);
_ForwardIterator __end = std::__inplace_stable_partition(__middle, __last,
__pred,
__len
- __len / 2);
std::rotate(__begin, __middle, __end);
std::advance(__begin, std::distance(__middle, __end));
return __begin;
_ForwardIterator __left_split =
std::__inplace_stable_partition(__first, __pred, __len / 2);
// Advance past true-predicate values to satisfy this
// function's preconditions.
_Distance __right_len = __len - __len / 2;
_ForwardIterator __right_split =
std::__find_if_not_n(__middle, __right_len, __pred);
if (__right_len)
__right_split = std::__inplace_stable_partition(__middle,
__pred,
__right_len);
std::rotate(__left_split, __middle, __right_split);
std::advance(__left_split, std::distance(__middle, __right_split));
return __left_split;
}
/// This is a helper function...
/// Requires __first != __last and !__pred(*__first)
/// and __len == distance(__first, __last).
///
/// !__pred(*__first) allows us to guarantee that we don't
/// move-assign an element onto itself.
template<typename _ForwardIterator, typename _Pointer, typename _Predicate,
typename _Distance>
_ForwardIterator
@ -1821,6 +1850,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
_ForwardIterator __result1 = __first;
_Pointer __result2 = __buffer;
// The precondition guarantees that !__pred(*__first), so
// move that element to the buffer before starting the loop.
// This ensures that we only call __pred once per element.
*__result2 = _GLIBCXX_MOVE(*__first);
++__result2;
++__first;
for (; __first != __last; ++__first)
if (__pred(*__first))
{
@ -1839,17 +1874,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
_ForwardIterator __middle = __first;
std::advance(__middle, __len / 2);
_ForwardIterator __begin =
_ForwardIterator __left_split =
std::__stable_partition_adaptive(__first, __middle, __pred,
__len / 2, __buffer,
__buffer_size);
_ForwardIterator __end =
std::__stable_partition_adaptive(__middle, __last, __pred,
__len - __len / 2,
__buffer, __buffer_size);
std::rotate(__begin, __middle, __end);
std::advance(__begin, std::distance(__middle, __end));
return __begin;
// Advance past true-predicate values to satisfy this
// function's preconditions.
_Distance __right_len = __len - __len / 2;
_ForwardIterator __right_split =
std::__find_if_not_n(__middle, __right_len, __pred);
if (__right_len)
__right_split =
std::__stable_partition_adaptive(__right_split, __last, __pred,
__right_len,
__buffer, __buffer_size);
std::rotate(__left_split, __middle, __right_split);
std::advance(__left_split, std::distance(__middle, __right_split));
return __left_split;
}
}
@ -1882,6 +1923,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typename iterator_traits<_ForwardIterator>::value_type>)
__glibcxx_requires_valid_range(__first, __last);
__first = std::__find_if_not(__first, __last, __pred);
if (__first == __last)
return __first;
else
@ -1901,7 +1944,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_DistanceType(__buf.size()));
else
return
std::__inplace_stable_partition(__first, __last, __pred,
std::__inplace_stable_partition(__first, __pred,
_DistanceType(__buf.requested_size()));
}
}

View File

@ -35,6 +35,11 @@ const int A[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
const int B[] = {2, 4, 6, 8, 10, 12, 14, 16, 1, 3, 5, 7, 9, 11, 13, 15, 17};
const int N = sizeof(A) / sizeof(int);
// Check that starting with a true predicate works too. (PR52822)
const int A2[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
const int B2[] = {2, 4, 6, 8, 10, 12, 14, 16, 3, 5, 7, 9, 11, 13, 15, 17};
const int N2 = sizeof(A2) / sizeof(int);
struct Pred
{
bool
@ -42,7 +47,7 @@ struct Pred
{ return (x.val % 2) == 0; }
};
// 25.2.12 stable_partition()
// 25.2.12 stable_partition(), starting with a false predicate.
void
test01()
{
@ -56,9 +61,24 @@ test01()
VERIFY( std::equal(s1, s1 + N, B) );
}
// 25.2.12 stable_partition(), starting with a true predicate.
void
test02()
{
bool test __attribute__((unused)) = true;
rvalstruct s1[N2];
std::copy(A2, A2 + N2, s1);
Container con(s1, s1 + N2);
std::stable_partition(con.begin(), con.end(), Pred());
VERIFY( std::equal(s1, s1 + N2, B2) );
}
int
main()
{
test01();
test02();
return 0;
}

View File

@ -0,0 +1,43 @@
// { dg-options "-std=gnu++0x" }
// Copyright (C) 2012 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 Pred 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/>.
// 25.2.12 [lib.alg.partitions] Partitions.
#include <algorithm>
#include <vector>
#include <testsuite_hooks.h>
bool true_vector_pred(const std::vector<int>&) { return true; }
void
test01()
{
std::vector<std::vector<int> > v(1);
v[0].push_back(7);
VERIFY( v[0].size() == 1 );
std::stable_partition(v.begin(), v.end(), &true_vector_pred);
VERIFY( v[0].size() == 1 );
}
int
main()
{
test01();
return 0;
}