Skip to content

[libcxx] cbegin should always return a constant iterator #99915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 32 commits into
base: main
Choose a base branch
from

Conversation

strega-nil
Copy link
Contributor

@strega-nil strega-nil commented Jul 22, 2024

Papers:

  • P2278R4: cbegin should always return a constant iterator
  • P2836R1: std::basic_const_iterator should follow its
    underlying type's convertibility

Issues:

  • LWG3765: const_sentinel should be constrained
  • LWG3766: view_interface::cbegin is underconstrained
  • LWG3770: const_sentinel_t is missing
  • LWG3769: basic_const_iterator::operator== causes infinite
    constraint recursion
  • LWG3811: views::as_const on ref_view<T> should return
    ref_view<const T>
  • LWG3850: views::as_const on empty_view<T> should return
    empty_view<const T>
  • LWG3853: basic_const_iterator<volatile int*>::operator-> is
    ill-formed
  • LWG3862: basic_const_iterator's common_type specialization
    is underconstrained
  • LWG3872: basic_const_iterator should have custom iter_move
  • LWG3946: The definition of const_iterator_t should be reworked
  • LWG3948: possibly-const-range and as-const-pointer
    should be noexcept
  • LWG4027: possibly-const-range should prefer returning
    const R&

Implements

  • basic_const_iterator, const_iterator, and const_sentinel
  • The const accessors - ranges::{cbegin,cend,crbegin,crend}
  • Add cbegin and cend member functions to view_interface
  • ranges::as_const_view, ranges::views::as_const
  • The changes to span
  • cdata
  • The changes from P2836R1.

Drive-by:

  • Fix some includes that would have been circular.

Closes #105201
Closes #105406
Closes #105032
Closes #105033
Closes #105034
Closes #105069
Closes #105071
Closes #105088
Closes #105090
Closes #105116
Closes #105123
Closes #105298
Closes #105300
Closes #118342

Copy link

github-actions bot commented Jul 22, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@strega-nil strega-nil changed the title [libcxx] P2278R4: cbegin and friends [libcxx] P2278R4: implement {basic_,}const_iterator, and have cbegin et. al. return it Jul 23, 2024
@strega-nil strega-nil marked this pull request as ready for review July 24, 2024 16:46
@strega-nil strega-nil requested a review from a team as a code owner July 24, 2024 16:46
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 24, 2024
@llvmbot
Copy link
Member

llvmbot commented Jul 24, 2024

@llvm/pr-subscribers-libcxx

Author: nicole mazzuca (strega-nil)

Changes

Implements basic_const_iterator and the const accessors.

Does not implement const_view.

Does not implement changes to span and friends.

Still to do: testing.


Patch is 101.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/99915.diff

16 Files Affected:

  • (modified) libcxx/docs/Status/Cxx23Papers.csv (+1-1)
  • (modified) libcxx/include/CMakeLists.txt (+2)
  • (added) libcxx/include/__iterator/const_iterator.h (+340)
  • (modified) libcxx/include/__ranges/access.h (-55)
  • (modified) libcxx/include/__ranges/concepts.h (+14)
  • (added) libcxx/include/__ranges/const_access.h (+208)
  • (modified) libcxx/include/__ranges/rbegin.h (-28)
  • (modified) libcxx/include/__ranges/rend.h (-27)
  • (modified) libcxx/include/iterator (+29)
  • (modified) libcxx/include/module.modulemap (+2)
  • (modified) libcxx/include/ranges (+8)
  • (modified) libcxx/modules/std/iterator.inc (+9-6)
  • (modified) libcxx/test/std/ranges/range.access/begin.pass.cpp (+96-64)
  • (modified) libcxx/test/std/ranges/range.access/end.pass.cpp (+92-67)
  • (modified) libcxx/test/std/ranges/range.access/rbegin.pass.cpp (+147-86)
  • (modified) libcxx/test/std/ranges/range.access/rend.pass.cpp (+141-88)
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index f8970320ce1fa..2d294e8501e5b 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -61,7 +61,7 @@
 "`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"
 "`P2093R14 <https://wg21.link/P2093R14>`__","LWG","Formatted output","July 2022","|Complete|","18.0","|format|"
 "`P2165R4 <https://wg21.link/P2165R4>`__","LWG","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","July 2022","",""
-"`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","","","|ranges|"
+"`P2278R4 <https://wg21.link/P2278R4>`__","LWG","``cbegin`` should always return a constant iterator","July 2022","In Progress","","|ranges|"
 "`P2286R8 <https://wg21.link/P2286R8>`__","LWG","Formatting Ranges","July 2022","|Complete|","16.0","|format| |ranges|"
 "`P2291R3 <https://wg21.link/P2291R3>`__","LWG","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","July 2022","|Complete|","16.0"
 "`P2302R4 <https://wg21.link/P2302R4>`__","LWG","``std::ranges::contains``","July 2022","|Complete|","19.0","|ranges|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8..577b7975f3eb2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -444,6 +444,7 @@ set(files
   __iterator/bounded_iter.h
   __iterator/common_iterator.h
   __iterator/concepts.h
+  __iterator/const_iterator.h
   __iterator/counted_iterator.h
   __iterator/cpp17_iterator_concepts.h
   __iterator/data.h
@@ -639,6 +640,7 @@ set(files
   __ranges/chunk_by_view.h
   __ranges/common_view.h
   __ranges/concepts.h
+  __ranges/const_access.h
   __ranges/container_compatible_range.h
   __ranges/counted.h
   __ranges/dangling.h
diff --git a/libcxx/include/__iterator/const_iterator.h b/libcxx/include/__iterator/const_iterator.h
new file mode 100644
index 0000000000000..4ad37345cd887
--- /dev/null
+++ b/libcxx/include/__iterator/const_iterator.h
@@ -0,0 +1,340 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ITERATOR_CONST_ITERATOR_H
+#define _LIBCPP___ITERATOR_CONST_ITERATOR_H
+
+#include <__compare/three_way_comparable.h>
+#include <__concepts/common_with.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/different_from.h>
+#include <__concepts/same_as.h>
+#include <__concepts/semiregular.h>
+#include <__concepts/totally_ordered.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__memory/pointer_traits.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/common_type.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_specialization.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <indirectly_readable _Iter>
+using iter_const_reference_t = common_reference_t<const iter_value_t<_Iter>&&, iter_reference_t<_Iter>>;
+
+template <class _Iter>
+concept __constant_iterator = input_iterator<_Iter> && same_as<iter_const_reference_t<_Iter>, iter_reference_t<_Iter>>;
+
+template <input_iterator _Iter>
+class basic_const_iterator;
+
+template <input_iterator _Iter>
+using const_iterator = conditional_t<__constant_iterator<_Iter>, _Iter, basic_const_iterator<_Iter>>;
+template <semiregular _Sent>
+using const_sentinel = conditional_t<input_iterator<_Sent>, const_iterator<_Sent>, _Sent>;
+
+template <class _Iter>
+concept __not_a_const_iterator = !__is_specialization_v<_Iter, basic_const_iterator>;
+
+template <indirectly_readable _Iter>
+using __iter_const_rvalue_reference_t = common_reference_t<const iter_value_t<_Iter>&&, iter_rvalue_reference_t<_Iter>>;
+
+template <class _Iter>
+struct __basic_const_iterator_concept {
+  // clang-format off
+  using iterator_concept =
+    conditional_t<contiguous_iterator<_Iter>,
+      contiguous_iterator_tag,
+    conditional_t<random_access_iterator<_Iter>,
+      random_access_iterator_tag,
+    conditional_t<bidirectional_iterator<_Iter>,
+      bidirectional_iterator_tag,
+    conditional_t<forward_iterator<_Iter>,
+      forward_iterator_tag,
+    // else
+      input_iterator_tag>>>>;
+  // clang-format on
+};
+
+template <class _Iter>
+struct __basic_const_iterator_category : __basic_const_iterator_concept<_Iter> {};
+template <forward_iterator _Iter>
+struct __basic_const_iterator_category<_Iter> : __basic_const_iterator_concept<_Iter> {
+  using iterator_category = __basic_const_iterator_concept<_Iter>::iterator_concept;
+};
+
+template <input_iterator _Iter>
+class _LIBCPP_TEMPLATE_VIS basic_const_iterator : __basic_const_iterator_category<_Iter> {
+  _Iter __current = _Iter();
+
+  using __reference        = iter_const_reference_t<_Iter>;
+  using __rvalue_reference = __iter_const_rvalue_reference_t<_Iter>;
+
+public:
+  using value_type      = iter_value_t<_Iter>;
+  using difference_type = iter_difference_t<_Iter>;
+
+  _LIBCPP_HIDE_FROM_ABI basic_const_iterator()
+    requires default_initializable<_Iter>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(_Iter __cur) : __current(std::move(__cur)) {}
+  template <convertible_to<_Iter> _Type>
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(basic_const_iterator<_Type> __cur)
+      : __current(std::move(__cur.__current)) {}
+  template <__different_from<basic_const_iterator> _Type>
+    requires convertible_to<_Type, _Iter>
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(_Type&& __cur) : __current(std::forward<_Type>(__cur)) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Iter& base() const& noexcept { return __current; }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Iter base() && { return std::move(__current); }
+
+  constexpr __reference operator*() const { return static_cast<__reference>(*__current); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const auto* operator->() const
+    requires is_lvalue_reference_v<iter_reference_t<_Iter>> &&
+             same_as<remove_cvref_t<iter_reference_t<_Iter>>, value_type>
+  {
+    if constexpr (contiguous_iterator<_Iter>) {
+      return std::to_address(__current);
+    } else {
+      return std::addressof(*__current);
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator++() {
+    ++__current;
+    return *this;
+  }
+  constexpr void operator++(int) { ++__current; }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator operator++(int)
+    requires forward_iterator<_Iter>
+  {
+    auto __tmp = *this;
+    ++__current;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator--()
+    requires bidirectional_iterator<_Iter>
+  {
+    --__current;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator operator--(int)
+    requires bidirectional_iterator<_Iter>
+  {
+    auto __tmp = *this;
+    --__current;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator+=(difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    __current += __n;
+    return *this;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator-=(difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    __current -= __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __reference operator[](difference_type __n) const
+    requires random_access_iterator<_Iter>
+  {
+    return static_cast<__reference>(__current[__n]);
+  }
+
+  template <sentinel_for<_Iter> _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Sent& __sent) const {
+    return __current == __sent;
+  }
+
+  template <__not_a_const_iterator _ConstIt>
+    requires __constant_iterator<_ConstIt> && convertible_to<_Iter const&, _ConstIt>
+  _LIBCPP_HIDE_FROM_ABI constexpr operator _ConstIt() const& {
+    return __current;
+  }
+  template <__not_a_const_iterator _ConstIt>
+    requires __constant_iterator<_ConstIt> && convertible_to<_Iter, _ConstIt>
+  _LIBCPP_HIDE_FROM_ABI constexpr operator _ConstIt() && {
+    return std::move(__current);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current < __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current > __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current <= __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter>
+  {
+    return __current >= __rhs.__current;
+  }
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const basic_const_iterator& __rhs) const
+    requires random_access_iterator<_Iter> && three_way_comparable<_Iter>
+  {
+    return __current <=> __rhs.__current;
+  }
+
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current < __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current > __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current <= __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __current >= __rhs.__current;
+  }
+  template <__different_from<basic_const_iterator> _Iter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const _Iter2& __rhs) const
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> &&
+             three_way_comparable_with<_Iter, _Iter2>
+  {
+    return __current <=> __rhs.__current;
+  }
+
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs < __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs > __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs <= __rhs.__current;
+  }
+  template <__not_a_const_iterator _Iter2>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Iter2& __lhs, const basic_const_iterator& __rhs)
+    requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2>
+  {
+    return __lhs >= __rhs.__current;
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator+(const basic_const_iterator& __it, difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current + __n);
+  }
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator+(difference_type __n, const basic_const_iterator& __it)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current + __n);
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator
+  operator-(const basic_const_iterator& __it, difference_type __n)
+    requires random_access_iterator<_Iter>
+  {
+    return basic_const_iterator(__it.__current - __n);
+  }
+  template <sized_sentinel_for<_Iter> _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr difference_type operator-(const _Sent& __rhs) const {
+    return __current - __rhs;
+  }
+  template <__not_a_const_iterator _Sent>
+    requires sized_sentinel_for<_Sent, _Iter>
+  friend _LIBCPP_HIDE_FROM_ABI constexpr difference_type
+  operator-(const _Sent& __lhs, const basic_const_iterator& __rhs) {
+    return __lhs - __rhs;
+  }
+
+  friend _LIBCPP_HIDE_FROM_ABI constexpr __rvalue_reference iter_move(const basic_const_iterator& __it) noexcept(
+      noexcept(static_cast<__rvalue_reference>(ranges::iter_move(__it.current_)))) {
+    return static_cast<__rvalue_reference>(ranges::iter_move(__it.current_));
+  }
+};
+
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<basic_const_iterator<_Type1>, _Type2> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<_Type2, basic_const_iterator<_Type1>> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+template <class _Type1, common_with<_Type1> _Type2>
+  requires input_iterator<common_type_t<_Type1, _Type2>>
+struct common_type<basic_const_iterator<_Type1>, basic_const_iterator<_Type2>> {
+  using type = basic_const_iterator<common_type_t<_Type1, _Type2>>;
+};
+
+template <input_iterator _Iter>
+_LIBCPP_HIDE_FROM_ABI constexpr const_iterator<_Iter> make_const_iterator(_Iter __it) {
+  return __it;
+}
+template <semiregular _Sent>
+_LIBCPP_HIDE_FROM_ABI constexpr const_sentinel<_Sent> make_const_sentinel(_Sent __sent) {
+  return __sent;
+}
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ITERATOR_CONST_ITERATOR_H
diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h
index c0a40c5e10178..7955b3b2166dc 100644
--- a/libcxx/include/__ranges/access.h
+++ b/libcxx/include/__ranges/access.h
@@ -148,61 +148,6 @@ inline constexpr auto end = __end::__fn{};
 } // namespace __cpo
 } // namespace ranges
 
-// [range.access.cbegin]
-
-namespace ranges {
-namespace __cbegin {
-struct __fn {
-  template <class _Tp>
-    requires is_lvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::begin(static_cast<const remove_reference_t<_Tp>&>(__t));
-  }
-
-  template <class _Tp>
-    requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::begin(static_cast<const _Tp&&>(__t))))
-          -> decltype(ranges::begin(static_cast<const _Tp&&>(__t))) {
-    return ranges::begin(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __cbegin
-
-inline namespace __cpo {
-inline constexpr auto cbegin = __cbegin::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
-// [range.access.cend]
-
-namespace ranges {
-namespace __cend {
-struct __fn {
-  template <class _Tp>
-    requires is_lvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))))
-          -> decltype(ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t))) {
-    return ranges::end(static_cast<const remove_reference_t<_Tp>&>(__t));
-  }
-
-  template <class _Tp>
-    requires is_rvalue_reference_v<_Tp&&>
-  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(
-      noexcept(ranges::end(static_cast<const _Tp&&>(__t)))) -> decltype(ranges::end(static_cast<const _Tp&&>(__t))) {
-    return ranges::end(static_cast<const _Tp&&>(__t));
-  }
-};
-} // namespace __cend
-
-inline namespace __cpo {
-inline constexpr auto cend = __cend::__fn{};
-} // namespace __cpo
-} // namespace ranges
-
 #endif // _LIBCPP_STD_VER >= 20
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h
index 674a3f359ff99..19264d4ac76c3 100644
--- a/libcxx/include/__ranges/concepts.h
+++ b/libcxx/include/__ranges/concepts.h
@@ -15,6 +15,7 @@
 #include <__concepts/same_as.h>
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__iterator/const_iterator.h>
 #include <__iterator/incrementable_traits.h>
 #include <__iterator/iter_move.h>
 #include <__iterator/iterator_traits.h>
@@ -61,6 +62,11 @@ concept borrowed_range =
 template <range _Rp>
 using sentinel_t = decltype(ranges::end(std::declval<_Rp&>()));
 
+#  if _LIBCPP_STD_VER >= 23
+template <range _Rp>
+using const_iterator_t = const_iterator<iterator_t<_Rp>>;
+#  endif // _LIBCPP_STD_VER >= 23
+
 template <range _Rp>
 using range_difference_t = iter_difference_t<iterator_t<_Rp>>;
 
@@ -70,6 +76,9 @@ using range_value_t = iter_value_t<iterator_t<_Rp>>;
 template <range _Rp>
 using range_reference_t = iter_reference_t<iterator_t<_Rp>>;
 
+template <range _Rp>
+using range_const_reference_t = iter_const_reference_t<iterator_t<_Rp>>;
+
 template <range _Rp>
 using range_rvalue_reference_t = iter_rvalue_reference_t<iterator_t<_Rp>>;
 
@@ -133,6 +142,11 @@ concept viewable_range =
       (is_lvalue_reference_v<_Tp> ||
        (movable<remove_reference_t<_Tp>> && !__is_std_initializer_list<remove_cvref_t<_Tp>>))));
 
+#  if _LIBCPP_STD_VER >= 23
+template <class _Tp>
+concept constant_range = input_range<_Tp> && __constant_iterator<iterator_t<_Tp>>;
+#  endif // _LIBCPP_STD_VER >= 23
+
 } // namespace ranges
 
 #endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__ranges/const_access.h b/libcxx/include/__ranges/const_access.h
new file mode 100644
index 0000000000000..df2811839bc46
--- /dev/null
+++ b/libcxx/include/__ranges/const_access.h
@@ -0,0 +1,208 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_CONST_ACCESS_H
+#define _LIBCPP___RANGES_CONST_ACCESS_H
+
+#include <__iterator/const_iterator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
+#include <__ranges/rbegin.h>
+#include <__ranges/rend.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/declval.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+// [range.const]
+#  if _LIBCPP_STD_VER >= 23
+namespace ranges {
+template <input_range _Rp>
+constexpr auto& __possibly_const_range(_Rp& __rng) {
+  if constexpr (constant_range<const _Rp> ...
[truncated]

@ldionne
Copy link
Member

ldionne commented Jul 31, 2024

This one definitely looks like a feature to me, do you have additional context on why the other implementations did this as a DR?

@frederick-vs-ja
Copy link
Contributor

This one definitely looks like a feature to me, do you have additional context on why the other implementations did this as a DR?

N4970 said that P2836R1 was accepted as a DR. Not sure whether we need to add a note for this.

@strega-nil
Copy link
Contributor Author

This one definitely looks like a feature to me, do you have additional context on why the other implementations did this as a DR?

As @frederick-vs-ja points out, the DR was a DR to this feature; the original paper (P2278R4) was not implemented as a DR.

@mordante
Copy link
Member

mordante commented Aug 3, 2024

@strega-nil Can you update the test test/std/containers/views/views.span/span.iterators/iterator.pass.cpp. I recently added it with some ifdefs since we didn't have this paper implemented. The ifdef should be adjusted to the proper language version; depending whether or not this is a DR.

@strega-nil
Copy link
Contributor Author

@mordante can do! I hope to write tests on Monday so I actually get this going again haha

@strega-nil
Copy link
Contributor Author

Okay, this should be complete (ish) with testing - ready for real review!

@strega-nil
Copy link
Contributor Author

While testing this code, I found some very weird behaviour in clang that seems (to me, at least) like a bug: https://gcc.godbolt.org/z/GrnbEa411

strega-nil added a commit to strega-nil/llvm-project that referenced this pull request Aug 7, 2024
Papers: [P2278R4][], [P2836R1][]
PR: [LLVM-PR-99915][]

Implements:

- `basic_const_iterator`, `const_iterator`, and `const_sentinel`
- The const accessors - `ranges::{cbegin,cend,crbegin,crend}`
- Add `cbegin` and `cend` member functions to `view_interface`
- The changes from [P2836R1][].

Still to be done:
- `ranges::as_const_view`, `ranges::views::as_const`
- `cdata`
- The changes to `span`

Drive-by:
- Fix some includes that would have been circular.

[P2278R4]: https://wg21.link/p2278r4
[P2836R1]: https://wg21.link/p2836r1
[LLVM-PR-99915]: llvm#99915
strega-nil added a commit to strega-nil/llvm-project that referenced this pull request Aug 8, 2024
Papers: [P2278R4][], [P2836R1][]
PR: [LLVM-PR-99915][]

Implements:

- `basic_const_iterator`, `const_iterator`, and `const_sentinel`
- The const accessors - `ranges::{cbegin,cend,crbegin,crend}`
- Add `cbegin` and `cend` member functions to `view_interface`
- The changes from [P2836R1][].

Still to be done:
- `ranges::as_const_view`, `ranges::views::as_const`
- `cdata`
- The changes to `span`

Drive-by:
- Fix some includes that would have been circular.

[P2278R4]: https://wg21.link/p2278r4
[P2836R1]: https://wg21.link/p2836r1
[LLVM-PR-99915]: llvm#99915
@ldionne
Copy link
Member

ldionne commented Aug 29, 2024

@strega-nil There were lots of conflicts due to some changes I've made to the status pages. I should have resolved everything and this should be ready for review IIUC.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the patch! I left some comments after my initial review. I am still a bit confused by the DR and partial implementation of this paper, so please bear with me if some of my questions seem dumb :-).

"`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","",""
"`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","",""
"`LWG3770 <https://wg21.link/LWG3770>`__","``const_sentinel_t`` is missing","2022-11 (Kona)","","",""
"`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","|Complete|","20.0",""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you confirm that you've gone through these LWG issues and made sure that we have test coverage for each of them? For example, I would expect a test that uses basic_const_iterator<volatile int*>::operator-> to guard against LWG3853.

long*& rend() const;
} e;

static_assert(!std::ranges::constant_range<PossiblyConstRange>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have a test dedicated to this concept, not only the ones here and in cend.pass.cpp.

Comment on lines +201 to +204
static_assert(testReturnTypes());

testArray();
static_assert(testArray());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General comment about coverage here: we have a lot of compile-only tests (e.g. static_assert(decltype(...))), but we have very few tests that actually run. Almost all tests that are not negative (e.g. not tests that do !invocable<...> to make sure we SFINAE out) could also be run to ensure we properly exercise these code paths at runtime.

I think it's probably overkill to run everything depending on the complexity it adds, but I'll leave that comment here and let you look into ways it can be addressed while being realistic, maintainable, etc. I don't want to prescribe one way of doing it, but the goal here would be to ensure that we actually hit these code paths at runtime.

Copy link
Contributor

@frederick-vs-ja frederick-vs-ja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can and should just merge the changes in #102301 to this PR. It's less confusing to me to have a unified PR completing this paper (and P2836R1, and several LWG issues).

@frederick-vs-ja
Copy link
Contributor

@strega-nil @ldionne I pushed small changes to resolve CI failures. Please double check that there are no undesired changes.

@ldionne
Copy link
Member

ldionne commented Nov 28, 2024

@frederick-vs-ja I spoke with @strega-nil a few weeks ago, who won't have time to push this through the finish line. If you'd like to pick this up, let me know once this is ready to re-review and I can commit some time to it.

@frederick-vs-ja
Copy link
Contributor

@ldionne @strega-nil I think I'll pick this up in this or the next week.

# Conflicts:
#	libcxx/docs/Status/Cxx23Issues.csv
#	libcxx/docs/Status/Cxx23Papers.csv
#	libcxx/docs/Status/Cxx2cIssues.csv
#	libcxx/include/iterator
#	libcxx/include/ranges
#	libcxx/include/span
#	libcxx/include/string_view
#	libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp
#	libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp
#	libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp
#	libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp
#	libcxx/test/std/containers/views/views.span/types.pass.cpp
- Retarget to LLVM 21.
- Restore emtpy lines.
- Restore damaged non-ASCII characters.
- Templatize `test_basic_operations`.
- Assert noexcept-ness.
- Change notes in implementation status.
# Conflicts:
#	libcxx/docs/ReleaseNotes/21.rst
- Add missed `_LIBCPP_NODEBUG`.
- Adjust dispatching order for `__as_const::__fn`.
- Reuse `__can_borrow`.
# Conflicts:
#	libcxx/docs/ReleaseNotes/21.rst
- missed `if constexpr`
- wrong order and comparison
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment