Commit 38250e57 by Jonathan Wakely

libstdc++: Fix view adaptors for mixed-const sentinels and iterators (PR 95322)

The bug report is that transform_view's sentinel<false> cannot be
compared to its iterator<true>.  The comparison is supposed to use
operator==(iterator<Const>, sentinel<Const>) after converting
sentinel<false> to sentinel<true>. However, the operator== is a hidden
friend so is not a candidate when comparing iterator<true> with
sentinel<false>. The required conversion would only happen if we'd found
the operator, but we can't find the operator until after the conversion
happens.

A new LWG issue has been reported, but not yet assigned a number.  The
solution suggested by Casey Carter is to make the hidden friends of the
sentinel types work with iterators of any const-ness, so that no
conversions are required.

Patrick Palka observed that join_view has a similar problem and a
similar fix is used for its sentinel.

	PR libstdc++/95322
	* include/std/ranges (transform_view::_Sentinel): Allow hidden
	friends to work with _Iterator<true> and _Iterator<false>.
	(join_view::_Sentinel): Likewise.
	* testsuite/std/ranges/adaptors/95322.cc: New test.

(cherry picked from commit 6c2582c0406250c66e2eb3651f8e8638796b7f53)
parent 6b196400
...@@ -1853,7 +1853,7 @@ namespace views ...@@ -1853,7 +1853,7 @@ namespace views
{ return ranges::iter_swap(__x._M_current, __y._M_current); } { return ranges::iter_swap(__x._M_current, __y._M_current); }
friend _Iterator<!_Const>; friend _Iterator<!_Const>;
friend _Sentinel<_Const>; template<bool> friend struct _Sentinel;
}; };
template<bool _Const> template<bool _Const>
...@@ -1863,13 +1863,15 @@ namespace views ...@@ -1863,13 +1863,15 @@ namespace views
using _Parent = __detail::__maybe_const_t<_Const, transform_view>; using _Parent = __detail::__maybe_const_t<_Const, transform_view>;
using _Base = __detail::__maybe_const_t<_Const, _Vp>; using _Base = __detail::__maybe_const_t<_Const, _Vp>;
constexpr range_difference_t<_Base> template<bool _Const2>
__distance_from(const _Iterator<_Const>& __i) const constexpr range_difference_t<_Base>
{ return _M_end - __i._M_current; } __distance_from(const _Iterator<_Const2>& __i) const
{ return _M_end - __i._M_current; }
constexpr bool template<bool _Const2>
__equal(const _Iterator<_Const>& __i) const constexpr bool
{ return __i._M_current == _M_end; } __equal(const _Iterator<_Const2>& __i) const
{ return __i._M_current == _M_end; }
sentinel_t<_Base> _M_end = sentinel_t<_Base>(); sentinel_t<_Base> _M_end = sentinel_t<_Base>();
...@@ -1892,19 +1894,26 @@ namespace views ...@@ -1892,19 +1894,26 @@ namespace views
base() const base() const
{ return _M_end; } { return _M_end; }
friend constexpr bool template<bool _Const2>
operator==(const _Iterator<_Const>& __x, const _Sentinel& __y) requires sentinel_for<sentinel_t<_Base>,
{ return __y.__equal(__x); } iterator_t<__detail::__maybe_const_t<_Const2, _Vp>>>
friend constexpr bool
friend constexpr range_difference_t<_Base> operator==(const _Iterator<_Const2>& __x, const _Sentinel& __y)
operator-(const _Iterator<_Const>& __x, const _Sentinel& __y) { return __y.__equal(__x); }
requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
{ return -__y.__distance_from(__x); } template<bool _Const2>
requires sized_sentinel_for<sentinel_t<_Base>,
friend constexpr range_difference_t<_Base> iterator_t<__detail::__maybe_const_t<_Const2, _Vp>>>
operator-(const _Sentinel& __y, const _Iterator<_Const>& __x) friend constexpr range_difference_t<_Base>
requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> operator-(const _Iterator<_Const2>& __x, const _Sentinel& __y)
{ return __y.__distance_from(__x); } { return -__y.__distance_from(__x); }
template<bool _Const2>
requires sized_sentinel_for<sentinel_t<_Base>,
iterator_t<__detail::__maybe_const_t<_Const2, _Vp>>>
friend constexpr range_difference_t<_Base>
operator-(const _Sentinel& __y, const _Iterator<_Const2>& __x)
{ return __y.__distance_from(__x); }
friend _Sentinel<!_Const>; friend _Sentinel<!_Const>;
}; };
...@@ -2571,7 +2580,7 @@ namespace views ...@@ -2571,7 +2580,7 @@ namespace views
{ return ranges::iter_swap(__x._M_inner, __y._M_inner); } { return ranges::iter_swap(__x._M_inner, __y._M_inner); }
friend _Iterator<!_Const>; friend _Iterator<!_Const>;
friend _Sentinel<_Const>; template<bool> friend struct _Sentinel;
}; };
template<bool _Const> template<bool _Const>
...@@ -2581,9 +2590,10 @@ namespace views ...@@ -2581,9 +2590,10 @@ namespace views
using _Parent = __detail::__maybe_const_t<_Const, join_view>; using _Parent = __detail::__maybe_const_t<_Const, join_view>;
using _Base = __detail::__maybe_const_t<_Const, _Vp>; using _Base = __detail::__maybe_const_t<_Const, _Vp>;
constexpr bool template<bool _Const2>
__equal(const _Iterator<_Const>& __i) const constexpr bool
{ return __i._M_outer == _M_end; } __equal(const _Iterator<_Const2>& __i) const
{ return __i._M_outer == _M_end; }
sentinel_t<_Base> _M_end = sentinel_t<_Base>(); sentinel_t<_Base> _M_end = sentinel_t<_Base>();
...@@ -2601,9 +2611,12 @@ namespace views ...@@ -2601,9 +2611,12 @@ namespace views
: _M_end(std::move(__s._M_end)) : _M_end(std::move(__s._M_end))
{ } { }
friend constexpr bool template<bool _Const2>
operator==(const _Iterator<_Const>& __x, const _Sentinel& __y) requires sentinel_for<sentinel_t<_Base>,
{ return __y.__equal(__x); } iterator_t<__detail::__maybe_const_t<_Const2, _Vp>>>
friend constexpr bool
operator==(const _Iterator<_Const2>& __x, const _Sentinel& __y)
{ return __y.__equal(__x); }
friend _Sentinel<!_Const>; friend _Sentinel<!_Const>;
}; };
......
...@@ -15,36 +15,44 @@ ...@@ -15,36 +15,44 @@
// with this library; see the file COPYING3. If not see // with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>. // <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" } // { dg-options "-std=gnu++20" }
// { dg-do run { target c++2a } } // { dg-do run { target c++2a } }
#include <ranges> #include <ranges>
#include <list> #include <testsuite_iterators.h>
#include <testsuite_hooks.h>
namespace ranges = std::ranges; using __gnu_test::test_forward_range;
namespace views = std::views;
void void
test01() test01()
{ {
std::list container{1, 2, 3, 4, 5}; // PR libstdc++/95322
auto v = (container int a[2]{1, 2};
| views::take(3) test_forward_range<int> v{a};
| views::transform(std::negate{}) auto view1 = v | std::views::take(2);
| views::common); auto view2 = view1 | std::views::transform(std::identity{});
auto i = ranges::cbegin(v); const bool eq = std::ranges::cbegin(view2) == std::ranges::end(view2);
VERIFY( *i == -1 ); VERIFY( !eq );
++i;
VERIFY( *i == -2 );
++i;
VERIFY( *i == -3 );
++i;
VERIFY( i == ranges::end(v) );
} }
int void
main() test02()
{
using irange = test_forward_range<int>;
int a[2]{1, 2};
int b[3]{3, 4, 5};
irange u[2]{ irange{a}, irange{b} };
test_forward_range<irange> v{u};
auto view = (std::views::counted(v.begin(), 2)
| std::views::transform(std::identity{})
| std::views::join);
const bool eq = std::ranges::cbegin(view) == std::ranges::end(view);
VERIFY( !eq );
}
int main()
{ {
test01(); test01();
test02();
} }
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