diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 10afd64e0a3946..e48e6f3ecd36d3 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/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index 19b1dd8eb5a446..3dee364c2b2956 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -46,7 +46,7 @@ "`P2255R2 `__","LWG","A type trait to detect reference binding to temporary","February 2022","","" "`P2273R3 `__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0" "`P2387R3 `__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|" -"`P2440R1 `__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|" +"`P2440R1 `__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","|In progress|","","|ranges|" "`P2441R2 `__","LWG","``views::join_with``","February 2022","|In Progress|","","|ranges|" "`P2442R1 `__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|" "`P2443R1 `__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|" diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv index 17c8953bf8d85d..e6dda790bf158d 100644 --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -10,3 +10,4 @@ C++23,`shift_right `_,Unassigned,No patch yet,Not sta C++23,`iota (algorithm) `_,Unassigned,No patch yet,Not started C++23,`fold `_,Unassigned,No patch yet,Not started C++23,`contains `_,Zijun Zhao,No patch yet,In Progress +C++23,`ranges::iota `_, James E T Smith, `PR68494 `_, In Progress diff --git a/libcxx/docs/Status/RangesMajorFeatures.csv b/libcxx/docs/Status/RangesMajorFeatures.csv index c0bec8d924e8a9..c0479d1c2708d8 100644 --- a/libcxx/docs/Status/RangesMajorFeatures.csv +++ b/libcxx/docs/Status/RangesMajorFeatures.csv @@ -2,4 +2,5 @@ Standard,Name,Assignee,CL,Status C++23,`ranges::to `_,Konstantin Varlamov,`D142335 `_,Complete C++23,`Pipe support for user-defined range adaptors `_,Unassigned,No patch yet,Not started C++23,`Formatting Ranges `_,Mark de Wever,Various,Complete +C++23, `ranges::iota `_, James E T Smith, `PR68494 `_, In Progress C++20,`Stashing stashing iterators for proper flattening `_,Jakub Mazurkiewicz,Various,In progress diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index b7b14200498a29..5a7421f45a09dd 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 @@ -564,6 +565,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 00000000000000..9e1e0e07286d46 --- /dev/null +++ b/libcxx/include/__algorithm/out_value_result.h @@ -0,0 +1,56 @@ +// -*- 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 +struct out_value_result { + _LIBCPP_NO_UNIQUE_ADDRESS _OutIter1 out; + _LIBCPP_NO_UNIQUE_ADDRESS _ValType1 value; + + template + requires convertible_to && convertible_to + _LIBCPP_HIDE_FROM_ABI constexpr operator out_value_result<_OutIter2, _ValType2>() const& { + return {out, value}; + } + + template + requires convertible_to<_OutIter1, _OutIter2> && convertible_to<_ValType1, _ValType2> + _LIBCPP_HIDE_FROM_ABI 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 00000000000000..3b92d4e94554ae --- /dev/null +++ b/libcxx/include/__numeric/ranges_iota.h @@ -0,0 +1,71 @@ +// -*- 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/access.h> +#include <__ranges/concepts.h> +#include <__ranges/dangling.h> +#include <__utility/as_const.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 +namespace ranges { +template +using iota_result = ranges::out_value_result<_Out, _Tp>; + +namespace __ranges_iota { +struct __iota_fn { +private: + // Private helper function + template + _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; + ++__value; + } + return {std::move(__first), std::move(__value)}; + } + +public: + // Public facing interfaces + template _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 _Range> + _LIBCPP_HIDE_FROM_ABI static constexpr iota_result, _Tp> + operator()(_Range&& __r, _Tp __value) { + 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 +} // 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 578de74d401dc2..a30c585c35e24f 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -42,6 +42,9 @@ namespace ranges { template struct in_found_result; // since C++20 + template + struct out_value_result; // since C++23 + template S, class Proj = identity, indirect_strict_weak_order> Comp = ranges::less> // since C++20 constexpr I min_element(I first, S last, Comp comp = {}, Proj proj = {}); @@ -1816,6 +1819,7 @@ template #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/module.modulemap.in b/libcxx/include/module.modulemap.in index f447b2fa0a0ce9..23571e64da5db0 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1596,6 +1596,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/include/numeric b/libcxx/include/numeric index d09d0a81fcc03a..0d80e26fa5055d 100644 --- a/libcxx/include/numeric +++ b/libcxx/include/numeric @@ -159,6 +159,7 @@ template #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 c41ce745b59304..a5206251cfe3fb 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -436,7 +436,7 @@ __cpp_lib_within_lifetime 202306L # 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/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc index b7900d15c10c2b..3ee048e3f8170f 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; - // using std::ranges::out_value_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/modules/std/numeric.inc b/libcxx/modules/std/numeric.inc index d2b7688d4e5f10..9204e8d8715935 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/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 8e0a81959f27c8..83a9e4c7da9028 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,11 @@ static_assert(sizeof(std::ranges::in_out_out_result) == 2); static_assert(sizeof(std::ranges::in_out_out_result) == sizeof(int)); static_assert(sizeof(std::ranges::in_out_out_result) == 3); +#if _LIBCPP_STD_VER >= 23 +static_assert(sizeof(std::ranges::out_value_result) == sizeof(int)); +static_assert(sizeof(std::ranges::out_value_result) == sizeof(int)); +static_assert(sizeof(std::ranges::out_value_result) == 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 { diff --git a/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp new file mode 100644 index 00000000000000..f935fc54feb64f --- /dev/null +++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// struct out_value_result; + +#include +#include +#include + +#include "MoveOnly.h" + +using std::ranges::out_value_result; + +// +// Helper structs +// + +// only explicit construction +struct IterTypeExplicit { + explicit IterTypeExplicit(int*); +}; + +// implicit construction +struct IterTypeImplicit { + IterTypeImplicit(int*); +}; + +struct IterTypeImplicitRef { + IterTypeImplicitRef(int&); +}; + +struct NotConvertible {}; + +template +struct ConvertibleFrom { + constexpr ConvertibleFrom(T c) : content{c} {} + 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 && convertible_to + static_assert(std::is_constructible_v, out_value_result>); + + // test failure when implicit conversion isn't allowed + static_assert(!std::is_constructible_v, out_value_result>); + + // test success when implicit conversion is allowed, checking combinations of value, reference, and const + static_assert(std::is_constructible_v, out_value_result>); + static_assert(std::is_constructible_v, out_value_result const>); + static_assert(std::is_constructible_v, out_value_result&>); + static_assert(std::is_constructible_v, out_value_result const&>); + + static_assert(!std::is_constructible_v, out_value_result&>); + + // has to be convertible via const& + static_assert(std::is_convertible_v&, out_value_result>); + static_assert(std::is_convertible_v&, out_value_result>); + static_assert(std::is_convertible_v&&, out_value_result>); + static_assert(std::is_convertible_v&&, out_value_result>); + + // should be move constructible + static_assert(std::is_move_constructible_v>); + static_assert(std::is_move_constructible_v>); + + // conversions should not work if there is no conversion + static_assert(!std::is_convertible_v, out_value_result>); + static_assert(!std::is_convertible_v, out_value_result>); + + // check standard layout + static_assert(std::is_standard_layout_v>); + static_assert(!std::is_standard_layout_v>); +} + +// Test results +constexpr bool test() { + { + // Check that conversion operator works + out_value_result res{10, 1}; + assert(res.out == 10); + assert(res.value == 1); + out_value_result, ConvertibleFrom> res2 = res; + assert(res2.out.content == 10); + assert(res2.value.content == 1); + } + { + // Check that out_value_result isn't overconstrained w.r.t. move/copy constructors + out_value_result 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); + } + { + // Check structured binding + auto [out, val] = out_value_result{1, 2}; + assert(out == 1); + assert(val == 2); + } + { + // Check default construction + out_value_result res; + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + { + // Check aggregate initiazliation + out_value_result res = {1, 2}; + assert(res.out == 1); + assert(res.value == 2); + } + + return true; +} + +int main(int, char**) { + test_constraints(); + test(); + static_assert(test()); + return 0; +} 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 a72c3a374c504c..6f82d8f8e71913 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 +#include #include #include +#include "test_macros.h" + using namespace std::ranges; static_assert(std::is_same_v, for_each_result>); @@ -59,4 +62,6 @@ static_assert(std::is_same_v, minmax_element_result>); static_assert(std::is_same_v, next_permutation_result>); static_assert(std::is_same_v, prev_permutation_result>); -// static_assert(std::is_same_v, iota_result>); +#if TEST_STD_VER >= 23 +static_assert(std::is_same_v, iota_result>); +#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 1057c747990d68..64db7933cd3d34 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 - +#include #include #include #include @@ -23,6 +23,7 @@ #include #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 -constexpr void dangling_1st(Func&& func, Input& in, Args&& ...args) { +template +constexpr void dangling_1st(Func&& func, Input& in, Args&&... args) { decltype(auto) result = func(R(in), std::forward(args)...); static_assert(std::same_as); } // (in, dangling_in, ...) -template -constexpr void dangling_2nd(Func&& func, Input& in1, Input& in2, Args&& ...args) { +template +constexpr void dangling_2nd(Func&& func, Input& in1, Input& in2, Args&&... args) { decltype(auto) result = func(in1, R(in2), std::forward(args)...); static_assert(std::same_as); } // (dangling_in1, dangling_in2, ...) -template -constexpr void dangling_both(Func&& func, Input& in1, Input& in2, Args&& ...args) { +template +constexpr void dangling_both(Func&& func, Input& in1, Input& in2, Args&&... args) { decltype(auto) result = func(R(in1), R(in2), std::forward(args)...); static_assert(std::same_as); } @@ -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>(std::ranges::transform, in, out_transform.begin(), unary_pred); + dangling_1st>( + std::ranges::transform, in, out_transform.begin(), unary_pred); dangling_1st>( std::ranges::transform, in, in2, out_transform.begin(), binary_pred); dangling_2nd>( @@ -206,6 +209,10 @@ constexpr bool test_all() { dangling_1st>(std::ranges::prev_permutation, in); dangling_1st>(std::ranges::next_permutation, in); +#if TEST_STD_VER >= 23 + dangling_1st< out_value_result>(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 5c8aa0153a63c3..638a6398883d25 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 +#include #include #include @@ -28,22 +29,22 @@ #include "test_macros.h" // (in, ...) -template -constexpr void test(Func&& func, Input& in, Args&& ...args) { +template +constexpr void test(Func&& func, Input& in, Args&&... args) { (void)func(in.begin(), in.end(), std::forward(args)...); (void)func(in, std::forward(args)...); } // (in1, in2, ...) -template -constexpr void test(Func&& func, Range1& r1, Range2& r2, Args&& ...args) { +template +constexpr void test(Func&& func, Range1& r1, Range2& r2, Args&&... args) { (void)func(r1.begin(), r1.end(), r2.begin(), r2.end(), std::forward(args)...); (void)func(r1, r2, std::forward(args)...); } // (in, mid, ...) -template -constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t mid, Args&& ...args) { +template +constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t mid, Args&&... args) { (void)func(in.begin(), mid, in.end(), std::forward(args)...); (void)func(in, mid, std::forward(args)...); } @@ -68,9 +69,9 @@ constexpr void run_tests() { Proxy x{num}; int count = 1; - auto unary_pred = [](const Proxy&) { return true; }; + auto unary_pred = [](const Proxy&) { return true; }; auto binary_func = [](const Proxy&, const Proxy&) -> Proxy { return Proxy(T()); }; - auto gen = [] { return Proxy(T{42}); }; + auto gen = [] { return Proxy(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) { + 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); 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 60004b06c5ff52..75ab121a241613 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 ed86c555221c54..56c294683538f7 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) diff --git a/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp new file mode 100644 index 00000000000000..f3cd022943cafb --- /dev/null +++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp @@ -0,0 +1,123 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" +#include "almost_satisfies_types.h" + +// Concepts to check different overloads of std::ranges::iota +template +concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) { + std::ranges::iota(std::forward(iter), std::forward(sent), std::forward(val)); +}; + +template +concept HasIotaRange = + requires(Range&& range, Value&& val) { std::ranges::iota(std::forward(range), std::forward(val)); }; + +constexpr void test_constraints() { + // Test constraints of the iterator/sentinel overload + // ================================================== + static_assert(HasIotaIter); + + // !input_or_output_iterator + static_assert(!HasIotaIter); + + // !sentinel_for + static_assert(!HasIotaIter); + static_assert(!HasIotaIter); + + // !weakly_incrementable + static_assert(!HasIotaIter); + + // !indirectly writable + static_assert(!HasIotaIter); + + // Test constraints for the range overload + // ======================================= + static_assert(HasIotaRange, int>); + + // !weakly_incrementable + static_assert(!HasIotaRange, WeaklyIncrementableNotMovable>); + + // !ranges::output_range + static_assert(!HasIotaRange, OutputIteratorNotIndirectlyWritable>); +} + +template +constexpr void test_result(std::array input, int starting_value, std::array const expected) { + { // (iterator, sentinel) overload + auto in_begin = Iter(input.data()); + auto in_end = Sent(Iter(input.data() + input.size())); + std::same_as> 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)); + } + + // 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> && + !std::is_same_v>) { // (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> 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)); + } +} + +template > +constexpr void test_results() { + // Empty + test_result({}, 0, {}); + // 1-element sequence + test_result({1}, 0, {0}); + // Longer sequence + test_result({1, 2, 3, 4, 5}, 0, {0, 1, 2, 3, 4}); +} + +void test_results() { + test_results>(); + test_results>(); + test_results>(); + test_results>(); + test_results>(); + test_results>(); + test_results>(); + test_results>(); + test_results(); +} + +int main(int, char**) { + test_constraints(); + test_results(); + return 0; +} diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h index 1133b9597d09cf..7559e347d90f59 100644 --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -1083,6 +1083,31 @@ rvalue_iterator(T*) -> rvalue_iterator; static_assert(std::random_access_iterator>); +// The ProxyDiffTBase allows us to conditionally specify Proxy::difference_type +// which we need in certain situations. For example when we want +// std::weakly_incrementable> to be true. +template +struct ProxyDiffTBase {}; + +template + requires requires { std::iter_difference_t{}; } +struct ProxyDiffTBase { + using difference_type = std::iter_difference_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> to be true. +template +concept HasPreIncrementOp = requires(T const& obj) { + ++obj; +}; + +template +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>); // 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 struct Proxy; @@ -1103,7 +1129,7 @@ template inline constexpr bool IsProxy> = true; template -struct Proxy { +struct Proxy : ProxyDiffTBase { T data; constexpr T& getData() & { return data; } @@ -1149,9 +1175,11 @@ struct Proxy { // Calling swap(Proxy{}, Proxy{}) 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 && !std::is_reference_v) - = default; + { + return lhs.data == rhs.data; + }; // Helps compare e.g. `Proxy` and `Proxy`. 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 && !std::is_reference_v) - = default; + { + return lhs.data <=> rhs.data; + }; // Helps compare e.g. `Proxy` and `Proxy`. 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> { return lhs.data <=> rhs.data; } + + // Needed to allow certain types to be weakly_incremental + constexpr Proxy& operator++() + requires(HasPreIncrementOp) + { + ++data; + return *this; + } + + constexpr Proxy operator++(int) + requires(HasPostIncrementOp) + { + Proxy tmp = *this; + operator++(); + return tmp; + } }; // This is to make ProxyIterator model `std::indirectly_readable` diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 76faa275d2d7e2..d0dcfd047cff06 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -804,7 +804,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", diff --git a/test b/test new file mode 100644 index 00000000000000..139597f9cb07c5 --- /dev/null +++ b/test @@ -0,0 +1,2 @@ + +