diff --git a/stl/inc/algorithm b/stl/inc/algorithm index a4b5675357..a4b5e3b9a7 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10,6 +10,10 @@ #if _STL_COMPILER_PREPROCESSOR #include +#if _HAS_CXX23 +#include +#endif // _HAS_CXX23 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -210,6 +214,26 @@ namespace ranges { return {_STD move(in), found}; } }; + +#if _HAS_CXX23 + _EXPORT_STD template + struct in_value_result { + /* [[no_unique_address]] */ _In in; + /* [[no_unique_address]] */ _Ty value; + + template + requires convertible_to && convertible_to + constexpr operator in_value_result<_IIn, _TTy>() const& { + return {in, value}; + } + + template + requires convertible_to<_In, _IIn> && convertible_to<_Ty, _TTy> + constexpr operator in_value_result<_IIn, _TTy>() && { + return {_STD move(in), _STD move(value)}; + } + }; +#endif // _HAS_CXX23 } // namespace ranges #endif // __cpp_lib_concepts @@ -2512,6 +2536,253 @@ namespace ranges { }; _EXPORT_STD inline constexpr _Ends_with_fn ends_with{_Not_quite_object::_Construct_tag{}}; + + template + class _Flipped { + private: + _Fn _Func; + + public: + template + requires invocable<_Fn&, _Uty, _Ty> + invoke_result_t<_Fn&, _Uty, _Ty> operator()(_Ty&&, _Uty&&); + }; + + template + concept _Indirectly_binary_left_foldable_impl = + movable<_Ty> && movable<_Uty> && convertible_to<_Ty, _Uty> && invocable<_Fn&, _Uty, iter_reference_t<_It>> + && assignable_from<_Uty&, invoke_result_t<_Fn&, _Uty, iter_reference_t<_It>>>; + + template + concept _Indirectly_binary_left_foldable = + copy_constructible<_Fn> && indirectly_readable<_It> && invocable<_Fn&, _Ty, iter_reference_t<_It>> + && convertible_to>, + decay_t>>> + && _Indirectly_binary_left_foldable_impl<_Fn, _Ty, _It, + decay_t>>>; + + template + concept _Indirectly_binary_right_foldable = _Indirectly_binary_left_foldable<_Flipped<_Fn>, _Ty, _It>; + + _EXPORT_STD template + using fold_left_with_iter_result = in_value_result<_It, _Ty>; + + _EXPORT_STD template + using fold_left_first_with_iter_result = in_value_result<_It, _Ty>; + + class _Fold_left_with_iter_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Ty, _Indirectly_binary_left_foldable<_Ty, _It> _Fn> + _NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const { + _Adl_verify_range(_First, _Last); + return _Fold_left_with_iter_impl<_It>( + _STD move(_First), _STD move(_Last), _STD move(_Init), _Pass_fn(_Func)); + } + + template > _Fn> + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const { + return _Fold_left_with_iter_impl>( + _RANGES begin(_Range), _RANGES end(_Range), _STD move(_Init), _Pass_fn(_Func)); + } + + private: + template + _NODISCARD constexpr auto _Fold_left_with_iter_impl(_It&& _First, _Se&& _Last, _Ty&& _Init, _Fn _Func) const { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_left_foldable<_Fn, _Ty, _It>); + + using _Uty = decay_t>>; + using _Return_type = fold_left_with_iter_result<_RetIt, _Uty>; + + if (_First == _Last) { + return _Return_type{_STD move(_First), static_cast<_Uty>(_STD move(_Init))}; + } else { + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); + auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); + + _Uty _Accum = _STD invoke(_Func, _STD move(_Init), *_UFirst); + for (++_UFirst; _UFirst != _ULast; ++_UFirst) { + _Accum = _STD invoke(_Func, _STD move(_Accum), *_UFirst); + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return _Return_type{_STD move(_First), _STD move(_Accum)}; + } + } + }; + + _EXPORT_STD inline constexpr _Fold_left_with_iter_fn fold_left_with_iter{_Not_quite_object::_Construct_tag{}}; + + class _Fold_left_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Ty, _Indirectly_binary_left_foldable<_Ty, _It> _Fn> + _NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const { + return _RANGES fold_left_with_iter(_STD move(_First), _Last, _STD move(_Init), _Pass_fn(_Func)).value; + } + + template > _Fn> + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const { + return _RANGES fold_left_with_iter(_STD forward<_Rng>(_Range), _STD move(_Init), _Pass_fn(_Func)).value; + } + }; + + _EXPORT_STD inline constexpr _Fold_left_fn fold_left{_Not_quite_object::_Construct_tag{}}; + + class _Fold_left_first_with_iter_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, + _Indirectly_binary_left_foldable, _It> _Fn> + requires constructible_from, iter_reference_t<_It>> + _NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const { + _Adl_verify_range(_First, _Last); + return _Fold_left_first_with_iter_impl<_It>(_STD move(_First), _STD move(_Last), _Pass_fn(_Func)); + } + + template , iterator_t<_Rng>> _Fn> + requires constructible_from, range_reference_t<_Rng>> + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const { + return _Fold_left_first_with_iter_impl>( + _RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Func)); + } + + private: + template + _NODISCARD constexpr auto _Fold_left_first_with_iter_impl(_It&& _First, _Se&& _Last, _Fn _Func) const { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_left_foldable<_Fn, iter_value_t<_It>, _It>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_reference_t<_It>>); + + using _Uty = + decltype(_RANGES fold_left(_STD move(_First), _Last, static_cast>(*_First), _Func)); + using _Return_type = fold_left_first_with_iter_result<_RetIt, optional<_Uty>>; + if (_First == _Last) { + return _Return_type{_STD move(_First), optional<_Uty>{}}; + } else { + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); + auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); + + optional<_Uty> _Init{in_place, *_UFirst}; + _Uty& _Init_ref = *_Init; + for (++_UFirst; _UFirst != _ULast; ++_UFirst) { + _Init_ref = _STD invoke(_Func, _STD move(_Init_ref), *_UFirst); + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return _Return_type{_STD move(_First), _STD move(_Init)}; + } + } + }; + + _EXPORT_STD inline constexpr _Fold_left_first_with_iter_fn fold_left_first_with_iter{ + _Not_quite_object::_Construct_tag{}}; + + class _Fold_left_first_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, + _Indirectly_binary_left_foldable, _It> _Fn> + requires constructible_from, iter_reference_t<_It>> + _NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const { + return _RANGES fold_left_first_with_iter(_STD move(_First), _STD move(_Last), _Pass_fn(_Func)).value; + } + + template , iterator_t<_Rng>> _Fn> + requires constructible_from, range_reference_t<_Rng>> + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const { + return _RANGES fold_left_first_with_iter(_STD forward<_Rng>(_Range), _Pass_fn(_Func)).value; + } + }; + + _EXPORT_STD inline constexpr _Fold_left_first_fn fold_left_first{_Not_quite_object::_Construct_tag{}}; + + template + _NODISCARD constexpr auto _Fold_right_unchecked(_It _First, _Se _Last, _Ty _Init, _Fn _Func) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_right_foldable<_Fn, _Ty, _It>); + + using _Uty = decay_t, _Ty>>; + if (_First == _Last) { + return static_cast<_Uty>(_STD move(_Init)); + } else { + _It _Tail = _RANGES next(_First, _Last); + _Uty _Accum = _STD invoke(_Func, *--_Tail, _STD move(_Init)); + while (_First != _Tail) { + _Accum = _STD invoke(_Func, *--_Tail, _STD move(_Accum)); + } + return _Accum; + } + } + + class _Fold_right_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Ty, + _Indirectly_binary_right_foldable<_Ty, _It> _Fn> + _NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Ty _Init, _Fn _Func) const { + _Adl_verify_range(_First, _Last); + return _Fold_right_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Last)), + _STD move(_Init), _Pass_fn(_Func)); + } + + template > _Fn> + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Ty _Init, _Fn _Func) const { + return _Fold_right_unchecked(_Ubegin(_Range), _Uend(_Range), _STD move(_Init), _Pass_fn(_Func)); + } + }; + + _EXPORT_STD inline constexpr _Fold_right_fn fold_right{_Not_quite_object::_Construct_tag{}}; + + class _Fold_right_last_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, + _Indirectly_binary_right_foldable, _It> _Fn> + requires constructible_from, iter_reference_t<_It>> + _NODISCARD constexpr auto operator()(_It _First, _Se _Last, _Fn _Func) const { + _Adl_verify_range(_First, _Last); + return _Fold_right_last_unchecked( + _Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Last)), _Pass_fn(_Func)); + } + + template , iterator_t<_Rng>> _Fn> + requires constructible_from, range_reference_t<_Rng>> + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Func) const { + return _Fold_right_last_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Func)); + } + + private: + template + _NODISCARD constexpr auto _Fold_right_last_unchecked(_It _First, _Se _Last, _Fn _Func) const { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(_Indirectly_binary_right_foldable<_Fn, iter_value_t<_It>, _It>); + _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_reference_t<_It>>); + + using _Uty = decltype(_RANGES fold_right(_First, _Last, static_cast>(*_First), _Func)); + if (_First == _Last) { + return optional<_Uty>{}; + } else { + _It _Tail = _RANGES prev(_RANGES next(_First, _STD move(_Last))); + return optional<_Uty>{in_place, _RANGES _Fold_right_unchecked(_STD move(_First), _Tail, + static_cast>(*_Tail), _STD move(_Func))}; + } + } + }; + + _EXPORT_STD inline constexpr _Fold_right_last_fn fold_right_last{_Not_quite_object::_Construct_tag{}}; #endif // _HAS_CXX23 } // namespace ranges #endif // __cpp_lib_concepts diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 8e91d51528..ea6dfbd35b 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -330,6 +330,7 @@ // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip // (missing views::zip_transform, views::adjacent, and views::adjacent_transform) +// P2322R6 ranges::fold_left, ranges::fold_right, Etc. // P2387R3 Pipe Support For User-Defined Range Adaptors // P2417R2 More constexpr bitset // P2438R2 string::substr() && @@ -1655,6 +1656,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_ranges_chunk 202202L #define __cpp_lib_ranges_chunk_by 202202L #define __cpp_lib_ranges_contains 202207L +#define __cpp_lib_ranges_fold 202207L #define __cpp_lib_ranges_iota 202202L #define __cpp_lib_ranges_join_with 202202L #define __cpp_lib_ranges_slide 202202L diff --git a/tests/std/test.lst b/tests/std/test.lst index bea60da307..89059b4307 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -552,6 +552,7 @@ tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference tests\P2321R2_views_zip +tests\P2322R6_ranges_alg_fold tests\P2387R3_bind_back tests\P2387R3_pipe_support_for_user_defined_range_adaptors tests\P2401R0_conditional_noexcept_for_exchange diff --git a/tests/std/tests/P2322R6_ranges_alg_fold/env.lst b/tests/std/tests/P2322R6_ranges_alg_fold/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P2322R6_ranges_alg_fold/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2322R6_ranges_alg_fold/test.cpp b/tests/std/tests/P2322R6_ranges_alg_fold/test.cpp new file mode 100644 index 0000000000..66e6105596 --- /dev/null +++ b/tests/std/tests/P2322R6_ranges_alg_fold/test.cpp @@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +struct instantiator { + static constexpr double some_doubles[] = {0.1, 0.2, 0.3}; + static constexpr double left_sum = 0.1 + 0.2 + 0.3; + static constexpr double left_difference = 0.1 - 0.2 - 0.3; + static constexpr double right_product = 0.1 * (0.2 * 0.3); + + template + static constexpr void call() { + using ranges::fold_left, ranges::fold_left_first, ranges::fold_left_with_iter, + ranges::fold_left_first_with_iter, ranges::fold_right, ranges::fold_right_last, + ranges::fold_left_with_iter_result, ranges::fold_left_first_with_iter_result, ranges::begin, ranges::end; + + constexpr auto empty_rng = views::empty; + const auto vec_of_doubles = some_doubles | ranges::to(); + + { // Validate fold_left iterator+sentinel overload + const Rng wrapped{some_doubles}; + const same_as auto sum1 = fold_left(begin(wrapped), end(wrapped), 0.0, plus{}); + assert(sum1 == left_sum); + + const same_as auto sum2 = fold_left(begin(vec_of_doubles), end(vec_of_doubles), 0.0, plus{}); + assert(sum2 == left_sum); + + const double single_value = 3.14; + const same_as auto sum3 = fold_left(begin(empty_rng), end(empty_rng), single_value, plus{}); + assert(sum3 == single_value); + } + + { // Validate fold_left range overload + const same_as auto sum1 = fold_left(Rng{some_doubles}, 0.0, plus{}); + assert(sum1 == left_sum); + + const same_as auto sum2 = fold_left(vec_of_doubles, 0.0, plus{}); + assert(sum2 == left_sum); + + const double single_value = 3.14; + const same_as auto sum3 = fold_left(empty_rng, single_value, plus{}); + assert(sum3 == single_value); + } + + { // Validate fold_left_first iterator+sentinel overload + const Rng wrapped{some_doubles}; + const same_as> auto diff1 = fold_left_first(begin(wrapped), end(wrapped), minus{}); + assert(diff1 == left_difference); + + const same_as> auto diff2 = + fold_left_first(begin(vec_of_doubles), end(vec_of_doubles), minus{}); + assert(diff2 == left_difference); + + const same_as> auto diff3 = + fold_left_first(begin(empty_rng), end(empty_rng), minus{}); + assert(diff3 == nullopt); + } + + { // Validate fold_left_first range overload + const same_as> auto diff1 = fold_left_first(Rng{some_doubles}, minus{}); + assert(diff1 == left_difference); + + const same_as> auto diff2 = fold_left_first(vec_of_doubles, minus{}); + assert(diff2 == left_difference); + + const same_as> auto diff3 = fold_left_first(empty_rng, minus{}); + assert(diff3 == nullopt); + } + + { // Validate fold_left_with_iter iterator+sentinel overload + const Rng wrapped{some_doubles}; + const same_as, double>> auto sum1 = + fold_left_with_iter(begin(wrapped), end(wrapped), 0.0, plus{}); + assert(sum1.in == end(wrapped)); + assert(sum1.value == left_sum); + + const same_as::const_iterator, double>> auto sum2 = + fold_left_with_iter(begin(vec_of_doubles), end(vec_of_doubles), 0.0, plus{}); + assert(sum2.in == end(vec_of_doubles)); + assert(sum2.value == left_sum); + + const double single_value = 3.14; + const same_as> auto sum3 = + fold_left_with_iter(begin(empty_rng), end(empty_rng), single_value, plus{}); + assert(sum3.in == empty_rng.end()); + assert(sum3.value == single_value); + } + + { // Validate fold_left_with_iter range overload + const Rng wrapped{some_doubles}; + const same_as, double>> auto sum1 = + fold_left_with_iter(wrapped, 0.0, plus{}); + assert(sum1.in == end(wrapped)); + assert(sum1.value == left_sum); + + const same_as::const_iterator, double>> auto sum2 = + fold_left_with_iter(vec_of_doubles, 0.0, plus{}); + assert(sum2.in == end(vec_of_doubles)); + assert(sum2.value == left_sum); + + const same_as> auto sum3 = + fold_left_with_iter(some_doubles | ranges::to(), 0.0, plus{}); + assert(sum3.value == left_sum); + + const double single_value = 3.14; + const same_as> auto sum4 = + fold_left_with_iter(empty_rng, single_value, plus{}); + assert(sum4.in == empty_rng.end()); + assert(sum4.value == single_value); + } + + { // Validate fold_left_first_with_iter iterator+sentinel overload + const Rng wrapped{some_doubles}; + const same_as, optional>> auto diff1 = + fold_left_first_with_iter(begin(wrapped), end(wrapped), minus{}); + assert(diff1.in == end(wrapped)); + assert(diff1.value == left_difference); + + const same_as::const_iterator, optional>> auto + diff2 = fold_left_first_with_iter(begin(vec_of_doubles), end(vec_of_doubles), minus{}); + assert(diff2.in == end(vec_of_doubles)); + assert(diff2.value == left_difference); + + const same_as>> auto diff3 = + fold_left_first_with_iter(begin(empty_rng), end(empty_rng), minus{}); + assert(diff3.in == end(empty_rng)); + assert(diff3.value == nullopt); + } + + { // Validate fold_left_first_with_iter range overload + const Rng wrapped{some_doubles}; + const same_as, optional>> auto diff1 = + fold_left_first_with_iter(wrapped, minus{}); + assert(diff1.in == end(wrapped)); + assert(diff1.value == left_difference); + + const same_as::const_iterator, optional>> auto + diff2 = fold_left_first_with_iter(vec_of_doubles, minus{}); + assert(diff2.in == end(vec_of_doubles)); + assert(diff2.value == left_difference); + + const same_as>> auto diff3 = + fold_left_first_with_iter(empty_rng, minus{}); + assert(diff3.in == end(empty_rng)); + assert(diff3.value == nullopt); + + const same_as>> auto diff4 = + fold_left_first_with_iter(some_doubles | ranges::to(), minus{}); + assert(diff4.value == left_difference); + } + + if constexpr (ranges::bidirectional_range) { + { // Validate fold_right iterator+sentinel overload + const Rng wrapped{some_doubles}; + const same_as auto prod1 = fold_right(begin(wrapped), end(wrapped), 1.0, multiplies{}); + assert(prod1 == right_product); + + const same_as auto prod2 = + fold_right(begin(vec_of_doubles), end(vec_of_doubles), 1.0, multiplies{}); + assert(prod2 == right_product); + + const double single_value = 3.14; + const same_as auto prod3 = + fold_right(begin(empty_rng), end(empty_rng), single_value, multiplies{}); + assert(prod3 == single_value); + } + + { // Validate fold_right range overload + const same_as auto prod1 = fold_right(Rng{some_doubles}, 1.0, multiplies{}); + assert(prod1 == right_product); + + const same_as auto prod2 = fold_right(vec_of_doubles, 1.0, multiplies{}); + assert(prod2 == right_product); + + const double single_value = 3.14; + const same_as auto prod3 = fold_right(empty_rng, single_value, multiplies{}); + assert(prod3 == single_value); + } + + { // Validate fold_right_last iterator+sentinel overload + const Rng wrapped{some_doubles}; + const same_as> auto prod1 = + fold_right_last(begin(wrapped), end(wrapped), multiplies{}); + assert(prod1 == right_product); + + const same_as> auto prod2 = + fold_right_last(begin(vec_of_doubles), end(vec_of_doubles), multiplies{}); + assert(prod2 == right_product); + + const same_as> auto prod3 = + fold_right_last(begin(empty_rng), end(empty_rng), multiplies{}); + assert(prod3 == nullopt); + } + + { // Validate fold_right_last range overload + const same_as> auto prod1 = fold_right_last(Rng{some_doubles}, multiplies{}); + assert(prod1 == right_product); + + const same_as> auto prod2 = fold_right_last(vec_of_doubles, multiplies{}); + assert(prod2 == right_product); + + const same_as> auto prod3 = fold_right_last(empty_rng, multiplies{}); + assert(prod3 == nullopt); + } + } + } +}; + +constexpr bool test_in_value_result() { + using ranges::in_value_result; + STATIC_ASSERT(is_aggregate_v>); + STATIC_ASSERT(is_trivial_v>); + + in_value_result res = {nullptr, 6}; + { // Validate binding + auto [in, value] = res; + assert(in == nullptr); + assert(value == 6); + } + + { // Validate conversion operator (const &) + in_value_result long_res = res; + assert(long_res.in == nullptr); + assert(long_res.value == 6L); + } + + { // Validate conversion operator (&&) + in_value_result long_long_res = std::move(res); + assert(long_long_res.in == nullptr); + assert(long_long_res.value == 6LL); + } + + return true; +} + +int main() { + STATIC_ASSERT((test_in(), true)); + test_in(); + + STATIC_ASSERT((test_bidi(), true)); + test_bidi(); + + STATIC_ASSERT(test_in_value_result()); + assert(test_in_value_result()); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index d1da9e6ebe..674bda681d 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1560,6 +1560,20 @@ STATIC_ASSERT(__cpp_lib_ranges_contains == 202207L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges_fold +#error __cpp_lib_ranges_fold is not defined +#elif __cpp_lib_ranges_fold != 202207L +#error __cpp_lib_ranges_fold is not 202207L +#else +STATIC_ASSERT(__cpp_lib_ranges_fold == 202207L); +#endif +#else +#ifdef __cpp_lib_ranges_fold +#error __cpp_lib_ranges_fold is defined +#endif +#endif + #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_ranges_iota #error __cpp_lib_ranges_iota is not defined