diff options
Diffstat (limited to 'libcxx')
22 files changed, 585 insertions, 68 deletions
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index bbcc76b..9015ccb 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -374,7 +374,7 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_find_last`` ``202207L`` ---------------------------------------------------------- ----------------- - ``__cpp_lib_ranges_iota`` *unimplemented* + ``__cpp_lib_ranges_iota`` ``202202L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_join_with`` *unimplemented* ---------------------------------------------------------- ----------------- diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst index 3a922ad..0f9d4bf 100644 --- a/libcxx/docs/Status/Cxx23.rst +++ b/libcxx/docs/Status/Cxx23.rst @@ -40,4 +40,4 @@ Library Working Group Issues Status .. csv-table:: :file: Cxx23Issues.csv :header-rows: 1 - :widths: auto + :widths: auto
\ No newline at end of file diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index 4e45deb..923d8bf 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -46,7 +46,7 @@ "`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","Implemented the type traits only." "`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","" "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","" -"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","","","" +"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","Only ``ranges::iota`` is implemented." "`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|In Progress|","","" "`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","" "`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index a021b9b..7b09beb 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -64,6 +64,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 @@ -615,6 +616,7 @@ set(files __numeric/midpoint.h __numeric/partial_sum.h __numeric/pstl.h + __numeric/ranges_iota.h __numeric/reduce.h __numeric/saturation_arithmetic.h __numeric/transform_exclusive_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 0000000..9e1e0e0 --- /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 <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> + _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> + _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 0000000..bf53dd8 --- /dev/null +++ b/libcxx/include/__numeric/ranges_iota.h @@ -0,0 +1,65 @@ +// -*- 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 <__iterator/concepts.h> +#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_PUSH_MACROS +#include <__undef_macros> + +_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 { +public: + template <input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp> + requires indirectly_writable<_Out, const _Tp&> + _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<_Out, _Tp> operator()(_Out __first, _Sent __last, _Tp __value) { + while (__first != __last) { + *__first = std::as_const(__value); + ++__first; + ++__value; + } + return {std::move(__first), std::move(__value)}; + } + + template <weakly_incrementable _Tp, ranges::output_range<const _Tp&> _Range> + _LIBCPP_HIDE_FROM_ABI static constexpr iota_result<ranges::borrowed_iterator_t<_Range>, _Tp> + operator()(_Range&& __r, _Tp __value) { + return operator()(ranges::begin(__r), ranges::end(__r), std::move(__value)); + } +}; + +inline constexpr auto iota = __iota_fn{}; +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___NUMERIC_RANGES_IOTA_H diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index 6ba903a..bf67d33 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -45,6 +45,9 @@ namespace ranges { template <class I, class T> struct in_value_result; // since C++23 + template <class O, class T> + struct out_value_result; // since C++23 + template<forward_iterator I, sentinel_for<I> S, class Proj = identity, indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less> // since C++20 constexpr I min_element(I first, S last, Comp comp = {}, Proj proj = {}); @@ -1936,6 +1939,7 @@ template <class BidirectionalIterator, class Compare> # include <__algorithm/in_out_result.h> # include <__algorithm/lexicographical_compare_three_way.h> # include <__algorithm/min_max_result.h> +# include <__algorithm/out_value_result.h> # include <__algorithm/ranges_adjacent_find.h> # include <__algorithm/ranges_all_of.h> # include <__algorithm/ranges_any_of.h> diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 0ce42fc..324931b1 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -490,6 +490,7 @@ module std [system] { module next_permutation { header "__algorithm/next_permutation.h" } module none_of { header "__algorithm/none_of.h" } module nth_element { header "__algorithm/nth_element.h" } + module out_value_result { header "__algorithm/out_value_result.h" } module partial_sort_copy { header "__algorithm/partial_sort_copy.h" } module partial_sort { header "__algorithm/partial_sort.h" } module partition_copy { header "__algorithm/partition_copy.h" } @@ -1727,6 +1728,7 @@ module std [system] { module partial_sum { header "__numeric/partial_sum.h" } module pstl { header "__numeric/pstl.h" } module reduce { header "__numeric/reduce.h" } + module ranges_iota { header "__numeric/ranges_iota.h" } module saturation_arithmetic { header "__numeric/saturation_arithmetic.h" } module transform_exclusive_scan { header "__numeric/transform_exclusive_scan.h" } module transform_inclusive_scan { header "__numeric/transform_inclusive_scan.h" } diff --git a/libcxx/include/numeric b/libcxx/include/numeric index 2f2b861..48c330f 100644 --- a/libcxx/include/numeric +++ b/libcxx/include/numeric @@ -172,6 +172,7 @@ constexpr T saturate_cast(U x) noexcept; // freestanding, Sin # include <__numeric/gcd_lcm.h> # include <__numeric/inclusive_scan.h> # include <__numeric/pstl.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 83ae11d..4910271 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -513,7 +513,7 @@ __cpp_lib_void_t 201411L <type_traits> # define __cpp_lib_ranges_chunk_by 202202L # define __cpp_lib_ranges_contains 202207L # define __cpp_lib_ranges_find_last 202207L -// # define __cpp_lib_ranges_iota 202202L +# define __cpp_lib_ranges_iota 202202L // # define __cpp_lib_ranges_join_with 202202L # define __cpp_lib_ranges_repeat 202207L // # define __cpp_lib_ranges_slide 202202L diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc index 3c2139c..95c05f0 100644 --- a/libcxx/modules/std/algorithm.inc +++ b/libcxx/modules/std/algorithm.inc @@ -20,7 +20,9 @@ export namespace std { using std::ranges::in_value_result; #endif 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 3bc7b23..5a54955 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 34dbd64..70da332 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,7 +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); -#if TEST_STD_VER >= 23 +#if _LIBCPP_STD_VER >= 23 +static_assert(sizeof(std::ranges::out_value_result<Empty, int>) == sizeof(int)); +static_assert(sizeof(std::ranges::out_value_result<int, Empty>) == sizeof(int)); +static_assert(sizeof(std::ranges::out_value_result<Empty, Empty>) == 2); static_assert(sizeof(std::ranges::in_value_result<Empty, int>) == sizeof(int)); static_assert(sizeof(std::ranges::in_value_result<int, Empty>) == sizeof(int)); 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 0000000..7bdbb7f --- /dev/null +++ b/libcxx/test/std/algorithms/algorithms.results/out_value_result.pass.cpp @@ -0,0 +1,141 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +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 <class T> +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 bool test_constraints() { + // requires convertible_to<const _OutIter1&, _OutIter2> && convertible_to<const _ValType1&, _ValType2> + static_assert(std::is_constructible_v<out_value_result<int*, int>, out_value_result<int*, int>>); + + // test failure when implicit conversion isn't allowed + 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<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<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<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<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>>); + + return true; +} + +// Test results +constexpr bool test() { + { + // Check that conversion operator works + out_value_result<double, int> res{10, 1}; + assert(res.out == 10); + assert(res.value == 1); + out_value_result<ConvertibleFrom<double>, ConvertibleFrom<int>> 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<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); + } + { + // 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<int, int> res = {1, 2}; + assert(res.out == 1); + assert(res.value == 2); + } + + return true; +} + +int main(int, char**) { + test_constraints(); + static_assert(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 6940b23..dcf2509 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,6 +13,7 @@ // ensure that all result alias declarations are defined #include <algorithm> +#include <numeric> #include <memory> #include <type_traits> @@ -62,9 +63,6 @@ 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>>); #if TEST_STD_VER >= 23 - static_assert(std::is_same_v<in_value_result<int, long>, fold_left_with_iter_result<int, long>>); - -// static_assert(std::is_same_v<out_value_result<int>, iota_result<int>>); - +static_assert(std::is_same_v<out_value_result<int, int>, iota_result<int, int>>); #endif // TEST_STD_VER 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 2691269..e0a6aaa 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp @@ -18,11 +18,13 @@ #include <concepts> #include <functional> #include <iterator> +#include <numeric> #include <ranges> #include <random> #include <utility> #include "test_iterators.h" +#include "test_macros.h" struct NonBorrowedRange { using Iter = int*; @@ -78,6 +80,9 @@ constexpr bool test_all() { using std::ranges::move_result; using std::ranges::move_backward_result; using std::ranges::next_permutation_result; +#if TEST_STD_VER >= 23 + using std::ranges::out_value_result; +#endif using std::ranges::partial_sort_copy_result; using std::ranges::partition_copy_result; using std::ranges::prev_permutation_result; @@ -217,6 +222,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 bebaeb0..9d4b0d6 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); @@ -106,6 +107,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::weakly_incrementable<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); 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 d132b7c..0743c6c 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/numeric.version.compile.pass.cpp @@ -197,17 +197,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should be defined in c++23" -# endif -# if __cpp_lib_ranges_iota != 202202L -# error "__cpp_lib_ranges_iota should have the value 202202L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_iota +# error "__cpp_lib_ranges_iota should be defined in c++23" +# endif +# if __cpp_lib_ranges_iota != 202202L +# error "__cpp_lib_ranges_iota should have the value 202202L in c++23" # endif # ifdef __cpp_lib_saturation_arithmetic @@ -250,17 +244,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should be defined in c++26" -# endif -# if __cpp_lib_ranges_iota != 202202L -# error "__cpp_lib_ranges_iota should have the value 202202L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_iota +# error "__cpp_lib_ranges_iota should be defined in c++26" +# endif +# if __cpp_lib_ranges_iota != 202202L +# error "__cpp_lib_ranges_iota should have the value 202202L in c++26" # endif # ifndef __cpp_lib_saturation_arithmetic diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index e5a6572..07e96e5 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 @@ -5771,17 +5771,11 @@ # error "__cpp_lib_ranges_find_last should have the value 202207L in c++23" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should be defined in c++23" -# endif -# if __cpp_lib_ranges_iota != 202202L -# error "__cpp_lib_ranges_iota should have the value 202202L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_iota +# error "__cpp_lib_ranges_iota should be defined in c++23" +# endif +# if __cpp_lib_ranges_iota != 202202L +# error "__cpp_lib_ranges_iota should have the value 202202L in c++23" # endif # if !defined(_LIBCPP_VERSION) @@ -7664,17 +7658,11 @@ # error "__cpp_lib_ranges_find_last should have the value 202207L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should be defined in c++26" -# endif -# if __cpp_lib_ranges_iota != 202202L -# error "__cpp_lib_ranges_iota should have the value 202202L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_iota -# error "__cpp_lib_ranges_iota should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_iota +# error "__cpp_lib_ranges_iota should be defined in c++26" +# endif +# if __cpp_lib_ranges_iota != 202202L +# error "__cpp_lib_ranges_iota should have the value 202202L in c++26" # endif # if !defined(_LIBCPP_VERSION) 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 0000000..9fa50f1 --- /dev/null +++ b/libcxx/test/std/numerics/numeric.ops/numeric.iota/ranges.iota.pass.cpp @@ -0,0 +1,215 @@ +//===----------------------------------------------------------------------===// +// +// 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 <algorithm> +#include <array> +#include <cassert> +#include <numeric> +#include <utility> + +#include "almost_satisfies_types.h" +#include "test_iterators.h" +#include "test_macros.h" + +// +// Testing constraints +// + +// Concepts to check different overloads of std::ranges::iota +template <class Iter = int*, class Sent = int*, class Value = int> +concept HasIotaIter = requires(Iter&& iter, Sent&& sent, Value&& val) { + 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)); }; + +// 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>); + +// +// Testing results +// + +struct DangerousCopyAssign { + int val; + using difference_type = int; + + constexpr explicit DangerousCopyAssign(int v) : val(v) {} + + // Needed in postfix + constexpr DangerousCopyAssign(DangerousCopyAssign const& other) { this->val = other.val; } + + /* + This class has a "mischievous" non-const overload of copy-assignment + operator that modifies the object being assigned from. `ranges::iota` + should not be invoking this overload thanks to the `std::as_const` in its + implementation. If for some reason it does invoke it, there will be a compiler + error. + */ + constexpr DangerousCopyAssign& operator=(DangerousCopyAssign& a) = delete; + + // safe copy assignment std::as_const inside ranges::iota should ensure this + // overload gets called + constexpr DangerousCopyAssign& operator=(DangerousCopyAssign const& a) { + this->val = a.val; + return *this; + } + + constexpr bool operator==(DangerousCopyAssign const& rhs) { return this->val == rhs.val; } + + // prefix + constexpr DangerousCopyAssign& operator++() { + ++(this->val); + return *this; + } + + // postfix + constexpr DangerousCopyAssign operator++(int) { + auto tmp = *this; + ++this->val; + return tmp; + } +}; + +template <class 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); + assert(result.value == starting_value + static_cast<int>(N)); + assert(std::ranges::equal(input, expected)); + } + + { // (range) overload + // in the range overload adds the additional constraint that it must be an output range + // so skip this for the input iterators we test + auto in_begin = Iter(input.data()); + auto in_end = Sent(Iter(input.data() + input.size())); + auto range = std::ranges::subrange(std::move(in_begin), std::move(in_end)); + + std::same_as<std::ranges::out_value_result<Iter, int>> decltype(auto) result = + std::ranges::iota(range, starting_value); + assert(result.out == in_end); + assert(result.value == starting_value + static_cast<int>(N)); + assert(std::ranges::equal(input, expected)); + } +} + +template <class Iter, class Sent = sentinel_wrapper<Iter>> +constexpr void test_results() { + // Empty + test_result<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}); +} + +constexpr void test_user_defined_type() { + // Simple non-fundamental type + struct UserDefinedType { + int val; + using difference_type = int; + + constexpr explicit UserDefinedType(int v) : val(v) {} + constexpr UserDefinedType(UserDefinedType const& other) { this->val = other.val; } + constexpr UserDefinedType& operator=(UserDefinedType const& a) { + this->val = a.val; + return *this; + } + + // prefix + constexpr UserDefinedType& operator++() { + ++(this->val); + return *this; + } + + // postfix + constexpr UserDefinedType operator++(int) { + auto tmp = *this; + ++this->val; + return tmp; + } + }; + + // Setup + using A = UserDefinedType; + std::array<UserDefinedType, 5> a = {A{0}, A{0}, A{0}, A{0}, A{0}}; + std::array<UserDefinedType, 5> expected = {A{0}, A{1}, A{2}, A{3}, A{4}}; + + // Fill with values + std::ranges::iota(a, A{0}); + auto proj_val = [](UserDefinedType const& el) { return el.val; }; + + // Check + assert(std::ranges::equal(a, expected, std::ranges::equal_to{}, proj_val, proj_val)); +} + +constexpr void test_dangerous_copy_assign() { + using A = DangerousCopyAssign; + + // If the dangerous non-const copy assignment is called, the final values in + // aa should increment by 2 rather than 1. + std::array<A, 3> aa = {A{0}, A{0}, A{0}}; + std::array<A, 3> expected = {A{0}, A{1}, A{2}}; + std::ranges::iota(aa, A{0}); + auto proj_val = [](DangerousCopyAssign const& el) { return el.val; }; + assert(std::ranges::equal(aa, expected, std::ranges::equal_to{}, proj_val, proj_val)); +} + +constexpr bool test_results() { + // Tests on fundamental types + types::for_each(types::cpp17_input_iterator_list<int*>{}, []<class Iter> { test_results< Iter>(); }); + test_results<cpp17_output_iterator<int*>>(); + test_results<cpp20_output_iterator<int*>>(); + test_results<int*, sized_sentinel<int*>>(); + + // Tests on non-fundamental types + test_user_defined_type(); + test_dangerous_copy_assign(); + return true; +} + +int main(int, char**) { + test_results(); + static_assert(test_results()); + return 0; +} diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h index ead8a3e..702b82b 100644 --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -1177,6 +1177,23 @@ 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 { + // Add default `operator<=>` so that the derived type, Proxy, can also use the default `operator<=>` + friend constexpr auto operator<=>(const ProxyDiffTBase&, const ProxyDiffTBase&) = default; +}; + +template <class T> + requires requires { std::iter_difference_t<T>{}; } +struct ProxyDiffTBase<T> { + using difference_type = std::iter_difference_t<T>; + // Add default `operator<=>` so that the derived type, Proxy, can also use the default `operator<=>` + friend constexpr auto operator<=>(const ProxyDiffTBase&, const ProxyDiffTBase&) = default; +}; + // Proxy // ====================================================================== // Proxy that can wrap a value or a reference. It simulates C++23's tuple @@ -1187,6 +1204,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; @@ -1197,7 +1215,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; } @@ -1248,7 +1266,7 @@ struct Proxy { // Compare operators are defined for the convenience of the tests friend constexpr bool operator==(const Proxy&, const Proxy&) - requires (std::equality_comparable<T> && !std::is_reference_v<T>) + requires(std::equality_comparable<T> && !std::is_reference_v<T>) = default; // Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default equality comparison operator is deleted @@ -1260,7 +1278,7 @@ struct Proxy { } friend constexpr auto operator<=>(const Proxy&, const Proxy&) - requires (std::three_way_comparable<T> && !std::is_reference_v<T>) + requires(std::three_way_comparable<T> && !std::is_reference_v<T>) = default; // Helps compare e.g. `Proxy<int>` and `Proxy<int&>`. Note that the default 3-way comparison operator is deleted when @@ -1270,6 +1288,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_incrementable + constexpr Proxy& operator++() + requires(std::weakly_incrementable<std::remove_reference_t<T>>) + { + ++data; + return *this; + } + + constexpr Proxy operator++(int) + requires(std::incrementable<std::remove_reference_t<T>>) + { + 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 53252d5..febfb0f 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1081,7 +1081,6 @@ feature_test_macros = [ "name": "__cpp_lib_ranges_iota", "values": {"c++23": 202202}, "headers": ["numeric"], - "unimplemented": True, }, { "name": "__cpp_lib_ranges_join_with", |