+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>
PR libstdc++/93470
{
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(noexcept(std::ranges::end(a)));
VERIFY( std::ranges::end(a) == (a + 2) );
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);
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);
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);
static_assert(same_as<decltype(std::ranges::end(o)), decltype(o.end())>);
R 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(!noexcept(std::ranges::end(r)));
VERIFY( std::ranges::end(r) == end(r) );
test01()
{
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(std::move(r)) == &r.i );
}
test02()
{
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)
== std::make_reverse_iterator(std::ranges::end(r)) );
static_assert( std::ranges::rbegin(std::move(r))
void
test03()
{
- using __gnu_test::test_range;
- using __gnu_test::bidirectional_iterator_wrapper;
+ struct R3
+ : __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] = { };
- 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)) );
}
test01()
{
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(std::move(r)) == &r.i + 1 );
}
struct R2
{
- int a[2] = { };
- long l[2] = { };
+ int i = 0;
- constexpr const int* begin() const { return a; }
- constexpr const int* end() const { return a + 2; }
+ int* rbegin() noexcept { return &i + 1; }
+ long* rend() noexcept { return nullptr; } // not a sentinel for rbegin()
- friend constexpr const long* begin(const R2&& r) { return r.l; }
- friend constexpr const long* end(const R2&& r) { return r.l + 2; }
+ friend long* rbegin(R2&) noexcept { return nullptr; }
+ 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
test02()
{
- constexpr R2 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))) );
+ R2 r;
+
+ // Otherwise, decay-copy(rend(t)) if it is a valid expression
+ // and its type S models sentinel_for<decltype(ranges::rbegin(E))>
+
+ 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
{
- int i = 0;
+ int a[2] = { };
+ long l[2] = { };
- int* rbegin() noexcept { return &i + 1; }
- long* rend() noexcept { return nullptr; } // not a sentinel for rbegin()
+ constexpr const int* begin() const { return a; }
+ constexpr const int* end() const { return a + 2; }
- friend long* rbegin(R3&) noexcept { return nullptr; }
- friend int* rend(R3& r) { return &r.i; }
+ friend constexpr const long* begin(const R3&& r) { return r.l; }
+ 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
test03()
{
- R3 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)) );
+ constexpr R3 r;
+
+ // Otherwise, make_reverse_iterator(ranges::begin(t)) if both
+ // ranges::begin(t) and ranges::end(t) are valid expressions
+ // of the same type I which models bidirectional_iterator.
+
+ 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
test04()
{
- using __gnu_test::test_range;
- using __gnu_test::bidirectional_iterator_wrapper;
+ struct R4
+ : __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] = { };
- test_range<int, bidirectional_iterator_wrapper> r(a);
+ R4 r(a);
VERIFY( std::ranges::rend(r) == std::make_reverse_iterator(std::ranges::begin(r)) );
}
test01();
test02();
test03();
+ test04();
}
decltype(std::declval<O&>().begin())> );
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>,
decltype(std::declval<O&>().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; }
+
+ 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