From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id C1C0E3851C3E; Mon, 29 Mar 2021 20:04:12 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C1C0E3851C3E MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r10-9603] libstdc++: Make std::copy_n work with negative and non-integral sizes X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/releases/gcc-10 X-Git-Oldrev: 8c1347013941b49c861d3e3a027195cf4a3409f4 X-Git-Newrev: af9017385ad4f10aca8d85086c85910997c10974 Message-Id: <20210329200412.C1C0E3851C3E@sourceware.org> Date: Mon, 29 Mar 2021 20:04:12 +0000 (GMT) X-BeenThere: libstdc++-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 29 Mar 2021 20:04:12 -0000 https://gcc.gnu.org/g:af9017385ad4f10aca8d85086c85910997c10974 commit r10-9603-gaf9017385ad4f10aca8d85086c85910997c10974 Author: Jonathan Wakely Date: Thu Jun 4 13:52:21 2020 +0100 libstdc++: Make std::copy_n work with negative and non-integral sizes Since it was added in C++11, std::copy_n and std::ranges::copy_n should do nothing given a negative size, but for random access iterators we add the size to the iterator, possibly resulting in undefined behaviour. Also, C++20 clarified that std::copy_n requires the Size type to be convertible to an integral type. We previously assumed that it could be directly used in arithmetic expressions, without conversion to an integral type. This also fixes a bug in the random_access_iterator_wrapper helper adds some convenience aliases for using the iterator wrappers. libstdc++-v3/ChangeLog: * include/bits/ranges_algobase.h (__copy_n_fn): Only call ranges::copy for positive values. * include/bits/stl_algo.h (copy_n): Convert Size argument to an integral type and only call __copy_n for positive values. * testsuite/util/testsuite_iterators.h (random_access_iterator_wrapper::operator+=): Fix range check for negative values. (output_container, input_container, forward_container) (bidirectional_container, random_access_container): New alias templates. * testsuite/25_algorithms/copy_n/5.cc: New test. (cherry picked from commit e1008cd1d8504775e6a5e39325e396e61b39b84c) Diff: --- libstdc++-v3/include/bits/ranges_algobase.h | 7 +- libstdc++-v3/include/bits/stl_algo.h | 11 ++- libstdc++-v3/testsuite/25_algorithms/copy_n/5.cc | 97 +++++++++++++++++++++++ libstdc++-v3/testsuite/util/testsuite_iterators.h | 28 ++++++- 4 files changed, 137 insertions(+), 6 deletions(-) diff --git a/libstdc++-v3/include/bits/ranges_algobase.h b/libstdc++-v3/include/bits/ranges_algobase.h index 9c6f1925763..2adff643ac2 100644 --- a/libstdc++-v3/include/bits/ranges_algobase.h +++ b/libstdc++-v3/include/bits/ranges_algobase.h @@ -499,13 +499,16 @@ namespace ranges _Out __result) const { if constexpr (random_access_iterator<_Iter>) - return ranges::copy(__first, __first + __n, std::move(__result)); + { + if (__n > 0) + return ranges::copy(__first, __first + __n, std::move(__result)); + } else { for (; __n > 0; --__n, (void)++__result, (void)++__first) *__result = *__first; - return {std::move(__first), std::move(__result)}; } + return {std::move(__first), std::move(__result)}; } }; diff --git a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h index bbb4313fdf5..430d4d2d558 100644 --- a/libstdc++-v3/include/bits/stl_algo.h +++ b/libstdc++-v3/include/bits/stl_algo.h @@ -771,10 +771,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>) __glibcxx_function_requires(_OutputIteratorConcept<_OutputIterator, typename iterator_traits<_InputIterator>::value_type>) - __glibcxx_requires_can_increment(__first, __n); - __glibcxx_requires_can_increment(__result, __n); - return std::__copy_n(__first, __n, __result, + const auto __n2 = std::__size_to_integer(__n); + if (__n2 <= 0) + return __result; + + __glibcxx_requires_can_increment(__first, __n2); + __glibcxx_requires_can_increment(__result, __n2); + + return std::__copy_n(__first, __n2, __result, std::__iterator_category(__first)); } diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_n/5.cc b/libstdc++-v3/testsuite/25_algorithms/copy_n/5.cc new file mode 100644 index 00000000000..2ea8c9da0dd --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/copy_n/5.cc @@ -0,0 +1,97 @@ +// 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 +// . + +// { dg-do run { target c++11 } } + +#include +#include + +void +test01() +{ + // Negative sizes should be a no-op + + const int from[2] = { 1, 2 }; + __gnu_test::input_container f(from); + int* to = nullptr; + std::copy_n(f.begin(), -1, to); + + std::copy_n(from, -20000, to); // random access + + __gnu_test::random_access_container f2(from); + std::copy_n(f2.end(), -1, to); + std::copy_n(f2.begin(), -1, to); +} + +struct Size +{ + operator long() const { return 2L; } + + void operator++() = delete; + void operator--() = delete; + void operator++(int) = delete; + void operator--(int) = delete; + + template friend void operator+(Size, T) = delete; + template friend void operator+(T, Size) = delete; + template friend void operator-(Size, T) = delete; + template friend void operator-(T, Size) = delete; + template friend void operator==(Size, T) = delete; + template friend void operator==(T, Size) = delete; + template friend void operator!=(Size, T) = delete; + template friend void operator!=(T, Size) = delete; + template friend void operator<(Size, T) = delete; + template friend void operator<(T, Size) = delete; + template friend void operator<=(Size, T) = delete; + template friend void operator<=(T, Size) = delete; + template friend void operator>(Size, T) = delete; + template friend void operator>(T, Size) = delete; + template friend void operator>=(Size, T) = delete; + template friend void operator>=(T, Size) = delete; +}; + +void +test02() +{ + // C++20 only requires that Size is convertible to an integral type, + // it doesn't need to support any arithmetic or relational expressions. + + const int from[3] = { 1, 2, 3 }; + __gnu_test::input_container f(from); + int to[3] = { }; + __gnu_test::output_container t(to); + Size s; + std::copy_n(f.begin(), s, t.begin()); + VERIFY( to[0] == 1 ); + VERIFY( to[1] == 2 ); + VERIFY( to[2] == 0 ); + + const int from2[3] = { 11, 22, 33 }; + __gnu_test::random_access_container f2(from2); + __gnu_test::output_container t2(to); + std::copy_n(f2.begin(), s, t2.begin()); + VERIFY( to[0] == 11 ); + VERIFY( to[1] == 22 ); + VERIFY( to[2] == 0 ); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/util/testsuite_iterators.h b/libstdc++-v3/testsuite/util/testsuite_iterators.h index 5be47f47915..60eba26eab4 100644 --- a/libstdc++-v3/testsuite/util/testsuite_iterators.h +++ b/libstdc++-v3/testsuite/util/testsuite_iterators.h @@ -496,7 +496,7 @@ namespace __gnu_test } else { - ITERATOR_VERIFY(n <= this->ptr - this->SharedInfo->first); + ITERATOR_VERIFY(-n <= this->ptr - this->SharedInfo->first); this->ptr += n; } return *this; @@ -614,6 +614,28 @@ namespace __gnu_test { return bounds.size(); } }; +#if __cplusplus >= 201103L + template + using output_container + = test_container; + + template + using input_container + = test_container; + + template + using forward_container + = test_container; + + template + using bidirectional_container + = test_container; + + template + using random_access_container + = test_container; +#endif + #if __cplusplus > 201703L template struct contiguous_iterator_wrapper @@ -677,6 +699,10 @@ namespace __gnu_test { return iter -= n; } }; + template + using contiguous_container + = test_container; + // A move-only input iterator type. template struct input_iterator_wrapper_nocopy : input_iterator_wrapper