diff --git a/stl/debugger/STL.natvis b/stl/debugger/STL.natvis index 80fe4fb993..b9f0787bff 100644 --- a/stl/debugger/STL.natvis +++ b/stl/debugger/STL.natvis @@ -619,6 +619,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + + bind_back({_Mypair}, {_Mypair._Myval2,view(noparens)}) + + _Mypair + _Mypair._Myval2 + + + diff --git a/stl/inc/functional b/stl/inc/functional index 2caf1cf2b9..5442e659d6 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2131,6 +2131,83 @@ _NODISCARD constexpr auto bind_front(_Fx&& _Func, _Types&&... _Args) { } #endif // _HAS_CXX20 +#if _HAS_CXX23 +template +constexpr auto _Call_back_binder(index_sequence<_Ix...>, _Cv_FD&& _Obj, _Cv_tuple_TiD&& _Tpl, + _Unbound&&... _Unbargs) noexcept(noexcept(_STD invoke(_STD forward<_Cv_FD>(_Obj), + _STD forward<_Unbound>(_Unbargs)..., _STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...))) + -> decltype(_STD invoke(_STD forward<_Cv_FD>(_Obj), _STD forward<_Unbound>(_Unbargs)..., + _STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...)) { + return _STD invoke(_STD forward<_Cv_FD>(_Obj), _STD forward<_Unbound>(_Unbargs)..., + _STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...); +} + +template +class _Back_binder { // wrap bound callable object and arguments +private: + using _Seq = index_sequence_for<_Types...>; + + _Compressed_pair<_Fx, tuple<_Types...>> _Mypair; + + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Fx, decay_t<_Fx>>); + _STL_INTERNAL_STATIC_ASSERT((is_same_v<_Types, decay_t<_Types>> && ...)); + +public: + template , _Back_binder>, int> = 0> + constexpr explicit _Back_binder(_FxInit&& _Func, _TypesInit&&... _Args) + : _Mypair(_One_then_variadic_args_t{}, _STD forward<_FxInit>(_Func), _STD forward<_TypesInit>(_Args)...) {} + + template + constexpr auto operator()(_Unbound&&... _Unbargs) & noexcept( + noexcept(_Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...))) + -> decltype(_Call_back_binder( + _Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)) { + return _Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...); + } + + template + constexpr auto operator()(_Unbound&&... _Unbargs) const& noexcept( + noexcept(_Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...))) + -> decltype(_Call_back_binder( + _Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)) { + return _Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...); + } + + template + constexpr auto operator()(_Unbound&&... _Unbargs) && noexcept(noexcept(_Call_back_binder( + _Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...))) + -> decltype(_Call_back_binder( + _Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)) { + return _Call_back_binder( + _Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...); + } + + template + constexpr auto operator()(_Unbound&&... _Unbargs) const&& noexcept(noexcept(_Call_back_binder( + _Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...))) + -> decltype(_Call_back_binder( + _Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)) { + return _Call_back_binder( + _Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...); + } +}; + +template +_NODISCARD constexpr auto bind_back(_Fx&& _Func, _Types&&... _Args) { + static_assert(is_constructible_v, _Fx>, + "std::bind_back requires the decayed callable to be constructible from an undecayed callable"); + static_assert( + is_move_constructible_v>, "std::bind_back requires the decayed callable to be move constructible"); + static_assert(conjunction_v, _Types>...>, + "std::bind_back requires the decayed bound arguments to be constructible from undecayed bound arguments"); + static_assert(conjunction_v>...>, + "std::bind_back requires the decayed bound arguments to be move constructible"); + + return _Back_binder, decay_t<_Types>...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...); +} +#endif // _HAS_CXX23 + #if _HAS_FUNCTION_ALLOCATOR_SUPPORT template struct uses_allocator, _Alloc> : true_type {}; // true_type if container allocator enabled diff --git a/stl/inc/ranges b/stl/inc/ranges index edee6db4a1..bbef590d24 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -61,108 +61,80 @@ namespace ranges { using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>; namespace _Pipe { - // clang-format off - template - concept _Can_pipe = requires(_Left&& __l, _Right&& __r) { - static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l)); - }; - - template - concept _Can_compose = constructible_from, _Left> - && constructible_from, _Right>; - // clang-format on - - template - struct _Pipeline; - template - struct _Base { - template - requires _Can_compose<_Derived, _Other> - constexpr auto operator|(_Base<_Other>&& __r) && noexcept( - noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)})) { - _STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>); - _STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>); - return _Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)}; - } - - template - requires _Can_compose<_Derived, const _Other&> - constexpr auto operator|(const _Base<_Other>& __r) && noexcept( - noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast(__r)})) { - _STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>); - _STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>); - return _Pipeline{static_cast<_Derived&&>(*this), static_cast(__r)}; - } - - template - requires _Can_compose - constexpr auto operator|(_Base<_Other>&& __r) const& noexcept( - noexcept(_Pipeline{static_cast(*this), static_cast<_Other&&>(__r)})) { - _STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>); - _STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>); - return _Pipeline{static_cast(*this), static_cast<_Other&&>(__r)}; - } - - template - requires _Can_compose - constexpr auto operator|(const _Base<_Other>& __r) const& noexcept( - noexcept(_Pipeline{static_cast(*this), static_cast(__r)})) { - _STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>); - _STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>); - return _Pipeline{static_cast(*this), static_cast(__r)}; - } - - template <_Can_pipe _Left> - friend constexpr auto operator|(_Left&& __l, const _Base& __r) -#ifdef __EDG__ // TRANSITION, VSO-1222776 - noexcept(noexcept(_STD declval()(_STD forward<_Left>(__l)))) -#else // ^^^ workaround / no workaround vvv - noexcept(noexcept(static_cast(__r)(_STD forward<_Left>(__l)))) -#endif // TRANSITION, VSO-1222776 - { - return static_cast(__r)(_STD forward<_Left>(__l)); - } + struct _Base {}; - template <_Can_pipe<_Derived> _Left> - friend constexpr auto operator|(_Left&& __l, _Base&& __r) -#ifdef __EDG__ // TRANSITION, VSO-1222776 - noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Left>(__l)))) -#else // ^^^ workaround / no workaround vvv - noexcept(noexcept(static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)))) -#endif // TRANSITION, VSO-1222776 - { - return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)); - } + template + _Ty* _Derived_from_range_adaptor_closure(_Base<_Ty>&); // not defined + + template + concept _Range_adaptor_closure_object = !range> && requires(remove_cvref_t<_Ty> & __t) { + { _Pipe::_Derived_from_range_adaptor_closure(__t) } -> same_as*>; }; - template - struct _Pipeline : _Base<_Pipeline<_Left, _Right>> { - /* [[no_unique_address]] */ _Left __l; - /* [[no_unique_address]] */ _Right __r; + template + struct _Pipeline : _Base<_Pipeline<_ClosureLeft, _ClosureRight>> { + _STL_INTERNAL_STATIC_ASSERT(_Range_adaptor_closure_object<_ClosureLeft>); + _STL_INTERNAL_STATIC_ASSERT(_Range_adaptor_closure_object<_ClosureRight>); + + /* [[no_unique_address]] */ _ClosureLeft _Left; + /* [[no_unique_address]] */ _ClosureRight _Right; template constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept( - is_nothrow_convertible_v<_Ty1, _Left>&& is_nothrow_convertible_v<_Ty2, _Right>) - : __l(_STD forward<_Ty1>(_Val1)), __r(_STD forward<_Ty2>(_Val2)) {} + is_nothrow_constructible_v<_Ty1, _ClosureLeft>&& is_nothrow_constructible_v<_Ty2, _ClosureRight>) + : _Left(_STD forward<_Ty1>(_Val1)), _Right(_STD forward<_Ty2>(_Val2)) {} template - _NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept( - noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { - __r(__l(static_cast<_Ty&&>(_Val))); + _NODISCARD constexpr auto operator()(_Ty&& _Val) & noexcept( + noexcept(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires { + _Right(_Left(_STD forward<_Ty>(_Val))); } - { return __r(__l(_STD forward<_Ty>(_Val))); } + { return _Right(_Left(_STD forward<_Ty>(_Val))); } template - _NODISCARD constexpr auto operator()(_Ty&& _Val) const - noexcept(noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { - __r(__l(static_cast<_Ty&&>(_Val))); + _NODISCARD constexpr auto operator()(_Ty&& _Val) const& noexcept( + noexcept(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires { + _Right(_Left(_STD forward<_Ty>(_Val))); } - { return __r(__l(_STD forward<_Ty>(_Val))); } + { return _Right(_Left(_STD forward<_Ty>(_Val))); } + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) && noexcept( + noexcept(_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))))) requires requires { + _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); + } + { return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); } + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) const&& noexcept( + noexcept(_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))))) requires requires { + _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); + } + { return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); } }; template _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; + + template + requires _Range_adaptor_closure_object<_Left> // + && _Range_adaptor_closure_object<_Right> // + && constructible_from, _Left> // + && constructible_from, _Right> // + _NODISCARD constexpr auto operator|(_Left&& __l, _Right&& __r) noexcept( + noexcept(_Pipeline{static_cast<_Left&&>(__l), static_cast<_Right&&>(__r)})) { + return _Pipeline{static_cast<_Left&&>(__l), static_cast<_Right&&>(__r)}; + } + + template + requires _Range_adaptor_closure_object<_Right> && range<_Left> // + _NODISCARD constexpr auto operator|(_Left&& __l, _Right&& __r) noexcept( + noexcept(_STD forward<_Right>(__r)(_STD forward<_Left>(__l)))) // + requires requires { + static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l)); + } + { return _STD forward<_Right>(__r)(_STD forward<_Left>(__l)); } } // namespace _Pipe template @@ -805,6 +777,13 @@ namespace ranges { }; }; +#if _HAS_CXX23 + template + requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> + class range_adaptor_closure : public _Pipe::_Base<_Derived> { + }; +#endif // _HAS_CXX23 + template class _Range_closure : public _Pipe::_Base<_Range_closure<_Fn, _Types...>> { public: @@ -4289,7 +4268,6 @@ namespace ranges { } } - public: using iterator_concept = typename _Outer_iter<_Const>::iterator_concept; using value_type = range_value_t<_BaseTy>; @@ -4506,7 +4484,7 @@ namespace ranges { #else // ^^^ no workaround / workaround vvv auto _Match = _RANGES search(subrange{_Current, _Last}, _Parent->_Pattern); auto _Begin = _Match.begin(); - auto _End = _Match.end(); + auto _End = _Match.end(); #endif // TRANSITION, DevCom-1559808 if (_Begin != _Last && _RANGES empty(_Parent->_Pattern)) { ++_Begin; @@ -4558,7 +4536,7 @@ namespace ranges { #else // ^^^ no workaround / workaround vvv auto _Match = _RANGES search(subrange{_It, _Last}, _Pattern); auto _Begin = _Match.begin(); - auto _End = _Match.end(); + auto _End = _Match.end(); #endif // TRANSITION, DevCom-1559808 if (_Begin != _Last && _RANGES empty(_Pattern)) { ++_Begin; @@ -5086,7 +5064,7 @@ namespace ranges { (void) _Off; #else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 / _ITERATOR_DEBUG_LEVEL != 0 vvv if constexpr (_Offset_verifiable_v>) { - _Current._Verify_offset(_Off); + _Current._Verify_offset(_Off); } #endif // _ITERATOR_DEBUG_LEVEL == 0 } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index c5253c4ec7..0b4a5d5d16 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -323,6 +323,7 @@ // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip // (changes to pair, tuple, and vector::reference only) +// P2387R3 Pipe Support For User-Defined Range Adaptors // P2417R2 More constexpr bitset // P2440R1 ranges::iota, ranges::shift_left, ranges::shift_right // P2441R2 views::join_with @@ -1459,10 +1460,6 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_polymorphic_allocator 201902L -#ifdef __cpp_lib_concepts // TRANSITION, GH-395 -#define __cpp_lib_ranges 202110L -#endif // __cpp_lib_concepts - #define __cpp_lib_remove_cvref 201711L #define __cpp_lib_semaphore 201907L #define __cpp_lib_smart_ptr_for_overwrite 202002L @@ -1495,6 +1492,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #endif // __cpp_lib_concepts #define __cpp_lib_associative_heterogeneous_erasure 202110L +#define __cpp_lib_bind_back 202202L #define __cpp_lib_byteswap 202110L #define __cpp_lib_constexpr_bitset 202207L #define __cpp_lib_constexpr_typeinfo 202106L @@ -1569,6 +1567,14 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_optional 201606L // P0307R2 Making Optional Greater Equal Again #endif // _HAS_CXX17 +#if defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#if _HAS_CXX23 +#define __cpp_lib_ranges 202202L // P2387R3 Pipe Support For User-Defined Range Adaptors +#elif _HAS_CXX20 // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv +#define __cpp_lib_ranges 202110L // P2415R2 What Is A `view`? +#endif // _HAS_CXX20 +#endif // defined(__cpp_lib_concepts) + #if _HAS_CXX20 #define __cpp_lib_shared_ptr_arrays 201707L // P0674R1 make_shared() For Arrays #else // _HAS_CXX20 diff --git a/tests/std/test.lst b/tests/std/test.lst index 08211d1fca..55b7738804 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -543,6 +543,8 @@ tests\P2273R3_constexpr_unique_ptr tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference +tests\P2387R3_bind_back +tests\P2387R3_pipe_support_for_user_defined_range_adaptors tests\P2401R0_conditional_noexcept_for_exchange tests\P2408R5_ranges_iterators_to_classic_algorithms tests\P2415R2_owning_view diff --git a/tests/std/tests/P2387R3_bind_back/env.lst b/tests/std/tests/P2387R3_bind_back/env.lst new file mode 100644 index 0000000000..642f530ffa --- /dev/null +++ b/tests/std/tests/P2387R3_bind_back/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P2387R3_bind_back/test.cpp b/tests/std/tests/P2387R3_bind_back/test.cpp new file mode 100644 index 0000000000..175c3497ca --- /dev/null +++ b/tests/std/tests/P2387R3_bind_back/test.cpp @@ -0,0 +1,216 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +constexpr int f0() { + return 1729; +} + +constexpr int f1(int x) { + return x * 10; +} + +constexpr int f2(int x, int y) { + return x * 100 + y * 10; +} + +constexpr int f3(int x, int y, int z) { + return x * 1000 + y * 100 + z * 10; +} + +struct Cat { + string name; +}; + +struct CatNoise { + string noise(const string& s, const Cat& cat) const { + return cat.name + " says " + s; + } +}; + +struct DetectQualifiers { + constexpr string_view operator()() & { + return "modifiable lvalue"; + } + + constexpr string_view operator()() const& { + return "const lvalue"; + } + + constexpr string_view operator()() && { + return "modifiable rvalue"; + } + + constexpr string_view operator()() const&& { + return "const rvalue"; + } +}; + +constexpr bool test_constexpr() { + // Test varying numbers of arguments. + assert(bind_back(f0)() == 1729); + + assert(bind_back(f1)(2) == 20); + assert(bind_back(f1, 3)() == 30); + + assert(bind_back(f2)(4, 5) == 450); + assert(bind_back(f2, 7)(6) == 670); + assert(bind_back(f2, 8, 9)() == 890); + + assert(bind_back(f3)(2, 3, 4) == 2340); + assert(bind_back(f3, 4, 5)(3) == 3450); + assert(bind_back(f3, 5, 6)(4) == 4560); + assert(bind_back(f3, 5, 6, 7)() == 5670); + + // Test function pointers. + assert(bind_back(&f0)() == 1729); + assert(bind_back(&f2, 7)(6) == 670); + + // Test stateless lambdas. + assert(bind_back([] { return 11; })() == 11); + assert(bind_back([](int x, int y) { return x * 2 + y * 3; }, 10)(100) == 230); + + // Test stateful lambdas. + int value = 0; + + auto bound0 = bind_back([&value] { ++value; }); + bound0(); + assert(value == 1); + bound0(); + assert(value == 2); + + auto bound1 = bind_back([&value](int x, int y) { value = value * x + y; }, 10); + bound1(3); + assert(value == 16); + bound1(4); + assert(value == 74); + + // Test "perfect forwarding call wrapper" behavior. + auto bound5 = bind_back(DetectQualifiers{}); + assert(bound5() == "modifiable lvalue"); + assert(as_const(bound5)() == "const lvalue"); + assert(move(bound5)() == "modifiable rvalue"); + assert(move(as_const(bound5))() == "const rvalue"); + + // Test decay when binding. + const int arr[] = {11, 22, 33}; + const int three = 3; + + auto bound8 = bind_back( + [](auto&& a, auto&& f, auto&& i) { + using FP = int (*)(int); + return is_same_v && is_same_v && is_same_v; + }, + arr, f1, three); + assert(bound8()); + + // Test forward when calling. + auto bound9 = bind_back([](auto&& a1, auto&& a2, auto&& a3, auto&& a4) { + constexpr bool same1 = is_same_v; + constexpr bool same2 = is_same_v; + constexpr bool same3 = is_same_v; + constexpr bool same4 = is_same_v; + return same1 && same2 && same3 && same4; + }); + assert(bound9(value, three, 1729, move(three))); + + return true; +} + +void test_move_only_types() { + // Test movable-only types. + auto unique_lambda = [up1 = make_unique(1200)](unique_ptr&& up2) { + if (up1 && up2) { + return make_unique(*up1 + *up2); + } else if (up1) { + return make_unique(*up1 * -1); + } else if (up2) { + return make_unique(*up2 * -10); + } else { + return make_unique(-9000); + } + }; + auto bound6 = bind_back(move(unique_lambda), make_unique(34)); + assert(*unique_lambda(make_unique(56)) == -560); + assert(*move(bound6)() == 1234); + auto bound7 = move(bound6); + assert(*move(bound6)() == -9000); + assert(*move(bound7)() == 1234); +} + +int main() { + assert(test_constexpr()); + static_assert(test_constexpr()); + + test_move_only_types(); + + // Also test GH-1292 "bind_front violates [func.require]p8" in which the return type of bind_front inadvertently + // depends on the value category and/or cv-qualification of its arguments. + { + struct S { + int i = 42; + }; + S s; + auto lambda = [](S x) { return x.i; }; + auto returns_lambda = [=] { return lambda; }; + auto returns_const_lambda = [=]() -> const decltype(lambda) { return lambda; }; + auto returns_const_S = []() -> const S { return {}; }; + + using T = decltype(bind_back(lambda, s)); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + } +} diff --git a/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/env.lst b/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/env.lst new file mode 100644 index 0000000000..8ac7033b20 --- /dev/null +++ b/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp b/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp new file mode 100644 index 0000000000..ab0eac62d5 --- /dev/null +++ b/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp @@ -0,0 +1,236 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; + +template +concept CanInstantiateRangeAdaptorClosure = requires { + typename ranges::range_adaptor_closure; +}; + +class EmptyTestType {}; +class IncompleteTestType; + +static_assert(CanInstantiateRangeAdaptorClosure); +static_assert(!CanInstantiateRangeAdaptorClosure); +static_assert(!CanInstantiateRangeAdaptorClosure); +static_assert(!CanInstantiateRangeAdaptorClosure); +static_assert(!CanInstantiateRangeAdaptorClosure); +static_assert(CanInstantiateRangeAdaptorClosure); + +template +concept CanPipe = requires(LHS lhs, RHS rhs) { + forward(lhs) | forward(rhs); +}; + +template +concept CanPipe_R = requires(LHS lhs, RHS rhs) { + { forward(lhs) | forward(rhs) } -> same_as; +}; + +using TestRange = array; + +template +constexpr bool is_range_adaptor_closure() { + return CanPipe || CanPipe // + || CanPipe || CanPipe; +} + +struct IdentityRangeAdaptorClosure : ranges::range_adaptor_closure { + template + constexpr decltype(auto) operator()(T&& range) const { + return forward(range); + } +}; + +// Is not a range adaptor closure, because it is not a function object. +struct NotCallable : ranges::range_adaptor_closure {}; +static_assert(!is_range_adaptor_closure()); + +// Is not a range adaptor closure, because it does not accept a range as argument. +struct NotCallableWithRange : ranges::range_adaptor_closure { + void operator()() {} +}; +static_assert(!is_range_adaptor_closure()); + +// Is not a range adaptor closure, because it doesn't derive from range_adaptor_closure. +struct NotDerivedFrom { + void operator()(const TestRange&) {} +}; +static_assert(!is_range_adaptor_closure()); + +// Is not a range adaptor closure, because it inherits privately from range_adaptor_closure. +struct DerivedPrivately : private ranges::range_adaptor_closure { + void operator()(const TestRange&) {} +}; +static_assert(!is_range_adaptor_closure()); + +// Is not a range adaptor closure, because it inherits from the wrong specialization of range_adaptor_closure. +struct DerivedFromWrongSpecialization : ranges::range_adaptor_closure { + void operator()(const TestRange&) {} +}; +static_assert(!is_range_adaptor_closure()); + +// Is not a range adaptor closure, because it has two base classes which are specializations of +// range_adaptor_closure. +struct DerivedFromTwoSpecializations : ranges::range_adaptor_closure, + ranges::range_adaptor_closure { + void operator()(const TestRange&) {} +}; +static_assert(!is_range_adaptor_closure()); + +// Is not a range adaptor closure, because it models ranges::range. +struct ModelsRange : ranges::range_adaptor_closure { + void operator()(const TestRange&) {} + + int* begin() { + return nullptr; + } + int* begin() const { + return nullptr; + } + int* end() { + return nullptr; + } + int* end() const { + return nullptr; + } +}; +static_assert(ranges::range); +static_assert(!is_range_adaptor_closure()); + +struct RangeAdaptorClosureMemberRefQualTest : ranges::range_adaptor_closure { + constexpr ranges::empty_view operator()(const auto&) & { + return views::empty; + } + + constexpr ranges::empty_view operator()(const auto&) && { + return views::empty; + } + + constexpr ranges::empty_view operator()(const auto&) const& { + return views::empty; + } + + constexpr ranges::empty_view operator()(const auto&) const&& { + return views::empty; + } +}; +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); + +using FirstIdentityThenMemberRefQualTest = decltype( + IdentityRangeAdaptorClosure{} | RangeAdaptorClosureMemberRefQualTest{}); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); + +using FirstTransformThenMemberRefQualTest = decltype( + views::transform([](auto x) { return x; }) | RangeAdaptorClosureMemberRefQualTest{}); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); + +using FirstMemberRefQualTestThenAll = decltype(RangeAdaptorClosureMemberRefQualTest{} | views::all); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); + +struct RangeAdaptorClosureParameterRefQualTest + : ranges::range_adaptor_closure { + constexpr char operator()(TestRange&) { + return {}; + } + + constexpr short operator()(TestRange&&) { + return {}; + } + + constexpr float operator()(const TestRange&) { + return {}; + } + + constexpr double operator()(const TestRange&&) { + return {}; + } +}; +static_assert(CanPipe_R); +static_assert(CanPipe_R); +static_assert(CanPipe_R); +static_assert(CanPipe_R); + +struct MoveOnlyRangeAdaptorClosure : ranges::range_adaptor_closure { + MoveOnlyRangeAdaptorClosure() = default; + MoveOnlyRangeAdaptorClosure(const MoveOnlyRangeAdaptorClosure&) = delete; + MoveOnlyRangeAdaptorClosure(MoveOnlyRangeAdaptorClosure&&) = default; + + void operator()(const TestRange&) {} +}; +static_assert(CanPipe); +static_assert(CanPipe); +static_assert(CanPipe); +static_assert(CanPipe); +static_assert(!CanPipe); +static_assert(!CanPipe); + +class TimesTwoAdaptor : public ranges::range_adaptor_closure { +public: + template + constexpr auto operator()(R&& range) const { + return forward(range) | views::transform([](auto x) { return x * 2; }); + } +}; + +class DividedByTwoAdaptor : public ranges::range_adaptor_closure { +public: + template + constexpr auto operator()(R&& range) const { + return forward(range) | views::transform([](auto x) { return x / 2; }); + } +}; + +constexpr bool test_user_defined_adaptors() { + const array numbers{1, 2, 3}; + const array numbers_times_two{2, 4, 6}; + assert(ranges::equal(numbers_times_two, numbers | TimesTwoAdaptor{})); + assert(ranges::equal(numbers, (numbers | TimesTwoAdaptor{}) | DividedByTwoAdaptor{})); + assert(ranges::equal(numbers, numbers | (TimesTwoAdaptor{} | DividedByTwoAdaptor{}))); + assert(ranges::equal(numbers_times_two, + (numbers | TimesTwoAdaptor{}) + | (TimesTwoAdaptor{} | TimesTwoAdaptor{} | DividedByTwoAdaptor{} | DividedByTwoAdaptor{}))); + + return true; +} + +constexpr bool test_mixing_of_range_adaptors() { + const array numbers{1, 2, 3}; + assert(ranges::equal(numbers, numbers | (TimesTwoAdaptor{} | views::transform([](int x) { return x / 2; })))); + + const auto mixed_pipeline = TimesTwoAdaptor{} | views::reverse | DividedByTwoAdaptor{} | views::reverse; + assert(ranges::equal(mixed_pipeline(numbers), numbers)); + assert(ranges::equal(numbers | mixed_pipeline, numbers)); + + auto factory_pipeline = views::iota(1) | TimesTwoAdaptor{} | views::take(3); + assert(ranges::equal(factory_pipeline, array{2, 4, 6})); + + return true; +} + +int main() { + assert(test_user_defined_adaptors()); + static_assert(test_user_defined_adaptors()); + + assert(test_mixing_of_range_adaptors()); + static_assert(test_mixing_of_range_adaptors()); +} 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 bf41dcd993..8d20d002e4 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 @@ -272,6 +272,20 @@ STATIC_ASSERT(__cpp_lib_barrier == 201907L); #endif #endif +#if _HAS_CXX23 +#ifndef __cpp_lib_bind_back +#error __cpp_lib_bind_back is not defined +#elif __cpp_lib_bind_back != 202202L +#error __cpp_lib_bind_back is not 202202L +#else +STATIC_ASSERT(__cpp_lib_bind_back == 202202L); +#endif +#else +#ifdef __cpp_lib_bind_back +#error __cpp_lib_bind_back is defined +#endif +#endif + #if _HAS_CXX20 #ifndef __cpp_lib_bind_front #error __cpp_lib_bind_front is not defined @@ -1440,7 +1454,15 @@ STATIC_ASSERT(__cpp_lib_polymorphic_allocator == 201902L); STATIC_ASSERT(__cpp_lib_quoted_string_io == 201304L); #endif -#if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges +#error __cpp_lib_ranges is not defined +#elif __cpp_lib_ranges != 202202L +#error __cpp_lib_ranges is not 202202L +#else +STATIC_ASSERT(__cpp_lib_ranges == 202202L); +#endif +#elif _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_ranges #error __cpp_lib_ranges is not defined #elif __cpp_lib_ranges != 202110L