public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jonathan Wakely <jwakely@redhat.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org
Subject: Re: [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator"
Date: Fri, 14 Apr 2023 11:14:19 +0100	[thread overview]
Message-ID: <ZDknezDIyFTNcNkE@redhat.com> (raw)
In-Reply-To: <20230414040042.1498825-2-ppalka@redhat.com>

On 14/04/23 00:00 -0400, Patrick Palka wrote:
>This also implements the approved follow-up LWG issues 3765, 3766, 3769,
>3770, 3811, 3850, 3853, 3862 and 3872.
>
>Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
>
>libstdc++-v3/ChangeLog:
>
>	* include/bits/ranges_base.h (const_iterator_t): Define for C++23.
>	(const_sentinel_t): Likewise.
>	(range_const_reference_t): Likewise.
>	(constant_range): Likewise.
>	(__cust_access::__possibly_const_range): Likewise, replacing ...
>	(__cust_access::__as_const): ... this.
>	(__cust_access::_CBegin::operator()): Redefine for C++23 as per P2278R4.
>	(__cust_access::_CEnd::operator()): Likewise.
>	(__cust_access::_CRBegin::operator()): Likewise.
>	(__cust_access::_CREnd::operator()): Likewise.
>	(__cust_access::_CData::operator()): Likewise.
>	(__cust_access::_CData::__as_const_pointer): Define for C++23
>	* include/bits/ranges_util.h (ranges::__detail::__different_from):
>	Make it an alias of std::__detail::__different_from.
>	(view_interface::cbegin): Define for C++23.
>	(view_interface::cend): Likewise.
>	* include/bits/stl_iterator.h (__detail::__different_from): Define.
>	(iter_const_reference_t): Define for C++23.
>	(__detail::__constant_iterator): Likewise.
>	(__detail::__is_const_iterator): Likewise.
>	(__detail::__not_a_const_iterator: Likewise.
>	(__detail::__iter_const_rvalue_reference_t): Likewise.
>	(__detail::__basic_const_iter_cat):: Likewise.
>	(const_iterator): Likewise.
>	(__detail::__const_sentinel): Likewise.
>	(const_sentinel): Likewise.
>	(basic_const_iterator): Likewise.
>	(common_type<basic_const_iterator<_Tp>, _Up>): Likewise.
>	(common_type<_Up, basic_const_iterator<_Tp>>): Likewise.
>	(common_type<basic_const_iterator<_Tp>, basic_const_iterator<Up>>):
>	Likewise.
>	(make_const_iterator): Define for C++23.
>	(make_const_sentinel): Likewise.
>	* include/std/ranges (__cpp_lib_ranges_as_const): Likewise.
>	(as_const_view): Likewise.
>	(enable_borrowed_range<as_const_view>): Likewise.
>	(views::__detail::__is_ref_view): Likewise.
>	(views::__detail::__can_is_const_view): Likewise.
>	(views::_AsConst, views::as_const): Likewise.
>	* include/std/span (span::const_iterator): Likewise.
>	(span::const_reverse_iterator): Likewise.
>	(span::cbegin): Likewise.
>	(span::cend): Likewise.
>	(span::crbegin): Likewise.
>	(span::crend): Likewise.
>	* include/std/version (__cpp_lib_ranges_as_const): Likewise.
>	* testsuite/std/ranges/adaptors/join.cc (test06): Adjust to
>	behave independently of C++20 vs C++23.
>	* testsuite/std/ranges/version_c++23.cc: Verify value of
>	__cpp_lib_ranges_as_const macro.
>	* testsuite/24_iterators/const_iterator/1.cc: New test.
>	* testsuite/std/ranges/adaptors/as_const/1.cc: New test.
>---
> libstdc++-v3/include/bits/ranges_base.h       |  99 +++++
> libstdc++-v3/include/bits/ranges_util.h       |  22 +-
> libstdc++-v3/include/bits/stl_iterator.h      | 366 ++++++++++++++++++
> libstdc++-v3/include/std/ranges               | 106 +++++
> libstdc++-v3/include/std/span                 |  22 ++
> libstdc++-v3/include/std/version              |   1 +
> .../24_iterators/const_iterator/1.cc          | 140 +++++++
> .../std/ranges/adaptors/as_const/1.cc         |  64 +++
> .../testsuite/std/ranges/adaptors/join.cc     |   5 +-
> .../testsuite/std/ranges/version_c++23.cc     |   4 +
> 10 files changed, 824 insertions(+), 5 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc
> create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc
>
>diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
>index c89cb3e976a..b3144bbae4d 100644
>--- a/libstdc++-v3/include/bits/ranges_base.h
>+++ b/libstdc++-v3/include/bits/ranges_base.h
>@@ -515,6 +515,17 @@ namespace ranges
>   template<range _Range>
>     using sentinel_t = decltype(ranges::end(std::declval<_Range&>()));
>
>+#if __cplusplus > 202002L
>+  template<range _Range>
>+    using const_iterator_t = const_iterator<iterator_t<_Range>>;
>+
>+  template<range _Range>
>+    using const_sentinel_t = const_sentinel<sentinel_t<_Range>>;
>+
>+  template<range _Range>
>+    using range_const_reference_t = iter_const_reference_t<iterator_t<_Range>>;
>+#endif
>+
>   template<range _Range>
>     using range_difference_t = iter_difference_t<iterator_t<_Range>>;
>
>@@ -607,8 +618,25 @@ namespace ranges
>     concept common_range
>       = range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>;
>
>+#if __cplusplus > 202002L
>+  template<typename _Tp>
>+    concept constant_range
>+      = input_range<_Tp> && std::__detail::__constant_iterator<iterator_t<_Tp>>;
>+#endif
>+
>   namespace __cust_access
>   {
>+#if __cplusplus > 202020L
>+    template<typename _Range>
>+      constexpr auto&
>+      __possibly_const_range(_Range& __r) noexcept
>+      {
>+	if constexpr (constant_range<const _Range> && !constant_range<_Range>)
>+	  return const_cast<const _Range&>(__r);
>+	else
>+	  return __r;
>+      }
>+#else
>     // If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
>     template<typename _To, typename _Tp>
>       constexpr decltype(auto)
>@@ -621,9 +649,23 @@ namespace ranges
> 	else
> 	  return static_cast<const _Tp&&>(__t);
>       }
>+#endif
>
>     struct _CBegin
>     {
>+#if __cplusplus > 202002L
>+      template<__maybe_borrowed_range _Tp>
>+	constexpr auto
>+	operator() [[nodiscard]] (_Tp&& __t) const
>+	noexcept(noexcept(std::make_const_iterator
>+			  (ranges::begin(__cust_access::__possibly_const_range(__t)))))
>+	requires requires { std::make_const_iterator
>+			    (ranges::begin(__cust_access::__possibly_const_range(__t))); }

Wow, that's an ugly declaration :-)

>+	{
>+	  auto& __r = __cust_access::__possibly_const_range(__t);
>+	  return const_iterator_t<decltype(__r)>(ranges::begin(__r));
>+	}
>+#else
>       template<typename _Tp>
> 	[[nodiscard]]
> 	constexpr auto
>@@ -633,10 +675,24 @@ namespace ranges
> 	{
> 	  return _Begin{}(__cust_access::__as_const<_Tp>(__e));
> 	}
>+#endif
>     };
>
>     struct _CEnd final
>     {
>+#if __cplusplus > 202002L
>+      template<__maybe_borrowed_range _Tp>
>+	constexpr auto
>+	operator() [[nodiscard]] (_Tp&& __t) const
>+	noexcept(noexcept(std::make_const_sentinel
>+			  (ranges::end(__cust_access::__possibly_const_range(__t)))))
>+	requires requires { std::make_const_sentinel
>+			    (ranges::end(__cust_access::__possibly_const_range(__t))); }
>+	{
>+	  auto& __r = __cust_access::__possibly_const_range(__t);
>+	  return const_sentinel_t<decltype(__r)>(ranges::end(__r));
>+	}
>+#else
>       template<typename _Tp>
> 	[[nodiscard]]
> 	constexpr auto
>@@ -646,10 +702,24 @@ namespace ranges
> 	{
> 	  return _End{}(__cust_access::__as_const<_Tp>(__e));
> 	}
>+#endif
>     };
>
>     struct _CRBegin
>     {
>+#if __cplusplus > 202002L
>+      template<__maybe_borrowed_range _Tp>
>+	constexpr auto
>+	operator() [[nodiscard]] (_Tp&& __t) const
>+	noexcept(noexcept(std::make_const_iterator
>+			  (ranges::rbegin(__cust_access::__possibly_const_range(__t)))))
>+	requires requires { std::make_const_iterator
>+			    (ranges::rbegin(__cust_access::__possibly_const_range(__t))); }
>+	{
>+	  auto& __r = __cust_access::__possibly_const_range(__t);
>+	  return const_iterator<decltype(ranges::rbegin(__r))>(ranges::rbegin(__r));
>+	}
>+#else
>       template<typename _Tp>
> 	[[nodiscard]]
> 	constexpr auto
>@@ -659,10 +729,24 @@ namespace ranges
> 	{
> 	  return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
> 	}
>+#endif
>     };
>
>     struct _CREnd
>     {
>+#if __cplusplus > 202002L
>+      template<__maybe_borrowed_range _Tp>
>+	constexpr auto
>+	operator() [[nodiscard]] (_Tp&& __t) const
>+	noexcept(noexcept(std::make_const_sentinel
>+			  (ranges::rend(__cust_access::__possibly_const_range(__t)))))
>+	requires requires { std::make_const_sentinel
>+			    (ranges::rend(__cust_access::__possibly_const_range(__t))); }
>+	{
>+	  auto& __r = __cust_access::__possibly_const_range(__t);
>+	  return const_sentinel<decltype(ranges::rend(__r))>(ranges::rend(__r));
>+	}
>+#else
>       template<typename _Tp>
> 	[[nodiscard]]
> 	constexpr auto
>@@ -672,10 +756,24 @@ namespace ranges
> 	{
> 	  return _REnd{}(__cust_access::__as_const<_Tp>(__e));
> 	}
>+#endif
>     };
>
>     struct _CData
>     {
>+#if __cplusplus > 202002L
>+      template<typename _Tp>
>+	static constexpr auto

Would it be slightly cheaper to compile this if it returned const _Tp*
instead of deducing the return type?

>+	__as_const_pointer(const _Tp* __p) noexcept
>+	{ return __p; }
>+
>+      template<__maybe_borrowed_range _Tp>
>+	constexpr auto

Do we even need __as_const_pointer or can we just return const auto*
here?

>+	operator() [[nodiscard]] (_Tp&& __t) const
>+	noexcept(noexcept(ranges::data(__cust_access::__possibly_const_range(__t))))
>+	requires requires { ranges::data(__cust_access::__possibly_const_range(__t)); }
>+	{ return __as_const_pointer(ranges::data(__cust_access::__possibly_const_range(__t))); }
>+#else
>       template<typename _Tp>
> 	[[nodiscard]]
> 	constexpr auto
>@@ -685,6 +783,7 @@ namespace ranges
> 	{
> 	  return _Data{}(__cust_access::__as_const<_Tp>(__e));
> 	}
>+#endif
>     };
>
>   } // namespace __cust_access
>diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h
>index 880a0ce0143..f7e3538af97 100644
>--- a/libstdc++-v3/include/bits/ranges_util.h
>+++ b/libstdc++-v3/include/bits/ranges_util.h
>@@ -53,9 +53,7 @@ namespace ranges
>       concept __has_arrow = input_iterator<_It>
> 	&& (is_pointer_v<_It> || requires(_It __it) { __it.operator->(); });
>
>-    template<typename _Tp, typename _Up>
>-      concept __different_from
>-	= !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
>+    using std::__detail::__different_from;
>   } // namespace __detail
>
>   /// The ranges::view_interface class template
>@@ -192,6 +190,24 @@ namespace ranges
> 	constexpr decltype(auto)
> 	operator[](range_difference_t<_Range> __n) const
> 	{ return ranges::begin(_M_derived())[__n]; }
>+
>+#if __cplusplus > 202002L
>+      constexpr auto
>+      cbegin() requires input_range<_Derived>
>+      { return ranges::cbegin(_M_derived()); }
>+
>+      constexpr auto
>+      cbegin() const requires input_range<const _Derived>
>+      { return ranges::cbegin(_M_derived()); }
>+
>+      constexpr auto
>+      cend() requires input_range<_Derived>
>+      { return ranges::cend(_M_derived()); }
>+
>+      constexpr auto
>+      cend() const requires input_range<const _Derived>
>+      { return ranges::cend(_M_derived()); }
>+#endif
>     };
>
>   namespace __detail
>diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
>index a6a09dbac16..0974d735328 100644
>--- a/libstdc++-v3/include/bits/stl_iterator.h
>+++ b/libstdc++-v3/include/bits/stl_iterator.h
>@@ -102,6 +102,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     template<typename _Cat, typename _Limit, typename _Otherwise = _Cat>
>       using __clamp_iter_cat
> 	= __conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>;
>+
>+    template<typename _Tp, typename _Up>
>+      concept __different_from
>+	= !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
>   }
> #endif
>
>@@ -2578,6 +2582,368 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 				      add_pointer_t<iter_reference_t<_It>>,
> 				      void>;
>     };
>+
>+#if __cplusplus > 202020L
>+  template<indirectly_readable _It>
>+    using iter_const_reference_t
>+      = common_reference_t<const iter_value_t<_It>&&, iter_reference_t<_It>>;
>+
>+  template<input_iterator _It> class basic_const_iterator;
>+
>+  namespace __detail
>+  {
>+    template<typename _It>
>+      concept __constant_iterator = input_iterator<_It>
>+	&& same_as<iter_const_reference_t<_It>, iter_reference_t<_It>>;
>+
>+    template<typename _Tp>
>+      inline constexpr bool __is_const_iterator = false;
>+
>+    template<typename _It>
>+      inline constexpr bool __is_const_iterator<basic_const_iterator<_It>> = true;
>+
>+    template<typename _Tp>
>+      concept __not_a_const_iterator = !__is_const_iterator<_Tp>;
>+
>+    template<indirectly_readable _It>
>+      using __iter_const_rvalue_reference_t
>+	= common_reference_t<const iter_value_t<_It>&&, iter_rvalue_reference_t<_It>>;
>+
>+    template<typename _It>
>+      struct __basic_const_iterator_iter_cat
>+      { };
>+
>+    template<forward_iterator _It>
>+      struct __basic_const_iterator_iter_cat<_It>
>+      { using iterator_category = iterator_traits<_It>::iterator_category; };
>+  } // namespace detail
>+
>+  template<input_iterator _It>
>+    using const_iterator
>+      = __conditional_t<__detail::__constant_iterator<_It>, _It, basic_const_iterator<_It>>;
>+
>+  namespace __detail
>+  {
>+    template<typename _Sent>
>+      struct __const_sentinel
>+      { using type = _Sent; };
>+
>+    template<input_iterator _Sent>
>+      struct __const_sentinel<_Sent>
>+      { using type = const_iterator<_Sent>; };
>+  } // namespace __detail
>+
>+  template<semiregular _Sent>
>+    using const_sentinel = typename __detail::__const_sentinel<_Sent>::type;
>+
>+  template<input_iterator _It>
>+  class basic_const_iterator : public __detail::__basic_const_iterator_iter_cat<_It>

Put the base-clause on a separate line please (I know we're being less
strict these days about 80 columns max, but we don't need to do it
here when we can just split it). All the other long lines seem OK
though.

OK with that change, and the changes to _CData if they make sense and
I'm not missing something there.

>+  {
>+    _It _M_current = _It();
>+    using __reference = iter_const_reference_t<_It>;
>+    using __rvalue_reference = __detail::__iter_const_rvalue_reference_t<_It>;
>+
>+    static auto
>+    _S_iter_concept()
>+    {
>+      if constexpr (contiguous_iterator<_It>)
>+	return contiguous_iterator_tag{};
>+      else if constexpr (random_access_iterator<_It>)
>+	return random_access_iterator_tag{};
>+      else if constexpr (bidirectional_iterator<_It>)
>+	return bidirectional_iterator_tag{};
>+      else if constexpr (forward_iterator<_It>)
>+	return forward_iterator_tag{};
>+      else
>+	return input_iterator_tag{};
>+    }
>+
>+    template<input_iterator _It2> friend class basic_const_iterator;
>+
>+  public:
>+    using iterator_concept = decltype(_S_iter_concept());
>+    using value_type = iter_value_t<_It>;
>+    using difference_type = iter_difference_t<_It>;
>+
>+    basic_const_iterator() requires default_initializable<_It> = default;
>+
>+    constexpr
>+    basic_const_iterator(_It __current)
>+    noexcept(is_nothrow_move_constructible_v<_It>)
>+    : _M_current(std::move(__current))
>+    { }
>+
>+    template<convertible_to<_It> _It2>
>+      constexpr
>+      basic_const_iterator(basic_const_iterator<_It2> __current)
>+      noexcept(is_nothrow_constructible_v<_It, _It2>)
>+      : _M_current(std::move(__current._M_current))
>+      { }
>+
>+    template<__detail::__different_from<basic_const_iterator> _Tp>
>+      requires convertible_to<_Tp, _It>
>+      constexpr
>+      basic_const_iterator(_Tp&& __current)
>+      noexcept(is_nothrow_constructible_v<_It, _Tp>)
>+      : _M_current(std::forward<_Tp>(__current))
>+      { }
>+
>+    constexpr const _It&
>+    base() const & noexcept
>+    { return _M_current; }
>+
>+    constexpr _It
>+    base() &&
>+    noexcept(is_nothrow_move_constructible_v<_It>)
>+    { return std::move(_M_current); }
>+
>+    constexpr __reference
>+    operator*() const
>+    noexcept(noexcept(static_cast<__reference>(*_M_current)))
>+    { return static_cast<__reference>(*_M_current); }
>+
>+    constexpr const auto*
>+    operator->() const
>+    noexcept(contiguous_iterator<_It> || noexcept(*_M_current))
>+    requires is_lvalue_reference_v<iter_reference_t<_It>>
>+      && same_as<remove_cvref_t<iter_reference_t<_It>>, value_type>
>+    {
>+      if constexpr (contiguous_iterator<_It>)
>+	return std::to_address(_M_current);
>+      else
>+	return std::__addressof(*_M_current);
>+    }
>+
>+    constexpr basic_const_iterator&
>+    operator++()
>+    noexcept(noexcept(++_M_current))
>+    {
>+      ++_M_current;
>+      return *this;
>+    }
>+
>+    constexpr void
>+    operator++(int)
>+    noexcept(noexcept(++_M_current))
>+    { ++_M_current; }
>+
>+    constexpr basic_const_iterator
>+    operator++(int)
>+    noexcept(noexcept(++*this) && is_nothrow_copy_constructible_v<basic_const_iterator>)
>+    requires forward_iterator<_It>
>+    {
>+      auto __tmp = *this;
>+      ++*this;
>+      return __tmp;
>+    }
>+
>+    constexpr basic_const_iterator&
>+    operator--()
>+    noexcept(noexcept(--_M_current))
>+    requires bidirectional_iterator<_It>
>+    {
>+      --_M_current;
>+      return *this;
>+    }
>+
>+    constexpr basic_const_iterator
>+    operator--(int)
>+    noexcept(noexcept(--*this) && is_nothrow_copy_constructible_v<basic_const_iterator>)
>+    requires bidirectional_iterator<_It>
>+    {
>+      auto __tmp = *this;
>+      --*this;
>+      return __tmp;
>+    }
>+
>+    constexpr basic_const_iterator&
>+    operator+=(difference_type __n)
>+    noexcept(noexcept(_M_current += __n))
>+    requires random_access_iterator<_It>
>+    {
>+      _M_current += __n;
>+      return *this;
>+    }
>+
>+    constexpr basic_const_iterator&
>+    operator-=(difference_type __n)
>+    noexcept(noexcept(_M_current -= __n))
>+    requires random_access_iterator<_It>
>+    {
>+      _M_current -= __n;
>+      return *this;
>+    }
>+
>+    constexpr __reference
>+    operator[](difference_type __n) const
>+    noexcept(noexcept(static_cast<__reference>(_M_current[__n])))
>+    requires random_access_iterator<_It>
>+    { return static_cast<__reference>(_M_current[__n]); }
>+
>+    template<sentinel_for<_It> _Sent>
>+      constexpr bool
>+      operator==(const _Sent& __s) const
>+      noexcept(noexcept(_M_current == __s))
>+      { return _M_current == __s; }
>+
>+    constexpr bool
>+    operator<(const basic_const_iterator& __y) const
>+    noexcept(noexcept(_M_current < __y._M_current))
>+    requires random_access_iterator<_It>
>+    { return _M_current < __y._M_current; }
>+
>+    constexpr bool
>+    operator>(const basic_const_iterator& __y) const
>+    noexcept(noexcept(_M_current > __y._M_current))
>+    requires random_access_iterator<_It>
>+    { return _M_current > __y._M_current; }
>+
>+    constexpr bool
>+    operator<=(const basic_const_iterator& __y) const
>+    noexcept(noexcept(_M_current <= __y._M_current))
>+    requires random_access_iterator<_It>
>+    { return _M_current <= __y._M_current; }
>+
>+    constexpr bool
>+    operator>=(const basic_const_iterator& __y) const
>+    noexcept(noexcept(_M_current >= __y._M_current))
>+    requires random_access_iterator<_It>
>+    { return _M_current >= __y._M_current; }
>+
>+    constexpr auto
>+    operator<=>(const basic_const_iterator& __y) const
>+    noexcept(noexcept(_M_current <=> __y._M_current))
>+    requires random_access_iterator<_It> && three_way_comparable<_It>
>+    { return _M_current <=> __y._M_current; }
>+
>+    template<__detail::__different_from<basic_const_iterator> _It2>
>+      constexpr bool
>+      operator<(const _It2& __y) const
>+      noexcept(noexcept(_M_current < __y))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+      { return _M_current < __y; }
>+
>+    template<__detail::__different_from<basic_const_iterator> _It2>
>+      constexpr bool
>+      operator>(const _It2& __y) const
>+      noexcept(noexcept(_M_current > __y))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+      { return _M_current > __y; }
>+
>+    template<__detail::__different_from<basic_const_iterator> _It2>
>+      constexpr bool
>+      operator<=(const _It2& __y) const
>+      noexcept(noexcept(_M_current <= __y))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+      { return _M_current <= __y; }
>+
>+    template<__detail::__different_from<basic_const_iterator> _It2>
>+      constexpr bool
>+      operator>=(const _It2& __y) const
>+      noexcept(noexcept(_M_current >= __y))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+      { return _M_current >= __y; }
>+
>+    template<__detail::__different_from<basic_const_iterator> _It2>
>+      constexpr auto
>+      operator<=>(const _It2& __y) const
>+      noexcept(noexcept(_M_current <=> __y))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+	&& three_way_comparable_with<_It, _It2>
>+      { return _M_current <=> __y; }
>+
>+    template<__detail::__not_a_const_iterator _It2>
>+      friend constexpr bool
>+      operator<(const _It2& __x, const basic_const_iterator& __y)
>+      noexcept(noexcept(__x < __y._M_current))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+      { return __x < __y._M_current; }
>+
>+    template<__detail::__not_a_const_iterator _It2>
>+      friend constexpr bool
>+      operator>(const _It2& __x, const basic_const_iterator& __y)
>+      noexcept(noexcept(__x > __y._M_current))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+      { return __x > __y._M_current; }
>+
>+    template<__detail::__not_a_const_iterator _It2>
>+      friend constexpr bool
>+      operator<=(const _It2& __x, const basic_const_iterator& __y)
>+      noexcept(noexcept(__x <= __y._M_current))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+      { return __x <= __y._M_current; }
>+
>+    template<__detail::__not_a_const_iterator _It2>
>+      friend constexpr bool
>+      operator>=(const _It2& __x, const basic_const_iterator& __y)
>+      noexcept(noexcept(__x >= __y._M_current))
>+      requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
>+      { return __x >= __y._M_current; }
>+
>+    friend constexpr basic_const_iterator
>+    operator+(const basic_const_iterator& __i, difference_type __n)
>+    noexcept(noexcept(basic_const_iterator(__i._M_current + __n)))
>+    requires random_access_iterator<_It>
>+    { return basic_const_iterator(__i._M_current + __n); }
>+
>+    friend constexpr basic_const_iterator
>+    operator+(difference_type __n, const basic_const_iterator& __i)
>+    noexcept(noexcept(basic_const_iterator(__i._M_current + __n)))
>+    requires random_access_iterator<_It>
>+    { return basic_const_iterator(__i._M_current + __n); }
>+
>+    friend constexpr basic_const_iterator
>+    operator-(const basic_const_iterator& __i, difference_type __n)
>+    noexcept(noexcept(basic_const_iterator(__i._M_current - __n)))
>+    requires random_access_iterator<_It>
>+    { return basic_const_iterator(__i._M_current - __n); }
>+
>+    template<sized_sentinel_for<_It> _Sent>
>+      constexpr difference_type
>+      operator-(const _Sent& __y) const
>+      noexcept(noexcept(_M_current - __y))
>+      { return _M_current - __y; }
>+
>+    template<__detail::__not_a_const_iterator _Sent>
>+      requires sized_sentinel_for<_Sent, _It>
>+      friend constexpr difference_type
>+      operator-(const _Sent& __x, const basic_const_iterator& __y)
>+      noexcept(noexcept(__x - __y._M_current))
>+      { return __x - __y._M_current; }
>+
>+    friend constexpr __rvalue_reference
>+    iter_move(const basic_const_iterator& __i)
>+      noexcept(noexcept(static_cast<__rvalue_reference>(ranges::iter_move(__i._M_current))))
>+    { return static_cast<__rvalue_reference>(ranges::iter_move(__i._M_current)); }
>+  };
>+
>+  template<typename _Tp, common_with<_Tp> _Up>
>+    requires input_iterator<common_type_t<_Tp, _Up>>
>+    struct common_type<basic_const_iterator<_Tp>, _Up>
>+    { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
>+
>+  template<typename _Tp, common_with<_Tp> _Up>
>+    requires input_iterator<common_type_t<_Tp, _Up>>
>+    struct common_type<_Up, basic_const_iterator<_Tp>>
>+    { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
>+
>+  template<typename _Tp, common_with<_Tp> _Up>
>+    requires input_iterator<common_type_t<_Tp, _Up>>
>+    struct common_type<basic_const_iterator<_Tp>, basic_const_iterator<_Up>>
>+    { using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
>+
>+  template<input_iterator _It>
>+    constexpr const_iterator<_It>
>+    make_const_iterator(_It __it)
>+    noexcept(is_nothrow_convertible_v<_It, const_iterator<_It>>)
>+    { return __it; }
>+
>+  template<semiregular _Sent>
>+    constexpr const_sentinel<_Sent>
>+    make_const_sentinel(_Sent __s)
>+    noexcept(is_nothrow_convertible_v<_Sent, const_sentinel<_Sent>>)
>+    { return __s; }
>+#endif // C++23
> #endif // C++20
>
>   /// @} group iterators
>diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
>index 3f6ff505617..283d757faa4 100644
>--- a/libstdc++-v3/include/std/ranges
>+++ b/libstdc++-v3/include/std/ranges
>@@ -8929,6 +8929,112 @@ namespace views::__adaptor
>
>     inline constexpr _Enumerate enumerate;
>   }
>+
>+#define __cpp_lib_ranges_as_const 202207L
>+
>+  template<view _Vp>
>+    requires input_range<_Vp>
>+  class as_const_view : public view_interface<as_const_view<_Vp>>
>+  {
>+    _Vp _M_base = _Vp();
>+
>+  public:
>+    as_const_view() requires default_initializable<_Vp> = default;
>+
>+    constexpr explicit
>+    as_const_view(_Vp __base)
>+    noexcept(is_nothrow_move_constructible_v<_Vp>)
>+    : _M_base(std::move(__base))
>+    { }
>+
>+    constexpr _Vp
>+    base() const &
>+    noexcept(is_nothrow_copy_constructible_v<_Vp>)
>+    requires copy_constructible<_Vp>
>+    { return _M_base; }
>+
>+    constexpr _Vp
>+    base() &&
>+    noexcept(is_nothrow_move_constructible_v<_Vp>)
>+    { return std::move(_M_base); }
>+
>+    constexpr auto
>+    begin() requires (!__detail::__simple_view<_Vp>)
>+    { return ranges::cbegin(_M_base); }
>+
>+    constexpr auto
>+    begin() const requires range<const _Vp>
>+    { return ranges::cbegin(_M_base); }
>+
>+    constexpr auto
>+    end() requires (!__detail::__simple_view<_Vp>)
>+    { return ranges::cend(_M_base); }
>+
>+    constexpr auto
>+    end() const requires range<const _Vp>
>+    { return ranges::cend(_M_base); }
>+
>+    constexpr auto
>+    size() requires sized_range<_Vp>
>+    { return ranges::size(_M_base); }
>+
>+    constexpr auto
>+    size() const requires sized_range<const _Vp>
>+    { return ranges::size(_M_base); }
>+  };
>+
>+  template<typename _Range>
>+    as_const_view(_Range&&) -> as_const_view<views::all_t<_Range>>;
>+
>+  template<typename _Tp>
>+    inline constexpr bool enable_borrowed_range<as_const_view<_Tp>>
>+      = enable_borrowed_range<_Tp>;
>+
>+  namespace views
>+  {
>+    namespace __detail
>+    {
>+      template<typename _Tp>
>+	inline constexpr bool __is_ref_view = false;
>+
>+      template<typename _Range>
>+	inline constexpr bool __is_ref_view<ref_view<_Range>> = true;
>+
>+      template<typename _Range>
>+	concept __can_as_const_view = requires { as_const_view(std::declval<_Range>()); };
>+    }
>+
>+    struct _AsConst : __adaptor::_RangeAdaptorClosure
>+    {
>+      template<viewable_range _Range>
>+      constexpr auto
>+      operator()(_Range&& __r) const
>+      noexcept(noexcept(as_const_view(std::declval<_Range>())))
>+      requires __detail::__can_as_const_view<_Range>
>+      {
>+	using _Tp = remove_cvref_t<_Range>;
>+	using element_type = remove_reference_t<range_reference_t<_Range>>;
>+	if constexpr (constant_range<views::all_t<_Range>>)
>+	  return views::all(std::forward<_Range>(__r));
>+	else if constexpr (__detail::__is_empty_view<_Tp>)
>+	  return views::empty<const element_type>;
>+	else if constexpr (std::__detail::__is_span<_Tp>)
>+	  return span<const element_type, _Tp::extent>(std::forward<_Range>(__r));
>+	else if constexpr (__detail::__is_ref_view<_Tp>
>+			   && constant_range<const element_type>)
>+	  return ref_view(static_cast<const element_type&>
>+			  (std::forward<_Range>(__r).base()));
>+	else if constexpr (is_lvalue_reference_v<_Range>
>+			   && constant_range<_Tp>
>+			   && !view<_Tp>)
>+	  return ref_view(static_cast<const _Tp&>(__r));
>+	else
>+	  return as_const_view(std::forward<_Range>(__r));
>+      }
>+    };
>+
>+    inline constexpr _AsConst as_const;
>+  }
> #endif // C++23
> } // namespace ranges
>
>diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
>index 06d5c185880..67633899665 100644
>--- a/libstdc++-v3/include/std/span
>+++ b/libstdc++-v3/include/std/span
>@@ -137,6 +137,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       using const_reference        = const element_type&;
>       using iterator = __gnu_cxx::__normal_iterator<pointer, span>;
>       using reverse_iterator       = std::reverse_iterator<iterator>;
>+#if __cplusplus > 202002L
>+      using const_iterator         = std::const_iterator<iterator>;
>+      using const_reverse_iterator = std::const_iterator<reverse_iterator>;
>+#endif
>
>       // member constants
>       static constexpr size_t extent = _Extent;
>@@ -301,6 +305,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       rend() const noexcept
>       { return reverse_iterator(this->begin()); }
>
>+#if __cplusplus > 202002L
>+      constexpr const_iterator
>+      cbegin() const noexcept
>+      { return begin(); }
>+
>+      constexpr const_iterator
>+      cend() const noexcept
>+      { return end(); }
>+
>+      constexpr const_reverse_iterator
>+      crbegin() const noexcept
>+      { return rbegin(); }
>+
>+      constexpr const_reverse_iterator
>+      crend() const noexcept
>+      { return rend(); }
>+#endif
>+
>       // subviews
>
>       template<size_t _Count>
>diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
>index d233b037d1a..9f31f25f1e9 100644
>--- a/libstdc++-v3/include/std/version
>+++ b/libstdc++-v3/include/std/version
>@@ -339,6 +339,7 @@
> #define __cpp_lib_ranges_stride 202207L
> #define __cpp_lib_ranges_cartesian_product 202207L
> #define __cpp_lib_ranges_as_rvalue 202207L
>+#define __cpp_lib_ranges_as_const 202207L
> #define __cpp_lib_ranges_enumerate 202302L
> #define __cpp_lib_fold 202207L
> #if __cpp_constexpr_dynamic_alloc
>diff --git a/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc b/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc
>new file mode 100644
>index 00000000000..51befd29541
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc
>@@ -0,0 +1,140 @@
>+// { dg-options "-std=gnu++23" }
>+// { dg-do run { target c++23 } }
>+
>+#include <iterator>
>+#include <array>
>+#include <concepts>
>+#include <string_view>
>+#include <vector>
>+#include <testsuite_iterators.h>
>+
>+using __gnu_test::test_input_range;
>+using __gnu_test::test_forward_range;
>+using __gnu_test::test_bidirectional_range;
>+using __gnu_test::test_random_access_range;
>+
>+namespace ranges = std::ranges;
>+
>+template<class Iter, bool Const>
>+void
>+test01()
>+{
>+  if constexpr (Const)
>+    {
>+      static_assert( std::same_as<std::const_iterator<Iter>, Iter> );
>+      static_assert( std::same_as<std::const_sentinel<Iter>, Iter> );
>+      static_assert( std::same_as<std::iter_const_reference_t<Iter>,
>+				 std::iter_reference_t<Iter>> );
>+    }
>+  else
>+    {
>+      using Wrapped = std::basic_const_iterator<Iter>;
>+
>+      static_assert( std::same_as<std::const_iterator<Iter>, Wrapped> );
>+      static_assert( std::same_as<std::const_sentinel<Iter>, Wrapped> );
>+      static_assert( std::same_as<std::iter_const_reference_t<Iter>,
>+				 std::iter_reference_t<Wrapped>> );
>+
>+      static_assert( std::input_iterator<Iter> == std::input_iterator<Wrapped> );
>+      static_assert( std::forward_iterator<Iter> == std::forward_iterator<Wrapped> );
>+      static_assert( std::bidirectional_iterator<Iter> == std::bidirectional_iterator<Wrapped> );
>+      static_assert( std::random_access_iterator<Iter> == std::random_access_iterator<Wrapped> );
>+    }
>+}
>+
>+template<class Range, bool Const>
>+void
>+test02()
>+{
>+  if constexpr (Const)
>+    {
>+      static_assert( ranges::constant_range<Range> );
>+      static_assert( std::same_as<ranges::const_iterator_t<Range>, ranges::iterator_t<Range>> );
>+      static_assert( std::same_as<ranges::const_sentinel_t<Range>, ranges::sentinel_t<Range>> );
>+      static_assert( std::same_as<ranges::range_const_reference_t<Range>,
>+				 ranges::range_reference_t<Range>> );
>+
>+      static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())),
>+				 decltype(ranges::begin(std::declval<Range&>()))> );
>+      static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())),
>+				 decltype(ranges::end(std::declval<Range&>()))> );
>+    }
>+  else
>+    {
>+      static_assert( !ranges::constant_range<Range> );
>+      using Wrapped = std::basic_const_iterator<ranges::iterator_t<Range>>;
>+
>+      static_assert( std::same_as<ranges::const_iterator_t<Range>, Wrapped> );
>+      if constexpr (ranges::common_range<Range>)
>+	static_assert( std::same_as<ranges::const_sentinel_t<Range>, Wrapped> );
>+      static_assert( std::same_as<ranges::range_const_reference_t<Range>,
>+				 std::iter_reference_t<Wrapped>> );
>+
>+      static_assert( ranges::input_range<Range> == std::input_iterator<Wrapped> );
>+      static_assert( ranges::forward_range<Range> == std::forward_iterator<Wrapped> );
>+      static_assert( ranges::bidirectional_range<Range> == std::bidirectional_iterator<Wrapped> );
>+      static_assert( ranges::random_access_range<Range> == std::random_access_iterator<Wrapped> );
>+
>+      if constexpr (ranges::constant_range<const Range&>)
>+	{
>+	  static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())),
>+				     decltype(ranges::begin(std::declval<const Range&>()))> );
>+	  static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())),
>+				     decltype(ranges::end(std::declval<const Range&>()))> );
>+	}
>+      else
>+	{
>+	  static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())), Wrapped> );
>+	  if constexpr (ranges::common_range<Range>)
>+	    static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())), Wrapped> );
>+	}
>+    }
>+}
>+
>+void
>+test03()
>+{
>+  static_assert( std::same_as<std::const_sentinel<std::unreachable_sentinel_t>,
>+			     std::unreachable_sentinel_t> );
>+}
>+
>+int
>+main()
>+{
>+  test01<int*, false>();
>+  test01<ranges::iterator_t<test_input_range<int>>, false>();
>+  test01<ranges::iterator_t<test_forward_range<int>>, false>();
>+  test01<ranges::iterator_t<test_bidirectional_range<int>>, false>();
>+  test01<ranges::iterator_t<test_random_access_range<int>>, false>();
>+  test01<std::array<int, 3>::iterator, false>();
>+  test01<std::vector<bool>::iterator, false>();
>+
>+  test01<const int*, true>();
>+  test01<ranges::iterator_t<test_input_range<const int>>, true>();
>+  test01<ranges::iterator_t<test_forward_range<const int>>, true>();
>+  test01<ranges::iterator_t<test_bidirectional_range<const int>>, true>();
>+  test01<ranges::iterator_t<test_random_access_range<const int>>, true>();
>+  test01<std::array<const int, 3>::iterator, true>();
>+  test01<std::string_view::iterator, true>();
>+  test01<std::vector<bool>::const_iterator, true>();
>+
>+  test02<int[42], false>();
>+  test02<test_input_range<int>, false>();
>+  test02<test_forward_range<int>, false>();
>+  test02<test_bidirectional_range<int>, false>();
>+  test02<test_random_access_range<int>, false>();
>+  test02<std::array<int, 3>, false>();
>+  test02<std::vector<bool>, false>();
>+
>+  test02<const int[42], true>();
>+  test02<test_input_range<const int>, true>();
>+  test02<test_forward_range<const int>, true>();
>+  test02<test_bidirectional_range<const int>, true>();
>+  test02<test_random_access_range<const int>, true>();
>+  test02<std::array<const int, 3>, true>();
>+  test02<const std::array<int, 3>, true>();
>+  test02<std::string_view, true>();
>+  test02<const std::vector<bool>, true>();
>+
>+  test03();
>+}
>diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc
>new file mode 100644
>index 00000000000..d04645f047e
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc
>@@ -0,0 +1,64 @@
>+// { dg-options "-std=gnu++23" }
>+// { dg-do run { target c++23 } }
>+
>+#include <ranges>
>+#include <algorithm>
>+#include <span>
>+#include <utility>
>+#include <testsuite_hooks.h>
>+#include <testsuite_iterators.h>
>+
>+#if __cpp_lib_ranges_as_const != 202207L
>+# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in <ranges>"
>+#endif
>+
>+namespace ranges = std::ranges;
>+namespace views = std::views;
>+
>+constexpr bool
>+test01()
>+{
>+  int x[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
>+  auto v = x | views::filter([](int x) { return (x % 2) == 0; }) | views::as_const;
>+
>+  using ty = decltype(v);
>+  static_assert(ranges::constant_range<ty>);
>+  static_assert(!ranges::constant_range<decltype(v.base())>);
>+  static_assert(std::same_as<ranges::range_reference_t<ty>, const int&>);
>+  static_assert(std::same_as<ranges::range_reference_t<decltype(v.base())>, int&>);
>+
>+  VERIFY( ranges::equal(v, (int[]){2, 4, 6, 8, 10}) );
>+  VERIFY( ranges::equal(v | views::reverse, (int[]){10, 8, 6, 4, 2}) );
>+
>+  return true;
>+}
>+
>+constexpr bool
>+test02()
>+{
>+  std::same_as<ranges::empty_view<const int>> auto v1
>+    = views::empty<int> | views::as_const;
>+
>+  int x[] = {1, 2, 3};
>+  std::same_as<ranges::as_const_view<ranges::ref_view<int[3]>>> auto v2
>+    = x | views::as_const;
>+  std::same_as<ranges::ref_view<const int[3]>> auto v3
>+    = std::as_const(x) | views::as_const;
>+  std::same_as<ranges::ref_view<const int[3]>> auto v4
>+    = std::as_const(x) | views::all | views::as_const;
>+  std::same_as<std::span<const int>> auto v5
>+    = std::span{x, x+3} | views::as_const;
>+
>+  std::same_as<ranges::as_const_view<ranges::chunk_view<ranges::ref_view<int[3]>>>> auto v6
>+     = x | views::chunk(2) | views::as_const;
>+  VERIFY( v6.size() == 2 );
>+
>+  return true;
>+}
>+
>+int
>+main()
>+{
>+  static_assert(test01());
>+  static_assert(test02());
>+}
>diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
>index e5ae9b7be20..cda2cbfc577 100644
>--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
>+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
>@@ -24,6 +24,7 @@
> #include <sstream>
> #include <string>
> #include <string_view>
>+#include <utility>
> #include <vector>
> #include <testsuite_hooks.h>
> #include <testsuite_iterators.h>
>@@ -120,8 +121,8 @@ test06()
>   // Verify that _Sentinel<false> is implicitly convertible to _Sentinel<true>.
>   static_assert(!ranges::common_range<decltype(v)>);
>   static_assert(!std::same_as<decltype(ranges::end(v)),
>-			      decltype(ranges::cend(v))>);
>-  auto b = ranges::cend(v);
>+			      decltype(std::as_const(v).end())>);
>+  auto b = std::as_const(v).end();
>   b = ranges::end(v);
> }
>
>diff --git a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
>index fa010bf166b..e2c14edc8ef 100644
>--- a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
>+++ b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc
>@@ -45,6 +45,10 @@
> # error "Feature-test macro __cpp_lib_ranges_as_rvalue has wrong value in <version>"
> #endif
>
>+#if __cpp_lib_ranges_as_const != 202207L
>+# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in <version>"
>+#endif
>+
> #if __cpp_lib_ranges_enumerate != 202302L
> # error "Feature-test macro __cpp_lib_ranges_enumerate has wrong value in <version>"
> #endif


  parent reply	other threads:[~2023-04-14 10:14 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-14  4:00 [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Patrick Palka
2023-04-14  4:00 ` [PATCH 2/2] libstdc++: Implement P2278R4 "cbegin should always return a constant iterator" Patrick Palka
2023-04-14  9:52   ` Ville Voutilainen
2023-04-14 10:14   ` Jonathan Wakely [this message]
2023-04-14 14:14     ` Patrick Palka
2023-04-14  9:49 ` [PATCH 1/2] libstdc++: Move down definitions of ranges::cbegin/cend/cetc Jonathan Wakely

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZDknezDIyFTNcNkE@redhat.com \
    --to=jwakely@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=libstdc++@gcc.gnu.org \
    --cc=ppalka@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).