[libcxx-commits] [libcxx] [flang] [clang-tools-extra] [libc] [clang] [compiler-rt] [llvm] [libc++] Implement ranges::iota (PR #68494)

James E T Smith via libcxx-commits libcxx-commits at lists.llvm.org
Tue Nov 14 17:41:01 PST 2023


https://github.com/jamesETsmith updated https://github.com/llvm/llvm-project/pull/68494

>From c4a3ccfbad090ad8314aa8ad53092edc8d5432bc Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Thu, 28 Sep 2023 10:11:15 -0400
Subject: [PATCH 01/17] [libc++] Implement ranges::iota and
 ranges::out_value_result

---
 libcxx/include/CMakeLists.txt                 |   2 +
 libcxx/include/__algorithm/out_value_result.h |  52 +++++++++
 libcxx/include/__numeric/ranges_iota.h        |  53 +++++++++
 libcxx/include/algorithm                      |   4 +
 libcxx/include/numeric                        |   1 +
 libcxx/include/version                        |   2 +-
 .../out_value_result.pass.cpp                 | 102 ++++++++++++++++++
 .../numeric.iota/ranges.iota.pass.cpp         |  52 +++++++++
 8 files changed, 267 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__algorithm/out_value_result.h
 create mode 100644 libcxx/include/__numeric/ranges_iota.h
 create mode 100644 libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
 create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 2ec755236dbaee2..c6eb03f1d68e984 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -63,6 +63,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
@@ -561,6 +562,7 @@ set(files
   __numeric/partial_sum.h
   __numeric/pstl_reduce.h
   __numeric/pstl_transform_reduce.h
+  __numeric/ranges_iota.h
   __numeric/reduce.h
   __numeric/transform_exclusive_scan.h
   __numeric/transform_inclusive_scan.h
diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h
new file mode 100644
index 000000000000000..8baffec7b9ef4da
--- /dev/null
+++ b/libcxx/include/__algorithm/out_value_result.h
@@ -0,0 +1,52 @@
+// -*- 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_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _OutIter1, class _ValType1>
+struct out_value_result {
+  _LIBCPP_NO_UNIQUE_ADDRESS _OutIter1 out;
+  _LIBCPP_NO_UNIQUE_ADDRESS _ValType1 value;
+
+  template <class _OutIter2, class _ValType2>
+    requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
+  constexpr operator out_value_result<_OutIter2, _ValType2>() const& { return {out, value}; }
+
+  template <class _OutIter2, class _ValType2>
+    requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2>
+  constexpr operator out_value_result<_OutIter2, _ValType2>() && { return {std::move(out), std::move(value)}; }
+};
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H
diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
new file mode 100644
index 000000000000000..20311a68c2a348c
--- /dev/null
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -0,0 +1,53 @@
+// -*- 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___NUMERIC_RANGES_IOTA_H
+#define _LIBCPP___NUMERIC_RANGES_IOTA_H
+
+#include <__algorithm/out_value_result.h>
+#include <__config>
+#include <__ranges/concepts.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 <typename _Out, typename _Tp>
+using iota_result = ranges::out_value_result<_Out, _Tp>;
+
+struct __iota_fn {
+  template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
+    requires indirectly_writable<_Out, const _Tp&>
+  constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
+    while (__first != __last) {
+      *__first = static_cast<const _Tp&>(__value);
+      ++__first;
+      ++__value;
+    }
+    return {std::move(__first), std::move(__value)};
+  }
+
+  template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range>
+  constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const {
+    return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value));
+  }
+};
+
+inline constexpr __iota_fn iota{};
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___NUMERIC_RANGES_IOTA_H
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 69ba9537dda6984..42f01a99a02cc93 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -42,6 +42,9 @@ namespace ranges {
   template <class I>
     struct in_found_result;              // since C++20
 
+  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 = {});
@@ -1817,6 +1820,7 @@ template <class BidirectionalIterator, class Compare>
 #include <__algorithm/next_permutation.h>
 #include <__algorithm/none_of.h>
 #include <__algorithm/nth_element.h>
+#include <__algorithm/out_value_result.h>
 #include <__algorithm/partial_sort.h>
 #include <__algorithm/partial_sort_copy.h>
 #include <__algorithm/partition.h>
diff --git a/libcxx/include/numeric b/libcxx/include/numeric
index 3fcf6cefdb4b884..363dfd229df54dc 100644
--- a/libcxx/include/numeric
+++ b/libcxx/include/numeric
@@ -160,6 +160,7 @@ template<class T>
 #include <__numeric/partial_sum.h>
 #include <__numeric/pstl_reduce.h>
 #include <__numeric/pstl_transform_reduce.h>
+#include <__numeric/ranges_iota.h>
 #include <__numeric/reduce.h>
 #include <__numeric/transform_exclusive_scan.h>
 #include <__numeric/transform_inclusive_scan.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index e5a995366a7aa48..164c60dfd6af6d4 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -436,7 +436,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # define __cpp_lib_ranges_as_rvalue                     202207L
 // # define __cpp_lib_ranges_chunk                         202202L
 # define __cpp_lib_ranges_chunk_by                      202202L
-// # 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
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 000000000000000..189149b0b4acb0c
--- /dev/null
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "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>>);
+
+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, NotConvertible>>);
+static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>,
+                                     std::ranges::out_value_result<NotConvertible, int>>);
+
+template <class T>
+struct ConvertibleFrom {
+  constexpr ConvertibleFrom(T c) : content{c} {}
+  T content;
+};
+
+constexpr bool test() {
+  {
+    std::ranges::out_value_result<double, int> res{10, 1};
+    assert(res.out == 10);
+    assert(res.value == 1);
+    std::ranges::out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res;
+    assert(res2.out.content == 10);
+    assert(res2.value.content == 1);
+  }
+  {
+    std::ranges::out_value_result<MoveOnly, int> res{MoveOnly{}, 10};
+    assert(res.out.get() == 1);
+    assert(res.value == 10);
+    auto res2 = std::move(res);
+    assert(res.out.get() == 0);
+    assert(res.value == 10);
+    assert(res2.out.get() == 1);
+    assert(res2.value == 10);
+  }
+  {
+    auto [min, max] = std::ranges::out_value_result<int, int>{1, 2};
+    assert(min == 1);
+    assert(max == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
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 000000000000000..1e2a6970adb35e3
--- /dev/null
+++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Testing std::ranges::iota
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <numeric>
+#include <array>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+// This is pulled directly from the std::iota test
+template <class InIter>
+TEST_CONSTEXPR_CXX20 void test0() {
+  int ia[]         = {1, 2, 3, 4, 5};
+  int ir[]         = {5, 6, 7, 8, 9};
+  const unsigned s = sizeof(ia) / sizeof(ia[0]);
+  std::ranges::iota(InIter(ia), InIter(ia + s), 5);
+  for (unsigned i = 0; i < s; ++i)
+    assert(ia[i] == ir[i]);
+}
+
+TEST_CONSTEXPR_CXX20 bool test0() {
+  test0<forward_iterator<int*> >();
+  test0<bidirectional_iterator<int*> >();
+  test0<random_access_iterator<int*> >();
+  test0<int*>();
+
+  return true;
+}
+
+TEST_CONSTEXPR_CXX20 void test1() {
+  std::array<int, 5> ia = {1, 2, 3, 4, 5};
+  std::array<int, 5> ir = {5, 6, 7, 8, 9};
+  std::ranges::iota(ia, 5);
+  for (unsigned i = 0; i < ir.size(); ++i)
+    assert(ia[i] == ir[i]);
+}
+
+int main(int, char**) {
+  test0();
+  test1();
+  return 0;
+}
\ No newline at end of file

>From 574c94ce4f7eed06013a5e5d1460da1d148cc7cd Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Fri, 29 Sep 2023 14:11:49 -0400
Subject: [PATCH 02/17] [libc++] Implement ranges::iota: update module and docs
 info

---
 libcxx/docs/FeatureTestMacroTable.rst                  | 2 +-
 libcxx/include/module.modulemap.in                     | 1 +
 libcxx/modules/std/numeric.inc                         | 8 ++++++--
 libcxx/utils/generate_feature_test_macro_components.py | 1 -
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index e1b4172b22c53da..ebb18dc7bed9bd2 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -348,7 +348,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_chunk_by``                       ``202202L``
     --------------------------------------------------- -----------------
-    ``__cpp_lib_ranges_iota``                           *unimplemented*
+    ``__cpp_lib_ranges_iota``                           ``202202L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_join_with``                      *unimplemented*
     --------------------------------------------------- -----------------
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 6d9bb8653fcb5e9..b092cc7c0e93bf1 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1602,6 +1602,7 @@ module std_private_numeric_pstl_transform_reduce    [system] {
   export *
 }
 module std_private_numeric_reduce                   [system] { header "__numeric/reduce.h" }
+module std_private_numeric_ranges_iota              [system] { header "__numeric/ranges_iota.h" }
 module std_private_numeric_transform_exclusive_scan [system] { header "__numeric/transform_exclusive_scan.h" }
 module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" }
 module std_private_numeric_transform_reduce         [system] { header "__numeric/transform_reduce.h" }
diff --git a/libcxx/modules/std/numeric.inc b/libcxx/modules/std/numeric.inc
index d2b7688d4e5f10a..9204e8d8715935d 100644
--- a/libcxx/modules/std/numeric.inc
+++ b/libcxx/modules/std/numeric.inc
@@ -42,8 +42,12 @@ export namespace std {
   using std::iota;
 
   namespace ranges {
-    // using std::ranges::iota_result;
-    // using std::ranges::iota;
+
+#if _LIBCPP_STD_VER >= 23
+    using std::ranges::iota;
+    using std::ranges::iota_result;
+#endif // _LIBCPP_STD_VER >= 23
+
   } // namespace ranges
 
   // [numeric.ops.gcd], greatest common divisor
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index ac342aff0beb701..9fd50d66edbe317 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -806,7 +806,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 e4cfe00cb1b348d5a3515ef907c7c87334c6824c Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Fri, 29 Sep 2023 16:50:46 -0400
Subject: [PATCH 03/17] [libc++] Implement ranges::iota: fixing formatting
 issues for out_value_result

---
 libcxx/include/__algorithm/out_value_result.h | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h
index 8baffec7b9ef4da..a99d206f7035ff2 100644
--- a/libcxx/include/__algorithm/out_value_result.h
+++ b/libcxx/include/__algorithm/out_value_result.h
@@ -34,11 +34,15 @@ struct out_value_result {
 
   template <class _OutIter2, class _ValType2>
     requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
-  constexpr operator out_value_result<_OutIter2, _ValType2>() const& { return {out, value}; }
+  constexpr operator out_value_result<_OutIter2, _ValType2>() const& {
+    return {out, value};
+  }
 
   template <class _OutIter2, class _ValType2>
     requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2>
-  constexpr operator out_value_result<_OutIter2, _ValType2>() && { return {std::move(out), std::move(value)}; }
+  constexpr operator out_value_result<_OutIter2, _ValType2>() && {
+    return {std::move(out), std::move(value)};
+  }
 };
 
 } // namespace ranges

>From 7cdfea2d5acf553345ee0c506ce9e3914cbb3ed0 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 7 Oct 2023 12:04:32 -0400
Subject: [PATCH 04/17] [libc++] Implement ranges::iota: Cleaning up tests for
 ranges::iota and out_value_result. Updating Cxx23Papers.csv too.

---
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/__numeric/ranges_iota.h        |   3 +-
 libcxx/modules/std/algorithm.inc              |   2 +-
 .../out_value_result.pass.cpp                 | 104 ++++++++++------
 .../numeric.iota/ranges.iota.pass.cpp         | 117 ++++++++++++++++--
 5 files changed, 171 insertions(+), 57 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 9cb49fd5176ea5f..4618908cfb73941 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -46,7 +46,7 @@
 "`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","",""
 "`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
 "`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|"
-"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
+"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|In progress|","","|ranges|"
 "`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","","","|ranges|"
 "`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
 "`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index 20311a68c2a348c..a8c069832da7e6d 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -13,6 +13,7 @@
 #include <__algorithm/out_value_result.h>
 #include <__config>
 #include <__ranges/concepts.h>
+#include <__utility/as_const.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -30,7 +31,7 @@ struct __iota_fn {
     requires indirectly_writable<_Out, const _Tp&>
   constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
     while (__first != __last) {
-      *__first = static_cast<const _Tp&>(__value);
+      *__first = std::as_const(__value);
       ++__first;
       ++__value;
     }
diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc
index b7900d15c10c2b5..62ce8cf02c14f95 100644
--- a/libcxx/modules/std/algorithm.inc
+++ b/libcxx/modules/std/algorithm.inc
@@ -18,7 +18,7 @@ export namespace std {
     using std::ranges::in_out_result;
     // using std::ranges::in_value_result;
     using std::ranges::min_max_result;
-    // using std::ranges::out_value_result;
+    using std::ranges::out_value_result;
   } // namespace ranges
 
   // [alg.nonmodifying], non-modifying sequence operations
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
index 189149b0b4acb0c..3a930bb063f975f 100644
--- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -17,48 +17,25 @@
 
 #include "MoveOnly.h"
 
-struct A {
-  explicit A(int);
+//
+// Helper structs
+//
+
+// only explicit construction
+struct IterTypeExplicit {
+  explicit IterTypeExplicit(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 construction
+struct IterTypeImplicit {
+  IterTypeImplicit(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&);
+
+struct IterTypeImplicitRef {
+  IterTypeImplicitRef(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>>);
 
 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, NotConvertible>>);
-static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>,
-                                     std::ranges::out_value_result<NotConvertible, int>>);
 
 template <class T>
 struct ConvertibleFrom {
@@ -66,6 +43,51 @@ struct ConvertibleFrom {
   T content;
 };
 
+//
+constexpr void test_constraints() {
+  // requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
+  static_assert(
+      std::is_constructible_v<std::ranges::out_value_result<int*, int>, std::ranges::out_value_result<int*, int>>);
+
+  // test failure when implicit conversion isn't allowed
+  static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeExplicit, int>,
+                                         std::ranges::out_value_result<int*, int>>);
+
+  // test success when implicit conversion is allowed, checking combinations of value, reference, and const
+  static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
+                                        std::ranges::out_value_result<int*, int>>);
+  static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
+                                        std::ranges::out_value_result<int*, int> const>);
+  static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
+                                        std::ranges::out_value_result<int*, int>&>);
+  static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
+                                        std::ranges::out_value_result<int*, int> const&>);
+
+  static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicitRef, int>,
+                                         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>>);
+
+  // 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, NotConvertible>>);
+  static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>,
+                                       std::ranges::out_value_result<NotConvertible, int>>);
+}
+
+// Test results
 constexpr bool test() {
   {
     std::ranges::out_value_result<double, int> res{10, 1};
@@ -86,17 +108,17 @@ constexpr bool test() {
     assert(res2.value == 10);
   }
   {
-    auto [min, max] = std::ranges::out_value_result<int, int>{1, 2};
-    assert(min == 1);
-    assert(max == 2);
+    auto [out, val] = std::ranges::out_value_result<int, int>{1, 2};
+    assert(out == 1);
+    assert(val == 2);
   }
 
   return true;
 }
 
 int main(int, char**) {
+  test_constraints();
   test();
   static_assert(test());
-
   return 0;
-}
+}
\ No newline at end of file
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 1e2a6970adb35e3..85f99942e97b2ac 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
@@ -12,41 +12,132 @@
 
 #include <cassert>
 #include <numeric>
+#include <algorithm>
 #include <array>
 
 #include "test_macros.h"
 #include "test_iterators.h"
+#include "almost_satisfies_types.h"
+
+// Concepts to check different overloads of std::ranges::iota
+template <class Iter = int*, class Sent = int*, class Value = int>
+concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) {
+  std::ranges::iota(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Value>(val));
+};
+
+template <class Range, class Value = int>
+concept HasIotaRange = requires(Range&& range, Value&& val) {
+  std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val));
+};
+
+constexpr void test_constraints() {
+  // Test constraints of the iterator/sentinel overload
+  // ==================================================
+  static_assert(HasIotaIter<int*, int*, int>);
+
+  // !input_or_output_iterator<O>
+  static_assert(!HasIotaIter<InputIteratorNotInputOrOutputIterator>);
+
+  // !sentinel_for<S, O>
+  static_assert(!HasIotaIter<int*, SentinelForNotSemiregular>);
+  static_assert(!HasIotaIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+  // !weakly_incrementable<T>
+  static_assert(!HasIotaIter<int*, int*, WeaklyIncrementableNotMovable>);
+
+  // !indirectly writable <O, T>
+  static_assert(!HasIotaIter<OutputIteratorNotIndirectlyWritable, int*, int>);
+
+  // Test constraints for the range overload
+  // =======================================
+  static_assert(HasIotaRange<UncheckedRange<int*>, int>);
+
+  // !weakly_incrementable<T>
+  static_assert(!HasIotaRange<UncheckedRange<int*>, WeaklyIncrementableNotMovable>);
+
+  // !ranges::output_range<const _Tp&>
+  static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>);
+}
 
 // This is pulled directly from the std::iota test
-template <class InIter>
-TEST_CONSTEXPR_CXX20 void test0() {
+template <class InOutIter, class Sent = InOutIter>
+constexpr void test0() {
   int ia[]         = {1, 2, 3, 4, 5};
   int ir[]         = {5, 6, 7, 8, 9};
   const unsigned s = sizeof(ia) / sizeof(ia[0]);
-  std::ranges::iota(InIter(ia), InIter(ia + s), 5);
-  for (unsigned i = 0; i < s; ++i)
-    assert(ia[i] == ir[i]);
+  std::ranges::iota(InOutIter(ia), InOutIter(ia + s), 5);
+  assert(std::ranges::equal(ia, ir));
 }
 
-TEST_CONSTEXPR_CXX20 bool test0() {
+constexpr bool test0() {
+  // TODO why don't these work?
+  // test0<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>();
+  // test0<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+  // test0<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>();
+  // test0<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>();
   test0<forward_iterator<int*> >();
   test0<bidirectional_iterator<int*> >();
   test0<random_access_iterator<int*> >();
+  test0<contiguous_iterator<int*>>();
   test0<int*>();
 
   return true;
 }
 
-TEST_CONSTEXPR_CXX20 void test1() {
-  std::array<int, 5> ia = {1, 2, 3, 4, 5};
-  std::array<int, 5> ir = {5, 6, 7, 8, 9};
-  std::ranges::iota(ia, 5);
-  for (unsigned i = 0; i < ir.size(); ++i)
-    assert(ia[i] == ir[i]);
+template <class Iter, class Sent, std::size_t N>
+constexpr void test_result(std::array<int, N> input, int starting_value, std::array<int, N> const expected) {
+  { // (iterator, sentinel) overload
+    auto in_begin = Iter(input.data());
+    auto in_end   = Sent(Iter(input.data() + input.size()));
+    std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result =
+        std::ranges::iota(std::move(in_begin), std::move(in_end), starting_value);
+    assert(result.out == in_end);
+    if constexpr (expected.size() > 0) {
+      assert(result.value == expected.back() + 1);
+    } else {
+      assert(result.value == starting_value);
+    }
+    assert(std::ranges::equal(input, expected));
+  }
+
+  { // (range) overload
+    auto in_begin = Iter(input.data());
+    auto in_end   = Sent(Iter(input.data() + input.size()));
+    auto range    = std::ranges::subrange(std::move(in_begin), std::move(in_end));
+
+    std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result =
+        std::ranges::iota(range, starting_value);
+    assert(result.out == in_end);
+    if constexpr (expected.size() > 0) {
+      assert(result.value == expected.back() + 1);
+    } else {
+      assert(result.value == starting_value);
+    }
+    assert(std::ranges::equal(input, expected));
+  }
+}
+
+constexpr bool test_results() {
+  using Iter = forward_iterator<int*>;
+  using Sent = sentinel_wrapper<Iter>;
+
+  // Empty
+  test_result<Iter, Sent, 0>({}, 0, {});
+  // 1-element sequence
+  test_result<Iter, Sent, 1>({1}, 0, {0});
+  // Longer sequence
+  test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
+
+  return true;
 }
 
 int main(int, char**) {
+  test_constraints();
+
   test0();
-  test1();
+  static_assert(test0());
+
+  test_results();
+  static_assert(test_results());
   return 0;
 }
\ No newline at end of file

>From a1d015c2affa40bb5f5313cdaed46c5a8cd4b243 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 7 Oct 2023 12:35:20 -0400
Subject: [PATCH 05/17] [libc++] Implement ranges::iota: Cleaning up tests for
 ranges::iota and adding testing for more iterators

---
 .../numeric.iota/ranges.iota.pass.cpp         | 53 ++++++-------------
 1 file changed, 17 insertions(+), 36 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 85f99942e97b2ac..611293debbf731b 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
@@ -59,31 +59,6 @@ constexpr void test_constraints() {
   static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>);
 }
 
-// This is pulled directly from the std::iota test
-template <class InOutIter, class Sent = InOutIter>
-constexpr void test0() {
-  int ia[]         = {1, 2, 3, 4, 5};
-  int ir[]         = {5, 6, 7, 8, 9};
-  const unsigned s = sizeof(ia) / sizeof(ia[0]);
-  std::ranges::iota(InOutIter(ia), InOutIter(ia + s), 5);
-  assert(std::ranges::equal(ia, ir));
-}
-
-constexpr bool test0() {
-  // TODO why don't these work?
-  // test0<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>();
-  // test0<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
-  // test0<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>();
-  // test0<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>();
-  test0<forward_iterator<int*> >();
-  test0<bidirectional_iterator<int*> >();
-  test0<random_access_iterator<int*> >();
-  test0<contiguous_iterator<int*>>();
-  test0<int*>();
-
-  return true;
-}
-
 template <class Iter, class Sent, std::size_t N>
 constexpr void test_result(std::array<int, N> input, int starting_value, std::array<int, N> const expected) {
   { // (iterator, sentinel) overload
@@ -100,7 +75,10 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar
     assert(std::ranges::equal(input, expected));
   }
 
-  { // (range) overload
+  // The range overload adds the additional constraint that it must be an outputrange
+  // so skip this for the input iterators we test
+  if constexpr (!std::is_same_v<Iter, cpp17_input_iterator<int*>> &&
+                !std::is_same_v<Iter, cpp20_input_iterator<int*>>) { // (range) overload
     auto in_begin = Iter(input.data());
     auto in_end   = Sent(Iter(input.data() + input.size()));
     auto range    = std::ranges::subrange(std::move(in_begin), std::move(in_end));
@@ -117,27 +95,30 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar
   }
 }
 
-constexpr bool test_results() {
-  using Iter = forward_iterator<int*>;
-  using Sent = sentinel_wrapper<Iter>;
-
+template <class Iter, class Sent = sentinel_wrapper<Iter>>
+constexpr void test_results() {
   // Empty
   test_result<Iter, Sent, 0>({}, 0, {});
   // 1-element sequence
   test_result<Iter, Sent, 1>({1}, 0, {0});
   // Longer sequence
   test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
+}
 
-  return true;
+void test_results() {
+  test_results<cpp17_input_iterator<int*>>();
+  test_results<cpp20_input_iterator<int*>>();
+  test_results<cpp17_output_iterator<int*>>();
+  test_results<cpp20_output_iterator<int*>>();
+  test_results<forward_iterator<int*>>();
+  test_results<bidirectional_iterator<int*>>();
+  test_results<random_access_iterator<int*>>();
+  test_results<contiguous_iterator<int*>>();
+  test_results<int*>();
 }
 
 int main(int, char**) {
   test_constraints();
-
-  test0();
-  static_assert(test0());
-
   test_results();
-  static_assert(test_results());
   return 0;
 }
\ No newline at end of file

>From ab4c67d3537379aab5117ac8dad232c5072991e9 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 7 Oct 2023 12:40:02 -0400
Subject: [PATCH 06/17] [libc++] Implement ranges::iota: Using clang-format-17
 to make CI checks happy

---
 .../numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp   | 5 ++---
 1 file changed, 2 insertions(+), 3 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 611293debbf731b..3a7bc4319bebd6e 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
@@ -26,9 +26,8 @@ concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) {
 };
 
 template <class Range, class Value = int>
-concept HasIotaRange = requires(Range&& range, Value&& val) {
-  std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val));
-};
+concept HasIotaRange =
+    requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); };
 
 constexpr void test_constraints() {
   // Test constraints of the iterator/sentinel overload

>From a455f42dbd6b97b8b6ccf41fad2ff9aae054fe0e Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Tue, 24 Oct 2023 20:39:52 -0400
Subject: [PATCH 07/17] [libc++] Implement ranges::iota: Adding helper function
 to implementation and updating docs.

---
 libcxx/docs/Status/RangesAlgorithms.csv       |  1 +
 libcxx/docs/Status/RangesMajorFeatures.csv    |  1 +
 libcxx/include/__numeric/ranges_iota.h        | 24 +++++++++++++++----
 .../out_value_result.pass.cpp                 |  2 +-
 .../numeric.iota/ranges.iota.pass.cpp         |  7 +++---
 5 files changed, 26 insertions(+), 9 deletions(-)

diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index 17c8953bf8d85db..e6dda790bf158d9 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -10,3 +10,4 @@ C++23,`shift_right <https://wg21.link/p2440r1>`_,Unassigned,No patch yet,Not sta
 C++23,`iota (algorithm) <https://wg21.link/p2440r1>`_,Unassigned,No patch yet,Not started
 C++23,`fold <https://wg21.link/p2322r5>`_,Unassigned,No patch yet,Not started
 C++23,`contains <https://wg21.link/p2302r2>`_,Zijun Zhao,No patch yet,In Progress
+C++23,`ranges::iota <https://wg21.link/P2440R1>`_, James E T Smith, `PR68494 <https://github.com/llvm/llvm-project/pull/68494>`_, In Progress
diff --git a/libcxx/docs/Status/RangesMajorFeatures.csv b/libcxx/docs/Status/RangesMajorFeatures.csv
index 259a0218ce15e32..5dd93a60fa7bd09 100644
--- a/libcxx/docs/Status/RangesMajorFeatures.csv
+++ b/libcxx/docs/Status/RangesMajorFeatures.csv
@@ -2,3 +2,4 @@ Standard,Name,Assignee,CL,Status
 C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete
 C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,Unassigned,No patch yet,Not started
 C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete
+C++23, `ranges::iota <https://wg21.link/P2440R1>`_, James E T Smith, `PR68494 <https://github.com/llvm/llvm-project/pull/68494>`_, In Progress
\ No newline at end of file
diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index a8c069832da7e6d..eca5712b3d0d4bd 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -14,6 +14,7 @@
 #include <__config>
 #include <__ranges/concepts.h>
 #include <__utility/as_const.h>
+#include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -26,10 +27,12 @@ namespace ranges {
 template <typename _Out, typename _Tp>
 using iota_result = ranges::out_value_result<_Out, _Tp>;
 
+namespace __ranges_iota {
 struct __iota_fn {
-  template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
-    requires indirectly_writable<_Out, const _Tp&>
-  constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
+private:
+  // Private helper function
+  template <class _Out, class _Sent, class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp> __iota_impl(_Out __first, _Sent __last, _Tp __value) {
     while (__first != __last) {
       *__first = std::as_const(__value);
       ++__first;
@@ -38,13 +41,24 @@ struct __iota_fn {
     return {std::move(__first), std::move(__value)};
   }
 
+public:
+  // Public facing interfaces
+  template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
+    requires indirectly_writable<_Out, const _Tp&>
+  constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
+    return __iota_impl(std::move(__first), std::move(__last), std::move(__value));
+  }
+
   template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range>
   constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const {
-    return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value));
+    return __iota_impl(ranges::begin(__r), ranges::end(__r), std::move(__value));
   }
 };
+} // namespace __ranges_iota
 
-inline constexpr __iota_fn iota{};
+inline namespace __cpo {
+inline constexpr auto iota = __ranges_iota::__iota_fn{};
+} // namespace __cpo
 } // namespace ranges
 
 #endif // _LIBCPP_STD_VER >= 23
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
index 3a930bb063f975f..e1d203929be738c 100644
--- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -121,4 +121,4 @@ int main(int, char**) {
   test();
   static_assert(test());
   return 0;
-}
\ No newline at end of file
+}
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 3a7bc4319bebd6e..7aedf7ee058445a 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
@@ -26,8 +26,9 @@ concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) {
 };
 
 template <class Range, class Value = int>
-concept HasIotaRange =
-    requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); };
+concept HasIotaRange = requires(Range&& range, Value&& val) {
+  std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val));
+};
 
 constexpr void test_constraints() {
   // Test constraints of the iterator/sentinel overload
@@ -120,4 +121,4 @@ int main(int, char**) {
   test_constraints();
   test_results();
   return 0;
-}
\ No newline at end of file
+}

>From 08e3c77d3b7395fc7e78156b693faa72879ca5a0 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Thu, 26 Oct 2023 16:53:08 -0400
Subject: [PATCH 08/17] [libc++] Implement ranges::iota: Adding tests to
 ranges_robust_against_* tests (proxy iterators is failing currently).

---
 libcxx/include/__numeric/ranges_iota.h        |  7 +--
 ...result_alias_declarations.compile.pass.cpp |  7 ++-
 .../ranges_robust_against_dangling.pass.cpp   | 43 +++++++++++--------
 ...es_robust_against_proxy_iterators.pass.cpp | 22 ++++++----
 4 files changed, 49 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index eca5712b3d0d4bd..9e55c1d1ea89257 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -44,13 +44,14 @@ struct __iota_fn {
 public:
   // Public facing interfaces
   template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
-    requires indirectly_writable<_Out, const _Tp&>
-  constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const {
+  requires indirectly_writable<_Out, const _Tp&> _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp>
+  operator()(_Out __first, _Sent __last, _Tp __value) {
     return __iota_impl(std::move(__first), std::move(__last), std::move(__value));
   }
 
   template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range>
-  constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const {
+  _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp>
+  operator()(_Range&& __r, _Tp __value) {
     return __iota_impl(ranges::begin(__r), ranges::end(__r), std::move(__value));
   }
 };
diff --git a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
index a72c3a374c504c2..6f82d8f8e719131 100644
--- a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp
@@ -13,9 +13,12 @@
 // ensure that all result alias declarations are defined
 
 #include <algorithm>
+#include <numeric>
 #include <memory>
 #include <type_traits>
 
+#include "test_macros.h"
+
 using namespace std::ranges;
 
 static_assert(std::is_same_v<in_fun_result<int, long>, for_each_result<int, long>>);
@@ -59,4 +62,6 @@ static_assert(std::is_same_v<min_max_result<int>, minmax_element_result<int>>);
 static_assert(std::is_same_v<in_found_result<int>, next_permutation_result<int>>);
 static_assert(std::is_same_v<in_found_result<int>, prev_permutation_result<int>>);
 
-// static_assert(std::is_same_v<out_value_result<int>, iota_result<int>>);
+#if TEST_STD_VER >= 23
+static_assert(std::is_same_v<out_value_result<int, int>, iota_result<int, int>>);
+#endif
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
index 1057c747990d68b..64db7933cd3d344 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -13,7 +13,7 @@
 // Range algorithms should return `std::ranges::dangling` when given a dangling range.
 
 #include <algorithm>
-
+#include <numeric>
 #include <array>
 #include <concepts>
 #include <functional>
@@ -23,6 +23,7 @@
 #include <utility>
 
 #include "test_iterators.h"
+#include "test_macros.h"
 
 struct NonBorrowedRange {
   using Iter = int*;
@@ -41,22 +42,22 @@ struct NonBorrowedRange {
 using R = NonBorrowedRange;
 
 // (dangling_in, ...)
-template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class ...Args>
-constexpr void dangling_1st(Func&& func, Input& in, Args&& ...args) {
+template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class... Args>
+constexpr void dangling_1st(Func&& func, Input& in, Args&&... args) {
   decltype(auto) result = func(R(in), std::forward<Args>(args)...);
   static_assert(std::same_as<decltype(result), ExpectedT>);
 }
 
 // (in, dangling_in, ...)
-template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class ...Args>
-constexpr void dangling_2nd(Func&& func, Input& in1, Input& in2, Args&& ...args) {
+template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class... Args>
+constexpr void dangling_2nd(Func&& func, Input& in1, Input& in2, Args&&... args) {
   decltype(auto) result = func(in1, R(in2), std::forward<Args>(args)...);
   static_assert(std::same_as<decltype(result), ExpectedT>);
 }
 
 // (dangling_in1, dangling_in2, ...)
-template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class ...Args>
-constexpr void dangling_both(Func&& func, Input& in1, Input& in2, Args&& ...args) {
+template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class... Args>
+constexpr void dangling_both(Func&& func, Input& in1, Input& in2, Args&&... args) {
   decltype(auto) result = func(R(in1), R(in2), std::forward<Args>(args)...);
   static_assert(std::same_as<decltype(result), ExpectedT>);
 }
@@ -68,23 +69,24 @@ constexpr bool test_all() {
   using std::ranges::dangling;
 
   using std::ranges::binary_transform_result;
-  using std::ranges::copy_result;
   using std::ranges::copy_backward_result;
   using std::ranges::copy_if_result;
+  using std::ranges::copy_result;
   using std::ranges::for_each_result;
   using std::ranges::merge_result;
   using std::ranges::minmax_result;
   using std::ranges::mismatch_result;
-  using std::ranges::move_result;
   using std::ranges::move_backward_result;
+  using std::ranges::move_result;
   using std::ranges::next_permutation_result;
+  using std::ranges::out_value_result;
   using std::ranges::partial_sort_copy_result;
   using std::ranges::partition_copy_result;
   using std::ranges::prev_permutation_result;
-  using std::ranges::remove_copy_result;
   using std::ranges::remove_copy_if_result;
-  using std::ranges::replace_copy_result;
+  using std::ranges::remove_copy_result;
   using std::ranges::replace_copy_if_result;
+  using std::ranges::replace_copy_result;
   using std::ranges::reverse_copy_result;
   using std::ranges::rotate_copy_result;
   using std::ranges::set_difference_result;
@@ -95,20 +97,20 @@ constexpr bool test_all() {
   using std::ranges::unary_transform_result;
   using std::ranges::unique_copy_result;
 
-  auto unary_pred = [](int i) { return i > 0; };
+  auto unary_pred  = [](int i) { return i > 0; };
   auto binary_pred = [](int i, int j) { return i < j; };
-  auto gen = [] { return 42; };
+  auto gen         = [] { return 42; };
 
-  std::array in = {1, 2, 3};
+  std::array in  = {1, 2, 3};
   std::array in2 = {4, 5, 6};
 
   auto mid = in.begin() + 1;
 
   std::array output = {7, 8, 9, 10, 11, 12};
-  auto out = output.begin();
-  auto out2 = output.begin() + 1;
+  auto out          = output.begin();
+  auto out2         = output.begin() + 1;
 
-  int x = 2;
+  int x             = 2;
   std::size_t count = 1;
 
   dangling_1st(std::ranges::find, in, x);
@@ -140,7 +142,8 @@ constexpr bool test_all() {
   dangling_1st(std::ranges::fill, in, x);
   { // transform
     std::array out_transform = {false, true, true};
-    dangling_1st<unary_transform_result<dangling, bool*>>(std::ranges::transform, in, out_transform.begin(), unary_pred);
+    dangling_1st<unary_transform_result<dangling, bool*>>(
+        std::ranges::transform, in, out_transform.begin(), unary_pred);
     dangling_1st<binary_transform_result<dangling, int*, bool*>>(
         std::ranges::transform, in, in2, out_transform.begin(), binary_pred);
     dangling_2nd<binary_transform_result<int*, dangling, bool*>>(
@@ -206,6 +209,10 @@ constexpr bool test_all() {
   dangling_1st<prev_permutation_result<dangling>>(std::ranges::prev_permutation, in);
   dangling_1st<next_permutation_result<dangling>>(std::ranges::next_permutation, in);
 
+#if TEST_STD_VER >= 23
+  dangling_1st< out_value_result<dangling, decltype(x)>>(std::ranges::iota, in, x);
+#endif
+
   return true;
 }
 
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
index 5c8aa0153a63c3f..638a6398883d25b 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
@@ -14,6 +14,7 @@
 // a customization point) rather than plain `swap` (which might not work with certain valid iterators).
 
 #include <algorithm>
+#include <numeric>
 
 #include <array>
 #include <concepts>
@@ -28,22 +29,22 @@
 #include "test_macros.h"
 
 // (in, ...)
-template <class Func, std::ranges::range Input, class ...Args>
-constexpr void test(Func&& func, Input& in, Args&& ...args) {
+template <class Func, std::ranges::range Input, class... Args>
+constexpr void test(Func&& func, Input& in, Args&&... args) {
   (void)func(in.begin(), in.end(), std::forward<Args>(args)...);
   (void)func(in, std::forward<Args>(args)...);
 }
 
 // (in1, in2, ...)
-template <class Func, std::ranges::range Range1, std::ranges::range Range2, class ...Args>
-constexpr void test(Func&& func, Range1& r1, Range2& r2, Args&& ...args) {
+template <class Func, std::ranges::range Range1, std::ranges::range Range2, class... Args>
+constexpr void test(Func&& func, Range1& r1, Range2& r2, Args&&... args) {
   (void)func(r1.begin(), r1.end(), r2.begin(), r2.end(), std::forward<Args>(args)...);
   (void)func(r1, r2, std::forward<Args>(args)...);
 }
 
 // (in, mid, ...)
-template <class Func, std::ranges::range Input, class ...Args>
-constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&& ...args) {
+template <class Func, std::ranges::range Input, class... Args>
+constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&&... args) {
   (void)func(in.begin(), mid, in.end(), std::forward<Args>(args)...);
   (void)func(in, mid, std::forward<Args>(args)...);
 }
@@ -68,9 +69,9 @@ constexpr void run_tests() {
   Proxy<T&> x{num};
   int count = 1;
 
-  auto unary_pred = [](const Proxy<T&>&) { return true; };
+  auto unary_pred  = [](const Proxy<T&>&) { return true; };
   auto binary_func = [](const Proxy<T>&, const Proxy<T>&) -> Proxy<T> { return Proxy<T>(T()); };
-  auto gen = [] { return Proxy<T>(T{42}); };
+  auto gen         = [] { return Proxy<T>(T{42}); };
 
   test(std::ranges::any_of, in, unary_pred);
   test(std::ranges::all_of, in, unary_pred);
@@ -100,6 +101,11 @@ constexpr void run_tests() {
   test(std::ranges::search, in, in2);
   test(std::ranges::search_n, in, count, x);
   test(std::ranges::find_end, in, in2);
+#if TEST_STD_VER >= 23
+  if constexpr (std::copyable<T>) {
+    test(std::ranges::iota, in, x);
+  }
+#endif
   test(std::ranges::is_partitioned, in, unary_pred);
   test(std::ranges::is_sorted, in);
   test(std::ranges::is_sorted_until, in);

>From 67fcac1fa4e47ac7231d4aa755596e01eb73036c Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Thu, 26 Oct 2023 17:23:38 -0400
Subject: [PATCH 09/17] [libc++] Implement ranges::iota: Fixing formatting
 problems

---
 libcxx/include/__numeric/ranges_iota.h                       | 4 ++--
 .../numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp   | 5 ++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index 9e55c1d1ea89257..fccb36396a057f7 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -44,8 +44,8 @@ struct __iota_fn {
 public:
   // Public facing interfaces
   template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
-  requires indirectly_writable<_Out, const _Tp&> _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp>
-  operator()(_Out __first, _Sent __last, _Tp __value) {
+    requires indirectly_writable<_Out, const _Tp&>
+  _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) {
     return __iota_impl(std::move(__first), std::move(__last), std::move(__value));
   }
 
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 7aedf7ee058445a..f3cd022943cafb8 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
@@ -26,9 +26,8 @@ concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) {
 };
 
 template <class Range, class Value = int>
-concept HasIotaRange = requires(Range&& range, Value&& val) {
-  std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val));
-};
+concept HasIotaRange =
+    requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); };
 
 constexpr void test_constraints() {
   // Test constraints of the iterator/sentinel overload

>From b13278430d0be6d4b4f14a8260b3dd12dbfa872e Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Thu, 26 Oct 2023 18:41:21 -0400
Subject: [PATCH 10/17] [libc++] Implement ranges::iota: Addressing comments
 about out_value_result tests from https://reviews.llvm.org/D121436

---
 libcxx/include/__algorithm/out_value_result.h |  4 +-
 .../no_unique_address.compile.pass.cpp        |  4 +
 .../out_value_result.pass.cpp                 | 80 +++++++++++--------
 3 files changed, 53 insertions(+), 35 deletions(-)

diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h
index a99d206f7035ff2..9e1e0e07286d46f 100644
--- a/libcxx/include/__algorithm/out_value_result.h
+++ b/libcxx/include/__algorithm/out_value_result.h
@@ -34,13 +34,13 @@ struct out_value_result {
 
   template <class _OutIter2, class _ValType2>
     requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
-  constexpr operator out_value_result<_OutIter2, _ValType2>() const& {
+  _LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_OutIter2, _ValType2>() const& {
     return {out, value};
   }
 
   template <class _OutIter2, class _ValType2>
     requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2>
-  constexpr operator out_value_result<_OutIter2, _ValType2>() && {
+  _LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_OutIter2, _ValType2>() && {
     return {std::move(out), std::move(value)};
   }
 };
diff --git a/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp
index 8e0a81959f27c81..c67b937590544a1 100644
--- a/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp
@@ -53,6 +53,10 @@ static_assert(sizeof(std::ranges::in_out_out_result<Empty, Empty, char>) == 2);
 static_assert(sizeof(std::ranges::in_out_out_result<int, Empty, Empty2>) == sizeof(int));
 static_assert(sizeof(std::ranges::in_out_out_result<Empty, Empty, Empty>) == 3);
 
+static_assert(sizeof(std::ranges::out_value_result<Empty, int>) == sizeof(int));
+static_assert(sizeof(std::ranges::out_value_result<int, Empty>) == sizeof(int));
+static_assert(sizeof(std::ranges::out_value_result<Empty, Empty>) == 2);
+
 // In min_max_result both elements have the same type, so they can't have the same address.
 // So the only way to test that [[no_unique_address]] is used is to have it in another struct
 struct MinMaxNoUniqueAddress {
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
index e1d203929be738c..99dfbd42050d178 100644
--- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -17,6 +17,8 @@
 
 #include "MoveOnly.h"
 
+using std::ranges::out_value_result;
+
 //
 // Helper structs
 //
@@ -43,62 +45,61 @@ struct ConvertibleFrom {
   T content;
 };
 
+// Standard layout classes can't have virtual functions
+struct NonStandardLayoutTypeBase {
+  virtual ~NonStandardLayoutTypeBase();
+};
+struct NonStandardLayoutType : public NonStandardLayoutTypeBase {};
+
 //
 constexpr void test_constraints() {
   // requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
-  static_assert(
-      std::is_constructible_v<std::ranges::out_value_result<int*, int>, std::ranges::out_value_result<int*, int>>);
+  static_assert(std::is_constructible_v<out_value_result<int*, int>, out_value_result<int*, int>>);
 
   // test failure when implicit conversion isn't allowed
-  static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeExplicit, int>,
-                                         std::ranges::out_value_result<int*, int>>);
+  static_assert(!std::is_constructible_v<out_value_result<IterTypeExplicit, int>, out_value_result<int*, int>>);
 
   // test success when implicit conversion is allowed, checking combinations of value, reference, and const
-  static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
-                                        std::ranges::out_value_result<int*, int>>);
-  static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
-                                        std::ranges::out_value_result<int*, int> const>);
-  static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
-                                        std::ranges::out_value_result<int*, int>&>);
-  static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>,
-                                        std::ranges::out_value_result<int*, int> const&>);
-
-  static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicitRef, int>,
-                                         std::ranges::out_value_result<int, int>&>);
+  static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int>>);
+  static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int> const>);
+  static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int>&>);
+  static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int> const&>);
+
+  static_assert(!std::is_constructible_v<out_value_result<IterTypeImplicitRef, int>, 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>>);
+  static_assert(std::is_convertible_v<out_value_result<int, int>&, out_value_result<long, long>>);
+  static_assert(std::is_convertible_v<const out_value_result<int, int>&, out_value_result<long, long>>);
+  static_assert(std::is_convertible_v<out_value_result<int, int>&&, out_value_result<long, long>>);
+  static_assert(std::is_convertible_v<const out_value_result<int, int>&&, 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>>);
+  static_assert(std::is_move_constructible_v<out_value_result<MoveOnly, int>>);
+  static_assert(std::is_move_constructible_v<out_value_result<int, MoveOnly>>);
 
   // 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, NotConvertible>>);
-  static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>,
-                                       std::ranges::out_value_result<NotConvertible, int>>);
+  static_assert(!std::is_convertible_v<out_value_result<NotConvertible, int>, out_value_result<int, NotConvertible>>);
+  static_assert(!std::is_convertible_v<out_value_result<int, NotConvertible>, out_value_result<NotConvertible, int>>);
+
+  // check standard layout
+  static_assert(std::is_standard_layout_v<out_value_result<int, int>>);
+  static_assert(!std::is_standard_layout_v<out_value_result<NonStandardLayoutType, int>>);
 }
 
 // Test results
 constexpr bool test() {
   {
-    std::ranges::out_value_result<double, int> res{10, 1};
+    // Check that conversion operator works
+    out_value_result<double, int> res{10, 1};
     assert(res.out == 10);
     assert(res.value == 1);
-    std::ranges::out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res;
+    out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res;
     assert(res2.out.content == 10);
     assert(res2.value.content == 1);
   }
   {
-    std::ranges::out_value_result<MoveOnly, int> res{MoveOnly{}, 10};
+    // Check that out_value_result isn't overconstrained w.r.t. move/copy constructors
+    out_value_result<MoveOnly, int> res{MoveOnly{}, 10};
     assert(res.out.get() == 1);
     assert(res.value == 10);
     auto res2 = std::move(res);
@@ -108,10 +109,23 @@ constexpr bool test() {
     assert(res2.value == 10);
   }
   {
-    auto [out, val] = std::ranges::out_value_result<int, int>{1, 2};
+    // Check structured binding
+    auto [out, val] = out_value_result<int, int>{1, 2};
     assert(out == 1);
     assert(val == 2);
   }
+  {
+    // Check default construction
+    out_value_result<int, double> res;
+    static_assert(std::is_same_v<int, decltype(res.out)>);
+    static_assert(std::is_same_v<double, decltype(res.value)>);
+  }
+  {
+    // Check aggregate initiazliation
+    out_value_result res = {1, 2};
+    assert(res.out == 1);
+    assert(res.value == 2);
+  }
 
   return true;
 }

>From 88e271a619f555d56f99f50c1e2b0172727914c7 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Fri, 27 Oct 2023 19:03:47 -0400
Subject: [PATCH 11/17] [libc++] Implement ranges::iota: Updating Proxy<T> in
 test_iterators.h to be weakly_incrementable for certain T (which we need to
 test ranges::iota). test_iterators.h is not formatted because a lot of the
 code outside of my changes would get reformatted and I don't want to
 overcomplicate the diff.

---
 libcxx/test/support/test_iterators.h | 56 +++++++++++++++++++++++++---
 1 file changed, 51 insertions(+), 5 deletions(-)

diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 1133b9597d09cfb..7559e347d90f596 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1083,6 +1083,31 @@ rvalue_iterator(T*) -> rvalue_iterator<T>;
 
 static_assert(std::random_access_iterator<rvalue_iterator<int*>>);
 
+// The ProxyDiffTBase allows us to conditionally specify Proxy<T>::difference_type
+// which we need in certain situations. For example when we want
+// std::weakly_incrementable<Proxy<T>> to be true.
+template <class T>
+struct ProxyDiffTBase {};
+
+template <class T>
+    requires requires { std::iter_difference_t<T>{}; }
+struct ProxyDiffTBase<T> {
+    using difference_type = std::iter_difference_t<T>;
+};
+
+// These concepts allow us to conditionally add the pre-/postfix operators
+// when T also supports those member functions. Like ProxyDiffTBase, this 
+// is necessary when we want std::weakly_incrementable<Proxy<T>> to be true.
+template <class T>
+concept HasPreIncrementOp = requires(T const& obj) {
+    ++obj;
+};
+
+template <class T>
+concept HasPostIncrementOp = requires(T const& obj) {
+    obj++;
+};
+
 // Proxy
 // ======================================================================
 // Proxy that can wrap a value or a reference. It simulates C++23's tuple
@@ -1093,6 +1118,7 @@ static_assert(std::random_access_iterator<rvalue_iterator<int*>>);
 // This class is useful for testing that if algorithms support proxy iterator
 // properly, i.e. calling ranges::iter_swap and ranges::iter_move instead of
 // plain swap and std::move.
+
 template <class T>
 struct Proxy;
 
@@ -1103,7 +1129,7 @@ template <class T>
 inline constexpr bool IsProxy<Proxy<T>> = true;
 
 template <class T>
-struct Proxy {
+struct Proxy : ProxyDiffTBase<T> {
   T data;
 
   constexpr T& getData() & { return data; }
@@ -1149,9 +1175,11 @@ struct Proxy {
   // Calling swap(Proxy<T>{}, Proxy<T>{}) would fail (pass prvalues)
 
   // Compare operators are defined for the convenience of the tests
-  friend constexpr bool operator==(const Proxy&, const Proxy&)
+  friend constexpr bool operator==(const Proxy& lhs, const Proxy& rhs)
     requires (std::equality_comparable<T> && !std::is_reference_v<T>)
-  = default;
+  {
+    return lhs.data == rhs.data;
+  };
 
   // Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default equality comparison operator is deleted
   // when `T` is a reference type.
@@ -1161,9 +1189,11 @@ struct Proxy {
     return lhs.data == rhs.data;
   }
 
-  friend constexpr auto operator<=>(const Proxy&, const Proxy&)
+  friend constexpr auto operator<=>(const Proxy& lhs, const Proxy& rhs)
     requires (std::three_way_comparable<T> && !std::is_reference_v<T>)
-  = default;
+  {
+    return lhs.data <=> rhs.data;
+  };
 
   // Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default 3-way comparison operator is deleted when
   // `T` is a reference type.
@@ -1172,6 +1202,22 @@ struct Proxy {
     requires std::three_way_comparable_with<std::decay_t<T>, std::decay_t<U>> {
     return lhs.data <=> rhs.data;
   }
+
+  // Needed to allow certain types to be weakly_incremental
+  constexpr Proxy& operator++()
+      requires(HasPreIncrementOp<T>)
+  {
+      ++data;
+      return *this;
+  }
+
+  constexpr Proxy operator++(int)
+      requires(HasPostIncrementOp<T>)
+  {
+      Proxy tmp = *this;
+      operator++();
+      return tmp;
+  }
 };
 
 // This is to make ProxyIterator model `std::indirectly_readable`

>From ff5bf791be9d650495151edd7c126b0827722e79 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 28 Oct 2023 11:44:15 -0400
Subject: [PATCH 12/17] [libc++] Implement ranges::iota: Fixing some of the
 buildkite errors, modules are still not working locally

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

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
index 99dfbd42050d178..f935fc54feb64fd 100644
--- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -122,7 +122,7 @@ constexpr bool test() {
   }
   {
     // Check aggregate initiazliation
-    out_value_result res = {1, 2};
+    out_value_result<int, int> res = {1, 2};
     assert(res.out == 1);
     assert(res.value == 2);
   }
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 60004b06c5ff528..75ab121a2416130 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
@@ -180,17 +180,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
 
 #elif TEST_STD_VER > 23
@@ -229,17 +223,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
 
 #endif // TEST_STD_VER > 23
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 ed86c555221c542..56c294683538f7f 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
@@ -4972,17 +4972,11 @@
 #   error "__cpp_lib_ranges_chunk_by should have the value 202202L 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)
@@ -6507,17 +6501,11 @@
 #   error "__cpp_lib_ranges_chunk_by should have the value 202202L 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)

>From b8f07b8fcb63cf88320734e8c7c2d98fcbc0e800 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Mon, 30 Oct 2023 11:23:39 -0400
Subject: [PATCH 13/17] [libc++] Implement ranges::iota: Explicitly adding
 #include for ranges::borrowed_iterator_t which was missing from module
 builds. Not sure why this hasn't caused problems before

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

diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index fccb36396a057f7..914d67a6d443b2d 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -13,6 +13,7 @@
 #include <__algorithm/out_value_result.h>
 #include <__config>
 #include <__ranges/concepts.h>
+#include <__ranges/dangling.h>
 #include <__utility/as_const.h>
 #include <__utility/move.h>
 

>From a4d34fd33830c1302200b87e6776299881957754 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Mon, 30 Oct 2023 12:04:02 -0400
Subject: [PATCH 14/17] [libc++] Implement ranges::iota: Explicitly adding
 #include for ranges::begin because the module builds are complaining that
 it's missing

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

diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index 914d67a6d443b2d..3b92d4e94554ae1 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -12,6 +12,7 @@
 
 #include <__algorithm/out_value_result.h>
 #include <__config>
+#include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__ranges/dangling.h>
 #include <__utility/as_const.h>

>From 815324428d4eaeebffa369ee881333be010488e9 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Mon, 30 Oct 2023 20:59:36 -0400
Subject: [PATCH 15/17] [libc++] Implement ranges::iota: Adding #ifdef
 _LIBCPP_STD_VER >= 23 checks around out_value_result tests.

---
 libcxx/modules/std/algorithm.inc                               | 2 ++
 .../algorithms.results/no_unique_address.compile.pass.cpp      | 3 ++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc
index 62ce8cf02c14f95..3ee048e3f8170f8 100644
--- a/libcxx/modules/std/algorithm.inc
+++ b/libcxx/modules/std/algorithm.inc
@@ -18,7 +18,9 @@ export namespace std {
     using std::ranges::in_out_result;
     // using std::ranges::in_value_result;
     using std::ranges::min_max_result;
+#if _LIBCPP_STD_VER >= 23
     using std::ranges::out_value_result;
+#endif
   } // namespace ranges
 
   // [alg.nonmodifying], non-modifying sequence operations
diff --git a/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp
index c67b937590544a1..83a9e4c7da9028f 100644
--- a/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp
+++ b/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp
@@ -53,10 +53,11 @@ static_assert(sizeof(std::ranges::in_out_out_result<Empty, Empty, char>) == 2);
 static_assert(sizeof(std::ranges::in_out_out_result<int, Empty, Empty2>) == sizeof(int));
 static_assert(sizeof(std::ranges::in_out_out_result<Empty, Empty, Empty>) == 3);
 
+#if _LIBCPP_STD_VER >= 23
 static_assert(sizeof(std::ranges::out_value_result<Empty, int>) == sizeof(int));
 static_assert(sizeof(std::ranges::out_value_result<int, Empty>) == sizeof(int));
 static_assert(sizeof(std::ranges::out_value_result<Empty, Empty>) == 2);
-
+#endif
 // In min_max_result both elements have the same type, so they can't have the same address.
 // So the only way to test that [[no_unique_address]] is used is to have it in another struct
 struct MinMaxNoUniqueAddress {

>From a872e3978d4ac876739dbf2e3abcd55668e49fd6 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 4 Nov 2023 18:00:58 -0400
Subject: [PATCH 16/17] [libc++] Implement ranges::iota: Missing a few more #if
 TEST_STD_VER >= 23 statments in tests (several c++20 tests were failing on
 buildkite)

---
 .../std/algorithms/algorithms.results/out_value_result.pass.cpp | 2 +-
 .../test/std/algorithms/ranges_robust_against_dangling.pass.cpp | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

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
index f935fc54feb64fd..bc8c665950f43ae 100644
--- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
+++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, C++20
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 //  template <class O, class T>
 //    struct out_value_result;
diff --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
index 64db7933cd3d344..05c598da24163b5 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -79,7 +79,9 @@ constexpr bool test_all() {
   using std::ranges::move_backward_result;
   using std::ranges::move_result;
   using std::ranges::next_permutation_result;
+#if TEST_STD_VER >= 23
   using std::ranges::out_value_result;
+#endif
   using std::ranges::partial_sort_copy_result;
   using std::ranges::partition_copy_result;
   using std::ranges::prev_permutation_result;

>From b84859b6a3e5f15c541734da313704eed3a97c69 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sun, 5 Nov 2023 07:38:22 -0500
Subject: [PATCH 17/17] [libc++] Implement ranges::iota: Removing several of
 the ranges_robust_against_* files from ignore_format.txt.

---
 libcxx/utils/data/ignore_format.txt | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/utils/data/ignore_format.txt b/libcxx/utils/data/ignore_format.txt
index 1eb3bf5d5e86751..cdc9d667a332be0 100644
--- a/libcxx/utils/data/ignore_format.txt
+++ b/libcxx/utils/data/ignore_format.txt
@@ -1288,9 +1288,7 @@ libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp
 libcxx/test/std/algorithms/alg.sorting/alg.three.way/lexicographical_compare_three_way_comp.pass.cpp
 libcxx/test/std/algorithms/alg.sorting/alg.three.way/lexicographical_compare_three_way.pass.cpp
 libcxx/test/std/algorithms/alg.sorting/sortable_helpers.h
-libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
 libcxx/test/std/algorithms/ranges_robust_against_differing_projections.pass.cpp
-libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
 libcxx/test/std/algorithms/robust_against_adl.compile.pass.cpp
 libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp
 libcxx/test/std/algorithms/robust_against_proxy_iterators_lifetime_bugs.pass.cpp



More information about the libcxx-commits mailing list