[libcxx-commits] [libcxx] [libc++][ranges] Ensure range access CPOs are provided in `<iterator>` (PR #151745)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Fri Aug 15 06:41:45 PDT 2025


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/151745

>From d73c62f02fc82a4504bcd71acfcb1c7b67fd6132 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sat, 2 Aug 2025 02:22:12 +0800
Subject: [PATCH 1/3] [libc++][ranges] Ensure range access CPOs are provided in
 `<iterator>`

Per [range.access.general]/1, these CPOs are also provided in
`<iterator>`. Currently only some of them are provided via transitive
inclusion when only `<iterator>` is included.

Drive-by: Add an entry for `ranges::reserve_hint` in the general test
file for CPOs.
---
 libcxx/include/iterator                       | 10 ++++
 libcxx/include/ranges                         |  8 +--
 .../cpo.compile.pass.cpp                      |  4 ++
 .../include.iterator.compile.pass.cpp         | 60 +++++++++++++++++++
 4 files changed, 75 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.access/include.iterator.compile.pass.cpp

diff --git a/libcxx/include/iterator b/libcxx/include/iterator
index d25fdfd2a8b33..fc8bdc5e6bcf6 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -737,6 +737,16 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
 #  include <compare>
 #  include <concepts>
 
+// [range.access.general]
+#  if _LIBCPP_STD_VER >= 20
+#    include <__ranges/access.h>
+#    include <__ranges/data.h>
+#    include <__ranges/empty.h>
+#    include <__ranges/rbegin.h>
+#    include <__ranges/rend.h>
+#    include <__ranges/size.h>
+#  endif
+
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #    pragma GCC system_header
 #  endif
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 96d7a6b897188..b86dd78ecb255 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -405,17 +405,14 @@ namespace std {
 #  include <__config>
 
 #  if _LIBCPP_STD_VER >= 20
-#    include <__ranges/access.h>
 #    include <__ranges/all.h>
 #    include <__ranges/common_view.h>
 #    include <__ranges/concepts.h>
 #    include <__ranges/counted.h>
 #    include <__ranges/dangling.h>
-#    include <__ranges/data.h>
 #    include <__ranges/drop_view.h>
 #    include <__ranges/drop_while_view.h>
 #    include <__ranges/elements_view.h>
-#    include <__ranges/empty.h>
 #    include <__ranges/empty_view.h>
 #    include <__ranges/enable_borrowed_range.h>
 #    include <__ranges/enable_view.h>
@@ -423,12 +420,9 @@ namespace std {
 #    include <__ranges/iota_view.h>
 #    include <__ranges/join_view.h>
 #    include <__ranges/lazy_split_view.h>
-#    include <__ranges/rbegin.h>
 #    include <__ranges/ref_view.h>
-#    include <__ranges/rend.h>
 #    include <__ranges/reverse_view.h>
 #    include <__ranges/single_view.h>
-#    include <__ranges/size.h>
 #    include <__ranges/split_view.h>
 #    include <__ranges/subrange.h>
 #    include <__ranges/take_view.h>
@@ -460,7 +454,7 @@ namespace std {
 // [ranges.syn]
 #  include <compare>
 #  include <initializer_list>
-#  include <iterator>
+#  include <iterator> // some CPOs are provided in <iterator> per [range.access.general]
 
 // [tuple.helper]
 #  include <__tuple/tuple_element.h>
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
index 678483b9b2f2f..49497875dcf95 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/cpo.compile.pass.cpp
@@ -81,6 +81,10 @@ static_assert(test(std::ranges::rend, a));
 static_assert(test(std::ranges::size, a));
 static_assert(test(std::ranges::ssize, a));
 
+#if TEST_STD_VER >= 26
+// static_assert(test(std::views::reserve_hint, a));
+#endif
+
 // [range.factories]
 // views::empty<T> is not a CPO
 static_assert(test(std::views::iota, 1));
diff --git a/libcxx/test/std/ranges/range.access/include.iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.access/include.iterator.compile.pass.cpp
new file mode 100644
index 0000000000000..a3c59166eeab0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/include.iterator.compile.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// [range.access.general]/1:
+// In addition to being available via inclusion of the <ranges> header, the customization point objects in
+// [range.access] are available when the header <iterator> is included.
+
+#include <iterator>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class CPO, class... Args>
+constexpr bool test(CPO& o, Args&&...) {
+  static_assert(std::is_const_v<CPO>);
+  static_assert(std::is_class_v<CPO>);
+  static_assert(std::is_trivially_copyable_v<CPO>);
+  static_assert(std::is_trivially_default_constructible_v<CPO>);
+
+  auto p  = o;
+  using T = decltype(p);
+
+  // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular.
+  static_assert(std::semiregular<T>);
+
+  // The type T of a customization point object, ignoring cv-qualifiers, shall model...
+  static_assert(std::invocable<T&, Args...>);
+  static_assert(std::invocable<const T&, Args...>);
+  static_assert(std::invocable<T, Args...>);
+  static_assert(std::invocable<const T, Args...>);
+
+  return true;
+}
+
+int a[10];
+
+static_assert(test(std::ranges::begin, a));
+static_assert(test(std::ranges::end, a));
+static_assert(test(std::ranges::cbegin, a));
+static_assert(test(std::ranges::cdata, a));
+static_assert(test(std::ranges::cend, a));
+static_assert(test(std::ranges::crbegin, a));
+static_assert(test(std::ranges::crend, a));
+static_assert(test(std::ranges::data, a));
+static_assert(test(std::ranges::empty, a));
+static_assert(test(std::ranges::rbegin, a));
+static_assert(test(std::ranges::rend, a));
+static_assert(test(std::ranges::size, a));
+static_assert(test(std::ranges::ssize, a));
+
+#if TEST_STD_VER >= 26
+// static_assert(test(std::views::reserve_hint, a));
+#endif

>From 48bdb7262ea94a64485e7c4d99f912d753698d1b Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 5 Aug 2025 17:22:43 +0800
Subject: [PATCH 2/3] Revert changes in `<ranges>`

---
 libcxx/include/ranges | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index b86dd78ecb255..96d7a6b897188 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -405,14 +405,17 @@ namespace std {
 #  include <__config>
 
 #  if _LIBCPP_STD_VER >= 20
+#    include <__ranges/access.h>
 #    include <__ranges/all.h>
 #    include <__ranges/common_view.h>
 #    include <__ranges/concepts.h>
 #    include <__ranges/counted.h>
 #    include <__ranges/dangling.h>
+#    include <__ranges/data.h>
 #    include <__ranges/drop_view.h>
 #    include <__ranges/drop_while_view.h>
 #    include <__ranges/elements_view.h>
+#    include <__ranges/empty.h>
 #    include <__ranges/empty_view.h>
 #    include <__ranges/enable_borrowed_range.h>
 #    include <__ranges/enable_view.h>
@@ -420,9 +423,12 @@ namespace std {
 #    include <__ranges/iota_view.h>
 #    include <__ranges/join_view.h>
 #    include <__ranges/lazy_split_view.h>
+#    include <__ranges/rbegin.h>
 #    include <__ranges/ref_view.h>
+#    include <__ranges/rend.h>
 #    include <__ranges/reverse_view.h>
 #    include <__ranges/single_view.h>
+#    include <__ranges/size.h>
 #    include <__ranges/split_view.h>
 #    include <__ranges/subrange.h>
 #    include <__ranges/take_view.h>
@@ -454,7 +460,7 @@ namespace std {
 // [ranges.syn]
 #  include <compare>
 #  include <initializer_list>
-#  include <iterator> // some CPOs are provided in <iterator> per [range.access.general]
+#  include <iterator>
 
 // [tuple.helper]
 #  include <__tuple/tuple_element.h>

>From bd0ac1c4343a393e841f30d25a32032595aed203 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 15 Aug 2025 21:41:32 +0800
Subject: [PATCH 3/3] Actually call CPOs in the test, at both compile- and
 run-time

---
 ...ile.pass.cpp => include.iterator.pass.cpp} | 42 +++++++++++--------
 1 file changed, 25 insertions(+), 17 deletions(-)
 rename libcxx/test/std/ranges/range.access/{include.iterator.compile.pass.cpp => include.iterator.pass.cpp} (68%)

diff --git a/libcxx/test/std/ranges/range.access/include.iterator.compile.pass.cpp b/libcxx/test/std/ranges/range.access/include.iterator.pass.cpp
similarity index 68%
rename from libcxx/test/std/ranges/range.access/include.iterator.compile.pass.cpp
rename to libcxx/test/std/ranges/range.access/include.iterator.pass.cpp
index a3c59166eeab0..bb2cda0e4d906 100644
--- a/libcxx/test/std/ranges/range.access/include.iterator.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.access/include.iterator.pass.cpp
@@ -18,7 +18,7 @@
 #include "test_macros.h"
 
 template <class CPO, class... Args>
-constexpr bool test(CPO& o, Args&&...) {
+constexpr void test(CPO& o, Args&&... args) {
   static_assert(std::is_const_v<CPO>);
   static_assert(std::is_class_v<CPO>);
   static_assert(std::is_trivially_copyable_v<CPO>);
@@ -26,6 +26,7 @@ constexpr bool test(CPO& o, Args&&...) {
 
   auto p  = o;
   using T = decltype(p);
+  (void)o(args...); // to make sure the CPO can actually be used
 
   // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular.
   static_assert(std::semiregular<T>);
@@ -35,26 +36,33 @@ constexpr bool test(CPO& o, Args&&...) {
   static_assert(std::invocable<const T&, Args...>);
   static_assert(std::invocable<T, Args...>);
   static_assert(std::invocable<const T, Args...>);
-
-  return true;
 }
 
 int a[10];
 
-static_assert(test(std::ranges::begin, a));
-static_assert(test(std::ranges::end, a));
-static_assert(test(std::ranges::cbegin, a));
-static_assert(test(std::ranges::cdata, a));
-static_assert(test(std::ranges::cend, a));
-static_assert(test(std::ranges::crbegin, a));
-static_assert(test(std::ranges::crend, a));
-static_assert(test(std::ranges::data, a));
-static_assert(test(std::ranges::empty, a));
-static_assert(test(std::ranges::rbegin, a));
-static_assert(test(std::ranges::rend, a));
-static_assert(test(std::ranges::size, a));
-static_assert(test(std::ranges::ssize, a));
+constexpr bool test() {
+  test(std::ranges::begin, a);
+  test(std::ranges::end, a);
+  test(std::ranges::cbegin, a);
+  test(std::ranges::cdata, a);
+  test(std::ranges::cend, a);
+  test(std::ranges::crbegin, a);
+  test(std::ranges::crend, a);
+  test(std::ranges::data, a);
+  test(std::ranges::empty, a);
+  test(std::ranges::rbegin, a);
+  test(std::ranges::rend, a);
+  test(std::ranges::size, a);
+  test(std::ranges::ssize, a);
 
 #if TEST_STD_VER >= 26
-// static_assert(test(std::views::reserve_hint, a));
+  // test(std::views::reserve_hint, a);
 #endif
+
+  return true;
+}
+
+int main() {
+  test();
+  static_assert(test());
+}



More information about the libcxx-commits mailing list