[libcxx-commits] [libcxx] [libc++] P2440R1: Implement ranges::iota (PR #109552)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Sep 21 14:51:59 PDT 2024


https://github.com/PaulXiCao created https://github.com/llvm/llvm-project/pull/109552

The paper [P2440](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2440r1.html) introduces three groups of functions: `std::ranges::iota`, `std::ranges::shift_left`, and `std::ranges::shift_right`.

This mr implements `std::ranges::iota`. For reference:
- https://en.cppreference.com/w/cpp/algorithm/ranges/iota
- https://eel.is/c++draft/numeric.iota.

>From d3d7dd74bb3bab32e7331cfe0e25351a39224c32 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 05:41:56 +0200
Subject: [PATCH 01/21] tests

---
 .../numeric.iota/ranges_iota.pass.cpp         | 58 +++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp
new file mode 100644
index 00000000000000..a15c48722d8079
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <numeric>
+
+// iota_result<O, T> iota(O first, S last, T value);
+
+#include <algorithm>
+#include <cassert>
+#include <numeric>
+#include <ranges>
+#include <vector>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+constexpr bool test() {
+  { // empty range
+    std::vector<int> vec;
+    constexpr int value = 42;
+    for (int i = 0; i < 2; ++i) {
+      const auto [last, final_value] =
+          (i == 0) ? std::ranges::iota(vec.begin(), vec.end(), value)
+                   : std::ranges::iota<decltype(vec.begin())>(vec, value);
+      assert(vec.empty());
+      assert(last == vec.end());
+      assert(final_value == value);
+    }
+  }
+
+  { // non-empty range
+    constexpr int size = 3;
+    std::vector<int> vec(size);
+    constexpr int value = 42;
+    for (int i = 0; i < 2; ++i) {
+      const auto [last, final_value] =
+          (i == 0) ? std::ranges::iota(vec.begin(), vec.end(), value)
+                   : std::ranges::iota<decltype(vec.begin())>(vec, value);
+      assert(std::ranges::equal(vec, std::vector{value, value + 1, value + 2}));
+      assert(last == vec.end());
+      assert(final_value == value + size);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+}

>From a90a3405ac7e2f46370ef84465c95a5e9365da54 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 05:42:25 +0200
Subject: [PATCH 02/21] first (straw-man) implementation for std::ranges::iota

---
 libcxx/include/__numeric/iota.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index 27bd84e395a3a5..bdebfe56722c9c 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -11,6 +11,8 @@
 #define _LIBCPP___NUMERIC_IOTA_H
 
 #include <__config>
+#include <__ranges/access.h>
+#include <__utility/pair.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -25,6 +27,25 @@ iota(_ForwardIterator __first, _ForwardIterator __last, _Tp __value) {
     *__first = __value;
 }
 
+#if _LIBCPP_STD_VER >= 23
+namespace ranges {
+template <class O, class S, class T>
+constexpr pair<O, T> iota(O first, S last, T value) {
+  while (first != last) {
+    *first = value;
+    ++first;
+    ++value;
+  }
+  return {std::move(first), std::move(value)};
+}
+
+template <class O, class R, class T>
+constexpr pair<O, T> iota(R&& r, T value) {
+  return std::ranges::iota(std::ranges::begin(r), std::ranges::end(r), std::move(value));
+}
+} // namespace ranges
+#endif // _LIBCPP_STD_VER >= 23
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___NUMERIC_IOTA_H

>From 2439dafc79626a31f1afe649110371d5cc92aa63 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 21:57:30 +0200
Subject: [PATCH 03/21] constrain template arguments via proper concepts

---
 libcxx/include/__numeric/iota.h | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index bdebfe56722c9c..c64cde4ca9fd62 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -11,7 +11,9 @@
 #define _LIBCPP___NUMERIC_IOTA_H
 
 #include <__config>
+#include <__iterator/concepts.h>
 #include <__ranges/access.h>
+#include <__ranges/concepts.h>
 #include <__utility/pair.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -29,7 +31,8 @@ iota(_ForwardIterator __first, _ForwardIterator __last, _Tp __value) {
 
 #if _LIBCPP_STD_VER >= 23
 namespace ranges {
-template <class O, class S, class T>
+template <input_or_output_iterator O, sentinel_for<O> S, weakly_incrementable T>
+  requires indirectly_writable<O, const T&>
 constexpr pair<O, T> iota(O first, S last, T value) {
   while (first != last) {
     *first = value;
@@ -39,7 +42,7 @@ constexpr pair<O, T> iota(O first, S last, T value) {
   return {std::move(first), std::move(value)};
 }
 
-template <class O, class R, class T>
+template < input_or_output_iterator O, weakly_incrementable T, output_range<const T&> R >
 constexpr pair<O, T> iota(R&& r, T value) {
   return std::ranges::iota(std::ranges::begin(r), std::ranges::end(r), std::move(value));
 }

>From e614e68ffd9891953fd994d0fa64bffce1bf669c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:07:26 +0200
Subject: [PATCH 04/21] uglify

---
 libcxx/include/__numeric/iota.h | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index c64cde4ca9fd62..5c3a2e32020b8d 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -31,20 +31,20 @@ iota(_ForwardIterator __first, _ForwardIterator __last, _Tp __value) {
 
 #if _LIBCPP_STD_VER >= 23
 namespace ranges {
-template <input_or_output_iterator O, sentinel_for<O> S, weakly_incrementable T>
-  requires indirectly_writable<O, const T&>
-constexpr pair<O, T> iota(O first, S last, T value) {
-  while (first != last) {
-    *first = value;
-    ++first;
-    ++value;
+template <input_or_output_iterator _O, sentinel_for<_O> _S, weakly_incrementable _T>
+  requires indirectly_writable<_O, const _T&>
+constexpr pair<_O, _T> iota(_O __first, _S __last, _T __value) {
+  while (__first != __last) {
+    *__first = __value;
+    ++__first;
+    ++__value;
   }
-  return {std::move(first), std::move(value)};
+  return {std::move(__first), std::move(__value)};
 }
 
-template < input_or_output_iterator O, weakly_incrementable T, output_range<const T&> R >
-constexpr pair<O, T> iota(R&& r, T value) {
-  return std::ranges::iota(std::ranges::begin(r), std::ranges::end(r), std::move(value));
+template < input_or_output_iterator _O, weakly_incrementable _T, output_range<const _T&> _R >
+constexpr pair<_O, _T> iota(_R&& __r, _T __value) {
+  return std::ranges::iota(std::ranges::begin(__r), std::ranges::end(__r), std::move(__value));
 }
 } // namespace ranges
 #endif // _LIBCPP_STD_VER >= 23

>From 5ac194f5539ce3614ed64741d04b2e36b0fd5d79 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:10:32 +0200
Subject: [PATCH 05/21] out_value_result

---
 libcxx/include/CMakeLists.txt                 |  1 +
 libcxx/include/__algorithm/out_value_result.h | 51 +++++++++++++++++++
 libcxx/include/__numeric/iota.h               | 10 ++--
 3 files changed, 59 insertions(+), 3 deletions(-)
 create mode 100644 libcxx/include/__algorithm/out_value_result.h

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 8c61009167ddce..5627588c3160ef 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -65,6 +65,7 @@ set(files
   __algorithm/next_permutation.h
   __algorithm/none_of.h
   __algorithm/nth_element.h
+  __algorithm/out_value_result.h
   __algorithm/partial_sort.h
   __algorithm/partial_sort_copy.h
   __algorithm/partition.h
diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h
new file mode 100644
index 00000000000000..e67154599a8cda
--- /dev/null
+++ b/libcxx/include/__algorithm/out_value_result.h
@@ -0,0 +1,51 @@
+// -*- 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___ALGORITHM_OUT_VALUE_RESULT_H
+#define _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H
+
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _O, class _T>
+struct out_value_result {
+  _LIBCPP_NO_UNIQUE_ADDRESS _O out;
+  _LIBCPP_NO_UNIQUE_ADDRESS _T value;
+
+  template <class _O2, class _T2>
+    requires convertible_to<const _O&, _O2> && convertible_to<const _T&, _T2>
+  _LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_O2, _T2>() const& {
+    return {out, value};
+  }
+
+  template <class _O2, class _T2>
+    requires convertible_to<_O, _O2> && convertible_to<_T, _T2>
+  _LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_O2, _T2>() && {
+    return {std::move(out), std::move(value)};
+  }
+};
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H
diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index 5c3a2e32020b8d..be93b3ff98b126 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -10,6 +10,7 @@
 #ifndef _LIBCPP___NUMERIC_IOTA_H
 #define _LIBCPP___NUMERIC_IOTA_H
 
+#include <__algorithm/out_value_result.h>
 #include <__config>
 #include <__iterator/concepts.h>
 #include <__ranges/access.h>
@@ -31,9 +32,12 @@ iota(_ForwardIterator __first, _ForwardIterator __last, _Tp __value) {
 
 #if _LIBCPP_STD_VER >= 23
 namespace ranges {
+template < class _O, class _T >
+using iota_result = ranges::out_value_result<_O, _T>;
+
 template <input_or_output_iterator _O, sentinel_for<_O> _S, weakly_incrementable _T>
   requires indirectly_writable<_O, const _T&>
-constexpr pair<_O, _T> iota(_O __first, _S __last, _T __value) {
+constexpr iota_result<_O, _T> iota(_O __first, _S __last, _T __value) {
   while (__first != __last) {
     *__first = __value;
     ++__first;
@@ -42,8 +46,8 @@ constexpr pair<_O, _T> iota(_O __first, _S __last, _T __value) {
   return {std::move(__first), std::move(__value)};
 }
 
-template < input_or_output_iterator _O, weakly_incrementable _T, output_range<const _T&> _R >
-constexpr pair<_O, _T> iota(_R&& __r, _T __value) {
+template <weakly_incrementable _T, output_range<const _T&> _R >
+constexpr iota_result<ranges::borrowed_iterator_t<_R>, _T> iota(_R&& __r, _T __value) {
   return std::ranges::iota(std::ranges::begin(__r), std::ranges::end(__r), std::move(__value));
 }
 } // namespace ranges

>From a6c92ca4effb2111de2e8c2a9581b16a306a08c3 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:25:51 +0200
Subject: [PATCH 06/21] update tests

---
 .../numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp  | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp
index a15c48722d8079..cc484258488345 100644
--- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges_iota.pass.cpp
@@ -27,8 +27,7 @@ constexpr bool test() {
     constexpr int value = 42;
     for (int i = 0; i < 2; ++i) {
       const auto [last, final_value] =
-          (i == 0) ? std::ranges::iota(vec.begin(), vec.end(), value)
-                   : std::ranges::iota<decltype(vec.begin())>(vec, value);
+          (i == 0) ? std::ranges::iota(vec.begin(), vec.end(), value) : std::ranges::iota(vec, value);
       assert(vec.empty());
       assert(last == vec.end());
       assert(final_value == value);
@@ -41,8 +40,7 @@ constexpr bool test() {
     constexpr int value = 42;
     for (int i = 0; i < 2; ++i) {
       const auto [last, final_value] =
-          (i == 0) ? std::ranges::iota(vec.begin(), vec.end(), value)
-                   : std::ranges::iota<decltype(vec.begin())>(vec, value);
+          (i == 0) ? std::ranges::iota(vec.begin(), vec.end(), value) : std::ranges::iota(vec, value);
       assert(std::ranges::equal(vec, std::vector{value, value + 1, value + 2}));
       assert(last == vec.end());
       assert(final_value == value + size);

>From 200fc655bf117dda5a97f094e2b00af7fccfbad9 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:29:13 +0200
Subject: [PATCH 07/21] make use of std::as_const()

---
 libcxx/include/__numeric/iota.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index be93b3ff98b126..93b3535caa1af6 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -15,6 +15,7 @@
 #include <__iterator/concepts.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
+#include <__utility/as_const.h>
 #include <__utility/pair.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -39,7 +40,7 @@ template <input_or_output_iterator _O, sentinel_for<_O> _S, weakly_incrementable
   requires indirectly_writable<_O, const _T&>
 constexpr iota_result<_O, _T> iota(_O __first, _S __last, _T __value) {
   while (__first != __last) {
-    *__first = __value;
+    *__first = std::as_const(__value);
     ++__first;
     ++__value;
   }

>From 7020d5fa6c18d2eeeea1803ebbc540ba6086fa88 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:33:06 +0200
Subject: [PATCH 08/21] cleanup namespace usage

---
 libcxx/include/__numeric/iota.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index 93b3535caa1af6..23bbc31305624c 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -34,7 +34,7 @@ iota(_ForwardIterator __first, _ForwardIterator __last, _Tp __value) {
 #if _LIBCPP_STD_VER >= 23
 namespace ranges {
 template < class _O, class _T >
-using iota_result = ranges::out_value_result<_O, _T>;
+using iota_result = out_value_result<_O, _T>;
 
 template <input_or_output_iterator _O, sentinel_for<_O> _S, weakly_incrementable _T>
   requires indirectly_writable<_O, const _T&>
@@ -48,7 +48,7 @@ constexpr iota_result<_O, _T> iota(_O __first, _S __last, _T __value) {
 }
 
 template <weakly_incrementable _T, output_range<const _T&> _R >
-constexpr iota_result<ranges::borrowed_iterator_t<_R>, _T> iota(_R&& __r, _T __value) {
+constexpr iota_result<borrowed_iterator_t<_R>, _T> iota(_R&& __r, _T __value) {
   return std::ranges::iota(std::ranges::begin(__r), std::ranges::end(__r), std::move(__value));
 }
 } // namespace ranges

>From 1797593f91b89b7f163ddacb9f2c8b2c1a42d1dd Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:33:51 +0200
Subject: [PATCH 09/21] make use of _LIBCPP_HIDE_FROM_ABI

---
 libcxx/include/__numeric/iota.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index 23bbc31305624c..5f3b17a1062033 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -38,7 +38,7 @@ using iota_result = out_value_result<_O, _T>;
 
 template <input_or_output_iterator _O, sentinel_for<_O> _S, weakly_incrementable _T>
   requires indirectly_writable<_O, const _T&>
-constexpr iota_result<_O, _T> iota(_O __first, _S __last, _T __value) {
+_LIBCPP_HIDE_FROM_ABI constexpr iota_result<_O, _T> iota(_O __first, _S __last, _T __value) {
   while (__first != __last) {
     *__first = std::as_const(__value);
     ++__first;
@@ -48,7 +48,7 @@ constexpr iota_result<_O, _T> iota(_O __first, _S __last, _T __value) {
 }
 
 template <weakly_incrementable _T, output_range<const _T&> _R >
-constexpr iota_result<borrowed_iterator_t<_R>, _T> iota(_R&& __r, _T __value) {
+_LIBCPP_HIDE_FROM_ABI constexpr iota_result<borrowed_iterator_t<_R>, _T> iota(_R&& __r, _T __value) {
   return std::ranges::iota(std::ranges::begin(__r), std::ranges::end(__r), std::move(__value));
 }
 } // namespace ranges

>From b47a764bb2042f5179f3a39cd1ba47d24cc788f6 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:34:35 +0200
Subject: [PATCH 10/21] include header for std::move

---
 libcxx/include/__numeric/iota.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index 5f3b17a1062033..f5fe4ae56f1649 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -16,6 +16,7 @@
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__utility/as_const.h>
+#include <__utility/move.h>
 #include <__utility/pair.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)

>From 3cf2e0ed5325c6eed21fb4cad8b54d3b1c3bf097 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:38:36 +0200
Subject: [PATCH 11/21] header cleanup

---
 libcxx/include/__numeric/iota.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/include/__numeric/iota.h b/libcxx/include/__numeric/iota.h
index f5fe4ae56f1649..7b3ebdf3589a8e 100644
--- a/libcxx/include/__numeric/iota.h
+++ b/libcxx/include/__numeric/iota.h
@@ -17,7 +17,6 @@
 #include <__ranges/concepts.h>
 #include <__utility/as_const.h>
 #include <__utility/move.h>
-#include <__utility/pair.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header

>From 37cefdb5278fa8dc61fdc5c4edd50198e99333d8 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:40:57 +0200
Subject: [PATCH 12/21] active feature macro

---
 libcxx/include/version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/version b/libcxx/include/version
index 5d679caac0b3b7..0ac8031b386a2a 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -489,7 +489,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_ranges_chunk_by                      202202L
 # define __cpp_lib_ranges_contains                      202207L
 # define __cpp_lib_ranges_find_last                     202207L
-// # define __cpp_lib_ranges_iota                          202202L
+# define __cpp_lib_ranges_iota                          202202L
 // # define __cpp_lib_ranges_join_with                     202202L
 # define __cpp_lib_ranges_repeat                        202207L
 // # define __cpp_lib_ranges_slide                         202202L

>From 82e5428cd45a288d32f6430af7fc0392a56663ef Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:50:06 +0200
Subject: [PATCH 13/21] numeric synopsis: add iota_result, iota

---
 libcxx/include/numeric | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libcxx/include/numeric b/libcxx/include/numeric
index 6b92ce3a071237..eb192c56fb1b17 100644
--- a/libcxx/include/numeric
+++ b/libcxx/include/numeric
@@ -128,6 +128,18 @@ template <class ForwardIterator, class T>
     constexpr void  // constexpr since C++20
     iota(ForwardIterator first, ForwardIterator last, T value);
 
+namespace ranges {
+    template<class O, class T>
+        using iota_result = out_value_result<O, T>;                                     // C++23
+
+    template<input_or_output_iterator O, sentinel_for<O> S, weakly_incrementable T>
+        requires indirectly_writable<O, const T&>
+        constexpr iota_result<O, T> iota(O first, S last, T value);                     // C++23
+
+    template<weakly_incrementable T, output_range<const T&> R>
+        constexpr iota_result<borrowed_iterator_t<R>, T> iota(R&& r, T value);          // C++23
+}
+
 template <class M, class N>
     constexpr common_type_t<M,N> gcd(M m, N n);    // C++17
 

>From 2b962fc04e972734cb7cb8bd636e4c6f29427cb3 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:55:07 +0200
Subject: [PATCH 14/21] numeric synopsis: match ISO standard's style

---
 libcxx/include/numeric | 47 +++++++++++++++++++++---------------------
 1 file changed, 24 insertions(+), 23 deletions(-)

diff --git a/libcxx/include/numeric b/libcxx/include/numeric
index eb192c56fb1b17..bac9e7274fe791 100644
--- a/libcxx/include/numeric
+++ b/libcxx/include/numeric
@@ -124,46 +124,47 @@ template <class InputIterator, class OutputIterator, class BinaryOperation>
     constexpr OutputIterator  // constexpr since C++20
     adjacent_difference(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op);
 
-template <class ForwardIterator, class T>
-    constexpr void  // constexpr since C++20
-    iota(ForwardIterator first, ForwardIterator last, T value);
+// [numeric.iota], iota
+template<class ForwardIterator, class T>
+  constexpr void iota(ForwardIterator first, ForwardIterator last, T value);        // constexpr since C++20
 
 namespace ranges {
-    template<class O, class T>
-        using iota_result = out_value_result<O, T>;                                     // C++23
+  template<class O, class T>
+    using iota_result = out_value_result<O, T>;                                     // C++23
 
-    template<input_or_output_iterator O, sentinel_for<O> S, weakly_incrementable T>
-        requires indirectly_writable<O, const T&>
-        constexpr iota_result<O, T> iota(O first, S last, T value);                     // C++23
+  template<input_or_output_iterator O, sentinel_for<O> S, weakly_incrementable T>
+    requires indirectly_writable<O, const T&>
+    constexpr iota_result<O, T> iota(O first, S last, T value);                     // C++23
 
-    template<weakly_incrementable T, output_range<const T&> R>
-        constexpr iota_result<borrowed_iterator_t<R>, T> iota(R&& r, T value);          // C++23
+  template<weakly_incrementable T, output_range<const T&> R>
+    constexpr iota_result<borrowed_iterator_t<R>, T> iota(R&& r, T value);          // C++23
 }
 
-template <class M, class N>
-    constexpr common_type_t<M,N> gcd(M m, N n);    // C++17
+// [numeric.ops.gcd], greatest common divisor
+template<class M, class N>
+  constexpr common_type_t<M, N> gcd(M m, N n);                // C++17
 
-template <class M, class N>
-    constexpr common_type_t<M,N> lcm(M m, N n);    // C++17
+// [numeric.ops.lcm], least common multiple
+template<class M, class N>
+  constexpr common_type_t<M, N> lcm(M m, N n);                // C++17
 
+// [numeric.ops.midpoint], midpoint
 template<class T>
-    constexpr T midpoint(T a, T b) noexcept;  // C++20
-
+  constexpr T midpoint(T a, T b) noexcept;                    // C++20
 template<class T>
-    constexpr T* midpoint(T* a, T* b);        // C++20
+  constexpr T* midpoint(T* a, T* b);                          // C++20
 
 // [numeric.sat], saturation arithmetic
 template<class T>
-constexpr T add_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+  constexpr T add_sat(T x, T y) noexcept;                     // freestanding, Since C++26
 template<class T>
-constexpr T sub_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+  constexpr T sub_sat(T x, T y) noexcept;                     // freestanding, Since C++26
 template<class T>
-constexpr T mul_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+  constexpr T mul_sat(T x, T y) noexcept;                     // freestanding, Since C++26
 template<class T>
-constexpr T div_sat(T x, T y) noexcept;                     // freestanding, Since C++26
+  constexpr T div_sat(T x, T y) noexcept;                     // freestanding, Since C++26
 template<class T, class U>
-constexpr T saturate_cast(U x) noexcept;                    // freestanding, Since C++26
-
+  constexpr T saturate_cast(U x) noexcept;                    // freestanding, Since C++26
 }  // std
 
 */

>From 6d7cafe5c9de0fc8748635805c4e25b097fd8505 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 22:59:11 +0200
Subject: [PATCH 15/21] algorithm synopsis: mention out_value_result

---
 libcxx/include/algorithm | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 36fd035b7e51b3..c0b6c0b77cd0d4 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -45,6 +45,9 @@ namespace ranges {
   template <class I, class T>
     struct in_value_result;              // since C++23
 
+  template<class O, class T>
+    struct out_value_result;             // since C++23
+
   template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
     indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>                                   // since C++20
   constexpr I min_element(I first, S last, Comp comp = {}, Proj proj = {});

>From eac5d45ad0ebdb8f9a09be49967318b5da8aafd1 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 23:15:04 +0200
Subject: [PATCH 16/21] add out_value_result.h to <algorithm> header

---
 libcxx/include/algorithm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index c0b6c0b77cd0d4..9058541824589b 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -2028,6 +2028,7 @@ template <class BidirectionalIterator, class Compare>
 #  include <__algorithm/ranges_ends_with.h>
 #  include <__algorithm/ranges_find_last.h>
 #  include <__algorithm/ranges_starts_with.h>
+#  include <__algorithm/out_value_result.h>
 #endif // _LIBCPP_STD_VER >= 23
 
 #include <version>

>From a9b3ba7385f11bfad3a373a56d18967b794e3e67 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 23:19:47 +0200
Subject: [PATCH 17/21] testing: out_value_result

---
 .../out_value_result.pass.cpp                 | 100 ++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp

diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
new file mode 100644
index 00000000000000..fa04acf4be17f2
--- /dev/null
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// template <class _O, class _T>
+// struct out_value_result;
+
+#include <algorithm>
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+#include "MoveOnly.h"
+
+struct A {
+  explicit A(int);
+};
+// no implicit conversion
+static_assert(!std::is_constructible_v<std::ranges::out_value_result<A, A>, std::ranges::out_value_result<int, int>>);
+
+struct B {
+  B(int);
+};
+// implicit conversion
+static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>>);
+static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>&>);
+static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>>);
+static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>&>);
+
+struct C {
+  C(int&);
+};
+static_assert(!std::is_constructible_v<std::ranges::out_value_result<C, C>, std::ranges::out_value_result<int, int>&>);
+
+// has to be convertible via const&
+static_assert(std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
+static_assert(std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>);
+static_assert(std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>);
+static_assert(std::is_convertible_v<const std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>);
+
+// should be move constructible
+static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>);
+static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>);
+
+// should not be copy constructible with move-only type
+static_assert(!std::is_copy_constructible_v<std::ranges::out_value_result<MoveOnly, int>>);
+static_assert(!std::is_copy_constructible_v<std::ranges::out_value_result<int, MoveOnly>>);
+
+struct NotConvertible {};
+// conversions should not work if there is no conversion
+static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>, std::ranges::out_value_result<int, int>>);
+static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>, std::ranges::out_value_result<int, int>>);
+
+template <class T>
+struct ConvertibleFrom {
+  constexpr ConvertibleFrom(T c) : content{c} {}
+  T content;
+};
+
+constexpr bool test() {
+  // Checks that conversion operations are correct.
+  {
+    const std::ranges::out_value_result<int, double> res{10, 0.};
+    assert(res.out == 10);
+    assert(res.value == 0.);
+    const std::ranges::out_value_result<ConvertibleFrom<int>, ConvertibleFrom<double>> res2 = res;
+    assert(res2.out.content == 10);
+    assert(res2.value.content == 0.);
+  }
+
+  // Checks that conversions are possible when one of the types is move-only.
+  {
+    std::ranges::out_value_result<MoveOnly, int> res{MoveOnly{}, 2};
+    assert(res.out.get() == 1);
+    assert(res.value == 2);
+    const auto res2 = static_cast<std::ranges::out_value_result<MoveOnly, int>>(std::move(res));
+    assert(res.out.get() == 0);
+    assert(res2.out.get() == 1);
+    assert(res2.value == 2);
+  }
+
+  // Checks that structured bindings get the correct values.
+  {
+    const auto [out, value] = std::ranges::out_value_result<int, int>{1, 2};
+    assert(out == 1);
+    assert(value == 2);
+  }
+  return true;
+}
+
+int main() {
+  test();
+  static_assert(test());
+}

>From 919c863b34783e847c0c0fb3630800bafaa6637f Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 23:22:33 +0200
Subject: [PATCH 18/21] update docs/Status

---
 libcxx/docs/Status/Cxx23Papers.csv | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 6dde153ee8aab5..5f4ff35fd90421 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -46,7 +46,7 @@
 "`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","","",""
 "`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16.0",""
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19.0",""
-"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","","",""
+"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|In Progress|","",""
 "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|In Progress|","",""
 "`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","",""
 "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18.0",""

>From 4adab5877e7b214d804268aad5e38046e868a147 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 23:29:08 +0200
Subject: [PATCH 19/21] update modulemap

---
 libcxx/include/module.modulemap | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index c1181a3622513f..a237dd19b2ee21 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -766,6 +766,7 @@ module std_private_algorithm_move_backward                               [system
 module std_private_algorithm_next_permutation                            [system] { header "__algorithm/next_permutation.h" }
 module std_private_algorithm_none_of                                     [system] { header "__algorithm/none_of.h" }
 module std_private_algorithm_nth_element                                 [system] { header "__algorithm/nth_element.h" }
+module std_private_algorithm_out_value_result                            [system] { header "__algorithm/out_value_result.h" }
 module std_private_algorithm_partial_sort                                [system] { header "__algorithm/partial_sort.h" }
 module std_private_algorithm_partial_sort_copy                           [system] { header "__algorithm/partial_sort_copy.h" }
 module std_private_algorithm_partition                                   [system] { header "__algorithm/partition.h" }

>From 648fe1baba53dd6895a2f8cf4519fb86f93217e5 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 23:31:45 +0200
Subject: [PATCH 20/21] feature test macro: update
 generate_feature_test_macro_components

---
 libcxx/utils/generate_feature_test_macro_components.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 3b8a52362ede68..a03b9b20b2fc52 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1043,7 +1043,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_ranges_iota",
             "values": {"c++23": 202202},
             "headers": ["numeric"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_ranges_join_with",

>From 40a283071edad4fcfbe1f7d80accf6c48f76061a Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Sat, 21 Sep 2024 23:35:47 +0200
Subject: [PATCH 21/21] enable feature test macro checks

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 .../numeric.version.compile.pass.cpp          | 32 ++++++-------------
 .../version.version.compile.pass.cpp          | 32 ++++++-------------
 3 files changed, 21 insertions(+), 45 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index c909a4300db1a6..76a6c0f9212001 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -364,7 +364,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_find_last``                             ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_iota``                                  *unimplemented*
+    ``__cpp_lib_ranges_iota``                                  ``202202L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ranges_join_with``                             *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
index d132b7c7b9c4f5..0743c6c71840c4 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp
@@ -197,17 +197,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_iota
-#     error "__cpp_lib_ranges_iota should be defined in c++23"
-#   endif
-#   if __cpp_lib_ranges_iota != 202202L
-#     error "__cpp_lib_ranges_iota should have the value 202202L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_iota
-#     error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_iota
+#   error "__cpp_lib_ranges_iota should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_iota != 202202L
+#   error "__cpp_lib_ranges_iota should have the value 202202L in c++23"
 # endif
 
 # ifdef __cpp_lib_saturation_arithmetic
@@ -250,17 +244,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_iota
-#     error "__cpp_lib_ranges_iota should be defined in c++26"
-#   endif
-#   if __cpp_lib_ranges_iota != 202202L
-#     error "__cpp_lib_ranges_iota should have the value 202202L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_iota
-#     error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_iota
+#   error "__cpp_lib_ranges_iota should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_iota != 202202L
+#   error "__cpp_lib_ranges_iota should have the value 202202L in c++26"
 # endif
 
 # ifndef __cpp_lib_saturation_arithmetic
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 985ffeffab96db..840e88cda7ce4c 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -5682,17 +5682,11 @@
 #   error "__cpp_lib_ranges_find_last should have the value 202207L in c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_iota
-#     error "__cpp_lib_ranges_iota should be defined in c++23"
-#   endif
-#   if __cpp_lib_ranges_iota != 202202L
-#     error "__cpp_lib_ranges_iota should have the value 202202L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_iota
-#     error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_iota
+#   error "__cpp_lib_ranges_iota should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_iota != 202202L
+#   error "__cpp_lib_ranges_iota should have the value 202202L in c++23"
 # endif
 
 # if !defined(_LIBCPP_VERSION)
@@ -7542,17 +7536,11 @@
 #   error "__cpp_lib_ranges_find_last should have the value 202207L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_ranges_iota
-#     error "__cpp_lib_ranges_iota should be defined in c++26"
-#   endif
-#   if __cpp_lib_ranges_iota != 202202L
-#     error "__cpp_lib_ranges_iota should have the value 202202L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_ranges_iota
-#     error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_ranges_iota
+#   error "__cpp_lib_ranges_iota should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_iota != 202202L
+#   error "__cpp_lib_ranges_iota should have the value 202202L in c++26"
 # endif
 
 # if !defined(_LIBCPP_VERSION)



More information about the libcxx-commits mailing list