Commit 5cd2e126 by Jonathan Wakely

libstdc++: Make tests for std::ranges access functions more robust

	* testsuite/std/ranges/access/end.cc: Do not assume test_range::end()
	returns the same type as test_range::begin(). Add comments.
	* testsuite/std/ranges/access/rbegin.cc: Likewise.
	* testsuite/std/ranges/access/rend.cc: Likewise.
	* testsuite/std/ranges/range.cc: Do not assume the sentinel for
	test_range is the same as its iterator type.
	* testsuite/util/testsuite_iterators.h (test_range::sentinel): Add
	operator- overloads to satisfy sized_sentinel_for when the iterator
	satisfies random_access_iterator.
parent 6693911f
2020-01-29 Jonathan Wakely <jwakely@redhat.com>
* testsuite/std/ranges/access/end.cc: Do not assume test_range::end()
returns the same type as test_range::begin(). Add comments.
* testsuite/std/ranges/access/rbegin.cc: Likewise.
* testsuite/std/ranges/access/rend.cc: Likewise.
* testsuite/std/ranges/range.cc: Do not assume the sentinel for
test_range is the same as its iterator type.
* testsuite/util/testsuite_iterators.h (test_range::sentinel): Add
operator- overloads to satisfy sized_sentinel_for when the iterator
satisfies random_access_iterator.
2020-01-28 Jonathan Wakely <jwakely@redhat.com> 2020-01-28 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/93470 PR libstdc++/93470
......
...@@ -29,6 +29,8 @@ test01() ...@@ -29,6 +29,8 @@ test01()
{ {
int a[2] = {}; int a[2] = {};
// t + extent_v<T> if E is of array type T.
static_assert(same_as<decltype(std::ranges::end(a)), decltype(a + 2)>); static_assert(same_as<decltype(std::ranges::end(a)), decltype(a + 2)>);
static_assert(noexcept(std::ranges::end(a))); static_assert(noexcept(std::ranges::end(a)));
VERIFY( std::ranges::end(a) == (a + 2) ); VERIFY( std::ranges::end(a) == (a + 2) );
...@@ -44,13 +46,16 @@ test02() ...@@ -44,13 +46,16 @@ test02()
int a[] = { 0, 1 }; int a[] = { 0, 1 };
// Otherwise, decay-copy(t.end()) if it is a valid expression
// and its type S models sentinel_for<decltype(ranges::begin(E))>.
test_range<int, random_access_iterator_wrapper> r(a); test_range<int, random_access_iterator_wrapper> r(a);
static_assert(same_as<decltype(std::ranges::end(r)), decltype(r.end())>); static_assert(same_as<decltype(std::ranges::end(r)), decltype(r.end())>);
VERIFY( std::ranges::end(r) == r.end() ); VERIFY( std::ranges::end(r) == std::ranges::next(r.begin(), 2) );
test_range<int, input_iterator_wrapper> i(a); test_range<int, input_iterator_wrapper> i(a);
static_assert(same_as<decltype(std::ranges::end(i)), decltype(i.end())>); static_assert(same_as<decltype(std::ranges::end(i)), decltype(i.end())>);
VERIFY( std::ranges::end(i) == i.end() ); VERIFY( std::ranges::end(i) == std::ranges::next(i.begin(), 2) );
test_range<int, output_iterator_wrapper> o(a); test_range<int, output_iterator_wrapper> o(a);
static_assert(same_as<decltype(std::ranges::end(o)), decltype(o.end())>); static_assert(same_as<decltype(std::ranges::end(o)), decltype(o.end())>);
...@@ -93,6 +98,9 @@ test03() ...@@ -93,6 +98,9 @@ test03()
R r; R r;
const R& c = r; const R& c = r;
// Otherwise, decay-copy(end(t)) if it is a valid expression
// and its type S models sentinel_for<decltype(ranges::begin(E))>.
static_assert(same_as<decltype(std::ranges::end(r)), decltype(end(r))>); static_assert(same_as<decltype(std::ranges::end(r)), decltype(end(r))>);
static_assert(!noexcept(std::ranges::end(r))); static_assert(!noexcept(std::ranges::end(r)));
VERIFY( std::ranges::end(r) == end(r) ); VERIFY( std::ranges::end(r) == end(r) );
......
...@@ -38,6 +38,8 @@ void ...@@ -38,6 +38,8 @@ void
test01() test01()
{ {
constexpr R1 r; constexpr R1 r;
// decay-copy(t.rbegin()) if it is a valid expression
// and its type I models input_or_output_iterator.
static_assert( std::ranges::rbegin(r) == &r.i ); static_assert( std::ranges::rbegin(r) == &r.i );
static_assert( std::ranges::rbegin(std::move(r)) == &r.i ); static_assert( std::ranges::rbegin(std::move(r)) == &r.i );
} }
...@@ -60,6 +62,8 @@ void ...@@ -60,6 +62,8 @@ void
test02() test02()
{ {
constexpr R2 r; constexpr R2 r;
// Otherwise, decay-copy(rbegin(t)) if it is a valid expression
// and its type I models input_or_output_iterator [...]
static_assert( std::ranges::rbegin(r) static_assert( std::ranges::rbegin(r)
== std::make_reverse_iterator(std::ranges::end(r)) ); == std::make_reverse_iterator(std::ranges::end(r)) );
static_assert( std::ranges::rbegin(std::move(r)) static_assert( std::ranges::rbegin(std::move(r))
...@@ -69,11 +73,29 @@ test02() ...@@ -69,11 +73,29 @@ test02()
void void
test03() test03()
{ {
using __gnu_test::test_range; struct R3
using __gnu_test::bidirectional_iterator_wrapper; : __gnu_test::test_range<int, __gnu_test::bidirectional_iterator_wrapper>
{
R3(int (&a)[2]) : test_range(a) { }
using test_range::begin;
// Replace test_range::end() to return same type as begin()
// so ranges::rbegin will wrap it in a reverse_iterator .
auto end() &
{
using __gnu_test::bidirectional_iterator_wrapper;
return bidirectional_iterator_wrapper<int>(bounds.last, &bounds);
}
};
int a[2] = { }; int a[2] = { };
test_range<int, bidirectional_iterator_wrapper> r(a); R3 r(a);
// Otherwise, make_reverse_iterator(ranges::end(t)) if both ranges::begin(t)
// and ranges::end(t) are valid expressions of the same type I which models
// bidirectional_iterator.
VERIFY( std::ranges::rbegin(r) == std::make_reverse_iterator(std::ranges::end(r)) ); VERIFY( std::ranges::rbegin(r) == std::make_reverse_iterator(std::ranges::end(r)) );
} }
......
...@@ -40,65 +40,91 @@ void ...@@ -40,65 +40,91 @@ void
test01() test01()
{ {
constexpr R1 r; constexpr R1 r;
// decay-copy(t.rend()) if it is a valid expression
// and its type S models sentinel_for<decltype(ranges::rbegin(E))>
static_assert( std::ranges::rend(r) == &r.i + 1 ); static_assert( std::ranges::rend(r) == &r.i + 1 );
static_assert( std::ranges::rend(std::move(r)) == &r.i + 1 ); static_assert( std::ranges::rend(std::move(r)) == &r.i + 1 );
} }
struct R2 struct R2
{ {
int a[2] = { }; int i = 0;
long l[2] = { };
constexpr const int* begin() const { return a; } int* rbegin() noexcept { return &i + 1; }
constexpr const int* end() const { return a + 2; } long* rend() noexcept { return nullptr; } // not a sentinel for rbegin()
friend constexpr const long* begin(const R2&& r) { return r.l; } friend long* rbegin(R2&) noexcept { return nullptr; }
friend constexpr const long* end(const R2&& r) { return r.l + 2; } friend int* rend(R2& r) { return &r.i; }
}; };
// N.B. this is a lie, begin/end on an R2 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<R2> = true;
void void
test02() test02()
{ {
constexpr R2 r; R2 r;
static_assert( std::ranges::rend(r)
== std::make_reverse_iterator(std::ranges::begin(r)) ); // Otherwise, decay-copy(rend(t)) if it is a valid expression
static_assert( std::ranges::rend(std::move(r)) // and its type S models sentinel_for<decltype(ranges::rbegin(E))>
== std::make_reverse_iterator(std::ranges::begin(std::move(r))) );
auto i1 = std::ranges::rbegin(r);
auto i2 = rend(r);
static_assert( std::sentinel_for<decltype(i2), decltype(i1)> );
VERIFY( std::ranges::rend(r) == &r.i );
static_assert( !noexcept(std::ranges::rend(r)) );
} }
struct R3 struct R3
{ {
int i = 0; int a[2] = { };
long l[2] = { };
int* rbegin() noexcept { return &i + 1; } constexpr const int* begin() const { return a; }
long* rend() noexcept { return nullptr; } // not a sentinel for rbegin() constexpr const int* end() const { return a + 2; }
friend long* rbegin(R3&) noexcept { return nullptr; } friend constexpr const long* begin(const R3&& r) { return r.l; }
friend int* rend(R3& r) { return &r.i; } friend constexpr const long* end(const R3&& r) { return r.l + 2; }
}; };
// N.B. this is a lie, begin/end on an R3 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<R3> = true;
void void
test03() test03()
{ {
R3 r; constexpr R3 r;
auto i1 = std::ranges::rbegin(r);
auto i2 = rend(r); // Otherwise, make_reverse_iterator(ranges::begin(t)) if both
static_assert( std::sentinel_for<decltype(i2), decltype(i1)> ); // ranges::begin(t) and ranges::end(t) are valid expressions
// VERIFY( std::ranges::rend(r) == r.i ); // of the same type I which models bidirectional_iterator.
// static_assert( !noexcept(std::ranges::rend(r)) );
static_assert( std::ranges::rend(r)
== std::make_reverse_iterator(std::ranges::begin(r)) );
static_assert( std::ranges::rend(std::move(r))
== std::make_reverse_iterator(std::ranges::begin(std::move(r))) );
} }
void void
test04() test04()
{ {
using __gnu_test::test_range; struct R4
using __gnu_test::bidirectional_iterator_wrapper; : __gnu_test::test_range<int, __gnu_test::bidirectional_iterator_wrapper>
{
R4(int (&a)[2]) : test_range(a) { }
using test_range::begin;
// Replace test_range::end() to return same type as begin()
// so ranges::rend will wrap it in a reverse_iterator.
auto end() &
{
using __gnu_test::bidirectional_iterator_wrapper;
return bidirectional_iterator_wrapper<int>(bounds.last, &bounds);
}
};
int a[2] = { }; int a[2] = { };
test_range<int, bidirectional_iterator_wrapper> r(a); R4 r(a);
VERIFY( std::ranges::rend(r) == std::make_reverse_iterator(std::ranges::begin(r)) ); VERIFY( std::ranges::rend(r) == std::make_reverse_iterator(std::ranges::begin(r)) );
} }
...@@ -108,4 +134,5 @@ main() ...@@ -108,4 +134,5 @@ main()
test01(); test01();
test02(); test02();
test03(); test03();
test04();
} }
...@@ -66,7 +66,7 @@ static_assert( same_as<std::ranges::iterator_t<O>, ...@@ -66,7 +66,7 @@ static_assert( same_as<std::ranges::iterator_t<O>,
decltype(std::declval<O&>().begin())> ); decltype(std::declval<O&>().begin())> );
static_assert( same_as<std::ranges::sentinel_t<C>, static_assert( same_as<std::ranges::sentinel_t<C>,
contiguous_iterator_wrapper<char>> ); decltype(std::declval<C&>().end())> );
static_assert( same_as<std::ranges::sentinel_t<O>, static_assert( same_as<std::ranges::sentinel_t<O>,
decltype(std::declval<O&>().end())> ); decltype(std::declval<O&>().end())> );
......
...@@ -675,8 +675,16 @@ namespace __gnu_test ...@@ -675,8 +675,16 @@ namespace __gnu_test
{ {
T* end; T* end;
friend bool operator==(const sentinel& s, const I& i) friend bool operator==(const sentinel& s, const I& i) noexcept
{ return s.end == i.ptr; } { return s.end == i.ptr; }
friend auto operator-(const sentinel& s, const I& i) noexcept
requires std::random_access_iterator<I>
{ return s.end - i.ptr; }
friend auto operator-(const I& i, const sentinel& s) noexcept
requires std::random_access_iterator<I>
{ return i.ptr - s.end; }
}; };
auto auto
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment