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.smith9...@gmail.com> Date: Thu, 28 Sep 2023 10:11:15 -0400 Subject: [PATCH 01/14] [libc++] Implement ranges::iota and ranges::out_value_result --- libcxx/include/CMakeLists.txt | 2 + libcxx/include/__algorithm/out_value_result.h | 52 +++++++++ libcxx/include/__numeric/ranges_iota.h | 53 +++++++++ libcxx/include/algorithm | 4 + libcxx/include/numeric | 1 + libcxx/include/version | 2 +- .../out_value_result.pass.cpp | 102 ++++++++++++++++++ .../numeric.iota/ranges.iota.pass.cpp | 52 +++++++++ 8 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 libcxx/include/__algorithm/out_value_result.h create mode 100644 libcxx/include/__numeric/ranges_iota.h create mode 100644 libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp create mode 100644 libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 2ec755236dbaee2..c6eb03f1d68e984 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -63,6 +63,7 @@ set(files __algorithm/next_permutation.h __algorithm/none_of.h __algorithm/nth_element.h + __algorithm/out_value_result.h __algorithm/partial_sort.h __algorithm/partial_sort_copy.h __algorithm/partition.h @@ -561,6 +562,7 @@ set(files __numeric/partial_sum.h __numeric/pstl_reduce.h __numeric/pstl_transform_reduce.h + __numeric/ranges_iota.h __numeric/reduce.h __numeric/transform_exclusive_scan.h __numeric/transform_inclusive_scan.h diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h new file mode 100644 index 000000000000000..8baffec7b9ef4da --- /dev/null +++ b/libcxx/include/__algorithm/out_value_result.h @@ -0,0 +1,52 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H +#define _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H + +#include <__concepts/convertible_to.h> +#include <__config> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { + +template <class _OutIter1, class _ValType1> +struct out_value_result { + _LIBCPP_NO_UNIQUE_ADDRESS _OutIter1 out; + _LIBCPP_NO_UNIQUE_ADDRESS _ValType1 value; + + template <class _OutIter2, class _ValType2> + requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> + constexpr operator out_value_result<_OutIter2, _ValType2>() const& { return {out, value}; } + + template <class _OutIter2, class _ValType2> + requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2> + constexpr operator out_value_result<_OutIter2, _ValType2>() && { return {std::move(out), std::move(value)}; } +}; + +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___ALGORITHM_OUT_VALUE_RESULT_H diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h new file mode 100644 index 000000000000000..20311a68c2a348c --- /dev/null +++ b/libcxx/include/__numeric/ranges_iota.h @@ -0,0 +1,53 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___NUMERIC_RANGES_IOTA_H +#define _LIBCPP___NUMERIC_RANGES_IOTA_H + +#include <__algorithm/out_value_result.h> +#include <__config> +#include <__ranges/concepts.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 +namespace ranges { +template <typename _Out, typename _Tp> +using iota_result = ranges::out_value_result<_Out, _Tp>; + +struct __iota_fn { + template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp> + requires indirectly_writable<_Out, const _Tp&> + constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const { + while (__first != __last) { + *__first = static_cast<const _Tp&>(__value); + ++__first; + ++__value; + } + return {std::move(__first), std::move(__value)}; + } + + template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range> + constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const { + return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value)); + } +}; + +inline constexpr __iota_fn iota{}; +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___NUMERIC_RANGES_IOTA_H diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index 69ba9537dda6984..42f01a99a02cc93 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -42,6 +42,9 @@ namespace ranges { template <class I> struct in_found_result; // since C++20 + template <class O, class T> + struct out_value_result; // since C++23 + template<forward_iterator I, sentinel_for<I> S, class Proj = identity, indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less> // since C++20 constexpr I min_element(I first, S last, Comp comp = {}, Proj proj = {}); @@ -1817,6 +1820,7 @@ template <class BidirectionalIterator, class Compare> #include <__algorithm/next_permutation.h> #include <__algorithm/none_of.h> #include <__algorithm/nth_element.h> +#include <__algorithm/out_value_result.h> #include <__algorithm/partial_sort.h> #include <__algorithm/partial_sort_copy.h> #include <__algorithm/partition.h> diff --git a/libcxx/include/numeric b/libcxx/include/numeric index 3fcf6cefdb4b884..363dfd229df54dc 100644 --- a/libcxx/include/numeric +++ b/libcxx/include/numeric @@ -160,6 +160,7 @@ template<class T> #include <__numeric/partial_sum.h> #include <__numeric/pstl_reduce.h> #include <__numeric/pstl_transform_reduce.h> +#include <__numeric/ranges_iota.h> #include <__numeric/reduce.h> #include <__numeric/transform_exclusive_scan.h> #include <__numeric/transform_inclusive_scan.h> diff --git a/libcxx/include/version b/libcxx/include/version index e5a995366a7aa48..164c60dfd6af6d4 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -436,7 +436,7 @@ __cpp_lib_within_lifetime 202306L <type_traits> # define __cpp_lib_ranges_as_rvalue 202207L // # define __cpp_lib_ranges_chunk 202202L # define __cpp_lib_ranges_chunk_by 202202L -// # define __cpp_lib_ranges_iota 202202L +# define __cpp_lib_ranges_iota 202202L // # define __cpp_lib_ranges_join_with 202202L # define __cpp_lib_ranges_repeat 202207L // # define __cpp_lib_ranges_slide 202202L diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp new file mode 100644 index 000000000000000..189149b0b4acb0c --- /dev/null +++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, C++20 + +// template <class O, class T> +// struct out_value_result; + +#include <algorithm> +#include <cassert> +#include <type_traits> + +#include "MoveOnly.h" + +struct A { + explicit A(int); +}; +// no implicit conversion +static_assert(!std::is_constructible_v<std::ranges::out_value_result<A, A>, std::ranges::out_value_result<int, int>>); + +struct B { + B(int); +}; +// implicit conversion +static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>>); +static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>&>); +static_assert( + std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>>); +static_assert( + std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>&>); + +struct C { + C(int&); +}; +static_assert(!std::is_constructible_v<std::ranges::out_value_result<C, C>, std::ranges::out_value_result<int, int>&>); + +// has to be convertible via const& +static_assert( + std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>); +static_assert( + std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>); +static_assert( + std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>); +static_assert( + std::is_convertible_v<const std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>); + +// should be move constructible +static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>); +static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>); + +struct NotConvertible {}; +// conversions should not work if there is no conversion +static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>, + std::ranges::out_value_result<int, NotConvertible>>); +static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>, + std::ranges::out_value_result<NotConvertible, int>>); + +template <class T> +struct ConvertibleFrom { + constexpr ConvertibleFrom(T c) : content{c} {} + T content; +}; + +constexpr bool test() { + { + std::ranges::out_value_result<double, int> res{10, 1}; + assert(res.out == 10); + assert(res.value == 1); + std::ranges::out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res; + assert(res2.out.content == 10); + assert(res2.value.content == 1); + } + { + std::ranges::out_value_result<MoveOnly, int> res{MoveOnly{}, 10}; + assert(res.out.get() == 1); + assert(res.value == 10); + auto res2 = std::move(res); + assert(res.out.get() == 0); + assert(res.value == 10); + assert(res2.out.get() == 1); + assert(res2.value == 10); + } + { + auto [min, max] = std::ranges::out_value_result<int, int>{1, 2}; + assert(min == 1); + assert(max == 2); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp new file mode 100644 index 000000000000000..1e2a6970adb35e3 --- /dev/null +++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Testing std::ranges::iota + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include <cassert> +#include <numeric> +#include <array> + +#include "test_macros.h" +#include "test_iterators.h" + +// This is pulled directly from the std::iota test +template <class InIter> +TEST_CONSTEXPR_CXX20 void test0() { + int ia[] = {1, 2, 3, 4, 5}; + int ir[] = {5, 6, 7, 8, 9}; + const unsigned s = sizeof(ia) / sizeof(ia[0]); + std::ranges::iota(InIter(ia), InIter(ia + s), 5); + for (unsigned i = 0; i < s; ++i) + assert(ia[i] == ir[i]); +} + +TEST_CONSTEXPR_CXX20 bool test0() { + test0<forward_iterator<int*> >(); + test0<bidirectional_iterator<int*> >(); + test0<random_access_iterator<int*> >(); + test0<int*>(); + + return true; +} + +TEST_CONSTEXPR_CXX20 void test1() { + std::array<int, 5> ia = {1, 2, 3, 4, 5}; + std::array<int, 5> ir = {5, 6, 7, 8, 9}; + std::ranges::iota(ia, 5); + for (unsigned i = 0; i < ir.size(); ++i) + assert(ia[i] == ir[i]); +} + +int main(int, char**) { + test0(); + test1(); + return 0; +} \ No newline at end of file >From 574c94ce4f7eed06013a5e5d1460da1d148cc7cd Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Fri, 29 Sep 2023 14:11:49 -0400 Subject: [PATCH 02/14] [libc++] Implement ranges::iota: update module and docs info --- libcxx/docs/FeatureTestMacroTable.rst | 2 +- libcxx/include/module.modulemap.in | 1 + libcxx/modules/std/numeric.inc | 8 ++++++-- libcxx/utils/generate_feature_test_macro_components.py | 1 - 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index e1b4172b22c53da..ebb18dc7bed9bd2 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -348,7 +348,7 @@ Status --------------------------------------------------- ----------------- ``__cpp_lib_ranges_chunk_by`` ``202202L`` --------------------------------------------------- ----------------- - ``__cpp_lib_ranges_iota`` *unimplemented* + ``__cpp_lib_ranges_iota`` ``202202L`` --------------------------------------------------- ----------------- ``__cpp_lib_ranges_join_with`` *unimplemented* --------------------------------------------------- ----------------- diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 6d9bb8653fcb5e9..b092cc7c0e93bf1 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1602,6 +1602,7 @@ module std_private_numeric_pstl_transform_reduce [system] { export * } module std_private_numeric_reduce [system] { header "__numeric/reduce.h" } +module std_private_numeric_ranges_iota [system] { header "__numeric/ranges_iota.h" } module std_private_numeric_transform_exclusive_scan [system] { header "__numeric/transform_exclusive_scan.h" } module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" } module std_private_numeric_transform_reduce [system] { header "__numeric/transform_reduce.h" } diff --git a/libcxx/modules/std/numeric.inc b/libcxx/modules/std/numeric.inc index d2b7688d4e5f10a..9204e8d8715935d 100644 --- a/libcxx/modules/std/numeric.inc +++ b/libcxx/modules/std/numeric.inc @@ -42,8 +42,12 @@ export namespace std { using std::iota; namespace ranges { - // using std::ranges::iota_result; - // using std::ranges::iota; + +#if _LIBCPP_STD_VER >= 23 + using std::ranges::iota; + using std::ranges::iota_result; +#endif // _LIBCPP_STD_VER >= 23 + } // namespace ranges // [numeric.ops.gcd], greatest common divisor diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index ac342aff0beb701..9fd50d66edbe317 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -806,7 +806,6 @@ def add_version_header(tc): "name": "__cpp_lib_ranges_iota", "values": {"c++23": 202202}, "headers": ["numeric"], - "unimplemented": True, }, { "name": "__cpp_lib_ranges_join_with", >From e4cfe00cb1b348d5a3515ef907c7c87334c6824c Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Fri, 29 Sep 2023 16:50:46 -0400 Subject: [PATCH 03/14] [libc++] Implement ranges::iota: fixing formatting issues for out_value_result --- libcxx/include/__algorithm/out_value_result.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h index 8baffec7b9ef4da..a99d206f7035ff2 100644 --- a/libcxx/include/__algorithm/out_value_result.h +++ b/libcxx/include/__algorithm/out_value_result.h @@ -34,11 +34,15 @@ struct out_value_result { template <class _OutIter2, class _ValType2> requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> - constexpr operator out_value_result<_OutIter2, _ValType2>() const& { return {out, value}; } + constexpr operator out_value_result<_OutIter2, _ValType2>() const& { + return {out, value}; + } template <class _OutIter2, class _ValType2> requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2> - constexpr operator out_value_result<_OutIter2, _ValType2>() && { return {std::move(out), std::move(value)}; } + constexpr operator out_value_result<_OutIter2, _ValType2>() && { + return {std::move(out), std::move(value)}; + } }; } // namespace ranges >From 7cdfea2d5acf553345ee0c506ce9e3914cbb3ed0 Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Sat, 7 Oct 2023 12:04:32 -0400 Subject: [PATCH 04/14] [libc++] Implement ranges::iota: Cleaning up tests for ranges::iota and out_value_result. Updating Cxx23Papers.csv too. --- libcxx/docs/Status/Cxx23Papers.csv | 2 +- libcxx/include/__numeric/ranges_iota.h | 3 +- libcxx/modules/std/algorithm.inc | 2 +- .../out_value_result.pass.cpp | 104 ++++++++++------ .../numeric.iota/ranges.iota.pass.cpp | 117 ++++++++++++++++-- 5 files changed, 171 insertions(+), 57 deletions(-) diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index 9cb49fd5176ea5f..4618908cfb73941 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -46,7 +46,7 @@ "`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","","" "`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0" "`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|" -"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|" +"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|In progress|","","|ranges|" "`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","","","|ranges|" "`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|" "`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|" diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h index 20311a68c2a348c..a8c069832da7e6d 100644 --- a/libcxx/include/__numeric/ranges_iota.h +++ b/libcxx/include/__numeric/ranges_iota.h @@ -13,6 +13,7 @@ #include <__algorithm/out_value_result.h> #include <__config> #include <__ranges/concepts.h> +#include <__utility/as_const.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -30,7 +31,7 @@ struct __iota_fn { requires indirectly_writable<_Out, const _Tp&> constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const { while (__first != __last) { - *__first = static_cast<const _Tp&>(__value); + *__first = std::as_const(__value); ++__first; ++__value; } diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc index b7900d15c10c2b5..62ce8cf02c14f95 100644 --- a/libcxx/modules/std/algorithm.inc +++ b/libcxx/modules/std/algorithm.inc @@ -18,7 +18,7 @@ export namespace std { using std::ranges::in_out_result; // using std::ranges::in_value_result; using std::ranges::min_max_result; - // using std::ranges::out_value_result; + using std::ranges::out_value_result; } // namespace ranges // [alg.nonmodifying], non-modifying sequence operations diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp index 189149b0b4acb0c..3a930bb063f975f 100644 --- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp +++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp @@ -17,48 +17,25 @@ #include "MoveOnly.h" -struct A { - explicit A(int); +// +// Helper structs +// + +// only explicit construction +struct IterTypeExplicit { + explicit IterTypeExplicit(int*); }; -// no implicit conversion -static_assert(!std::is_constructible_v<std::ranges::out_value_result<A, A>, std::ranges::out_value_result<int, int>>); -struct B { - B(int); +// implicit construction +struct IterTypeImplicit { + IterTypeImplicit(int*); }; -// implicit conversion -static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>>); -static_assert(std::is_constructible_v<std::ranges::out_value_result<B, B>, std::ranges::out_value_result<int, int>&>); -static_assert( - std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>>); -static_assert( - std::is_constructible_v<std::ranges::out_value_result<B, B>, const std::ranges::out_value_result<int, int>&>); - -struct C { - C(int&); + +struct IterTypeImplicitRef { + IterTypeImplicitRef(int&); }; -static_assert(!std::is_constructible_v<std::ranges::out_value_result<C, C>, std::ranges::out_value_result<int, int>&>); - -// has to be convertible via const& -static_assert( - std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>); -static_assert( - std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>); -static_assert( - std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>); -static_assert( - std::is_convertible_v<const std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>); - -// should be move constructible -static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>); -static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>); struct NotConvertible {}; -// conversions should not work if there is no conversion -static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>, - std::ranges::out_value_result<int, NotConvertible>>); -static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>, - std::ranges::out_value_result<NotConvertible, int>>); template <class T> struct ConvertibleFrom { @@ -66,6 +43,51 @@ struct ConvertibleFrom { T content; }; +// +constexpr void test_constraints() { + // requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> + static_assert( + std::is_constructible_v<std::ranges::out_value_result<int*, int>, std::ranges::out_value_result<int*, int>>); + + // test failure when implicit conversion isn't allowed + static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeExplicit, int>, + std::ranges::out_value_result<int*, int>>); + + // test success when implicit conversion is allowed, checking combinations of value, reference, and const + static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>, + std::ranges::out_value_result<int*, int>>); + static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>, + std::ranges::out_value_result<int*, int> const>); + static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>, + std::ranges::out_value_result<int*, int>&>); + static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>, + std::ranges::out_value_result<int*, int> const&>); + + static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicitRef, int>, + std::ranges::out_value_result<int, int>&>); + + // has to be convertible via const& + static_assert( + std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>); + static_assert( + std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>); + static_assert( + std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>); + static_assert(std::is_convertible_v<const std::ranges::out_value_result<int, int>&&, + std::ranges::out_value_result<long, long>>); + + // should be move constructible + static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>); + static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>); + + // conversions should not work if there is no conversion + static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>, + std::ranges::out_value_result<int, NotConvertible>>); + static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>, + std::ranges::out_value_result<NotConvertible, int>>); +} + +// Test results constexpr bool test() { { std::ranges::out_value_result<double, int> res{10, 1}; @@ -86,17 +108,17 @@ constexpr bool test() { assert(res2.value == 10); } { - auto [min, max] = std::ranges::out_value_result<int, int>{1, 2}; - assert(min == 1); - assert(max == 2); + auto [out, val] = std::ranges::out_value_result<int, int>{1, 2}; + assert(out == 1); + assert(val == 2); } return true; } int main(int, char**) { + test_constraints(); test(); static_assert(test()); - return 0; -} +} \ No newline at end of file diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp index 1e2a6970adb35e3..85f99942e97b2ac 100644 --- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp +++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp @@ -12,41 +12,132 @@ #include <cassert> #include <numeric> +#include <algorithm> #include <array> #include "test_macros.h" #include "test_iterators.h" +#include "almost_satisfies_types.h" + +// Concepts to check different overloads of std::ranges::iota +template <class Iter = int*, class Sent = int*, class Value = int> +concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) { + std::ranges::iota(std::forward<Iter>(iter), std::forward<Sent>(sent), std::forward<Value>(val)); +}; + +template <class Range, class Value = int> +concept HasIotaRange = requires(Range&& range, Value&& val) { + std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); +}; + +constexpr void test_constraints() { + // Test constraints of the iterator/sentinel overload + // ================================================== + static_assert(HasIotaIter<int*, int*, int>); + + // !input_or_output_iterator<O> + static_assert(!HasIotaIter<InputIteratorNotInputOrOutputIterator>); + + // !sentinel_for<S, O> + static_assert(!HasIotaIter<int*, SentinelForNotSemiregular>); + static_assert(!HasIotaIter<int*, SentinelForNotWeaklyEqualityComparableWith>); + + // !weakly_incrementable<T> + static_assert(!HasIotaIter<int*, int*, WeaklyIncrementableNotMovable>); + + // !indirectly writable <O, T> + static_assert(!HasIotaIter<OutputIteratorNotIndirectlyWritable, int*, int>); + + // Test constraints for the range overload + // ======================================= + static_assert(HasIotaRange<UncheckedRange<int*>, int>); + + // !weakly_incrementable<T> + static_assert(!HasIotaRange<UncheckedRange<int*>, WeaklyIncrementableNotMovable>); + + // !ranges::output_range<const _Tp&> + static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>); +} // This is pulled directly from the std::iota test -template <class InIter> -TEST_CONSTEXPR_CXX20 void test0() { +template <class InOutIter, class Sent = InOutIter> +constexpr void test0() { int ia[] = {1, 2, 3, 4, 5}; int ir[] = {5, 6, 7, 8, 9}; const unsigned s = sizeof(ia) / sizeof(ia[0]); - std::ranges::iota(InIter(ia), InIter(ia + s), 5); - for (unsigned i = 0; i < s; ++i) - assert(ia[i] == ir[i]); + std::ranges::iota(InOutIter(ia), InOutIter(ia + s), 5); + assert(std::ranges::equal(ia, ir)); } -TEST_CONSTEXPR_CXX20 bool test0() { +constexpr bool test0() { + // TODO why don't these work? + // test0<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>(); + // test0<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>(); + // test0<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>(); + // test0<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>(); test0<forward_iterator<int*> >(); test0<bidirectional_iterator<int*> >(); test0<random_access_iterator<int*> >(); + test0<contiguous_iterator<int*>>(); test0<int*>(); return true; } -TEST_CONSTEXPR_CXX20 void test1() { - std::array<int, 5> ia = {1, 2, 3, 4, 5}; - std::array<int, 5> ir = {5, 6, 7, 8, 9}; - std::ranges::iota(ia, 5); - for (unsigned i = 0; i < ir.size(); ++i) - assert(ia[i] == ir[i]); +template <class Iter, class Sent, std::size_t N> +constexpr void test_result(std::array<int, N> input, int starting_value, std::array<int, N> const expected) { + { // (iterator, sentinel) overload + auto in_begin = Iter(input.data()); + auto in_end = Sent(Iter(input.data() + input.size())); + std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result = + std::ranges::iota(std::move(in_begin), std::move(in_end), starting_value); + assert(result.out == in_end); + if constexpr (expected.size() > 0) { + assert(result.value == expected.back() + 1); + } else { + assert(result.value == starting_value); + } + assert(std::ranges::equal(input, expected)); + } + + { // (range) overload + auto in_begin = Iter(input.data()); + auto in_end = Sent(Iter(input.data() + input.size())); + auto range = std::ranges::subrange(std::move(in_begin), std::move(in_end)); + + std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result = + std::ranges::iota(range, starting_value); + assert(result.out == in_end); + if constexpr (expected.size() > 0) { + assert(result.value == expected.back() + 1); + } else { + assert(result.value == starting_value); + } + assert(std::ranges::equal(input, expected)); + } +} + +constexpr bool test_results() { + using Iter = forward_iterator<int*>; + using Sent = sentinel_wrapper<Iter>; + + // Empty + test_result<Iter, Sent, 0>({}, 0, {}); + // 1-element sequence + test_result<Iter, Sent, 1>({1}, 0, {0}); + // Longer sequence + test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4}); + + return true; } int main(int, char**) { + test_constraints(); + test0(); - test1(); + static_assert(test0()); + + test_results(); + static_assert(test_results()); return 0; } \ No newline at end of file >From a1d015c2affa40bb5f5313cdaed46c5a8cd4b243 Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Sat, 7 Oct 2023 12:35:20 -0400 Subject: [PATCH 05/14] [libc++] Implement ranges::iota: Cleaning up tests for ranges::iota and adding testing for more iterators --- .../numeric.iota/ranges.iota.pass.cpp | 53 ++++++------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp index 85f99942e97b2ac..611293debbf731b 100644 --- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp +++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp @@ -59,31 +59,6 @@ constexpr void test_constraints() { static_assert(!HasIotaRange<UncheckedRange<int*>, OutputIteratorNotIndirectlyWritable>); } -// This is pulled directly from the std::iota test -template <class InOutIter, class Sent = InOutIter> -constexpr void test0() { - int ia[] = {1, 2, 3, 4, 5}; - int ir[] = {5, 6, 7, 8, 9}; - const unsigned s = sizeof(ia) / sizeof(ia[0]); - std::ranges::iota(InOutIter(ia), InOutIter(ia + s), 5); - assert(std::ranges::equal(ia, ir)); -} - -constexpr bool test0() { - // TODO why don't these work? - // test0<cpp17_input_iterator<int*>, sentinel_wrapper<cpp17_input_iterator<int*>>>(); - // test0<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>(); - // test0<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>(); - // test0<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>(); - test0<forward_iterator<int*> >(); - test0<bidirectional_iterator<int*> >(); - test0<random_access_iterator<int*> >(); - test0<contiguous_iterator<int*>>(); - test0<int*>(); - - return true; -} - template <class Iter, class Sent, std::size_t N> constexpr void test_result(std::array<int, N> input, int starting_value, std::array<int, N> const expected) { { // (iterator, sentinel) overload @@ -100,7 +75,10 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar assert(std::ranges::equal(input, expected)); } - { // (range) overload + // The range overload adds the additional constraint that it must be an outputrange + // so skip this for the input iterators we test + if constexpr (!std::is_same_v<Iter, cpp17_input_iterator<int*>> && + !std::is_same_v<Iter, cpp20_input_iterator<int*>>) { // (range) overload auto in_begin = Iter(input.data()); auto in_end = Sent(Iter(input.data() + input.size())); auto range = std::ranges::subrange(std::move(in_begin), std::move(in_end)); @@ -117,27 +95,30 @@ constexpr void test_result(std::array<int, N> input, int starting_value, std::ar } } -constexpr bool test_results() { - using Iter = forward_iterator<int*>; - using Sent = sentinel_wrapper<Iter>; - +template <class Iter, class Sent = sentinel_wrapper<Iter>> +constexpr void test_results() { // Empty test_result<Iter, Sent, 0>({}, 0, {}); // 1-element sequence test_result<Iter, Sent, 1>({1}, 0, {0}); // Longer sequence test_result<Iter, Sent, 5>({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4}); +} - return true; +void test_results() { + test_results<cpp17_input_iterator<int*>>(); + test_results<cpp20_input_iterator<int*>>(); + test_results<cpp17_output_iterator<int*>>(); + test_results<cpp20_output_iterator<int*>>(); + test_results<forward_iterator<int*>>(); + test_results<bidirectional_iterator<int*>>(); + test_results<random_access_iterator<int*>>(); + test_results<contiguous_iterator<int*>>(); + test_results<int*>(); } int main(int, char**) { test_constraints(); - - test0(); - static_assert(test0()); - test_results(); - static_assert(test_results()); return 0; } \ No newline at end of file >From ab4c67d3537379aab5117ac8dad232c5072991e9 Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Sat, 7 Oct 2023 12:40:02 -0400 Subject: [PATCH 06/14] [libc++] Implement ranges::iota: Using clang-format-17 to make CI checks happy --- .../numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp index 611293debbf731b..3a7bc4319bebd6e 100644 --- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp +++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp @@ -26,9 +26,8 @@ concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) { }; template <class Range, class Value = int> -concept HasIotaRange = requires(Range&& range, Value&& val) { - std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); -}; +concept HasIotaRange = + requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); }; constexpr void test_constraints() { // Test constraints of the iterator/sentinel overload >From a455f42dbd6b97b8b6ccf41fad2ff9aae054fe0e Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Tue, 24 Oct 2023 20:39:52 -0400 Subject: [PATCH 07/14] [libc++] Implement ranges::iota: Adding helper function to implementation and updating docs. --- libcxx/docs/Status/RangesAlgorithms.csv | 1 + libcxx/docs/Status/RangesMajorFeatures.csv | 1 + libcxx/include/__numeric/ranges_iota.h | 24 +++++++++++++++---- .../out_value_result.pass.cpp | 2 +- .../numeric.iota/ranges.iota.pass.cpp | 7 +++--- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv index 17c8953bf8d85db..e6dda790bf158d9 100644 --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -10,3 +10,4 @@ C++23,`shift_right <https://wg21.link/p2440r1>`_,Unassigned,No patch yet,Not sta C++23,`iota (algorithm) <https://wg21.link/p2440r1>`_,Unassigned,No patch yet,Not started C++23,`fold <https://wg21.link/p2322r5>`_,Unassigned,No patch yet,Not started C++23,`contains <https://wg21.link/p2302r2>`_,Zijun Zhao,No patch yet,In Progress +C++23,`ranges::iota <https://wg21.link/P2440R1>`_, James E T Smith, `PR68494 <https://github.com/llvm/llvm-project/pull/68494>`_, In Progress diff --git a/libcxx/docs/Status/RangesMajorFeatures.csv b/libcxx/docs/Status/RangesMajorFeatures.csv index 259a0218ce15e32..5dd93a60fa7bd09 100644 --- a/libcxx/docs/Status/RangesMajorFeatures.csv +++ b/libcxx/docs/Status/RangesMajorFeatures.csv @@ -2,3 +2,4 @@ Standard,Name,Assignee,CL,Status C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,Unassigned,No patch yet,Not started C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete +C++23, `ranges::iota <https://wg21.link/P2440R1>`_, James E T Smith, `PR68494 <https://github.com/llvm/llvm-project/pull/68494>`_, In Progress \ No newline at end of file diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h index a8c069832da7e6d..eca5712b3d0d4bd 100644 --- a/libcxx/include/__numeric/ranges_iota.h +++ b/libcxx/include/__numeric/ranges_iota.h @@ -14,6 +14,7 @@ #include <__config> #include <__ranges/concepts.h> #include <__utility/as_const.h> +#include <__utility/move.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -26,10 +27,12 @@ namespace ranges { template <typename _Out, typename _Tp> using iota_result = ranges::out_value_result<_Out, _Tp>; +namespace __ranges_iota { struct __iota_fn { - template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp> - requires indirectly_writable<_Out, const _Tp&> - constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const { +private: + // Private helper function + template <class _Out, class _Sent, class _Tp> + _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp> __iota_impl(_Out __first, _Sent __last, _Tp __value) { while (__first != __last) { *__first = std::as_const(__value); ++__first; @@ -38,13 +41,24 @@ struct __iota_fn { return {std::move(__first), std::move(__value)}; } +public: + // Public facing interfaces + template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp> + requires indirectly_writable<_Out, const _Tp&> + constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const { + return __iota_impl(std::move(__first), std::move(__last), std::move(__value)); + } + template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range> constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const { - return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value)); + return __iota_impl(ranges::begin(__r), ranges::end(__r), std::move(__value)); } }; +} // namespace __ranges_iota -inline constexpr __iota_fn iota{}; +inline namespace __cpo { +inline constexpr auto iota = __ranges_iota::__iota_fn{}; +} // namespace __cpo } // namespace ranges #endif // _LIBCPP_STD_VER >= 23 diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp index 3a930bb063f975f..e1d203929be738c 100644 --- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp +++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp @@ -121,4 +121,4 @@ int main(int, char**) { test(); static_assert(test()); return 0; -} \ No newline at end of file +} diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp index 3a7bc4319bebd6e..7aedf7ee058445a 100644 --- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp +++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp @@ -26,8 +26,9 @@ concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) { }; template <class Range, class Value = int> -concept HasIotaRange = - requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); }; +concept HasIotaRange = requires(Range&& range, Value&& val) { + std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); +}; constexpr void test_constraints() { // Test constraints of the iterator/sentinel overload @@ -120,4 +121,4 @@ int main(int, char**) { test_constraints(); test_results(); return 0; -} \ No newline at end of file +} >From 08e3c77d3b7395fc7e78156b693faa72879ca5a0 Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Thu, 26 Oct 2023 16:53:08 -0400 Subject: [PATCH 08/14] [libc++] Implement ranges::iota: Adding tests to ranges_robust_against_* tests (proxy iterators is failing currently). --- libcxx/include/__numeric/ranges_iota.h | 7 +-- ...result_alias_declarations.compile.pass.cpp | 7 ++- .../ranges_robust_against_dangling.pass.cpp | 43 +++++++++++-------- ...es_robust_against_proxy_iterators.pass.cpp | 22 ++++++---- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h index eca5712b3d0d4bd..9e55c1d1ea89257 100644 --- a/libcxx/include/__numeric/ranges_iota.h +++ b/libcxx/include/__numeric/ranges_iota.h @@ -44,13 +44,14 @@ struct __iota_fn { public: // Public facing interfaces template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp> - requires indirectly_writable<_Out, const _Tp&> - constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) const { + requires indirectly_writable<_Out, const _Tp&> _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp> + operator()(_Out __first, _Sent __last, _Tp __value) { return __iota_impl(std::move(__first), std::move(__last), std::move(__value)); } template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range> - constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> operator()(_Range&& __r, _Tp __value) const { + _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> + operator()(_Range&& __r, _Tp __value) { return __iota_impl(ranges::begin(__r), ranges::end(__r), std::move(__value)); } }; diff --git a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp index a72c3a374c504c2..6f82d8f8e719131 100644 --- a/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_result_alias_declarations.compile.pass.cpp @@ -13,9 +13,12 @@ // ensure that all result alias declarations are defined #include <algorithm> +#include <numeric> #include <memory> #include <type_traits> +#include "test_macros.h" + using namespace std::ranges; static_assert(std::is_same_v<in_fun_result<int, long>, for_each_result<int, long>>); @@ -59,4 +62,6 @@ static_assert(std::is_same_v<min_max_result<int>, minmax_element_result<int>>); static_assert(std::is_same_v<in_found_result<int>, next_permutation_result<int>>); static_assert(std::is_same_v<in_found_result<int>, prev_permutation_result<int>>); -// static_assert(std::is_same_v<out_value_result<int>, iota_result<int>>); +#if TEST_STD_VER >= 23 +static_assert(std::is_same_v<out_value_result<int, int>, iota_result<int, int>>); +#endif diff --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp index 1057c747990d68b..64db7933cd3d344 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp @@ -13,7 +13,7 @@ // Range algorithms should return `std::ranges::dangling` when given a dangling range. #include <algorithm> - +#include <numeric> #include <array> #include <concepts> #include <functional> @@ -23,6 +23,7 @@ #include <utility> #include "test_iterators.h" +#include "test_macros.h" struct NonBorrowedRange { using Iter = int*; @@ -41,22 +42,22 @@ struct NonBorrowedRange { using R = NonBorrowedRange; // (dangling_in, ...) -template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class ...Args> -constexpr void dangling_1st(Func&& func, Input& in, Args&& ...args) { +template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class... Args> +constexpr void dangling_1st(Func&& func, Input& in, Args&&... args) { decltype(auto) result = func(R(in), std::forward<Args>(args)...); static_assert(std::same_as<decltype(result), ExpectedT>); } // (in, dangling_in, ...) -template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class ...Args> -constexpr void dangling_2nd(Func&& func, Input& in1, Input& in2, Args&& ...args) { +template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class... Args> +constexpr void dangling_2nd(Func&& func, Input& in1, Input& in2, Args&&... args) { decltype(auto) result = func(in1, R(in2), std::forward<Args>(args)...); static_assert(std::same_as<decltype(result), ExpectedT>); } // (dangling_in1, dangling_in2, ...) -template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class ...Args> -constexpr void dangling_both(Func&& func, Input& in1, Input& in2, Args&& ...args) { +template <class ExpectedT = std::ranges::dangling, class Func, std::ranges::range Input, class... Args> +constexpr void dangling_both(Func&& func, Input& in1, Input& in2, Args&&... args) { decltype(auto) result = func(R(in1), R(in2), std::forward<Args>(args)...); static_assert(std::same_as<decltype(result), ExpectedT>); } @@ -68,23 +69,24 @@ constexpr bool test_all() { using std::ranges::dangling; using std::ranges::binary_transform_result; - using std::ranges::copy_result; using std::ranges::copy_backward_result; using std::ranges::copy_if_result; + using std::ranges::copy_result; using std::ranges::for_each_result; using std::ranges::merge_result; using std::ranges::minmax_result; using std::ranges::mismatch_result; - using std::ranges::move_result; using std::ranges::move_backward_result; + using std::ranges::move_result; using std::ranges::next_permutation_result; + using std::ranges::out_value_result; using std::ranges::partial_sort_copy_result; using std::ranges::partition_copy_result; using std::ranges::prev_permutation_result; - using std::ranges::remove_copy_result; using std::ranges::remove_copy_if_result; - using std::ranges::replace_copy_result; + using std::ranges::remove_copy_result; using std::ranges::replace_copy_if_result; + using std::ranges::replace_copy_result; using std::ranges::reverse_copy_result; using std::ranges::rotate_copy_result; using std::ranges::set_difference_result; @@ -95,20 +97,20 @@ constexpr bool test_all() { using std::ranges::unary_transform_result; using std::ranges::unique_copy_result; - auto unary_pred = [](int i) { return i > 0; }; + auto unary_pred = [](int i) { return i > 0; }; auto binary_pred = [](int i, int j) { return i < j; }; - auto gen = [] { return 42; }; + auto gen = [] { return 42; }; - std::array in = {1, 2, 3}; + std::array in = {1, 2, 3}; std::array in2 = {4, 5, 6}; auto mid = in.begin() + 1; std::array output = {7, 8, 9, 10, 11, 12}; - auto out = output.begin(); - auto out2 = output.begin() + 1; + auto out = output.begin(); + auto out2 = output.begin() + 1; - int x = 2; + int x = 2; std::size_t count = 1; dangling_1st(std::ranges::find, in, x); @@ -140,7 +142,8 @@ constexpr bool test_all() { dangling_1st(std::ranges::fill, in, x); { // transform std::array out_transform = {false, true, true}; - dangling_1st<unary_transform_result<dangling, bool*>>(std::ranges::transform, in, out_transform.begin(), unary_pred); + dangling_1st<unary_transform_result<dangling, bool*>>( + std::ranges::transform, in, out_transform.begin(), unary_pred); dangling_1st<binary_transform_result<dangling, int*, bool*>>( std::ranges::transform, in, in2, out_transform.begin(), binary_pred); dangling_2nd<binary_transform_result<int*, dangling, bool*>>( @@ -206,6 +209,10 @@ constexpr bool test_all() { dangling_1st<prev_permutation_result<dangling>>(std::ranges::prev_permutation, in); dangling_1st<next_permutation_result<dangling>>(std::ranges::next_permutation, in); +#if TEST_STD_VER >= 23 + dangling_1st< out_value_result<dangling, decltype(x)>>(std::ranges::iota, in, x); +#endif + return true; } diff --git a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp index 5c8aa0153a63c3f..638a6398883d25b 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp @@ -14,6 +14,7 @@ // a customization point) rather than plain `swap` (which might not work with certain valid iterators). #include <algorithm> +#include <numeric> #include <array> #include <concepts> @@ -28,22 +29,22 @@ #include "test_macros.h" // (in, ...) -template <class Func, std::ranges::range Input, class ...Args> -constexpr void test(Func&& func, Input& in, Args&& ...args) { +template <class Func, std::ranges::range Input, class... Args> +constexpr void test(Func&& func, Input& in, Args&&... args) { (void)func(in.begin(), in.end(), std::forward<Args>(args)...); (void)func(in, std::forward<Args>(args)...); } // (in1, in2, ...) -template <class Func, std::ranges::range Range1, std::ranges::range Range2, class ...Args> -constexpr void test(Func&& func, Range1& r1, Range2& r2, Args&& ...args) { +template <class Func, std::ranges::range Range1, std::ranges::range Range2, class... Args> +constexpr void test(Func&& func, Range1& r1, Range2& r2, Args&&... args) { (void)func(r1.begin(), r1.end(), r2.begin(), r2.end(), std::forward<Args>(args)...); (void)func(r1, r2, std::forward<Args>(args)...); } // (in, mid, ...) -template <class Func, std::ranges::range Input, class ...Args> -constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&& ...args) { +template <class Func, std::ranges::range Input, class... Args> +constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&&... args) { (void)func(in.begin(), mid, in.end(), std::forward<Args>(args)...); (void)func(in, mid, std::forward<Args>(args)...); } @@ -68,9 +69,9 @@ constexpr void run_tests() { Proxy<T&> x{num}; int count = 1; - auto unary_pred = [](const Proxy<T&>&) { return true; }; + auto unary_pred = [](const Proxy<T&>&) { return true; }; auto binary_func = [](const Proxy<T>&, const Proxy<T>&) -> Proxy<T> { return Proxy<T>(T()); }; - auto gen = [] { return Proxy<T>(T{42}); }; + auto gen = [] { return Proxy<T>(T{42}); }; test(std::ranges::any_of, in, unary_pred); test(std::ranges::all_of, in, unary_pred); @@ -100,6 +101,11 @@ constexpr void run_tests() { test(std::ranges::search, in, in2); test(std::ranges::search_n, in, count, x); test(std::ranges::find_end, in, in2); +#if TEST_STD_VER >= 23 + if constexpr (std::copyable<T>) { + test(std::ranges::iota, in, x); + } +#endif test(std::ranges::is_partitioned, in, unary_pred); test(std::ranges::is_sorted, in); test(std::ranges::is_sorted_until, in); >From 67fcac1fa4e47ac7231d4aa755596e01eb73036c Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Thu, 26 Oct 2023 17:23:38 -0400 Subject: [PATCH 09/14] [libc++] Implement ranges::iota: Fixing formatting problems --- libcxx/include/__numeric/ranges_iota.h | 4 ++-- .../numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h index 9e55c1d1ea89257..fccb36396a057f7 100644 --- a/libcxx/include/__numeric/ranges_iota.h +++ b/libcxx/include/__numeric/ranges_iota.h @@ -44,8 +44,8 @@ struct __iota_fn { public: // Public facing interfaces template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp> - requires indirectly_writable<_Out, const _Tp&> _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp> - operator()(_Out __first, _Sent __last, _Tp __value) { + requires indirectly_writable<_Out, const _Tp&> + _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) { return __iota_impl(std::move(__first), std::move(__last), std::move(__value)); } diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp index 7aedf7ee058445a..f3cd022943cafb8 100644 --- a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp +++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp @@ -26,9 +26,8 @@ concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) { }; template <class Range, class Value = int> -concept HasIotaRange = requires(Range&& range, Value&& val) { - std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); -}; +concept HasIotaRange = + requires(Range&& range, Value&& val) { std::ranges::iota(std::forward<Range>(range), std::forward<Value>(val)); }; constexpr void test_constraints() { // Test constraints of the iterator/sentinel overload >From b13278430d0be6d4b4f14a8260b3dd12dbfa872e Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Thu, 26 Oct 2023 18:41:21 -0400 Subject: [PATCH 10/14] [libc++] Implement ranges::iota: Addressing comments about out_value_result tests from https://reviews.llvm.org/D121436 --- libcxx/include/__algorithm/out_value_result.h | 4 +- .../no_unique_address.compile.pass.cpp | 4 + .../out_value_result.pass.cpp | 80 +++++++++++-------- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/libcxx/include/__algorithm/out_value_result.h b/libcxx/include/__algorithm/out_value_result.h index a99d206f7035ff2..9e1e0e07286d46f 100644 --- a/libcxx/include/__algorithm/out_value_result.h +++ b/libcxx/include/__algorithm/out_value_result.h @@ -34,13 +34,13 @@ struct out_value_result { template <class _OutIter2, class _ValType2> requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> - constexpr operator out_value_result<_OutIter2, _ValType2>() const& { + _LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_OutIter2, _ValType2>() const& { return {out, value}; } template <class _OutIter2, class _ValType2> requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2> - constexpr operator out_value_result<_OutIter2, _ValType2>() && { + _LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_OutIter2, _ValType2>() && { return {std::move(out), std::move(value)}; } }; diff --git a/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp index 8e0a81959f27c81..c67b937590544a1 100644 --- a/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp +++ b/libcxx/test/std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp @@ -53,6 +53,10 @@ static_assert(sizeof(std::ranges::in_out_out_result<Empty, Empty, char>) == 2); static_assert(sizeof(std::ranges::in_out_out_result<int, Empty, Empty2>) == sizeof(int)); static_assert(sizeof(std::ranges::in_out_out_result<Empty, Empty, Empty>) == 3); +static_assert(sizeof(std::ranges::out_value_result<Empty, int>) == sizeof(int)); +static_assert(sizeof(std::ranges::out_value_result<int, Empty>) == sizeof(int)); +static_assert(sizeof(std::ranges::out_value_result<Empty, Empty>) == 2); + // In min_max_result both elements have the same type, so they can't have the same address. // So the only way to test that [[no_unique_address]] is used is to have it in another struct struct MinMaxNoUniqueAddress { diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp index e1d203929be738c..99dfbd42050d178 100644 --- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp +++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp @@ -17,6 +17,8 @@ #include "MoveOnly.h" +using std::ranges::out_value_result; + // // Helper structs // @@ -43,62 +45,61 @@ struct ConvertibleFrom { T content; }; +// Standard layout classes can't have virtual functions +struct NonStandardLayoutTypeBase { + virtual ~NonStandardLayoutTypeBase(); +}; +struct NonStandardLayoutType : public NonStandardLayoutTypeBase {}; + // constexpr void test_constraints() { // requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> - static_assert( - std::is_constructible_v<std::ranges::out_value_result<int*, int>, std::ranges::out_value_result<int*, int>>); + static_assert(std::is_constructible_v<out_value_result<int*, int>, out_value_result<int*, int>>); // test failure when implicit conversion isn't allowed - static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeExplicit, int>, - std::ranges::out_value_result<int*, int>>); + static_assert(!std::is_constructible_v<out_value_result<IterTypeExplicit, int>, out_value_result<int*, int>>); // test success when implicit conversion is allowed, checking combinations of value, reference, and const - static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>, - std::ranges::out_value_result<int*, int>>); - static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>, - std::ranges::out_value_result<int*, int> const>); - static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>, - std::ranges::out_value_result<int*, int>&>); - static_assert(std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicit, int>, - std::ranges::out_value_result<int*, int> const&>); - - static_assert(!std::is_constructible_v<std::ranges::out_value_result<IterTypeImplicitRef, int>, - std::ranges::out_value_result<int, int>&>); + static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int>>); + static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int> const>); + static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int>&>); + static_assert(std::is_constructible_v<out_value_result<IterTypeImplicit, int>, out_value_result<int*, int> const&>); + + static_assert(!std::is_constructible_v<out_value_result<IterTypeImplicitRef, int>, out_value_result<int, int>&>); // has to be convertible via const& - static_assert( - std::is_convertible_v<std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>); - static_assert( - std::is_convertible_v<const std::ranges::out_value_result<int, int>&, std::ranges::out_value_result<long, long>>); - static_assert( - std::is_convertible_v<std::ranges::out_value_result<int, int>&&, std::ranges::out_value_result<long, long>>); - static_assert(std::is_convertible_v<const std::ranges::out_value_result<int, int>&&, - std::ranges::out_value_result<long, long>>); + static_assert(std::is_convertible_v<out_value_result<int, int>&, out_value_result<long, long>>); + static_assert(std::is_convertible_v<const out_value_result<int, int>&, out_value_result<long, long>>); + static_assert(std::is_convertible_v<out_value_result<int, int>&&, out_value_result<long, long>>); + static_assert(std::is_convertible_v<const out_value_result<int, int>&&, out_value_result<long, long>>); // should be move constructible - static_assert(std::is_move_constructible_v<std::ranges::out_value_result<MoveOnly, int>>); - static_assert(std::is_move_constructible_v<std::ranges::out_value_result<int, MoveOnly>>); + static_assert(std::is_move_constructible_v<out_value_result<MoveOnly, int>>); + static_assert(std::is_move_constructible_v<out_value_result<int, MoveOnly>>); // conversions should not work if there is no conversion - static_assert(!std::is_convertible_v<std::ranges::out_value_result<NotConvertible, int>, - std::ranges::out_value_result<int, NotConvertible>>); - static_assert(!std::is_convertible_v<std::ranges::out_value_result<int, NotConvertible>, - std::ranges::out_value_result<NotConvertible, int>>); + static_assert(!std::is_convertible_v<out_value_result<NotConvertible, int>, out_value_result<int, NotConvertible>>); + static_assert(!std::is_convertible_v<out_value_result<int, NotConvertible>, out_value_result<NotConvertible, int>>); + + // check standard layout + static_assert(std::is_standard_layout_v<out_value_result<int, int>>); + static_assert(!std::is_standard_layout_v<out_value_result<NonStandardLayoutType, int>>); } // Test results constexpr bool test() { { - std::ranges::out_value_result<double, int> res{10, 1}; + // Check that conversion operator works + out_value_result<double, int> res{10, 1}; assert(res.out == 10); assert(res.value == 1); - std::ranges::out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res; + out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> res2 = res; assert(res2.out.content == 10); assert(res2.value.content == 1); } { - std::ranges::out_value_result<MoveOnly, int> res{MoveOnly{}, 10}; + // Check that out_value_result isn't overconstrained w.r.t. move/copy constructors + out_value_result<MoveOnly, int> res{MoveOnly{}, 10}; assert(res.out.get() == 1); assert(res.value == 10); auto res2 = std::move(res); @@ -108,10 +109,23 @@ constexpr bool test() { assert(res2.value == 10); } { - auto [out, val] = std::ranges::out_value_result<int, int>{1, 2}; + // Check structured binding + auto [out, val] = out_value_result<int, int>{1, 2}; assert(out == 1); assert(val == 2); } + { + // Check default construction + out_value_result<int, double> res; + static_assert(std::is_same_v<int, decltype(res.out)>); + static_assert(std::is_same_v<double, decltype(res.value)>); + } + { + // Check aggregate initiazliation + out_value_result res = {1, 2}; + assert(res.out == 1); + assert(res.value == 2); + } return true; } >From 88e271a619f555d56f99f50c1e2b0172727914c7 Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Fri, 27 Oct 2023 19:03:47 -0400 Subject: [PATCH 11/14] [libc++] Implement ranges::iota: Updating Proxy<T> in test_iterators.h to be weakly_incrementable for certain T (which we need to test ranges::iota). test_iterators.h is not formatted because a lot of the code outside of my changes would get reformatted and I don't want to overcomplicate the diff. --- libcxx/test/support/test_iterators.h | 56 +++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h index 1133b9597d09cfb..7559e347d90f596 100644 --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -1083,6 +1083,31 @@ rvalue_iterator(T*) -> rvalue_iterator<T>; static_assert(std::random_access_iterator<rvalue_iterator<int*>>); +// The ProxyDiffTBase allows us to conditionally specify Proxy<T>::difference_type +// which we need in certain situations. For example when we want +// std::weakly_incrementable<Proxy<T>> to be true. +template <class T> +struct ProxyDiffTBase {}; + +template <class T> + requires requires { std::iter_difference_t<T>{}; } +struct ProxyDiffTBase<T> { + using difference_type = std::iter_difference_t<T>; +}; + +// These concepts allow us to conditionally add the pre-/postfix operators +// when T also supports those member functions. Like ProxyDiffTBase, this +// is necessary when we want std::weakly_incrementable<Proxy<T>> to be true. +template <class T> +concept HasPreIncrementOp = requires(T const& obj) { + ++obj; +}; + +template <class T> +concept HasPostIncrementOp = requires(T const& obj) { + obj++; +}; + // Proxy // ====================================================================== // Proxy that can wrap a value or a reference. It simulates C++23's tuple @@ -1093,6 +1118,7 @@ static_assert(std::random_access_iterator<rvalue_iterator<int*>>); // This class is useful for testing that if algorithms support proxy iterator // properly, i.e. calling ranges::iter_swap and ranges::iter_move instead of // plain swap and std::move. + template <class T> struct Proxy; @@ -1103,7 +1129,7 @@ template <class T> inline constexpr bool IsProxy<Proxy<T>> = true; template <class T> -struct Proxy { +struct Proxy : ProxyDiffTBase<T> { T data; constexpr T& getData() & { return data; } @@ -1149,9 +1175,11 @@ struct Proxy { // Calling swap(Proxy<T>{}, Proxy<T>{}) would fail (pass prvalues) // Compare operators are defined for the convenience of the tests - friend constexpr bool operator==(const Proxy&, const Proxy&) + friend constexpr bool operator==(const Proxy& lhs, const Proxy& rhs) requires (std::equality_comparable<T> && !std::is_reference_v<T>) - = default; + { + return lhs.data == rhs.data; + }; // Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default equality comparison operator is deleted // when `T` is a reference type. @@ -1161,9 +1189,11 @@ struct Proxy { return lhs.data == rhs.data; } - friend constexpr auto operator<=>(const Proxy&, const Proxy&) + friend constexpr auto operator<=>(const Proxy& lhs, const Proxy& rhs) requires (std::three_way_comparable<T> && !std::is_reference_v<T>) - = default; + { + return lhs.data <=> rhs.data; + }; // Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default 3-way comparison operator is deleted when // `T` is a reference type. @@ -1172,6 +1202,22 @@ struct Proxy { requires std::three_way_comparable_with<std::decay_t<T>, std::decay_t<U>> { return lhs.data <=> rhs.data; } + + // Needed to allow certain types to be weakly_incremental + constexpr Proxy& operator++() + requires(HasPreIncrementOp<T>) + { + ++data; + return *this; + } + + constexpr Proxy operator++(int) + requires(HasPostIncrementOp<T>) + { + Proxy tmp = *this; + operator++(); + return tmp; + } }; // This is to make ProxyIterator model `std::indirectly_readable` >From ff5bf791be9d650495151edd7c126b0827722e79 Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Sat, 28 Oct 2023 11:44:15 -0400 Subject: [PATCH 12/14] [libc++] Implement ranges::iota: Fixing some of the buildkite errors, modules are still not working locally --- .../out_value_result.pass.cpp | 2 +- .../numeric.version.compile.pass.cpp | 32 ++++++------------- .../version.version.compile.pass.cpp | 32 ++++++------------- 3 files changed, 21 insertions(+), 45 deletions(-) diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp index 99dfbd42050d178..f935fc54feb64fd 100644 --- a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp +++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp @@ -122,7 +122,7 @@ constexpr bool test() { } { // Check aggregate initiazliation - out_value_result res = {1, 2}; + out_value_result<int, int> res = {1, 2}; assert(res.out == 1); assert(res.value == 2); } diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp index 60004b06c5ff528..75ab121a2416130 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp @@ -180,17 +180,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should be defined in c++23" -# endif -# if __cpp_lib_ranges_iota != 202202L -# error "__cpp_lib_ranges_iota should have the value 202202L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_iota +# error "__cpp_lib_ranges_iota should be defined in c++23" +# endif +# if __cpp_lib_ranges_iota != 202202L +# error "__cpp_lib_ranges_iota should have the value 202202L in c++23" # endif #elif TEST_STD_VER > 23 @@ -229,17 +223,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should be defined in c++26" -# endif -# if __cpp_lib_ranges_iota != 202202L -# error "__cpp_lib_ranges_iota should have the value 202202L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_iota +# error "__cpp_lib_ranges_iota should be defined in c++26" +# endif +# if __cpp_lib_ranges_iota != 202202L +# error "__cpp_lib_ranges_iota should have the value 202202L in c++26" # endif #endif // TEST_STD_VER > 23 diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index ed86c555221c542..56c294683538f7f 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -4972,17 +4972,11 @@ # error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should be defined in c++23" -# endif -# if __cpp_lib_ranges_iota != 202202L -# error "__cpp_lib_ranges_iota should have the value 202202L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_iota +# error "__cpp_lib_ranges_iota should be defined in c++23" +# endif +# if __cpp_lib_ranges_iota != 202202L +# error "__cpp_lib_ranges_iota should have the value 202202L in c++23" # endif # if !defined(_LIBCPP_VERSION) @@ -6507,17 +6501,11 @@ # error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should be defined in c++26" -# endif -# if __cpp_lib_ranges_iota != 202202L -# error "__cpp_lib_ranges_iota should have the value 202202L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_iota +# error "__cpp_lib_ranges_iota should be defined in c++26" +# endif +# if __cpp_lib_ranges_iota != 202202L +# error "__cpp_lib_ranges_iota should have the value 202202L in c++26" # endif # if !defined(_LIBCPP_VERSION) >From b8f07b8fcb63cf88320734e8c7c2d98fcbc0e800 Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Mon, 30 Oct 2023 11:23:39 -0400 Subject: [PATCH 13/14] [libc++] Implement ranges::iota: Explicitly adding #include for ranges::borrowed_iterator_t which was missing from module builds. Not sure why this hasn't caused problems before --- libcxx/include/__numeric/ranges_iota.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h index fccb36396a057f7..914d67a6d443b2d 100644 --- a/libcxx/include/__numeric/ranges_iota.h +++ b/libcxx/include/__numeric/ranges_iota.h @@ -13,6 +13,7 @@ #include <__algorithm/out_value_result.h> #include <__config> #include <__ranges/concepts.h> +#include <__ranges/dangling.h> #include <__utility/as_const.h> #include <__utility/move.h> >From a4d34fd33830c1302200b87e6776299881957754 Mon Sep 17 00:00:00 2001 From: James Smith <james.smith9...@gmail.com> Date: Mon, 30 Oct 2023 12:04:02 -0400 Subject: [PATCH 14/14] [libc++] Implement ranges::iota: Explicitly adding #include for ranges::begin because the module builds are complaining that it's missing --- libcxx/include/__numeric/ranges_iota.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libcxx/include/__numeric/ranges_iota.h b/libcxx/include/__numeric/ranges_iota.h index 914d67a6d443b2d..3b92d4e94554ae1 100644 --- a/libcxx/include/__numeric/ranges_iota.h +++ b/libcxx/include/__numeric/ranges_iota.h @@ -12,6 +12,7 @@ #include <__algorithm/out_value_result.h> #include <__config> +#include <__ranges/access.h> #include <__ranges/concepts.h> #include <__ranges/dangling.h> #include <__utility/as_const.h> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits