[libcxx-commits] [libcxx] [libc++] Implement ranges::iota (PR #68494)
James E T Smith via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jul 24 09:18:55 PDT 2024
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/30] [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 2ec755236dbae..c6eb03f1d68e9 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 0000000000000..8baffec7b9ef4
--- /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 0000000000000..20311a68c2a34
--- /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 69ba9537dda69..42f01a99a02cc 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 3fcf6cefdb4b8..363dfd229df54 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 e5a995366a7aa..164c60dfd6af6 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 0000000000000..189149b0b4acb
--- /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 0000000000000..1e2a6970adb35
--- /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/30] [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 e1b4172b22c53..ebb18dc7bed9b 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 6d9bb8653fcb5..b092cc7c0e93b 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 d2b7688d4e5f1..9204e8d871593 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 ac342aff0beb7..9fd50d66edbe3 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/30] [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 8baffec7b9ef4..a99d206f7035f 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/30] [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 9cb49fd5176ea..4618908cfb739 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 20311a68c2a34..a8c069832da7e 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 b7900d15c10c2..62ce8cf02c14f 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 189149b0b4acb..3a930bb063f97 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 1e2a6970adb35..85f99942e97b2 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/30] [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 85f99942e97b2..611293debbf73 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/30] [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 611293debbf73..3a7bc4319bebd 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/30] [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 17c8953bf8d85..e6dda790bf158 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 259a0218ce15e..5dd93a60fa7bd 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 a8c069832da7e..eca5712b3d0d4 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 3a930bb063f97..e1d203929be73 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 3a7bc4319bebd..7aedf7ee05844 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/30] [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 eca5712b3d0d4..9e55c1d1ea892 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 a72c3a374c504..6f82d8f8e7191 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 1057c747990d6..64db7933cd3d3 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 5c8aa0153a63c..638a6398883d2 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/30] [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 9e55c1d1ea892..fccb36396a057 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 7aedf7ee05844..f3cd022943caf 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/30] [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 a99d206f7035f..9e1e0e07286d4 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 8e0a81959f27c..c67b937590544 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 e1d203929be73..99dfbd42050d1 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/30] [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 1133b9597d09c..7559e347d90f5 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/30] [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 99dfbd42050d1..f935fc54feb64 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 60004b06c5ff5..75ab121a24161 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 ed86c555221c5..56c294683538f 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/30] [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 fccb36396a057..914d67a6d443b 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/30] [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 914d67a6d443b..3b92d4e94554a 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/30] [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 62ce8cf02c14f..3ee048e3f8170 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 c67b937590544..83a9e4c7da902 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/30] [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 f935fc54feb64..bc8c665950f43 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 64db7933cd3d3..05c598da24163 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/30] [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 1eb3bf5d5e867..cdc9d667a332b 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
>From 72125eaf8f7e1bd728013c5189948339efb7e4d6 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Mon, 18 Dec 2023 15:53:41 -0500
Subject: [PATCH 18/30] [libc++] Implement ranges::iota: Updating with upstream
and adding std::out_value_result to modulemap
---
libcxx/include/module.modulemap.in | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 5113e2f30470f..e69eb02bce97a 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -713,6 +713,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" }
@@ -1580,7 +1581,10 @@ 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_ranges_iota [system] {
+ header "__numeric/ranges_iota.h"
+ export std_private_algorithm_out_value_result
+}
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" }
>From 028ce92e61f1e6cbcf72db2d51e32072055c2738 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Tue, 19 Dec 2023 16:36:00 -0500
Subject: [PATCH 19/30] [libc++] Implement ranges::iota: Fixing formatting
problems with test_iterators.h
---
libcxx/test/support/test_iterators.h | 32 ++++++++++++----------------
1 file changed, 14 insertions(+), 18 deletions(-)
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index f49c90f4141e6..6bae0a4c9def4 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1090,23 +1090,19 @@ template <class T>
struct ProxyDiffTBase {};
template <class T>
- requires requires { std::iter_difference_t<T>{}; }
+ requires requires { std::iter_difference_t<T>{}; }
struct ProxyDiffTBase<T> {
- using difference_type = std::iter_difference_t<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
+// 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;
-};
+concept HasPreIncrementOp = requires(T const& obj) { ++obj; };
template <class T>
-concept HasPostIncrementOp = requires(T const& obj) {
- obj++;
-};
+concept HasPostIncrementOp = requires(T const& obj) { obj++; };
// Proxy
// ======================================================================
@@ -1176,7 +1172,7 @@ struct Proxy : ProxyDiffTBase<T> {
// Compare operators are defined for the convenience of the tests
friend constexpr bool operator==(const Proxy& lhs, const Proxy& rhs)
- requires (std::equality_comparable<T> && !std::is_reference_v<T>)
+ requires(std::equality_comparable<T> && !std::is_reference_v<T>)
{
return lhs.data == rhs.data;
};
@@ -1190,7 +1186,7 @@ struct Proxy : ProxyDiffTBase<T> {
}
friend constexpr auto operator<=>(const Proxy& lhs, const Proxy& rhs)
- requires (std::three_way_comparable<T> && !std::is_reference_v<T>)
+ requires(std::three_way_comparable<T> && !std::is_reference_v<T>)
{
return lhs.data <=> rhs.data;
};
@@ -1205,18 +1201,18 @@ struct Proxy : ProxyDiffTBase<T> {
// Needed to allow certain types to be weakly_incremental
constexpr Proxy& operator++()
- requires(HasPreIncrementOp<T>)
+ requires(HasPreIncrementOp<T>)
{
- ++data;
- return *this;
+ ++data;
+ return *this;
}
constexpr Proxy operator++(int)
- requires(HasPostIncrementOp<T>)
+ requires(HasPostIncrementOp<T>)
{
- Proxy tmp = *this;
- operator++();
- return tmp;
+ Proxy tmp = *this;
+ operator++();
+ return tmp;
}
};
>From b3f260ec9db7cd7f61ecd694c73253308bc3c7e8 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Fri, 5 Jan 2024 16:31:00 -0500
Subject: [PATCH 20/30] [libc++] Implement ranges::iota: Improving ranges::iota
tests and docs to address review comments
---
libcxx/docs/Status/Cxx23.rst | 1 +
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
.../numeric.iota/ranges.iota.pass.cpp | 134 ++++++++++++------
3 files changed, 93 insertions(+), 44 deletions(-)
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index 3e6e33f08c7cc..c576f1809219e 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -49,6 +49,7 @@ Paper Status
.. [#note-P2693R1] P2693R1: The formatter for ``std::thread::id`` is implemented.
The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is
not implemented yet.
+ .. [#note-P2400R1] P2400R1: ranges::shift_left and ranges::shift_right have not been implemented yet.
.. _issues-status-cxx23:
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 67e95ae44dd51..db0ba4dbd3b9b 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","|In progress|","","|ranges|"
+"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|In progress| [#note-P2400R1]_","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|In Progress|","","|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/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 f3cd022943caf..80ba3493c5a7d 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
@@ -14,11 +14,16 @@
#include <numeric>
#include <algorithm>
#include <array>
+#include <iostream> // TODO RM
#include "test_macros.h"
#include "test_iterators.h"
#include "almost_satisfies_types.h"
+//
+// Testing constraints
+//
+
// 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) {
@@ -29,41 +34,84 @@ 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>);
+// Test constraints of the iterator/sentinel overload
+// ==================================================
+static_assert(HasIotaIter<int*, int*, int>);
- // !input_or_output_iterator<O>
- static_assert(!HasIotaIter<InputIteratorNotInputOrOutputIterator>);
+// !input_or_output_iterator<O>
+static_assert(!HasIotaIter<InputIteratorNotInputOrOutputIterator>);
- // !sentinel_for<S, O>
- static_assert(!HasIotaIter<int*, SentinelForNotSemiregular>);
- static_assert(!HasIotaIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+// !sentinel_for<S, O>
+static_assert(!HasIotaIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasIotaIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
- // !weakly_incrementable<T>
- static_assert(!HasIotaIter<int*, int*, WeaklyIncrementableNotMovable>);
+// !weakly_incrementable<T>
+static_assert(!HasIotaIter<int*, int*, WeaklyIncrementableNotMovable>);
- // !indirectly writable <O, T>
- static_assert(!HasIotaIter<OutputIteratorNotIndirectlyWritable, int*, int>);
+// !indirectly writable <O, T>
+static_assert(!HasIotaIter<OutputIteratorNotIndirectlyWritable, int*, int>);
- // Test constraints for the range overload
- // =======================================
- static_assert(HasIotaRange<UncheckedRange<int*>, int>);
+// Test constraints for the range overload
+// =======================================
+static_assert(HasIotaRange<UncheckedRange<int*>, int>);
- // !weakly_incrementable<T>
- static_assert(!HasIotaRange<UncheckedRange<int*>, WeaklyIncrementableNotMovable>);
+// !weakly_incrementable<T>
+static_assert(!HasIotaRange<UncheckedRange<int*>, WeaklyIncrementableNotMovable>);
- // !ranges::output_range<const _Tp&>
- static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>);
-}
+// !ranges::output_range<const _Tp&>
+static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>);
+
+//
+// Testing results
+//
+
+struct DangerousCopyAssign {
+ int val;
+ using difference_type = int;
+
+ constexpr explicit DangerousCopyAssign(int v) : val(v) {}
+
+ // Needed in postfix
+ constexpr DangerousCopyAssign(DangerousCopyAssign const& other) { this->val = other.val; }
+
+ // mischievious copy assignment that we won't use if the
+ // std::as_const inside ranges::iota isn't working, this should perturb the
+ // results
+ constexpr DangerousCopyAssign& operator=(DangerousCopyAssign& a) {
+ ++a.val;
+ this->val = a.val;
+ return *this;
+ }
+
+ // safe copy assignment std::as_const inside ranges::iota should ensure this
+ // overload gets called
+ constexpr DangerousCopyAssign& operator=(DangerousCopyAssign const& a) {
+ this->val = a.val;
+ return *this;
+ }
-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) {
+ constexpr bool operator==(DangerousCopyAssign const& rhs) { return this->val == rhs.val; }
+
+ // prefix
+ constexpr DangerousCopyAssign& operator++() {
+ ++(this->val);
+ return *this;
+ }
+
+ // postfix
+ constexpr DangerousCopyAssign operator++(int) {
+ auto tmp = *this;
+ ++this->val;
+ return tmp;
+ }
+};
+
+template <class T, class Iter, class Sent, std::size_t N>
+constexpr void test_result(std::array<T, N> input, T starting_value, std::array<T, 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::same_as<std::ranges::out_value_result<Iter, T>> 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) {
@@ -76,20 +124,16 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar
// 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
+ if constexpr (!std::is_same_v<Iter, cpp17_input_iterator<T*>> &&
+ !std::is_same_v<Iter, cpp20_input_iterator<T*>>) { // (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::same_as<std::ranges::out_value_result<Iter, T>> 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(result.value == starting_value + N);
assert(std::ranges::equal(input, expected));
}
}
@@ -97,27 +141,31 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar
template <class Iter, class Sent = sentinel_wrapper<Iter>>
constexpr void test_results() {
// Empty
- test_result<Iter, Sent, 0>({}, 0, {});
+ test_result<int, Iter, Sent, 0>({}, 0, {});
// 1-element sequence
- test_result<Iter, Sent, 1>({1}, 0, {0});
+ test_result<int, Iter, Sent, 1>({1}, 0, {0});
// Longer sequence
- test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
+ test_result<int, Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
+}
+
+constexpr void test_DangerousCopyAssign() {
+ using A = DangerousCopyAssign;
+ using Iter = contiguous_iterator<A*>;
+ std::array<A, 3> aa = {A{1}, A{2}, A{3}};
+ std::array<A, 3> expected = {A{0}, A{1}, A{2}};
+ std::ranges::iota(aa, A{0});
+ auto proj_val = [](DangerousCopyAssign const& el) { return el.val; };
+ assert(std::ranges::equal(aa, expected, std::ranges::equal_to{}, proj_val, proj_val));
}
void test_results() {
- test_results<cpp17_input_iterator<int*>>();
- test_results<cpp20_input_iterator<int*>>();
+ types::for_each(types::cpp20_input_iterator_list<int*>{}, []<class Iter> { test_results< Iter>(); });
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*>();
+ test_DangerousCopyAssign();
}
int main(int, char**) {
- test_constraints();
test_results();
return 0;
}
>From be7faa6fb11c09822a9a855c7cf1aecab4f4c773 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Sat, 6 Jan 2024 14:41:37 -0500
Subject: [PATCH 21/30] [libc++] Implement ranges::iota: Removing unnecessary
namespaces in ranges::iota impl and cleaning pu small other comments.
---
libcxx/include/__numeric/ranges_iota.h | 6 +--
.../ranges_robust_against_dangling.pass.cpp | 47 +++++++++----------
.../numeric.iota/ranges.iota.pass.cpp | 47 +++++++++----------
3 files changed, 46 insertions(+), 54 deletions(-)
diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index 3b92d4e94554a..750964ff77a4c 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -29,7 +29,6 @@ namespace ranges {
template <typename _Out, typename _Tp>
using iota_result = ranges::out_value_result<_Out, _Tp>;
-namespace __ranges_iota {
struct __iota_fn {
private:
// Private helper function
@@ -57,11 +56,8 @@ struct __iota_fn {
return __iota_impl(ranges::begin(__r), ranges::end(__r), std::move(__value));
}
};
-} // namespace __ranges_iota
-inline namespace __cpo {
-inline constexpr auto iota = __ranges_iota::__iota_fn{};
-} // namespace __cpo
+inline constexpr auto iota = __iota_fn{};
} // namespace ranges
#endif // _LIBCPP_STD_VER >= 23
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 d91d4f83e5a0d..79631af03b218 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -13,11 +13,12 @@
// Range algorithms should return `std::ranges::dangling` when given a dangling range.
#include <algorithm>
-#include <numeric>
+
#include <array>
#include <concepts>
#include <functional>
#include <iterator>
+#include <numeric>
#include <ranges>
#include <random>
#include <utility>
@@ -42,22 +43,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>);
}
@@ -69,15 +70,15 @@ 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_backward_result;
using std::ranges::move_result;
+ using std::ranges::move_backward_result;
using std::ranges::next_permutation_result;
#if TEST_STD_VER >= 23
using std::ranges::out_value_result;
@@ -85,10 +86,10 @@ constexpr bool test_all() {
using std::ranges::partial_sort_copy_result;
using std::ranges::partition_copy_result;
using std::ranges::prev_permutation_result;
- using std::ranges::remove_copy_if_result;
using std::ranges::remove_copy_result;
- using std::ranges::replace_copy_if_result;
+ using std::ranges::remove_copy_if_result;
using std::ranges::replace_copy_result;
+ using std::ranges::replace_copy_if_result;
using std::ranges::reverse_copy_result;
using std::ranges::rotate_copy_result;
using std::ranges::set_difference_result;
@@ -98,23 +99,23 @@ constexpr bool test_all() {
using std::ranges::swap_ranges_result;
using std::ranges::unary_transform_result;
using std::ranges::unique_copy_result;
- using InIter = std::array<int, 3>::iterator;
+ using InIter = std::array<int, 3>::iterator;
using OutIter = std::array<int, 6>::iterator;
- 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.data() + 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);
@@ -145,11 +146,10 @@ constexpr bool test_all() {
dangling_1st<move_backward_result<dangling, OutIter>>(std::ranges::move_backward, in, output.end());
dangling_1st(std::ranges::fill, in, x);
{ // transform
- using OutTransformIter = std::array<bool, 3>::iterator;
+ using OutTransformIter = std::array<bool, 3>::iterator;
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<binary_transform_result<dangling, int*, bool*>>(
+ dangling_1st<unary_transform_result<dangling, OutTransformIter>>(std::ranges::transform, in, out_transform.begin(), unary_pred);
+ dangling_1st<binary_transform_result<dangling, InIter, OutTransformIter>>(
std::ranges::transform, in, in2, out_transform.begin(), binary_pred);
dangling_2nd<binary_transform_result<InIter, dangling, OutTransformIter>>(
std::ranges::transform, in, in2, out_transform.begin(), binary_pred);
@@ -169,8 +169,7 @@ constexpr bool test_all() {
dangling_1st<reverse_copy_result<dangling, OutIter>>(std::ranges::reverse_copy, in, out);
dangling_1st<rotate_copy_result<dangling, OutIter>>(std::ranges::rotate_copy, in, mid, out);
dangling_1st<unique_copy_result<dangling, OutIter>>(std::ranges::unique_copy, in, out);
- dangling_1st<partition_copy_result<dangling, OutIter, OutIter>>(
- std::ranges::partition_copy, in, out, out2, unary_pred);
+ dangling_1st<partition_copy_result<dangling, OutIter, OutIter>>(std::ranges::partition_copy, in, out, out2, unary_pred);
dangling_1st<partial_sort_copy_result<dangling, InIter>>(std::ranges::partial_sort_copy, in, in2);
dangling_2nd<partial_sort_copy_result<InIter, dangling>>(std::ranges::partial_sort_copy, in, in2);
dangling_both<partial_sort_copy_result<dangling, dangling>>(std::ranges::partial_sort_copy, in, in2);
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 80ba3493c5a7d..c24d77c347470 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
@@ -14,7 +14,6 @@
#include <numeric>
#include <algorithm>
#include <array>
-#include <iostream> // TODO RM
#include "test_macros.h"
#include "test_iterators.h"
@@ -106,46 +105,44 @@ struct DangerousCopyAssign {
}
};
-template <class T, class Iter, class Sent, std::size_t N>
-constexpr void test_result(std::array<T, N> input, T starting_value, std::array<T, N> const expected) {
+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, T>> decltype(auto) result =
+ 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(result.value == starting_value + static_cast<int>(N));
assert(std::ranges::equal(input, expected));
}
- // 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<T*>> &&
- !std::is_same_v<Iter, cpp20_input_iterator<T*>>) { // (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, T>> decltype(auto) result =
- std::ranges::iota(range, starting_value);
- assert(result.out == in_end);
- assert(result.value == starting_value + N);
- assert(std::ranges::equal(input, expected));
+ { // (range) overload
+ // inthe 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*>>) {
+ 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);
+ assert(result.value == starting_value + static_cast<int>(N));
+ assert(std::ranges::equal(input, expected));
+ }
}
}
template <class Iter, class Sent = sentinel_wrapper<Iter>>
constexpr void test_results() {
// Empty
- test_result<int, Iter, Sent, 0>({}, 0, {});
+ test_result<Iter, Sent, 0>({}, 0, {});
// 1-element sequence
- test_result<int, Iter, Sent, 1>({1}, 0, {0});
+ test_result<Iter, Sent, 1>({1}, 0, {0});
// Longer sequence
- test_result<int, Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
+ test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
}
constexpr void test_DangerousCopyAssign() {
>From 20b848b566499e7dbd0c1557a716a35ddce35170 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Mon, 15 Jan 2024 12:28:09 -0500
Subject: [PATCH 22/30] [libc++] Implement ranges::iota: Refactoring some of
Proxy's member functions inside test_iterators.h. Some clean-up on
ranges.iota.pass.cpp too.
---
.../numeric.iota/ranges.iota.pass.cpp | 21 ++++------
libcxx/test/support/test_iterators.h | 41 +++++++++----------
2 files changed, 29 insertions(+), 33 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 c24d77c347470..77d215e5eb4e3 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
@@ -120,18 +120,15 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar
{ // (range) overload
// inthe 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*>>) {
- 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);
- assert(result.value == starting_value + static_cast<int>(N));
- assert(std::ranges::equal(input, expected));
- }
+ 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);
+ assert(result.value == starting_value + static_cast<int>(N));
+ assert(std::ranges::equal(input, expected));
}
}
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 6bae0a4c9def4..e40352873954b 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -650,6 +650,7 @@ class cpp20_input_iterator
constexpr decltype(auto) operator*() const { return *it_; }
constexpr cpp20_input_iterator& operator++() { ++it_; return *this; }
constexpr void operator++(int) { ++it_; }
+ constexpr cpp20_input_iterator operator++(int) requires(std::incrementable<It>) { auto tmp = *this; ++it_; return tmp;}
friend constexpr It base(const cpp20_input_iterator& i) { return i.it_; }
@@ -1087,23 +1088,19 @@ static_assert(std::random_access_iterator<rvalue_iterator<int*>>);
// which we need in certain situations. For example when we want
// std::weakly_incrementable<Proxy<T>> to be true.
template <class T>
-struct ProxyDiffTBase {};
+struct ProxyDiffTBase {
+ // Add default `operator<=>` so that the derived type, Proxy, can also use the default `operator<=>`
+ friend constexpr auto operator<=>(const ProxyDiffTBase&, const ProxyDiffTBase&) = default;
+};
template <class T>
requires requires { std::iter_difference_t<T>{}; }
struct ProxyDiffTBase<T> {
using difference_type = std::iter_difference_t<T>;
+ // Add default `operator<=>` so that the derived type, Proxy, can also use the default `operator<=>`
+ friend constexpr auto operator<=>(const ProxyDiffTBase&, const ProxyDiffTBase&) = default;
};
-// 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
@@ -1172,10 +1169,7 @@ struct Proxy : ProxyDiffTBase<T> {
// Compare operators are defined for the convenience of the tests
friend constexpr bool operator==(const Proxy& lhs, const Proxy& rhs)
- requires(std::equality_comparable<T> && !std::is_reference_v<T>)
- {
- return lhs.data == rhs.data;
- };
+ requires(std::equality_comparable<T> && !std::is_reference_v<T>) = default;
// Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default equality comparison operator is deleted
// when `T` is a reference type.
@@ -1186,10 +1180,7 @@ struct Proxy : ProxyDiffTBase<T> {
}
friend constexpr auto operator<=>(const Proxy& lhs, const Proxy& rhs)
- requires(std::three_way_comparable<T> && !std::is_reference_v<T>)
- {
- return lhs.data <=> rhs.data;
- };
+ requires(std::three_way_comparable<T> && !std::is_reference_v<T>) = default;
// 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.
@@ -1199,16 +1190,24 @@ struct Proxy : ProxyDiffTBase<T> {
return lhs.data <=> rhs.data;
}
- // Needed to allow certain types to be weakly_incremental
+ // Needed to allow certain types to be weakly_incrementable
constexpr Proxy& operator++()
- requires(HasPreIncrementOp<T>)
+ requires(std::weakly_incrementable<std::remove_reference_t<T>>)
{
++data;
return *this;
}
constexpr Proxy operator++(int)
- requires(HasPostIncrementOp<T>)
+ requires(std::incrementable<std::remove_reference_t<T>>)
+ {
+ Proxy tmp = *this;
+ operator++();
+ return tmp;
+ }
+
+ void operator++(int)
+ requires(std::weakly_incrementable<std::remove_reference_t<T>> && !std::incrementable<std::remove_reference_t<T>>)
{
Proxy tmp = *this;
operator++();
>From 2803493cba2a05fce5b3379c8ba43989aa4eaf73 Mon Sep 17 00:00:00 2001
From: James Smith <james.smith9113 at gmail.com>
Date: Mon, 15 Jan 2024 15:43:12 -0500
Subject: [PATCH 23/30] [libc++] Implement ranges::iota: Undoing some
problematic additions to cpp20_input_iterator and fixing formatting.
---
.../numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp | 3 ++-
libcxx/test/support/test_iterators.h | 7 ++++---
2 files changed, 6 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 77d215e5eb4e3..557888e272eb0 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
@@ -153,9 +153,10 @@ constexpr void test_DangerousCopyAssign() {
}
void test_results() {
- types::for_each(types::cpp20_input_iterator_list<int*>{}, []<class Iter> { test_results< Iter>(); });
+ types::for_each(types::cpp17_input_iterator_list<int*>{}, []<class Iter> { test_results< Iter>(); });
test_results<cpp17_output_iterator<int*>>();
test_results<cpp20_output_iterator<int*>>();
+ test_results<int*, sized_sentinel<int*>>();
test_DangerousCopyAssign();
}
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index e40352873954b..b06f7a6433b8c 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -650,7 +650,6 @@ class cpp20_input_iterator
constexpr decltype(auto) operator*() const { return *it_; }
constexpr cpp20_input_iterator& operator++() { ++it_; return *this; }
constexpr void operator++(int) { ++it_; }
- constexpr cpp20_input_iterator operator++(int) requires(std::incrementable<It>) { auto tmp = *this; ++it_; return tmp;}
friend constexpr It base(const cpp20_input_iterator& i) { return i.it_; }
@@ -1169,7 +1168,8 @@ struct Proxy : ProxyDiffTBase<T> {
// Compare operators are defined for the convenience of the tests
friend constexpr bool operator==(const Proxy& lhs, const Proxy& rhs)
- requires(std::equality_comparable<T> && !std::is_reference_v<T>) = default;
+ requires(std::equality_comparable<T> && !std::is_reference_v<T>)
+ = default;
// Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default equality comparison operator is deleted
// when `T` is a reference type.
@@ -1180,7 +1180,8 @@ struct Proxy : ProxyDiffTBase<T> {
}
friend constexpr auto operator<=>(const Proxy& lhs, const Proxy& rhs)
- requires(std::three_way_comparable<T> && !std::is_reference_v<T>) = default;
+ requires(std::three_way_comparable<T> && !std::is_reference_v<T>)
+ = default;
// 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.
>From 5f0389c42b539cfc54d82176894b96b44ca1bd7b Mon Sep 17 00:00:00 2001
From: jamesETsmith <james.smith9113 at gmail.com>
Date: Sun, 28 Apr 2024 11:57:04 -0400
Subject: [PATCH 24/30] [libc++] Implement ranges::iota: Fixing review comments
on ranges.iota.pass.cpp, still more work to do.
---
.../ranges_robust_against_dangling.pass.cpp | 2 +-
.../numeric.iota/ranges.iota.pass.cpp | 39 ++++++++++++-------
libcxx/test/support/test_iterators.h | 7 ----
3 files changed, 25 insertions(+), 23 deletions(-)
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 79631af03b218..1d91172870277 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -215,7 +215,7 @@ constexpr bool test_all() {
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);
+ dangling_1st<out_value_result<dangling, decltype(x)>>(std::ranges::iota, in, x);
#endif
return true;
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 557888e272eb0..96fed39a006f4 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
@@ -10,14 +10,15 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-#include <cassert>
-#include <numeric>
#include <algorithm>
#include <array>
+#include <cassert>
+#include <numeric>
+#include <utility>
-#include "test_macros.h"
-#include "test_iterators.h"
#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+#include "test_macros.h"
//
// Testing constraints
@@ -73,9 +74,13 @@ struct DangerousCopyAssign {
// Needed in postfix
constexpr DangerousCopyAssign(DangerousCopyAssign const& other) { this->val = other.val; }
- // mischievious copy assignment that we won't use if the
- // std::as_const inside ranges::iota isn't working, this should perturb the
- // results
+ /*
+ This class has a "mischievous" non-const overload of copy-assignment
+ operator that modifies the object being assigned from. `ranges::iota`
+ should not be invoking this overload thanks to the `std::as_const` in its
+ implementation. If for some reason it does invoke it, the values written
+ by ranges::iota will increment by 2 rather than 1.
+ */
constexpr DangerousCopyAssign& operator=(DangerousCopyAssign& a) {
++a.val;
this->val = a.val;
@@ -118,7 +123,7 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar
}
{ // (range) overload
- // inthe range overload adds the additional constraint that it must be an outputrange
+ // in the range overload adds the additional constraint that it must be an output range
// so skip this for the input iterators we test
auto in_begin = Iter(input.data());
auto in_end = Sent(Iter(input.data() + input.size()));
@@ -142,25 +147,29 @@ constexpr void test_results() {
test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
}
-constexpr void test_DangerousCopyAssign() {
- using A = DangerousCopyAssign;
- using Iter = contiguous_iterator<A*>;
- std::array<A, 3> aa = {A{1}, A{2}, A{3}};
+constexpr void test_danderous_copy_assign() {
+ using A = DangerousCopyAssign;
+ using Iter = contiguous_iterator<A*>;
+
+ // If the dangerous non-const copy assignment is called, the final values in
+ // aa should increment by 2 rather than 1.
+ std::array<A, 3> aa = {A{0}, A{0}, A{0}};
std::array<A, 3> expected = {A{0}, A{1}, A{2}};
std::ranges::iota(aa, A{0});
auto proj_val = [](DangerousCopyAssign const& el) { return el.val; };
assert(std::ranges::equal(aa, expected, std::ranges::equal_to{}, proj_val, proj_val));
}
-void test_results() {
+constexpr bool test_results() {
types::for_each(types::cpp17_input_iterator_list<int*>{}, []<class Iter> { test_results< Iter>(); });
test_results<cpp17_output_iterator<int*>>();
test_results<cpp20_output_iterator<int*>>();
test_results<int*, sized_sentinel<int*>>();
- test_DangerousCopyAssign();
+ test_danderous_copy_assign();
+ return true;
}
int main(int, char**) {
- test_results();
+ static_assert(test_results());
return 0;
}
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index b06f7a6433b8c..6dbd90b8a3931 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1207,13 +1207,6 @@ struct Proxy : ProxyDiffTBase<T> {
return tmp;
}
- void operator++(int)
- requires(std::weakly_incrementable<std::remove_reference_t<T>> && !std::incrementable<std::remove_reference_t<T>>)
- {
- Proxy tmp = *this;
- operator++();
- return tmp;
- }
};
// This is to make ProxyIterator model `std::indirectly_readable`
>From 79d6cc5d0906ed343b0111bda68d730ed76e8c13 Mon Sep 17 00:00:00 2001
From: jamesETsmith <james.smith9113 at gmail.com>
Date: Sun, 28 Apr 2024 15:38:53 -0400
Subject: [PATCH 25/30] [libc++] Implementing ranges::iota: Adding user-defined
type tests and updating status docs.
---
libcxx/docs/Status/Cxx23.rst | 2 +-
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
.../numeric.iota/ranges.iota.pass.cpp | 47 ++++++++++++++++++-
3 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index c576f1809219e..a17098b24692f 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -49,7 +49,7 @@ Paper Status
.. [#note-P2693R1] P2693R1: The formatter for ``std::thread::id`` is implemented.
The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is
not implemented yet.
- .. [#note-P2400R1] P2400R1: ranges::shift_left and ranges::shift_right have not been implemented yet.
+ .. [#note-P2440R1] P2440R1: ranges::shift_left and ranges::shift_right have not been implemented yet.
.. _issues-status-cxx23:
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index db0ba4dbd3b9b..b2755f2cd5a0d 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","|In progress| [#note-P2400R1]_","","|ranges|"
+"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|In progress| [#note-P2440R1]_","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|In Progress|","","|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/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 96fed39a006f4..ffd29855a3369 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
@@ -147,9 +147,48 @@ constexpr void test_results() {
test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4});
}
+constexpr void test_user_defined_type() {
+ // Simple non-fundamental type
+ struct UserDefinedType {
+ int val;
+ using difference_type = int;
+
+ constexpr explicit UserDefinedType(int v) : val(v) {}
+ constexpr UserDefinedType(UserDefinedType const& other) { this->val = other.val; }
+ constexpr UserDefinedType& operator=(UserDefinedType const& a) {
+ this->val = a.val;
+ return *this;
+ }
+
+ // prefix
+ constexpr UserDefinedType& operator++() {
+ ++(this->val);
+ return *this;
+ }
+
+ // postfix
+ constexpr UserDefinedType operator++(int) {
+ auto tmp = *this;
+ ++this->val;
+ return tmp;
+ }
+ };
+
+ // Setup
+ using A = UserDefinedType;
+ std::array<UserDefinedType, 5> a = {A{0}, A{0}, A{0}, A{0}, A{0}};
+ std::array<UserDefinedType, 5> expected = {A{0}, A{1}, A{2}, A{3}, A{4}};
+
+ // Fill with values
+ std::ranges::iota(a, A{0});
+ auto proj_val = [](UserDefinedType const& el) { return el.val; };
+
+ // Check
+ assert(std::ranges::equal(a, expected, std::ranges::equal_to{}, proj_val, proj_val));
+}
+
constexpr void test_danderous_copy_assign() {
- using A = DangerousCopyAssign;
- using Iter = contiguous_iterator<A*>;
+ using A = DangerousCopyAssign;
// If the dangerous non-const copy assignment is called, the final values in
// aa should increment by 2 rather than 1.
@@ -161,10 +200,14 @@ constexpr void test_danderous_copy_assign() {
}
constexpr bool test_results() {
+ // Tests on fundamental types
types::for_each(types::cpp17_input_iterator_list<int*>{}, []<class Iter> { test_results< Iter>(); });
test_results<cpp17_output_iterator<int*>>();
test_results<cpp20_output_iterator<int*>>();
test_results<int*, sized_sentinel<int*>>();
+
+ // Tests on non-fundamental types
+ test_user_defined_type();
test_danderous_copy_assign();
return true;
}
>From 07b818348152338b430b5bf154b2e4042014d605 Mon Sep 17 00:00:00 2001
From: jamesETsmith <james.smith9113 at gmail.com>
Date: Sun, 28 Apr 2024 16:56:13 -0400
Subject: [PATCH 26/30] [libc++] Implementing ranges::iota: Reverting to
original strategy for ranges:iota implementation that doesn't use helper
functions
---
libcxx/include/__numeric/ranges_iota.h | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index 750964ff77a4c..f7d854633c3fd 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -30,10 +30,10 @@ template <typename _Out, typename _Tp>
using iota_result = ranges::out_value_result<_Out, _Tp>;
struct __iota_fn {
-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) {
+public:
+ 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) {
while (__first != __last) {
*__first = std::as_const(__value);
++__first;
@@ -42,18 +42,10 @@ 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&>
- _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>
_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));
+ return __iota_fn::operator()(ranges::begin(__r), ranges::end(__r), std::move(__value));
}
};
>From fd814003e929322013c5b9679712f678e25c6709 Mon Sep 17 00:00:00 2001
From: jamesETsmith <james.smith9113 at gmail.com>
Date: Thu, 2 May 2024 21:15:33 -0400
Subject: [PATCH 27/30] [libc++] Implementing ranges::iota: Needed to include
__undef_macros and missed one update in version.version.compile.pass.cpp
---
libcxx/include/__numeric/ranges_iota.h | 5 +++++
.../version.version.compile.pass.cpp | 17 ++++++-----------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h
index f7d854633c3fd..b9f90dc5f3ae6 100644
--- a/libcxx/include/__numeric/ranges_iota.h
+++ b/libcxx/include/__numeric/ranges_iota.h
@@ -22,6 +22,9 @@
# pragma GCC system_header
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 23
@@ -56,4 +59,6 @@ inline constexpr auto iota = __iota_fn{};
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // _LIBCPP___NUMERIC_RANGES_IOTA_H
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 c5ddb0143723e..128a883ff0979 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
@@ -5526,18 +5526,13 @@
# error "__cpp_lib_ranges_contains 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)
# ifndef __cpp_lib_ranges_join_with
>From 3285f4e86efd1fd0295f1b033d37b63275a79e3a Mon Sep 17 00:00:00 2001
From: jamesETsmith <james.smith9113 at gmail.com>
Date: Fri, 3 May 2024 12:15:44 -0400
Subject: [PATCH 28/30] [libc++] Implementing ranges::iota: Fixing
autogenerated version.version.compile.pass.cpp and formatting in
test_iterators.h
---
libcxx/include/version | 2 +-
.../support.limits.general/version.version.compile.pass.cpp | 5 ++---
libcxx/test/support/test_iterators.h | 1 -
3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/libcxx/include/version b/libcxx/include/version
index a9835d0aa6644..003504aa1a63f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -474,8 +474,8 @@ __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_contains 202207L
+# 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/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 128a883ff0979..f5c721e9a1380 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
@@ -5533,7 +5533,6 @@
# error "__cpp_lib_ranges_iota should have the value 202202L in c++23"
# endif
-
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++23"
@@ -7316,10 +7315,10 @@
# endif
# ifndef __cpp_lib_ranges_iota
-# error "__cpp_lib_ranges_iota should be defined in c++23"
+# 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++23"
+# error "__cpp_lib_ranges_iota should have the value 202202L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 4d61cc5a6c64f..9a428d9f3f694 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -1222,7 +1222,6 @@ struct Proxy : ProxyDiffTBase<T> {
operator++();
return tmp;
}
-
};
// This is to make ProxyIterator model `std::indirectly_readable`
>From 3f0670fa9237c8035b8c04e046853f12db315a8f Mon Sep 17 00:00:00 2001
From: jamesETsmith <james.smith9113 at gmail.com>
Date: Wed, 22 May 2024 09:25:11 -0400
Subject: [PATCH 29/30] [libc++] Implementing ranges::iota: Simplifying some
tests, adding static_assert to others, and update status in the papers csv.
---
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
.../algorithms.results/out_value_result.pass.cpp | 5 ++++-
.../numeric.ops/numeric.iota/ranges.iota.pass.cpp | 15 ++++++---------
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 65db8a6d41869..c01233e072488 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","|Complete|","19.0","|ranges|"
-"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|In progress| [#note-P2440R1]_","","|ranges|"
+"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|Partial| [#note-P2440R1]_","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|In Progress|","","|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/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp
index bc8c665950f43..7bdbb7f60e9a2 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
@@ -52,7 +52,7 @@ struct NonStandardLayoutTypeBase {
struct NonStandardLayoutType : public NonStandardLayoutTypeBase {};
//
-constexpr void test_constraints() {
+constexpr bool test_constraints() {
// requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2>
static_assert(std::is_constructible_v<out_value_result<int*, int>, out_value_result<int*, int>>);
@@ -84,6 +84,8 @@ constexpr void test_constraints() {
// 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>>);
+
+ return true;
}
// Test results
@@ -132,6 +134,7 @@ constexpr bool test() {
int main(int, char**) {
test_constraints();
+ static_assert(test_constraints());
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
index ffd29855a3369..9fa50f1326f1d 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
@@ -78,14 +78,10 @@ struct DangerousCopyAssign {
This class has a "mischievous" non-const overload of copy-assignment
operator that modifies the object being assigned from. `ranges::iota`
should not be invoking this overload thanks to the `std::as_const` in its
- implementation. If for some reason it does invoke it, the values written
- by ranges::iota will increment by 2 rather than 1.
+ implementation. If for some reason it does invoke it, there will be a compiler
+ error.
*/
- constexpr DangerousCopyAssign& operator=(DangerousCopyAssign& a) {
- ++a.val;
- this->val = a.val;
- return *this;
- }
+ constexpr DangerousCopyAssign& operator=(DangerousCopyAssign& a) = delete;
// safe copy assignment std::as_const inside ranges::iota should ensure this
// overload gets called
@@ -187,7 +183,7 @@ constexpr void test_user_defined_type() {
assert(std::ranges::equal(a, expected, std::ranges::equal_to{}, proj_val, proj_val));
}
-constexpr void test_danderous_copy_assign() {
+constexpr void test_dangerous_copy_assign() {
using A = DangerousCopyAssign;
// If the dangerous non-const copy assignment is called, the final values in
@@ -208,11 +204,12 @@ constexpr bool test_results() {
// Tests on non-fundamental types
test_user_defined_type();
- test_danderous_copy_assign();
+ test_dangerous_copy_assign();
return true;
}
int main(int, char**) {
+ test_results();
static_assert(test_results());
return 0;
}
>From d50b7a127ddbde25cedcebb4b46d87b6450ffbf6 Mon Sep 17 00:00:00 2001
From: jamesETsmith <james.smith9113 at gmail.com>
Date: Wed, 24 Jul 2024 12:17:39 -0400
Subject: [PATCH 30/30] [libc++] Implementing ranges::iota: Accidentally
manually updated the version.version.compile tests rather than generating it
with the python script so the formatting was off
---
.../support.limits.general/version.version.compile.pass.cpp | 2 --
1 file changed, 2 deletions(-)
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 49472aa37f816..45c5ab6c2bfec 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
@@ -5686,7 +5686,6 @@
# 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)
@@ -7541,7 +7540,6 @@
# 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