Commit b5b2e387 by Jonathan Wakely Committed by Jonathan Wakely

libstdc++: Implement ranges::safe_range for C++20 (P1870R1)

This change replaces the __forwarding_range implementation detail with
the ranges::safe_range concept and adds the ranges::enable_safe_range
variable template for opt-in in to the concept.

It also adjusts the begin/end/rbegin/rend customization point objects to
match the new rules for accessing rvalue ranges only when safe to do so.

	* include/bits/range_access.h (ranges::enable_safe_range): Define.
	(ranges::begin, ranges::end, ranges::rbegin, ranges::rend): Constrain
	to only accept types satisfying safe_range and treat argument as an
	lvalue when calling a member of performing ADL.
	(ranges::__detail::__range_impl, ranges::__detail::__forwarding_range):
	Remove.
	(ranges::range): Adjust definition.
	(ranges::safe_range): Define.
	(ranges::iterator_t, ranges::range_difference_t): Reorder definitions
	to match the synopsis in the working draft.
	(ranges::disable_sized_range): Remove duplicate definition.
	* include/experimental/string_view (ranges::enable_safe_range): Add
	partial specialization for std::experimental::basic_string_view.
	* include/std/ranges (ranges::viewable_range, ranges::subrange)
	(ranges::empty_view, ranges::iota_view): Use safe_range. Specialize
	enable_safe_range.
	(ranges::safe_iterator_t, ranges::safe_subrange_t): Define.
	* include/std/span (ranges::enable_safe_range): Add partial
	specialization for std::span.
	* include/std/string_view (ranges::enable_safe_range): Likewise for
	std::basic_string_view.
	* testsuite/std/ranges/access/begin.cc: Adjust expected results.
	* testsuite/std/ranges/access/cbegin.cc: Likewise.
	* testsuite/std/ranges/access/cdata.cc: Likewise.
	* testsuite/std/ranges/access/cend.cc: Likewise.
	* testsuite/std/ranges/access/crbegin.cc: Likewise.
	* testsuite/std/ranges/access/crend.cc: Likewise.
	* testsuite/std/ranges/access/data.cc: Likewise.
	* testsuite/std/ranges/access/end.cc: Likewise.
	* testsuite/std/ranges/access/rbegin.cc: Likewise.
	* testsuite/std/ranges/access/rend.cc: Likewise.
	* testsuite/std/ranges/empty_view.cc: Test ranges::begin and
	ranges::end instead of unqualified calls to begin and end.
	* testsuite/std/ranges/safe_range.cc: New test.
	* testsuite/std/ranges/safe_range_types.cc: New test.
	* testsuite/util/testsuite_iterators.h: Add comment about safe_range.

From-SVN: r279135
parent 43c2de7a
2019-12-09 Jonathan Wakely <jwakely@redhat.com> 2019-12-09 Jonathan Wakely <jwakely@redhat.com>
* include/bits/range_access.h (ranges::enable_safe_range): Define.
(ranges::begin, ranges::end, ranges::rbegin, ranges::rend): Constrain
to only accept types satisfying safe_range and treat argument as an
lvalue when calling a member of performing ADL.
(ranges::__detail::__range_impl, ranges::__detail::__forwarding_range):
Remove.
(ranges::range): Adjust definition.
(ranges::safe_range): Define.
(ranges::iterator_t, ranges::range_difference_t): Reorder definitions
to match the synopsis in the working draft.
(ranges::disable_sized_range): Remove duplicate definition.
* include/experimental/string_view (ranges::enable_safe_range): Add
partial specialization for std::experimental::basic_string_view.
* include/std/ranges (ranges::viewable_range, ranges::subrange)
(ranges::empty_view, ranges::iota_view): Use safe_range. Specialize
enable_safe_range.
(ranges::safe_iterator_t, ranges::safe_subrange_t): Define.
* include/std/span (ranges::enable_safe_range): Add partial
specialization for std::span.
* include/std/string_view (ranges::enable_safe_range): Likewise for
std::basic_string_view.
* testsuite/std/ranges/access/begin.cc: Adjust expected results.
* testsuite/std/ranges/access/cbegin.cc: Likewise.
* testsuite/std/ranges/access/cdata.cc: Likewise.
* testsuite/std/ranges/access/cend.cc: Likewise.
* testsuite/std/ranges/access/crbegin.cc: Likewise.
* testsuite/std/ranges/access/crend.cc: Likewise.
* testsuite/std/ranges/access/data.cc: Likewise.
* testsuite/std/ranges/access/end.cc: Likewise.
* testsuite/std/ranges/access/rbegin.cc: Likewise.
* testsuite/std/ranges/access/rend.cc: Likewise.
* testsuite/std/ranges/empty_view.cc: Test ranges::begin and
ranges::end instead of unqualified calls to begin and end.
* testsuite/std/ranges/safe_range.cc: New test.
* testsuite/std/ranges/safe_range_types.cc: New test.
* testsuite/util/testsuite_iterators.h: Add comment about safe_range.
* testsuite/27_io/filesystem/path/concat/strings.cc: Test more cases. * testsuite/27_io/filesystem/path/concat/strings.cc: Test more cases.
PR libstdc++/92853 PR libstdc++/92853
......
...@@ -691,6 +691,18 @@ namespace experimental ...@@ -691,6 +691,18 @@ namespace experimental
} // namespace literals } // namespace literals
} // namespace experimental } // namespace experimental
#if __cpp_lib_concepts
namespace ranges
{
template<typename> extern inline const bool enable_safe_range;
// Opt-in to safe_range concept
template<typename _CharT, typename _Traits>
inline constexpr bool
enable_safe_range<experimental::basic_string_view<_CharT, _Traits>>
= true;
}
#endif
_GLIBCXX_END_NAMESPACE_VERSION _GLIBCXX_END_NAMESPACE_VERSION
} // namespace std } // namespace std
......
...@@ -89,9 +89,10 @@ namespace ranges ...@@ -89,9 +89,10 @@ namespace ranges
= range<_Tp> && movable<_Tp> && default_initializable<_Tp> = range<_Tp> && movable<_Tp> && default_initializable<_Tp>
&& enable_view<_Tp>; && enable_view<_Tp>;
/// A range which can be safely converted to a view.
template<typename _Tp> template<typename _Tp>
concept viewable_range = range<_Tp> concept viewable_range = range<_Tp>
&& (__detail::__forwarding_range<_Tp> || view<decay_t<_Tp>>); && (safe_range<_Tp> || view<decay_t<_Tp>>);
namespace __detail namespace __detail
{ {
...@@ -295,7 +296,7 @@ namespace ranges ...@@ -295,7 +296,7 @@ namespace ranges
} }
template<__detail::__not_same_as<subrange> _Rng> template<__detail::__not_same_as<subrange> _Rng>
requires __detail::__forwarding_range<_Rng> requires safe_range<_Rng>
&& convertible_to<iterator_t<_Rng>, _It> && convertible_to<iterator_t<_Rng>, _It>
&& convertible_to<sentinel_t<_Rng>, _Sent> && convertible_to<sentinel_t<_Rng>, _Sent>
constexpr constexpr
...@@ -306,7 +307,7 @@ namespace ranges ...@@ -306,7 +307,7 @@ namespace ranges
_M_size._M_size = ranges::size(__r); _M_size._M_size = ranges::size(__r);
} }
template<__detail::__forwarding_range _Rng> template<safe_range _Rng>
requires convertible_to<iterator_t<_Rng>, _It> requires convertible_to<iterator_t<_Rng>, _It>
&& convertible_to<sentinel_t<_Rng>, _Sent> && convertible_to<sentinel_t<_Rng>, _Sent>
constexpr constexpr
...@@ -401,12 +402,6 @@ namespace ranges ...@@ -401,12 +402,6 @@ namespace ranges
ranges::advance(_M_begin, __n, _M_end); ranges::advance(_M_begin, __n, _M_end);
return *this; return *this;
} }
friend constexpr _It
begin(subrange&& __r) { return __r.begin(); }
friend constexpr _Sent
end(subrange&& __r) { return __r.end(); }
}; };
template<input_or_output_iterator _It, sentinel_for<_It> _Sent> template<input_or_output_iterator _It, sentinel_for<_It> _Sent>
...@@ -424,14 +419,14 @@ namespace ranges ...@@ -424,14 +419,14 @@ namespace ranges
-> subrange<tuple_element_t<0, _Pr>, tuple_element_t<1, _Pr>, -> subrange<tuple_element_t<0, _Pr>, tuple_element_t<1, _Pr>,
subrange_kind::sized>; subrange_kind::sized>;
template<__detail::__forwarding_range _Rng> template<safe_range _Rng>
subrange(_Rng&&) subrange(_Rng&&)
-> subrange<iterator_t<_Rng>, sentinel_t<_Rng>, -> subrange<iterator_t<_Rng>, sentinel_t<_Rng>,
(sized_range<_Rng> (sized_range<_Rng>
|| sized_sentinel_for<sentinel_t<_Rng>, iterator_t<_Rng>>) || sized_sentinel_for<sentinel_t<_Rng>, iterator_t<_Rng>>)
? subrange_kind::sized : subrange_kind::unsized>; ? subrange_kind::sized : subrange_kind::unsized>;
template<__detail::__forwarding_range _Rng> template<safe_range _Rng>
subrange(_Rng&&, subrange(_Rng&&,
__detail::__make_unsigned_like_t<range_difference_t<_Rng>>) __detail::__make_unsigned_like_t<range_difference_t<_Rng>>)
-> subrange<iterator_t<_Rng>, sentinel_t<_Rng>, subrange_kind::sized>; -> subrange<iterator_t<_Rng>, sentinel_t<_Rng>, subrange_kind::sized>;
...@@ -457,6 +452,12 @@ namespace ranges ...@@ -457,6 +452,12 @@ namespace ranges
else else
return __r.end(); return __r.end();
} }
template<input_or_output_iterator _It, sentinel_for<_It> _Sent,
subrange_kind _Kind>
inline constexpr bool
enable_safe_range<subrange<_It, _Sent, _Kind>> = true;
} // namespace ranges } // namespace ranges
using ranges::get; using ranges::get;
...@@ -471,8 +472,19 @@ namespace ranges ...@@ -471,8 +472,19 @@ namespace ranges
constexpr dangling(_Args&&...) noexcept { } constexpr dangling(_Args&&...) noexcept { }
}; };
template<range _Range>
using safe_iterator_t = conditional_t<safe_range<_Range>,
iterator_t<_Range>,
dangling>;
template<range _Range>
using safe_subrange_t = conditional_t<safe_range<_Range>,
subrange<iterator_t<_Range>>,
dangling>;
template<typename _Tp> requires is_object_v<_Tp> template<typename _Tp> requires is_object_v<_Tp>
class empty_view : public view_interface<empty_view<_Tp>> class empty_view
: public view_interface<empty_view<_Tp>>
{ {
public: public:
static constexpr _Tp* begin() noexcept { return nullptr; } static constexpr _Tp* begin() noexcept { return nullptr; }
...@@ -480,11 +492,11 @@ namespace ranges ...@@ -480,11 +492,11 @@ namespace ranges
static constexpr _Tp* data() noexcept { return nullptr; } static constexpr _Tp* data() noexcept { return nullptr; }
static constexpr size_t size() noexcept { return 0; } static constexpr size_t size() noexcept { return 0; }
static constexpr bool empty() noexcept { return true; } static constexpr bool empty() noexcept { return true; }
friend constexpr _Tp* begin(empty_view) noexcept { return nullptr; }
friend constexpr _Tp* end(empty_view) noexcept { return nullptr; }
}; };
template<typename _Tp>
inline constexpr bool enable_safe_range<empty_view<_Tp>> = true;
namespace __detail namespace __detail
{ {
template<copy_constructible _Tp> requires is_object_v<_Tp> template<copy_constructible _Tp> requires is_object_v<_Tp>
...@@ -899,6 +911,9 @@ namespace ranges ...@@ -899,6 +911,9 @@ namespace ranges
== __detail::__is_signed_integer_like<_Bound>)) == __detail::__is_signed_integer_like<_Bound>))
iota_view(_Winc, _Bound) -> iota_view<_Winc, _Bound>; iota_view(_Winc, _Bound) -> iota_view<_Winc, _Bound>;
template<weakly_incrementable _Winc, semiregular _Bound>
inline constexpr bool enable_safe_range<iota_view<_Winc, _Bound>> = true;
namespace views namespace views
{ {
template<typename _Tp> template<typename _Tp>
......
...@@ -399,16 +399,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -399,16 +399,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return {this->data() + __offset, __count}; return {this->data() + __offset, __count};
} }
// observers: range helpers
friend constexpr iterator
begin(span __sp) noexcept
{ return __sp.begin(); }
friend constexpr iterator
end(span __sp) noexcept
{ return __sp.end(); }
private: private:
[[no_unique_address]] __detail::__extent_storage<extent> _M_extent; [[no_unique_address]] __detail::__extent_storage<extent> _M_extent;
pointer _M_ptr; pointer _M_ptr;
...@@ -478,6 +468,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -478,6 +468,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
using type = _Type; using type = _Type;
}; };
namespace ranges
{
template<typename> extern inline const bool enable_safe_range;
// Opt-in to safe_range concept
template<typename _ElementType, size_t _Extent>
inline constexpr bool
enable_safe_range<span<_ElementType, _Extent>> = true;
}
_GLIBCXX_END_NAMESPACE_VERSION _GLIBCXX_END_NAMESPACE_VERSION
} // namespace std } // namespace std
#endif // concepts #endif // concepts
......
...@@ -724,6 +724,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -724,6 +724,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} // namespace string_literals } // namespace string_literals
} // namespace literals } // namespace literals
#if __cpp_lib_concepts
namespace ranges
{
template<typename> extern inline const bool enable_safe_range;
// Opt-in to safe_range concept
template<typename _CharT, typename _Traits>
inline constexpr bool
enable_safe_range<basic_string_view<_CharT, _Traits>> = true;
}
#endif
_GLIBCXX_END_NAMESPACE_VERSION _GLIBCXX_END_NAMESPACE_VERSION
} // namespace std } // namespace std
......
...@@ -71,11 +71,22 @@ struct R ...@@ -71,11 +71,22 @@ struct R
int a[4] = { 0, 1, 2, 3 }; int a[4] = { 0, 1, 2, 3 };
friend int* begin(R& r) { return r.a + 0; } friend int* begin(R& r) { return r.a + 0; }
friend int* begin(R&& r) { return r.a + 1; } friend int* begin(R&& r); // this overload is not defined
friend const int* begin(const R& r) noexcept { return r.a + 2; } friend const int* begin(const R& r) noexcept { return r.a + 2; }
friend const int* begin(const R&& r) noexcept { return r.a + 3; } friend const int* begin(const R&& r) noexcept; // not defined
}; };
struct RV // view on an R
{
R& r;
friend int* begin(RV& rv) { return begin(rv.r); }
friend const int* begin(const RV& rv) noexcept { return begin(rv.r); }
};
// Allow ranges::begin to work with RV&&
template<> constexpr bool std::ranges::enable_safe_range<RV> = true;
void void
test03() test03()
{ {
...@@ -86,20 +97,23 @@ test03() ...@@ -86,20 +97,23 @@ test03()
static_assert(!noexcept(std::ranges::begin(r))); static_assert(!noexcept(std::ranges::begin(r)));
VERIFY( std::ranges::begin(r) == begin(r) ); VERIFY( std::ranges::begin(r) == begin(r) );
static_assert(same_as<decltype(std::ranges::begin(std::move(r))),
decltype(begin(std::move(r)))>);
static_assert(!noexcept(std::ranges::begin(std::move(r))));
VERIFY( std::ranges::begin(std::move(r)) == begin(std::move(r)) );
static_assert(same_as<decltype(std::ranges::begin(c)), decltype(begin(c))>); static_assert(same_as<decltype(std::ranges::begin(c)), decltype(begin(c))>);
static_assert(noexcept(std::ranges::begin(c))); static_assert(noexcept(std::ranges::begin(c)));
VERIFY( std::ranges::begin(c) == begin(c) ); VERIFY( std::ranges::begin(c) == begin(c) );
static_assert(same_as<decltype(std::ranges::begin(std::move(c))), RV v{r};
decltype(begin(std::move(c)))>); // enable_safe_range<RV> allows ranges::begin to work for rvalues,
static_assert(noexcept(std::ranges::begin(std::move(c)))); // but it will call v.begin() or begin(v) on an lvalue:
VERIFY( std::ranges::begin(std::move(c)) == begin(std::move(c)) ); static_assert(same_as<decltype(std::ranges::begin(std::move(v))),
decltype(begin(v))>);
static_assert(!noexcept(std::ranges::begin(std::move(v))));
VERIFY( std::ranges::begin(std::move(v)) == begin(v) );
const RV cv{r};
static_assert(same_as<decltype(std::ranges::begin(std::move(cv))),
decltype(begin(cv))>);
static_assert(noexcept(std::ranges::begin(std::move(cv))));
VERIFY( std::ranges::begin(std::move(cv)) == begin(cv) );
} }
struct RR struct RR
...@@ -111,12 +125,15 @@ struct RR ...@@ -111,12 +125,15 @@ struct RR
short* begin() noexcept { return &s; } short* begin() noexcept { return &s; }
const long* begin() const { return &l; } const long* begin() const { return &l; }
friend int* begin(RR& r) { return r.a + 0; } friend int* begin(RR& r) noexcept { return r.a + 0; }
friend int* begin(RR&& r) { return r.a + 1; } friend int* begin(RR&& r); // not defined
friend const int* begin(const RR& r) { return r.a + 2; } friend const int* begin(const RR& r) { return r.a + 2; }
friend const int* begin(const RR&& r) noexcept { return r.a + 3; } friend const int* begin(const RR&& r) noexcept; // not defined
}; };
// N.B. this is a lie, begin on an RR rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<RR> = true;
void void
test04() test04()
{ {
...@@ -125,14 +142,16 @@ test04() ...@@ -125,14 +142,16 @@ test04()
VERIFY( std::ranges::begin(r) == &r.s ); VERIFY( std::ranges::begin(r) == &r.s );
static_assert(noexcept(std::ranges::begin(r))); static_assert(noexcept(std::ranges::begin(r)));
VERIFY( std::ranges::begin(std::move(r)) == r.a + 1 ); // calls r.begin() on an lvalue, not rvalue
static_assert(!noexcept(std::ranges::begin(std::move(r)))); VERIFY( std::ranges::begin(std::move(r)) == std::ranges::begin(r) );
static_assert(noexcept(std::ranges::begin(std::move(r))));
VERIFY( std::ranges::begin(c) == &r.l ); VERIFY( std::ranges::begin(c) == &r.l );
static_assert(!noexcept(std::ranges::begin(c))); static_assert(!noexcept(std::ranges::begin(c)));
VERIFY( std::ranges::begin(std::move(c)) == r.a + 3 ); // calls r.begin() on a const lvalue, not rvalue
static_assert(noexcept(std::ranges::begin(std::move(c)))); VERIFY( std::ranges::begin(std::move(c)) == std::ranges::begin(c) );
static_assert(!noexcept(std::ranges::begin(std::move(c))));
} }
int int
......
...@@ -40,20 +40,34 @@ struct R ...@@ -40,20 +40,34 @@ struct R
int a[4] = { 0, 1, 2, 3 }; int a[4] = { 0, 1, 2, 3 };
friend int* begin(R& r) { return r.a + 0; } friend int* begin(R& r) { return r.a + 0; }
friend int* begin(R&& r) { return r.a + 1; } friend int* begin(R&&); // this function is not defined
friend const int* begin(const R& r) noexcept { return r.a + 2; } friend const int* begin(const R& r) noexcept { return r.a + 2; }
friend const int* begin(const R&& r) noexcept { return r.a + 3; } friend const int* begin(const R&&); // this function is not defined
}; };
struct RV // view on an R
{
R& r;
friend int* begin(RV&); // this function is not defined
friend const int* begin(const RV& rv) noexcept { return begin(std::as_const(rv.r)); }
};
// Allow ranges::begin to work with RV&&
template<> constexpr bool std::ranges::enable_safe_range<RV> = true;
void void
test03() test03()
{ {
R r; R r;
const R& c = r; const R& c = r;
VERIFY(std::ranges::cbegin(r) == std::ranges::begin(c)); VERIFY(std::ranges::cbegin(r) == std::ranges::begin(c));
VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(std::move(c)));
VERIFY(std::ranges::cbegin(c) == std::ranges::begin(c)); VERIFY(std::ranges::cbegin(c) == std::ranges::begin(c));
VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(std::move(c)));
RV v{r};
VERIFY(std::ranges::cbegin(std::move(v)) == std::ranges::begin(c));
const RV cv{r};
VERIFY(std::ranges::cbegin(std::move(cv)) == std::ranges::begin(c));
} }
struct RR struct RR
...@@ -71,15 +85,18 @@ struct RR ...@@ -71,15 +85,18 @@ struct RR
friend const int* begin(const RR&& r) noexcept { return r.a + 3; } friend const int* begin(const RR&& r) noexcept { return r.a + 3; }
}; };
// N.B. this is a lie, cbegin on an RR rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<RR> = true;
void void
test04() test04()
{ {
RR r; RR r;
const RR& c = r; const RR& c = r;
VERIFY(std::ranges::cbegin(r) == std::ranges::begin(c)); VERIFY(std::ranges::cbegin(r) == std::ranges::begin(c));
VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(std::move(c))); VERIFY(std::ranges::cbegin(std::move(r)) == std::ranges::begin(c));
VERIFY(std::ranges::cbegin(c) == std::ranges::begin(c)); VERIFY(std::ranges::cbegin(c) == std::ranges::begin(c));
VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(std::move(c))); VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(c));
} }
int int
......
...@@ -51,17 +51,22 @@ struct R ...@@ -51,17 +51,22 @@ struct R
long l = 0; long l = 0;
int* data() const { return nullptr; } int* data() const { return nullptr; }
friend long* begin(R&& r) { return &r.l; } friend long* begin(R&& r); // this function is not defined
friend const long* begin(const R&& r) { return &r.l + 1; } friend const long* begin(const R& r) { return &r.l; }
friend const short* begin(const R&&); // not defined
}; };
// This is a lie, ranges::begin(R&&) returns a dangling iterator.
template<> constexpr bool std::ranges::enable_safe_range<R> = true;
void void
test03() test03()
{ {
R r; R r;
const R& c = r; const R& c = r;
VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::data(std::move(c)) ); VERIFY( std::ranges::cdata(r) == std::ranges::data(c) );
VERIFY( std::ranges::cdata(std::move(c)) == std::ranges::data(std::move(c)) ); VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::begin(c) );
VERIFY( std::ranges::cdata(std::move(c)) == std::ranges::begin(c) );
} }
int int
......
...@@ -49,15 +49,31 @@ struct R ...@@ -49,15 +49,31 @@ struct R
friend const int* end(const R&& r) noexcept { return r.a + 3; } friend const int* end(const R&& r) noexcept { return r.a + 3; }
}; };
struct RV // view on an R
{
R& r;
friend const int* begin(RV& rv) { return rv.r.begin(); }
friend int* end(RV& rv) { return end(rv.r); }
friend const int* begin(const RV& rv) noexcept { return rv.r.begin(); }
friend const int* end(const RV& rv) noexcept { return end(std::as_const(rv.r)); }
};
// Allow ranges::end to work with RV&&
template<> constexpr bool std::ranges::enable_safe_range<RV> = true;
void void
test03() test03()
{ {
R r; R r;
const R& c = r; const R& c = r;
VERIFY( std::ranges::cend(r) == std::ranges::end(c) ); VERIFY( std::ranges::cend(r) == std::ranges::end(c) );
VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(std::move(c)) );
VERIFY( std::ranges::cend(c) == std::ranges::end(c) ); VERIFY( std::ranges::cend(c) == std::ranges::end(c) );
VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(std::move(c)) );
RV v{r};
const RV cv{r};
VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(c) );
VERIFY( std::ranges::cend(std::move(cv)) == std::ranges::end(c) );
} }
struct RR struct RR
...@@ -81,15 +97,19 @@ struct RR ...@@ -81,15 +97,19 @@ struct RR
friend const int* end(const RR&& r) noexcept { return r.a + 3; } friend const int* end(const RR&& r) noexcept { return r.a + 3; }
}; };
// N.B. this is a lie, begin/end on an RR rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<RR> = true;
void void
test04() test04()
{ {
RR r; RR r;
const RR& c = r; const RR& c = r;
VERIFY( std::ranges::cend(r) == std::ranges::end(c) ); VERIFY( std::ranges::cend(r) == std::ranges::end(c) );
VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(std::move(c)) );
VERIFY( std::ranges::cend(c) == std::ranges::end(c) ); VERIFY( std::ranges::cend(c) == std::ranges::end(c) );
VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(std::move(c)) );
VERIFY( std::ranges::cend(std::move(r)) == std::ranges::end(c) );
VERIFY( std::ranges::cend(std::move(c)) == std::ranges::end(c) );
} }
int int
......
...@@ -31,15 +31,29 @@ struct R1 ...@@ -31,15 +31,29 @@ struct R1
friend const int* rbegin(const R1&& r) { return &r.j; } friend const int* rbegin(const R1&& r) { return &r.j; }
}; };
struct R1V // view on an R1
{
R1& r;
friend const long* rbegin(R1V&); // this is not defined
friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); }
};
// Allow ranges::end to work with R1V&&
template<> constexpr bool std::ranges::enable_safe_range<R1V> = true;
void void
test01() test01()
{ {
R1 r; R1 r;
const R1& c = r; const R1& c = r;
VERIFY( std::ranges::crbegin(r) == std::ranges::rbegin(c) ); VERIFY( std::ranges::crbegin(r) == std::ranges::rbegin(c) );
VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(std::move(c)) );
VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) ); VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(std::move(c)) );
R1V v{r};
const R1V cv{r};
VERIFY( std::ranges::crbegin(std::move(v)) == std::ranges::rbegin(c) );
VERIFY( std::ranges::crbegin(std::move(cv)) == std::ranges::rbegin(c) );
} }
struct R2 struct R2
...@@ -50,19 +64,23 @@ struct R2 ...@@ -50,19 +64,23 @@ struct R2
const int* begin() const { return a; } const int* begin() const { return a; }
const int* end() const { return a + 2; } const int* end() const { return a + 2; }
friend const long* begin(const R2&& r) { return r.l; } friend const long* begin(const R2&&); // not defined
friend const long* end(const R2&& r) { return r.l + 2; } friend const long* end(const R2&&); // not defined
}; };
// N.B. this is a lie, rbegin on an R2 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<R2> = true;
void void
test02() test02()
{ {
R2 r; R2 r;
const R2& c = r; const R2& c = r;
VERIFY( std::ranges::crbegin(r) == std::ranges::rbegin(c) ); VERIFY( std::ranges::crbegin(r) == std::ranges::rbegin(c) );
VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(std::move(c)) );
VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) ); VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(std::move(c)) );
VERIFY( std::ranges::crbegin(std::move(r)) == std::ranges::rbegin(c) );
VERIFY( std::ranges::crbegin(std::move(c)) == std::ranges::rbegin(c) );
} }
int int
......
...@@ -33,15 +33,18 @@ struct R1 ...@@ -33,15 +33,18 @@ struct R1
friend constexpr const int* rend(const R1&& r) { return &r.j + 1; } friend constexpr const int* rend(const R1&& r) { return &r.j + 1; }
}; };
// N.B. this is a lie, rend on an R1 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<R1> = true;
void void
test01() test01()
{ {
R1 r; R1 r;
const R1& c = r; const R1& c = r;
VERIFY( std::ranges::crend(r) == std::ranges::rend(c) ); VERIFY( std::ranges::crend(r) == std::ranges::rend(c) );
VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) );
VERIFY( std::ranges::crend(c) == std::ranges::rend(c) ); VERIFY( std::ranges::crend(c) == std::ranges::rend(c) );
VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(std::move(c)) ); VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(c) );
VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(c) );
} }
struct R2 struct R2
...@@ -56,14 +59,17 @@ struct R2 ...@@ -56,14 +59,17 @@ struct R2
friend const long* end(const R2&& r) { return r.l + 2; } friend const long* end(const R2&& r) { return r.l + 2; }
}; };
// N.B. this is a lie, rend on an R2 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<R2> = true;
void void
test02() test02()
{ {
R2 r; R2 r;
const R2& c = r; const R2& c = r;
VERIFY( std::ranges::crend(r) == std::ranges::rend(c) ); VERIFY( std::ranges::crend(r) == std::ranges::rend(c) );
VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) );
VERIFY( std::ranges::crend(c) == std::ranges::rend(c) ); VERIFY( std::ranges::crend(c) == std::ranges::rend(c) );
VERIFY( std::ranges::crend(std::move(r)) == std::ranges::rend(std::move(c)) );
VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(std::move(c)) ); VERIFY( std::ranges::crend(std::move(c)) == std::ranges::rend(std::move(c)) );
} }
...@@ -78,6 +84,9 @@ struct R3 ...@@ -78,6 +84,9 @@ struct R3
friend const int* rend(const R3& r) { return &r.i; } friend const int* rend(const R3& r) { return &r.i; }
}; };
// N.B. this is a lie, rend on an R3 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<R3> = true;
void void
test03() test03()
{ {
......
...@@ -56,15 +56,20 @@ struct R3 ...@@ -56,15 +56,20 @@ struct R3
long l = 0; long l = 0;
int* data() const { return nullptr; } int* data() const { return nullptr; }
friend long* begin(R3&& r) { return &r.l; } friend long* begin(R3& r) { return &r.l; }
friend const long* begin(const R3&& r) { return &r.l + 1; } friend const long* begin(const R3& r) { return &r.l + 1; }
}; };
// N.B. this is a lie, begin 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; R3 r;
const R3& c = r; const R3& c = r;
// r.data() can only be used on an lvalue, but ranges::begin(R3&&) is OK
// because R3 satisfies ranges::safe_range.
VERIFY( std::ranges::data(std::move(r)) == std::to_address(std::ranges::begin(std::move(r))) ); VERIFY( std::ranges::data(std::move(r)) == std::to_address(std::ranges::begin(std::move(r))) );
VERIFY( std::ranges::data(std::move(c)) == std::to_address(std::ranges::begin(std::move(c))) ); VERIFY( std::ranges::data(std::move(c)) == std::to_address(std::ranges::begin(std::move(c))) );
} }
......
...@@ -74,6 +74,19 @@ struct R ...@@ -74,6 +74,19 @@ struct R
friend const int* end(const R&& r) noexcept { return r.a + 3; } friend const int* end(const R&& r) noexcept { return r.a + 3; }
}; };
struct RV // view on an R
{
R& r;
const int* begin() const;
friend int* end(RV& v) noexcept { return end(v.r); }
friend const int* end(const RV& v) { return end(std::as_const(v.r)); }
};
// Allow ranges::begin to work with RV&&
template<> constexpr bool std::ranges::enable_safe_range<RV> = true;
void void
test03() test03()
{ {
...@@ -84,20 +97,21 @@ test03() ...@@ -84,20 +97,21 @@ test03()
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) );
static_assert(same_as<decltype(std::ranges::end(std::move(r))),
decltype(end(std::move(r)))>);
static_assert(!noexcept(std::ranges::end(std::move(r))));
VERIFY( std::ranges::end(std::move(r)) == end(std::move(r)) );
static_assert(same_as<decltype(std::ranges::end(c)), decltype(end(c))>); static_assert(same_as<decltype(std::ranges::end(c)), decltype(end(c))>);
static_assert(noexcept(std::ranges::end(c))); static_assert(noexcept(std::ranges::end(c)));
VERIFY( std::ranges::end(c) == end(c) ); VERIFY( std::ranges::end(c) == end(c) );
static_assert(same_as<decltype(std::ranges::end(std::move(c))), RV v{r};
decltype(end(std::move(c)))>); static_assert(same_as<decltype(std::ranges::end(std::move(v))),
static_assert(noexcept(std::ranges::end(std::move(c)))); decltype(end(r))>);
VERIFY( std::ranges::end(std::move(c)) == end(std::move(c)) ); static_assert(noexcept(std::ranges::end(std::move(v))));
VERIFY( std::ranges::end(std::move(v)) == end(r) );
const RV cv{r};
static_assert(same_as<decltype(std::ranges::end(std::move(cv))),
decltype(end(c))>);
static_assert(!noexcept(std::ranges::end(std::move(cv))));
VERIFY( std::ranges::end(std::move(cv)) == end(c) );
} }
struct RR struct RR
...@@ -123,6 +137,9 @@ struct RR ...@@ -123,6 +137,9 @@ struct RR
friend const int* end(const RR&& r) noexcept { return r.a + 3; } friend const int* end(const RR&& r) noexcept { return r.a + 3; }
}; };
// N.B. this is a lie, end on an RR rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<RR> = true;
void void
test04() test04()
{ {
...@@ -131,14 +148,14 @@ test04() ...@@ -131,14 +148,14 @@ test04()
VERIFY( std::ranges::end(r) == &r.s ); VERIFY( std::ranges::end(r) == &r.s );
static_assert(noexcept(std::ranges::end(r))); static_assert(noexcept(std::ranges::end(r)));
VERIFY( std::ranges::end(std::move(r)) == r.a + 1 ); VERIFY( std::ranges::end(std::move(r)) == &r.s );
static_assert(!noexcept(std::ranges::end(std::move(r)))); static_assert(noexcept(std::ranges::end(std::move(r))));
VERIFY( std::ranges::end(c) == &r.l ); VERIFY( std::ranges::end(c) == &r.l );
static_assert(!noexcept(std::ranges::end(c))); static_assert(!noexcept(std::ranges::end(c)));
VERIFY( std::ranges::end(std::move(c)) == r.a + 3 ); VERIFY( std::ranges::end(std::move(c)) == &r.l );
static_assert(noexcept(std::ranges::end(std::move(c)))); static_assert(!noexcept(std::ranges::end(std::move(c))));
} }
int int
......
...@@ -31,26 +31,31 @@ struct R1 ...@@ -31,26 +31,31 @@ struct R1
friend constexpr const int* rbegin(const R1&& r) { return &r.j; } friend constexpr const int* rbegin(const R1&& r) { return &r.j; }
}; };
// N.B. this is a lie, rbegin on an R1 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<R1> = true;
void void
test01() test01()
{ {
constexpr R1 r; constexpr R1 r;
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.j ); static_assert( std::ranges::rbegin(std::move(r)) == &r.i );
} }
struct R2 struct R2
{ {
int a[2] = { }; int a[2] = { };
long l[2] = { };
constexpr const int* begin() const { return a; } constexpr const int* begin() const { return a; }
constexpr const int* end() const { return a + 2; } constexpr const int* end() const { return a + 2; }
friend constexpr const long* begin(const R2&& r) { return r.l; } friend constexpr const long* begin(const R2&&); // not defined
friend constexpr const long* end(const R2&& r) { return r.l + 2; } friend constexpr const long* end(const R2&&); // not defined
}; };
// 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()
{ {
......
...@@ -29,16 +29,19 @@ struct R1 ...@@ -29,16 +29,19 @@ struct R1
constexpr const int* rbegin() const { return &i; } constexpr const int* rbegin() const { return &i; }
constexpr const int* rend() const { return &i + 1; } constexpr const int* rend() const { return &i + 1; }
friend constexpr const int* rbegin(const R1&& r) { return &r.j; } friend constexpr const int* rbegin(const R1&&); // not defined
friend constexpr const int* rend(const R1&& r) { return &r.j + 1; } friend constexpr const int* rend(const R1&&); // not defined
}; };
// N.B. this is a lie, rend on an R1 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_safe_range<R1> = true;
void void
test01() test01()
{ {
constexpr R1 r; constexpr R1 r;
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.j + 1 ); static_assert( std::ranges::rend(std::move(r)) == &r.i + 1 );
} }
struct R2 struct R2
...@@ -53,6 +56,9 @@ struct R2 ...@@ -53,6 +56,9 @@ struct R2
friend constexpr const long* end(const R2&& r) { return r.l + 2; } friend constexpr const long* end(const R2&& r) { return r.l + 2; }
}; };
// 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()
{ {
......
...@@ -31,5 +31,5 @@ static_assert(e.end() == nullptr); ...@@ -31,5 +31,5 @@ static_assert(e.end() == nullptr);
static_assert(e.data() == nullptr); static_assert(e.data() == nullptr);
static_assert(e.empty()); static_assert(e.empty());
static_assert(begin(e) == nullptr); static_assert(std::ranges::begin(e) == nullptr);
static_assert(end(e) == nullptr); static_assert(std::ranges::end(e) == nullptr);
// Copyright (C) 2019 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/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <ranges>
#include <testsuite_iterators.h>
static_assert( std::ranges::safe_range<int(&)[1]> );
static_assert( std::ranges::safe_range<const int(&)[1]> );
static_assert( !std::ranges::safe_range<int[1]> );
static_assert( !std::ranges::safe_range<int*> );
using __gnu_test::test_contiguous_range;
static_assert( !std::ranges::safe_range<test_contiguous_range<int>> );
static_assert( std::ranges::safe_range<test_contiguous_range<int>&> );
static_assert( !std::ranges::safe_range<test_contiguous_range<int>&&> );
template<>
constexpr bool
std::ranges::enable_safe_range<test_contiguous_range<long>> = true;
static_assert( std::ranges::safe_range<test_contiguous_range<long>> );
static_assert( std::ranges::safe_range<test_contiguous_range<long>&> );
static_assert( std::ranges::safe_range<test_contiguous_range<long>&&> );
// Copyright (C) 2019 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/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <ranges>
#include <span>
#include <string>
#include <experimental/string_view>
template<typename T>
constexpr bool
rvalue_is_safe_range()
{
using std::ranges::safe_range;
// An lvalue range always models safe_range
static_assert( safe_range<T&> );
static_assert( safe_range<const T&> );
// Result should not depend on addition of const or rvalue-reference.
static_assert( safe_range<T&&> == safe_range<T> );
static_assert( safe_range<const T> == safe_range<T> );
static_assert( safe_range<const T&&> == safe_range<T> );
return std::ranges::safe_range<T>;
}
static_assert( rvalue_is_safe_range<std::ranges::subrange<int*, int*>>() );
static_assert( rvalue_is_safe_range<std::ranges::empty_view<int>>() );
static_assert( rvalue_is_safe_range<std::ranges::iota_view<int>>() );
static_assert( rvalue_is_safe_range<std::ranges::iota_view<int, int>>() );
static_assert( rvalue_is_safe_range<std::span<int>>() );
static_assert( rvalue_is_safe_range<std::span<int, 99>>() );
static_assert( ! rvalue_is_safe_range<std::string>() );
static_assert( ! rvalue_is_safe_range<std::wstring>() );
static_assert( rvalue_is_safe_range<std::string_view>() );
static_assert( rvalue_is_safe_range<std::wstring_view>() );
static_assert( rvalue_is_safe_range<std::experimental::string_view>() );
static_assert( rvalue_is_safe_range<std::experimental::wstring_view>() );
...@@ -758,6 +758,11 @@ namespace __gnu_test ...@@ -758,6 +758,11 @@ namespace __gnu_test
template<typename T> template<typename T>
using test_output_sized_range using test_output_sized_range
= test_sized_range<T, output_iterator_wrapper>; = test_sized_range<T, output_iterator_wrapper>;
// test_container, test_range and test_sized_range do not own their elements,
// so they all model std::ranges::safe_range. This file does not define
// specializations of std::ranges::enable_safe_range, so that individual
// test can decide whether or not to do so.
#endif // C++20 #endif // C++20
} // namespace __gnu_test } // namespace __gnu_test
#endif // _TESTSUITE_ITERATORS #endif // _TESTSUITE_ITERATORS
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