From 341aa7883f17034f86daf3736ca5ad5263a4d07a Mon Sep 17 00:00:00 2001 From: Kilian Date: Sun, 10 Apr 2022 19:03:34 +0200 Subject: [PATCH 01/34] P2387R3 add bind_back --- stl/debugger/STL.natvis | 8 + stl/inc/functional | 76 +++++++ stl/inc/yvals_core.h | 5 +- tests/std/test.lst | 1 + tests/std/tests/P2387R3_bind_back/env.lst | 4 + tests/std/tests/P2387R3_bind_back/test.cpp | 212 ++++++++++++++++++ .../test.compile.pass.cpp | 14 ++ 7 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 tests/std/tests/P2387R3_bind_back/env.lst create mode 100644 tests/std/tests/P2387R3_bind_back/test.cpp diff --git a/stl/debugger/STL.natvis b/stl/debugger/STL.natvis index 5885050d8d..692a19afec 100644 --- a/stl/debugger/STL.natvis +++ b/stl/debugger/STL.natvis @@ -616,6 +616,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 e25f138da0..d73ed351f0 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2117,6 +2117,82 @@ _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/yvals_core.h b/stl/inc/yvals_core.h index 6f7882d38e..50f9192649 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -302,6 +302,7 @@ // P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr // P2186R2 Removing Garbage Collection Support // P2273R3 constexpr unique_ptr +// P2387R3 Pipe Support For User-Defined Range Adaptors // P2443R1 views::chunk_by // Parallel Algorithms Notes @@ -1335,7 +1336,7 @@ #define __cpp_lib_polymorphic_allocator 201902L #if defined(__cpp_lib_concepts) // TRANSITION, GH-395 -#define __cpp_lib_ranges 202110L +#define __cpp_lib_ranges 202202L #endif // defined(__cpp_lib_concepts) #define __cpp_lib_remove_cvref 201711L @@ -1371,6 +1372,8 @@ #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_invoke_r 202106L #define __cpp_lib_is_scoped_enum 202011L diff --git a/tests/std/test.lst b/tests/std/test.lst index 2a8cf5e9ac..d0126651be 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -468,6 +468,7 @@ tests\P2136R3_invoke_r tests\P2162R2_std_visit_for_derived_classes_from_variant tests\P2231R1_complete_constexpr_optional_variant tests\P2273R3_constexpr_unique_ptr +tests\P2387R3_bind_back tests\P2401R0_conditional_noexcept_for_exchange tests\P2415R2_owning_view tests\P2443R1_views_chunk_by 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..07026578b3 --- /dev/null +++ b/tests/std/tests/P2387R3_bind_back/test.cpp @@ -0,0 +1,212 @@ +// 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; +} + +int main() { + assert(test_constexpr()); + static_assert(test_constexpr()); + + // 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); + + // 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/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 96bf42755d..fda53c0c38 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 @@ -258,6 +258,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 From ac7e672e462cda5f0573f44246635f7a0158cd45 Mon Sep 17 00:00:00 2001 From: Kilian Date: Tue, 12 Apr 2022 15:37:07 +0200 Subject: [PATCH 02/34] Use cassert instead of assert.h in test. Move tests into extra function --- tests/std/tests/P2387R3_bind_back/test.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P2387R3_bind_back/test.cpp b/tests/std/tests/P2387R3_bind_back/test.cpp index 07026578b3..175c3497ca 100644 --- a/tests/std/tests/P2387R3_bind_back/test.cpp +++ b/tests/std/tests/P2387R3_bind_back/test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include +#include #include #include #include @@ -126,10 +126,7 @@ constexpr bool test_constexpr() { return true; } -int main() { - assert(test_constexpr()); - static_assert(test_constexpr()); - +void test_move_only_types() { // Test movable-only types. auto unique_lambda = [up1 = make_unique(1200)](unique_ptr&& up2) { if (up1 && up2) { @@ -148,6 +145,13 @@ int main() { 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. From 1f390cbc1102ab34995a0693bc82f88f09a5a801 Mon Sep 17 00:00:00 2001 From: Kilian Date: Thu, 21 Apr 2022 17:04:54 +0200 Subject: [PATCH 03/34] Add std::ranges::range_adaptor_closure as well as tests --- stl/inc/ranges | 131 +++++++++++ tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 206 ++++++++++++++++++ 4 files changed, 342 insertions(+) create mode 100644 tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/env.lst create mode 100644 tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp diff --git a/stl/inc/ranges b/stl/inc/ranges index 181ff5adb9..6ffcf1ae00 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1433,6 +1433,137 @@ namespace ranges { inline constexpr _Istream_fn<_Ty> istream; } // namespace views + template + requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> + class range_adaptor_closure; + + namespace _StdPipe { + template + void _Derived_from_range_adaptor_closure_impl(const volatile range_adaptor_closure<_Ty>&); + + template + concept _Derived_from_range_adaptor_closure = requires(const volatile _Ty& _Obj) { + _Derived_from_range_adaptor_closure_impl(_Obj); + }; + + template + concept _Is_range_adaptor_common = derived_from, + range_adaptor_closure>> && _Derived_from_range_adaptor_closure<_Ty> && !range<_Ty>; + + template + concept _Is_invocable_range_adaptor_closure = + range<_Rng> && invocable<_Ty, _Rng> && _Is_range_adaptor_common<_Ty>; + + template + concept _Are_composable_range_adaptor_closures = + _Is_range_adaptor_common<_Left> && _Is_range_adaptor_common<_Right> && _Pipe::_Can_compose<_Left, _Right>; + + template + struct _StdPipeline; + } // namespace _StdPipe + + template + requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> + class range_adaptor_closure { + public: + template + requires _StdPipe::_Are_composable_range_adaptor_closures<_Derived&, _Other> + constexpr auto operator|(_Other&& __r) & noexcept( + noexcept(_StdPipe::_StdPipeline{static_cast<_Derived&>(*this), _STD forward<_Other>(__r)})) { + return _StdPipe::_StdPipeline{static_cast<_Derived&>(*this), _STD forward<_Other>(__r)}; + } + + template + requires _StdPipe::_Are_composable_range_adaptor_closures<_Derived, _Other> + constexpr auto operator|(_Other&& __r) && noexcept( + noexcept(_StdPipe::_StdPipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(__r)})) { + return _StdPipe::_StdPipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(__r)}; + } + + template + requires _StdPipe::_Are_composable_range_adaptor_closures + constexpr auto operator|(_Other&& __r) & noexcept( + noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(__r)})) { + return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(__r)}; + } + + template + requires _StdPipe::_Are_composable_range_adaptor_closures + constexpr auto operator|(_Other&& __r) const&& noexcept( + noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(__r)})) { + return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(__r)}; + } + + template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&> _Left> + friend constexpr auto operator|(_Left&& __l, _Derived& __r) noexcept( + noexcept(static_cast<_Derived&>(__r)(_STD forward<_Left>(__l)))) { + return static_cast<_Derived&>(__r)(_STD forward<_Left>(__l)); + } + + template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Left> + friend constexpr auto operator|(_Left&& __l, _Derived&& __r) noexcept( + noexcept(static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)))) { + return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)); + } + + template <_StdPipe::_Is_invocable_range_adaptor_closure _Left> + friend constexpr auto operator|(_Left&& __l, const _Derived& __r) noexcept( + noexcept(static_cast(__r)(_STD forward<_Left>(__l)))) { + return static_cast(__r)(_STD forward<_Left>(__l)); + } + + template <_StdPipe::_Is_invocable_range_adaptor_closure _Left> + friend constexpr auto operator|(_Left&& __l, const _Derived&& __r) noexcept( + noexcept(static_cast(__r)(_STD forward<_Left>(__l)))) { + return static_cast(__r)(_STD forward<_Left>(__l)); + } + }; + + namespace _StdPipe { + template + struct _StdPipeline : range_adaptor_closure<_StdPipeline<_Left, _Right>> { + /* [[no_unique_address]] */ _Left __l; + /* [[no_unique_address]] */ _Right __r; + + template + constexpr explicit _StdPipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept( + is_nothrow_constructible_v<_Ty1, _Left>&& is_nothrow_constructible_v<_Ty2, _Right>) + : __l(_STD forward<_Ty1>(_Val1)), __r(_STD forward<_Ty2>(_Val2)) {} + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) & noexcept( + noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { + __r(__l(_STD forward<_Ty>(_Val))); + } + { return __r(__l(_STD forward<_Ty>(_Val))); } + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) && noexcept( + noexcept(_STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))))) requires requires { + _STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))); + } + { return _STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))); } + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) const& noexcept( + noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { + __r(__l(_STD forward<_Ty>(_Val))); + } + { return __r(__l(_STD forward<_Ty>(_Val))); } + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) const&& noexcept( + noexcept(_STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))))) requires requires { + _STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))); + } + { return _STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))); } + }; + + template + _StdPipeline(_Ty1, _Ty2) -> _StdPipeline<_Ty1, _Ty2>; + + } // namespace _StdPipe + template requires is_object_v<_Rng> class ref_view : public view_interface> { diff --git a/tests/std/test.lst b/tests/std/test.lst index edf8946374..eb519a0c71 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -470,6 +470,7 @@ tests\P2162R2_std_visit_for_derived_classes_from_variant tests\P2231R1_complete_constexpr_optional_variant tests\P2273R3_constexpr_unique_ptr tests\P2387R3_bind_back +tests\P2387R3_pipe_support_for_user_defined_range_adaptors tests\P2401R0_conditional_noexcept_for_exchange tests\P2415R2_owning_view tests\P2443R1_views_chunk_by 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..642f530ffa --- /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 ..\usual_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..8257acbad2 --- /dev/null +++ b/tests/std/tests/P2387R3_pipe_support_for_user_defined_range_adaptors/test.cpp @@ -0,0 +1,206 @@ +// 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 std::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) { + std::forward(lhs) | std::forward(rhs); +}; + +template +concept CanPipe_R = requires(LHS lhs, RHS rhs) { + { std::forward(lhs) | std::forward(rhs) } -> std::same_as; +}; + +using TestRange = std::array; + +template +constexpr bool does_not_satisfy_range_adaptor_closure() { + return !CanPipe && !CanPipe && !CanPipe && !CanPipe; +} + +struct IdentityRangeAdaptorClosure : std::ranges::range_adaptor_closure { + template + constexpr decltype(auto) operator()(T&& range) const { + return std::forward(range); + } +}; + +// Is not a range adaptor closure, because it is not a function object. +struct NotCallable : std::ranges::range_adaptor_closure {}; +static_assert(does_not_satisfy_range_adaptor_closure()); + +// Is not a range adaptor closure, because it does not accept a range as argument. +struct NotCallableWithRange : std::ranges::range_adaptor_closure { + void operator()() {} +}; +static_assert(does_not_satisfy_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(does_not_satisfy_range_adaptor_closure()); + +// Is not a range adaptor closure, because it inherits privately from range_adaptor_closure. +struct DerivedPrivately : private std::ranges::range_adaptor_closure { + void operator()(const TestRange&) {} +}; +static_assert(does_not_satisfy_range_adaptor_closure()); + +// Is not a range adaptor closure, because it inherits from the wrong specialization of range_adaptor_closure. +struct DerivedFromWrongSpecialization : std::ranges::range_adaptor_closure { + void operator()(const TestRange&) {} +}; +static_assert(does_not_satisfy_range_adaptor_closure()); + +// Is not a range adaptor closure, because it has two base classes which are specializations of +// range_adaptor_closure. +struct DerivedFromTwoSpecializations : std::ranges::range_adaptor_closure, + std::ranges::range_adaptor_closure { + void operator()(const TestRange&) {} +}; +static_assert(does_not_satisfy_range_adaptor_closure()); + +// Is not a range adaptor closure, because it models std::ranges::range. +struct ModelsRange : std::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(does_not_satisfy_range_adaptor_closure()); + +struct RangeAdaptorClosureMemberRefQualTest : std::ranges::range_adaptor_closure { + constexpr char operator()(const TestRange&) & { + return {}; + } + + constexpr short operator()(const TestRange&) && { + return {}; + } + + constexpr unsigned char operator()(const TestRange&) const& { + return {}; + } + + constexpr unsigned short operator()(const TestRange&) const&& { + return {}; + } +}; +static_assert(CanPipe_R); +static_assert(CanPipe_R); +static_assert(CanPipe_R); +static_assert(CanPipe_R); + +struct RangeAdaptorClosureParameterRefQualTest + : std::ranges::range_adaptor_closure { + constexpr char operator()(TestRange&) { + return {}; + } + + constexpr short operator()(TestRange&&) { + return {}; + } + + constexpr unsigned char operator()(const TestRange&) { + return {}; + } + + constexpr unsigned short operator()(const TestRange&&) { + return {}; + } +}; +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); + +struct MoveOnlyRangeAdaptorClosure : std::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 std::ranges::range_adaptor_closure { +public: + template + constexpr auto operator()(R&& range) const { + return std::forward(range) | std::views::transform([](auto x) { return x * 2; }); + } +}; + +class DividedByTwoAdaptor : public std::ranges::range_adaptor_closure { +public: + template + constexpr auto operator()(R&& range) const { + return std::forward(range) | std::views::transform([](auto x) { return x / 2; }); + } +}; + +constexpr bool test_constexpr() { + const std::array numbers{1, 2, 3}; + const std::array numbers_times_two{2, 4, 6}; + assert(std::ranges::equal(numbers_times_two, numbers | TimesTwoAdaptor{})); + assert(std::ranges::equal(numbers, (numbers | TimesTwoAdaptor{}) | DividedByTwoAdaptor{})); + assert(std::ranges::equal(numbers, numbers | (TimesTwoAdaptor{} | DividedByTwoAdaptor{}))); + assert(std::ranges::equal(numbers_times_two, + (numbers | TimesTwoAdaptor{}) + | (TimesTwoAdaptor{} | TimesTwoAdaptor{} | DividedByTwoAdaptor{} | DividedByTwoAdaptor{}))); + + return true; +} + +int main() { + assert(test_constexpr()); + static_assert(test_constexpr()); +} From 17f7b9a74d3173f964f1e352352360275e491e86 Mon Sep 17 00:00:00 2001 From: Kilian Date: Thu, 21 Apr 2022 19:08:38 +0200 Subject: [PATCH 04/34] Use strict_concepts_latest_matrix.lst --- .../env.lst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 642f530ffa..8ac7033b20 100644 --- 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 @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_latest_matrix.lst +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst From ccb8f5a433a90ea4c70f87c13d94d0c2d8fbdf1c Mon Sep 17 00:00:00 2001 From: Kilian Date: Thu, 21 Apr 2022 19:23:46 +0200 Subject: [PATCH 05/34] clang-format stl/inc/functional --- stl/inc/functional | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/functional b/stl/inc/functional index d73ed351f0..ba187d899a 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2145,16 +2145,16 @@ public: : _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)...))) + 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)...))) + 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)...); @@ -2182,8 +2182,8 @@ 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( + 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>...>, From 82d74a5c50bd2c16369bfd2f77e957b56f1879c3 Mon Sep 17 00:00:00 2001 From: Kilian Date: Thu, 21 Apr 2022 19:25:06 +0200 Subject: [PATCH 06/34] clang-format yvals_core.h --- stl/inc/yvals_core.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index f1e9f4d661..a6f438e179 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1397,11 +1397,11 @@ #define __cpp_lib_associative_heterogeneous_erasure 202110L -#define __cpp_lib_bind_back 202202L -#define __cpp_lib_byteswap 202110L -#define __cpp_lib_invoke_r 202106L -#define __cpp_lib_is_scoped_enum 202011L -#define __cpp_lib_move_only_function 202110L +#define __cpp_lib_bind_back 202202L +#define __cpp_lib_byteswap 202110L +#define __cpp_lib_invoke_r 202106L +#define __cpp_lib_is_scoped_enum 202011L +#define __cpp_lib_move_only_function 202110L #ifdef __cpp_lib_concepts #define __cpp_lib_out_ptr 202106L From 57026a2f047c8dbda02d4f30db2280e942342545 Mon Sep 17 00:00:00 2001 From: Kilian Date: Thu, 21 Apr 2022 19:49:01 +0200 Subject: [PATCH 07/34] Use correct naming convetions --- stl/inc/ranges | 96 +++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 6ffcf1ae00..daba47a070 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1442,8 +1442,8 @@ namespace ranges { void _Derived_from_range_adaptor_closure_impl(const volatile range_adaptor_closure<_Ty>&); template - concept _Derived_from_range_adaptor_closure = requires(const volatile _Ty& _Obj) { - _Derived_from_range_adaptor_closure_impl(_Obj); + concept _Derived_from_range_adaptor_closure = requires(const volatile _Ty& __obj) { + _Derived_from_range_adaptor_closure_impl(__obj); }; template @@ -1468,95 +1468,95 @@ namespace ranges { public: template requires _StdPipe::_Are_composable_range_adaptor_closures<_Derived&, _Other> - constexpr auto operator|(_Other&& __r) & noexcept( - noexcept(_StdPipe::_StdPipeline{static_cast<_Derived&>(*this), _STD forward<_Other>(__r)})) { - return _StdPipe::_StdPipeline{static_cast<_Derived&>(*this), _STD forward<_Other>(__r)}; + constexpr auto operator|(_Other&& _Right) & noexcept( + noexcept(_StdPipe::_StdPipeline{static_cast<_Derived&>(*this), _STD forward<_Other>(_Right)})) { + return _StdPipe::_StdPipeline{static_cast<_Derived&>(*this), _STD forward<_Other>(_Right)}; } template requires _StdPipe::_Are_composable_range_adaptor_closures<_Derived, _Other> - constexpr auto operator|(_Other&& __r) && noexcept( - noexcept(_StdPipe::_StdPipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(__r)})) { - return _StdPipe::_StdPipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(__r)}; + constexpr auto operator|(_Other&& _Right) && noexcept( + noexcept(_StdPipe::_StdPipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) { + return _StdPipe::_StdPipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)}; } template requires _StdPipe::_Are_composable_range_adaptor_closures - constexpr auto operator|(_Other&& __r) & noexcept( - noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(__r)})) { - return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(__r)}; + constexpr auto operator|(_Other&& _Right) & noexcept( + noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { + return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)}; } template requires _StdPipe::_Are_composable_range_adaptor_closures - constexpr auto operator|(_Other&& __r) const&& noexcept( - noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(__r)})) { - return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(__r)}; + constexpr auto operator|(_Other&& _Right) const&& noexcept( + noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { + return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)}; } - template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&> _Left> - friend constexpr auto operator|(_Left&& __l, _Derived& __r) noexcept( - noexcept(static_cast<_Derived&>(__r)(_STD forward<_Left>(__l)))) { - return static_cast<_Derived&>(__r)(_STD forward<_Left>(__l)); + template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> + friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right) noexcept( + noexcept(static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)))) { + return static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)); } - template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Left> - friend constexpr auto operator|(_Left&& __l, _Derived&& __r) noexcept( - noexcept(static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)))) { - return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)); + template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> + friend constexpr auto operator|(_Rng&& _Left, _Derived&& _Right) noexcept( + noexcept(static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)))) { + return static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)); } - template <_StdPipe::_Is_invocable_range_adaptor_closure _Left> - friend constexpr auto operator|(_Left&& __l, const _Derived& __r) noexcept( - noexcept(static_cast(__r)(_STD forward<_Left>(__l)))) { - return static_cast(__r)(_STD forward<_Left>(__l)); + template <_StdPipe::_Is_invocable_range_adaptor_closure _Rng> + friend constexpr auto operator|(_Rng&& _Left, const _Derived& _Right) noexcept( + noexcept(static_cast(_Right)(_STD forward<_Rng>(_Left)))) { + return static_cast(_Right)(_STD forward<_Rng>(_Left)); } - template <_StdPipe::_Is_invocable_range_adaptor_closure _Left> - friend constexpr auto operator|(_Left&& __l, const _Derived&& __r) noexcept( - noexcept(static_cast(__r)(_STD forward<_Left>(__l)))) { - return static_cast(__r)(_STD forward<_Left>(__l)); + template <_StdPipe::_Is_invocable_range_adaptor_closure _Rng> + friend constexpr auto operator|(_Rng&& _Left, const _Derived&& _Right) noexcept( + noexcept(static_cast(_Right)(_STD forward<_Rng>(_Left)))) { + return static_cast(_Right)(_STD forward<_Rng>(_Left)); } }; namespace _StdPipe { - template - struct _StdPipeline : range_adaptor_closure<_StdPipeline<_Left, _Right>> { - /* [[no_unique_address]] */ _Left __l; - /* [[no_unique_address]] */ _Right __r; + template + struct _StdPipeline : range_adaptor_closure<_StdPipeline<_ClosureLeft, _ClosureRight>> { + /* [[no_unique_address]] */ _ClosureLeft _Left; + /* [[no_unique_address]] */ _ClosureRight _Right; template constexpr explicit _StdPipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept( - is_nothrow_constructible_v<_Ty1, _Left>&& is_nothrow_constructible_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(_STD forward<_Ty>(_Val))); + 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(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))))) requires requires { - _STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))); + 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(__r)(_STD move(__l)(_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(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { - __r(__l(_STD forward<_Ty>(_Val))); + 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(_STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))))) requires requires { - _STD move(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))); + 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(__r)(_STD move(__l)(_STD forward<_Ty>(_Val))); } + { return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); } }; template From 0a8b75c722ce05140e282a5dee3da6b5c3c321ea Mon Sep 17 00:00:00 2001 From: Kilian Date: Thu, 21 Apr 2022 21:12:33 +0200 Subject: [PATCH 08/34] Remove superfluous empty line --- stl/inc/yvals_core.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index a6f438e179..50cb185eee 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1396,12 +1396,11 @@ #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_invoke_r 202106L -#define __cpp_lib_is_scoped_enum 202011L -#define __cpp_lib_move_only_function 202110L +#define __cpp_lib_bind_back 202202L +#define __cpp_lib_byteswap 202110L +#define __cpp_lib_invoke_r 202106L +#define __cpp_lib_is_scoped_enum 202011L +#define __cpp_lib_move_only_function 202110L #ifdef __cpp_lib_concepts #define __cpp_lib_out_ptr 202106L From fe9aaf231214dc58eac9b5ecffcf5ad9baa6eefa Mon Sep 17 00:00:00 2001 From: Kilian Date: Thu, 21 Apr 2022 21:21:46 +0200 Subject: [PATCH 09/34] Adopt test for feature_test_macro to increased value of __cpp_lib_ranges in C++23-mode --- .../test.compile.pass.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 fda53c0c38..f285a95802 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 @@ -1352,7 +1352,15 @@ STATIC_ASSERT(__cpp_lib_polymorphic_allocator == 201902L); STATIC_ASSERT(__cpp_lib_quoted_string_io == 201304L); #endif -#if _HAS_CXX20 && !defined(__EDG__) // TRANSITION, EDG concepts support +#if _HAS_CXX23 && !defined(__EDG__) // TRANSITION, EDG concepts support +#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 #ifndef __cpp_lib_ranges #error __cpp_lib_ranges is not defined #elif __cpp_lib_ranges != 202110L From c92222839ac387d352752da74906d0479210f6a4 Mon Sep 17 00:00:00 2001 From: Kilian Date: Thu, 21 Apr 2022 21:59:23 +0200 Subject: [PATCH 10/34] Add missing const --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index daba47a070..24a0b03062 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1482,7 +1482,7 @@ namespace ranges { template requires _StdPipe::_Are_composable_range_adaptor_closures - constexpr auto operator|(_Other&& _Right) & noexcept( + constexpr auto operator|(_Other&& _Right) const& noexcept( noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)}; } From 097bde2568c297fe63a19b355609fd52dd085391 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 08:00:24 +0200 Subject: [PATCH 11/34] Define value of __cpp_lib_ranges depending on c++20 or c++23 mode --- stl/inc/yvals_core.h | 4 ++++ .../VSO_0157762_feature_test_macros/test.compile.pass.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 50cb185eee..a686455ea6 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1360,7 +1360,11 @@ #define __cpp_lib_polymorphic_allocator 201902L #if defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#if _HAS_CXX23 #define __cpp_lib_ranges 202202L +#elif _HAS_CXX20 // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv +#define __cpp_lib_ranges 202110L +#endif // _HAS_CXX23 #endif // defined(__cpp_lib_concepts) #define __cpp_lib_remove_cvref 201711L 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 f285a95802..c9eeddf3b3 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 @@ -1360,7 +1360,7 @@ STATIC_ASSERT(__cpp_lib_quoted_string_io == 201304L); #else STATIC_ASSERT(__cpp_lib_ranges == 202202L); #endif -#elif _HAS_CXX20 +#elif _HAS_CXX20 && !defined(__EDG__) // TRANSITION, EDG concepts support #ifndef __cpp_lib_ranges #error __cpp_lib_ranges is not defined #elif __cpp_lib_ranges != 202110L From 2e16ab58bf496485d1d0addafffa68dbf96d07ed Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 09:34:07 +0200 Subject: [PATCH 12/34] A function object only requires callable, not invocable --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 24a0b03062..82740155e5 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1452,7 +1452,7 @@ namespace ranges { template concept _Is_invocable_range_adaptor_closure = - range<_Rng> && invocable<_Ty, _Rng> && _Is_range_adaptor_common<_Ty>; + range<_Rng> && _Pipe::_Can_pipe<_Rng, _Ty> && _Is_range_adaptor_common<_Ty>; template concept _Are_composable_range_adaptor_closures = From 0afb0611fdc637d25107f9b907a4c422c04940b1 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 10:08:28 +0200 Subject: [PATCH 13/34] Overloads of ref-qualifier & and const&& for operator| composing two range adaptor closures got removed, because it is superfluous. Both will bind to const& --- stl/inc/ranges | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 82740155e5..1131750b61 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1466,13 +1466,6 @@ namespace ranges { requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> class range_adaptor_closure { public: - template - requires _StdPipe::_Are_composable_range_adaptor_closures<_Derived&, _Other> - constexpr auto operator|(_Other&& _Right) & noexcept( - noexcept(_StdPipe::_StdPipeline{static_cast<_Derived&>(*this), _STD forward<_Other>(_Right)})) { - return _StdPipe::_StdPipeline{static_cast<_Derived&>(*this), _STD forward<_Other>(_Right)}; - } - template requires _StdPipe::_Are_composable_range_adaptor_closures<_Derived, _Other> constexpr auto operator|(_Other&& _Right) && noexcept( @@ -1487,13 +1480,6 @@ namespace ranges { return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)}; } - template - requires _StdPipe::_Are_composable_range_adaptor_closures - constexpr auto operator|(_Other&& _Right) const&& noexcept( - noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { - return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)}; - } - template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right) noexcept( noexcept(static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)))) { From d1a5c1b7935d083ba6b2da0ebd67b52d75032957 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 10:38:20 +0200 Subject: [PATCH 14/34] Add __EDG__ workaround to range_adaptor_closure to mimic _Pipe::_Base --- stl/inc/ranges | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1131750b61..fd740ae053 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1481,26 +1481,46 @@ namespace ranges { } template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> - friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right) noexcept( - noexcept(static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)))) { + friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right) +#ifdef __EDG__ // TRANSITION, VSO-1222776 + noexcept(noexcept(_STD declval<_Derived&>()(_STD forward<_Rng>(_Left)))) +#else // ^^^ workaround / no workaround vvv + noexcept(noexcept(static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)))) +#endif // TRANSITION, VSO-1222776 + { return static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)); } template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> - friend constexpr auto operator|(_Rng&& _Left, _Derived&& _Right) noexcept( - noexcept(static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)))) { + friend constexpr auto operator|(_Rng&& _Left, _Derived&& _Right) +#ifdef __EDG__ // TRANSITION, VSO-1222776 + noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Rng>(_Left)))) +#else // ^^^ workaround / no workaround vvv + noexcept(noexcept(static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)))) +#endif // TRANSITION, VSO-1222776 + { return static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)); } template <_StdPipe::_Is_invocable_range_adaptor_closure _Rng> - friend constexpr auto operator|(_Rng&& _Left, const _Derived& _Right) noexcept( - noexcept(static_cast(_Right)(_STD forward<_Rng>(_Left)))) { + friend constexpr auto operator|(_Rng&& _Left, const _Derived& _Right) +#ifdef __EDG__ // TRANSITION, VSO-1222776 + noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) +#else // ^^^ workaround / no workaround vvv + noexcept(noexcept(static_cast(_Right)(_STD forward<_Rng>(_Left)))) +#endif // TRANSITION, VSO-1222776 + { return static_cast(_Right)(_STD forward<_Rng>(_Left)); } template <_StdPipe::_Is_invocable_range_adaptor_closure _Rng> - friend constexpr auto operator|(_Rng&& _Left, const _Derived&& _Right) noexcept( - noexcept(static_cast(_Right)(_STD forward<_Rng>(_Left)))) { + friend constexpr auto operator|(_Rng&& _Left, const _Derived&& _Right) +#ifdef __EDG__ // TRANSITION, VSO-1222776 + noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) +#else // ^^^ workaround / no workaround vvv + noexcept(noexcept(static_cast(_Right)(_STD forward<_Rng>(_Left)))) +#endif // TRANSITION, VSO-1222776 + { return static_cast(_Right)(_STD forward<_Rng>(_Left)); } }; From 55256e410902ba44e2eadd7ce49b04af70a60950 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 10:45:04 +0200 Subject: [PATCH 15/34] Let the standard range adaptor closure types inherit from range_adaptor_closure --- stl/inc/ranges | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index fd740ae053..ce93084693 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -805,8 +805,12 @@ namespace ranges { }; }; + template + requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> + class range_adaptor_closure; + template - class _Range_closure : public _Pipe::_Base<_Range_closure<_Fn, _Types...>> { + class _Range_closure : public range_adaptor_closure<_Range_closure<_Fn, _Types...>> { public: // We assume that _Fn is the type of a customization point object. That means // 1. The behavior of operator() is independent of cvref qualifiers, so we can use `invocable<_Fn, ` without @@ -1433,10 +1437,6 @@ namespace ranges { inline constexpr _Istream_fn<_Ty> istream; } // namespace views - template - requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> - class range_adaptor_closure; - namespace _StdPipe { template void _Derived_from_range_adaptor_closure_impl(const volatile range_adaptor_closure<_Ty>&); @@ -1716,7 +1716,7 @@ namespace ranges { owning_view{static_cast<_Rng&&>(__r)}; }; - class _All_fn : public _Pipe::_Base<_All_fn> { + class _All_fn : public range_adaptor_closure<_All_fn> { private: enum class _St { _None, _View, _Ref, _Own }; @@ -3535,7 +3535,7 @@ namespace ranges { explicit join_view(_Rng&&) -> join_view>; namespace views { - class _Join_fn : public _Pipe::_Base<_Join_fn> { + class _Join_fn : public range_adaptor_closure<_Join_fn> { public: // clang-format off template @@ -4292,7 +4292,7 @@ namespace ranges { inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Rng>; namespace views { - class _Common_fn : public _Pipe::_Base<_Common_fn> { + class _Common_fn : public range_adaptor_closure<_Common_fn> { private: enum class _St { _None, _All, _Common }; @@ -4416,7 +4416,7 @@ namespace ranges { reverse_view{static_cast<_Rng&&>(__r)}; }; - class _Reverse_fn : public _Pipe::_Base<_Reverse_fn> { + class _Reverse_fn : public range_adaptor_closure<_Reverse_fn> { private: enum class _St { _None, _Base, _Subrange_unsized, _Subrange_sized, _Reverse }; @@ -4868,7 +4868,7 @@ namespace ranges { namespace views { template - class _Elements_fn : public _Pipe::_Base<_Elements_fn<_Index>> { + class _Elements_fn : public range_adaptor_closure<_Elements_fn<_Index>> { public: template _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept( From 383ecfa3615e41a323fe318028a4ad822af3dacf Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 10:49:59 +0200 Subject: [PATCH 16/34] Remove namespace _Pipe and its content --- stl/inc/ranges | 120 ++++++------------------------------------------- 1 file changed, 13 insertions(+), 107 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index ce93084693..bf71645088 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -60,111 +60,6 @@ namespace ranges { template 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)); - } - - 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 - struct _Pipeline : _Base<_Pipeline<_Left, _Right>> { - /* [[no_unique_address]] */ _Left __l; - /* [[no_unique_address]] */ _Right __r; - - 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)) {} - - template - _NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept( - noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { - __r(__l(static_cast<_Ty&&>(_Val))); - } - { return __r(__l(_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))); - } - { return __r(__l(_STD forward<_Ty>(_Val))); } - }; - - template - _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; - } // namespace _Pipe - template class _Cached_position : public view_interface<_Derived> { static_assert(_Always_false<_Rng>, "A range must be at least forward for position caching to be worthwhile."); @@ -1446,17 +1341,28 @@ namespace ranges { _Derived_from_range_adaptor_closure_impl(__obj); }; + // 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 concept _Is_range_adaptor_common = derived_from, range_adaptor_closure>> && _Derived_from_range_adaptor_closure<_Ty> && !range<_Ty>; template concept _Is_invocable_range_adaptor_closure = - range<_Rng> && _Pipe::_Can_pipe<_Rng, _Ty> && _Is_range_adaptor_common<_Ty>; + range<_Rng> && _Can_pipe<_Rng, _Ty> && _Is_range_adaptor_common<_Ty>; template concept _Are_composable_range_adaptor_closures = - _Is_range_adaptor_common<_Left> && _Is_range_adaptor_common<_Right> && _Pipe::_Can_compose<_Left, _Right>; + _Is_range_adaptor_common<_Left> && _Is_range_adaptor_common<_Right> && _Can_compose<_Left, _Right>; template struct _StdPipeline; From cbf346a8615b0f2eb5cc6c5bd6392031fa3e4a99 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 10:53:33 +0200 Subject: [PATCH 17/34] Rename _StdPipe to _Pipe --- stl/inc/ranges | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index bf71645088..1c6e460b14 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1332,7 +1332,7 @@ namespace ranges { inline constexpr _Istream_fn<_Ty> istream; } // namespace views - namespace _StdPipe { + namespace _Pipe { template void _Derived_from_range_adaptor_closure_impl(const volatile range_adaptor_closure<_Ty>&); @@ -1365,28 +1365,28 @@ namespace ranges { _Is_range_adaptor_common<_Left> && _Is_range_adaptor_common<_Right> && _Can_compose<_Left, _Right>; template - struct _StdPipeline; - } // namespace _StdPipe + struct _Pipeline; + } // namespace _Pipe template requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> class range_adaptor_closure { public: template - requires _StdPipe::_Are_composable_range_adaptor_closures<_Derived, _Other> + requires _Pipe::_Are_composable_range_adaptor_closures<_Derived, _Other> constexpr auto operator|(_Other&& _Right) && noexcept( - noexcept(_StdPipe::_StdPipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) { - return _StdPipe::_StdPipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)}; + noexcept(_Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) { + return _Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)}; } template - requires _StdPipe::_Are_composable_range_adaptor_closures + requires _Pipe::_Are_composable_range_adaptor_closures constexpr auto operator|(_Other&& _Right) const& noexcept( - noexcept(_StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { - return _StdPipe::_StdPipeline{static_cast(*this), _STD forward<_Other>(_Right)}; + noexcept(_Pipe::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { + return _Pipe::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)}; } - template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> + template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval<_Derived&>()(_STD forward<_Rng>(_Left)))) @@ -1397,7 +1397,7 @@ namespace ranges { return static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)); } - template <_StdPipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> + template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> friend constexpr auto operator|(_Rng&& _Left, _Derived&& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Rng>(_Left)))) @@ -1408,7 +1408,7 @@ namespace ranges { return static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)); } - template <_StdPipe::_Is_invocable_range_adaptor_closure _Rng> + template <_Pipe::_Is_invocable_range_adaptor_closure _Rng> friend constexpr auto operator|(_Rng&& _Left, const _Derived& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) @@ -1419,7 +1419,7 @@ namespace ranges { return static_cast(_Right)(_STD forward<_Rng>(_Left)); } - template <_StdPipe::_Is_invocable_range_adaptor_closure _Rng> + template <_Pipe::_Is_invocable_range_adaptor_closure _Rng> friend constexpr auto operator|(_Rng&& _Left, const _Derived&& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) @@ -1431,14 +1431,14 @@ namespace ranges { } }; - namespace _StdPipe { + namespace _Pipe { template - struct _StdPipeline : range_adaptor_closure<_StdPipeline<_ClosureLeft, _ClosureRight>> { + struct _Pipeline : range_adaptor_closure<_Pipeline<_ClosureLeft, _ClosureRight>> { /* [[no_unique_address]] */ _ClosureLeft _Left; /* [[no_unique_address]] */ _ClosureRight _Right; template - constexpr explicit _StdPipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept( + constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept( is_nothrow_constructible_v<_Ty1, _ClosureLeft>&& is_nothrow_constructible_v<_Ty2, _ClosureRight>) : _Left(_STD forward<_Ty1>(_Val1)), _Right(_STD forward<_Ty2>(_Val2)) {} @@ -1472,9 +1472,9 @@ namespace ranges { }; template - _StdPipeline(_Ty1, _Ty2) -> _StdPipeline<_Ty1, _Ty2>; + _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; - } // namespace _StdPipe + } // namespace _Pipe template requires is_object_v<_Rng> From c9ac78edadebaf35575c1639d9b3d0ecc6de5fc4 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 10:55:40 +0200 Subject: [PATCH 18/34] Correct indentation of clang-format on comment --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1c6e460b14..e17c875490 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1350,7 +1350,7 @@ namespace ranges { template concept _Can_compose = constructible_from, _Left> && constructible_from, _Right>; - // clang-format on + // clang-format on template concept _Is_range_adaptor_common = derived_from, From c56d373efc401e0cf17b6490f624f90a42def265 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 10:56:36 +0200 Subject: [PATCH 19/34] clang-format --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index e17c875490..1b127ad144 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3936,7 +3936,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; @@ -3988,7 +3988,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; From 26d9bc569a75b64bf748b4c3510fe373ee0e66fc Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 11:59:52 +0200 Subject: [PATCH 20/34] Add tests for mixing user defined and standard range adaptor closures --- .../test.cpp | 83 +++++++++++++------ 1 file changed, 57 insertions(+), 26 deletions(-) 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 index 8257acbad2..282193e2c3 100644 --- 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 @@ -106,26 +106,46 @@ struct ModelsRange : std::ranges::range_adaptor_closure { static_assert(does_not_satisfy_range_adaptor_closure()); struct RangeAdaptorClosureMemberRefQualTest : std::ranges::range_adaptor_closure { - constexpr char operator()(const TestRange&) & { - return {}; + constexpr std::ranges::empty_view operator()(const auto&) & { + return std::views::empty; } - constexpr short operator()(const TestRange&) && { - return {}; + constexpr std::ranges::empty_view operator()(const auto&) && { + return std::views::empty; } - constexpr unsigned char operator()(const TestRange&) const& { - return {}; + constexpr std::ranges::empty_view operator()(const auto&) const& { + return std::views::empty; } - constexpr unsigned short operator()(const TestRange&) const&& { - return {}; + constexpr std::ranges::empty_view operator()(const auto&) const&& { + return std::views::empty; } }; -static_assert(CanPipe_R); -static_assert(CanPipe_R); -static_assert(CanPipe_R); -static_assert(CanPipe_R); +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( + std::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{} | std::views::all); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); struct RangeAdaptorClosureParameterRefQualTest : std::ranges::range_adaptor_closure { @@ -137,25 +157,18 @@ struct RangeAdaptorClosureParameterRefQualTest return {}; } - constexpr unsigned char operator()(const TestRange&) { + constexpr float operator()(const TestRange&) { return {}; } - constexpr unsigned short operator()(const TestRange&&) { + constexpr double operator()(const TestRange&&) { return {}; } }; 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); +static_assert(CanPipe_R); +static_assert(CanPipe_R); struct MoveOnlyRangeAdaptorClosure : std::ranges::range_adaptor_closure { MoveOnlyRangeAdaptorClosure() = default; @@ -187,7 +200,7 @@ class DividedByTwoAdaptor : public std::ranges::range_adaptor_closure numbers{1, 2, 3}; const std::array numbers_times_two{2, 4, 6}; assert(std::ranges::equal(numbers_times_two, numbers | TimesTwoAdaptor{})); @@ -200,7 +213,25 @@ constexpr bool test_constexpr() { return true; } +constexpr bool test_mixing_of_range_adaptors() { + const std::array numbers{1, 2, 3}; + assert(std::ranges::equal( + numbers, numbers | (TimesTwoAdaptor{} | std::views::transform([](int x) { return x / 2; })))); + + const auto mixed_pipeline = TimesTwoAdaptor{} | std::views::reverse | DividedByTwoAdaptor{} | std::views::reverse; + assert(std::ranges::equal(mixed_pipeline(numbers), numbers)); + assert(std::ranges::equal(numbers | mixed_pipeline, numbers)); + + auto factory_pipeline = std::views::iota(1) | TimesTwoAdaptor{} | std::views::take(3); + assert(std::ranges::equal(factory_pipeline, std::array{2, 4, 6})); + + return true; +} + int main() { - assert(test_constexpr()); - static_assert(test_constexpr()); + assert(test_user_defined_adaptors()); + static_assert(test_user_defined_adaptors()); + + assert(test_mixing_of_range_adaptors()); + static_assert(test_mixing_of_range_adaptors()); } From ababc873e9c68f104fabc9e770400bec9f6f2a29 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 13:31:28 +0200 Subject: [PATCH 21/34] Only provide and use range_adaptor_closure when _HAS_CXX23 is defined --- stl/inc/ranges | 157 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 137 insertions(+), 20 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1b127ad144..1f32097bec 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -60,6 +60,113 @@ namespace ranges { template using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>; +#if !_HAS_CXX23 + 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)); + } + + 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 + struct _Pipeline : _Base<_Pipeline<_Left, _Right>> { + /* [[no_unique_address]] */ _Left __l; + /* [[no_unique_address]] */ _Right __r; + + 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)) {} + + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept( + noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { + __r(__l(static_cast<_Ty&&>(_Val))); + } + { return __r(__l(_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))); + } + { return __r(__l(_STD forward<_Ty>(_Val))); } + }; + + template + _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; + } // namespace _Pipe +#endif //!_HAS_CXX23 + template class _Cached_position : public view_interface<_Derived> { static_assert(_Always_false<_Rng>, "A range must be at least forward for position caching to be worthwhile."); @@ -700,12 +807,20 @@ namespace ranges { }; }; +#if _HAS_CXX23 template requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> class range_adaptor_closure; + template + using _Range_Adaptor_Base = range_adaptor_closure<_Ty>; +#else // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv + template + using _Range_Adaptor_Base = _Pipe::Base<_Ty>; +#endif + template - class _Range_closure : public range_adaptor_closure<_Range_closure<_Fn, _Types...>> { + class _Range_closure : public _Range_Adaptor_Base<_Range_closure<_Fn, _Types...>> { public: // We assume that _Fn is the type of a customization point object. That means // 1. The behavior of operator() is independent of cvref qualifiers, so we can use `invocable<_Fn, ` without @@ -1332,7 +1447,8 @@ namespace ranges { inline constexpr _Istream_fn<_Ty> istream; } // namespace views - namespace _Pipe { +#if _HAS_CXX23 + namespace _Pipe_cpp23 { template void _Derived_from_range_adaptor_closure_impl(const volatile range_adaptor_closure<_Ty>&); @@ -1366,27 +1482,27 @@ namespace ranges { template struct _Pipeline; - } // namespace _Pipe + } // namespace _Pipe_cpp23 template requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> class range_adaptor_closure { public: template - requires _Pipe::_Are_composable_range_adaptor_closures<_Derived, _Other> + requires _Pipe_cpp23::_Are_composable_range_adaptor_closures<_Derived, _Other> constexpr auto operator|(_Other&& _Right) && noexcept( - noexcept(_Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) { - return _Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)}; + noexcept(_Pipe_cpp23::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) { + return _Pipe_cpp23::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)}; } template - requires _Pipe::_Are_composable_range_adaptor_closures + requires _Pipe_cpp23::_Are_composable_range_adaptor_closures constexpr auto operator|(_Other&& _Right) const& noexcept( - noexcept(_Pipe::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { - return _Pipe::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)}; + noexcept(_Pipe_cpp23::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { + return _Pipe_cpp23::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)}; } - template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> + template <_Pipe_cpp23::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval<_Derived&>()(_STD forward<_Rng>(_Left)))) @@ -1397,7 +1513,7 @@ namespace ranges { return static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)); } - template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> + template <_Pipe_cpp23::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> friend constexpr auto operator|(_Rng&& _Left, _Derived&& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Rng>(_Left)))) @@ -1408,7 +1524,7 @@ namespace ranges { return static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)); } - template <_Pipe::_Is_invocable_range_adaptor_closure _Rng> + template <_Pipe_cpp23::_Is_invocable_range_adaptor_closure _Rng> friend constexpr auto operator|(_Rng&& _Left, const _Derived& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) @@ -1419,7 +1535,7 @@ namespace ranges { return static_cast(_Right)(_STD forward<_Rng>(_Left)); } - template <_Pipe::_Is_invocable_range_adaptor_closure _Rng> + template <_Pipe_cpp23::_Is_invocable_range_adaptor_closure _Rng> friend constexpr auto operator|(_Rng&& _Left, const _Derived&& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) @@ -1431,7 +1547,7 @@ namespace ranges { } }; - namespace _Pipe { + namespace _Pipe_cpp23 { template struct _Pipeline : range_adaptor_closure<_Pipeline<_ClosureLeft, _ClosureRight>> { /* [[no_unique_address]] */ _ClosureLeft _Left; @@ -1474,7 +1590,8 @@ namespace ranges { template _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; - } // namespace _Pipe + } // namespace _Pipe_cpp23 +#endif //_HAS_CXX23 template requires is_object_v<_Rng> @@ -1622,7 +1739,7 @@ namespace ranges { owning_view{static_cast<_Rng&&>(__r)}; }; - class _All_fn : public range_adaptor_closure<_All_fn> { + class _All_fn : public _Range_Adaptor_Base<_All_fn> { private: enum class _St { _None, _View, _Ref, _Own }; @@ -3441,7 +3558,7 @@ namespace ranges { explicit join_view(_Rng&&) -> join_view>; namespace views { - class _Join_fn : public range_adaptor_closure<_Join_fn> { + class _Join_fn : public _Range_Adaptor_Base<_Join_fn> { public: // clang-format off template @@ -4198,7 +4315,7 @@ namespace ranges { inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Rng>; namespace views { - class _Common_fn : public range_adaptor_closure<_Common_fn> { + class _Common_fn : public _Range_Adaptor_Base<_Common_fn> { private: enum class _St { _None, _All, _Common }; @@ -4322,7 +4439,7 @@ namespace ranges { reverse_view{static_cast<_Rng&&>(__r)}; }; - class _Reverse_fn : public range_adaptor_closure<_Reverse_fn> { + class _Reverse_fn : public _Range_Adaptor_Base<_Reverse_fn> { private: enum class _St { _None, _Base, _Subrange_unsized, _Subrange_sized, _Reverse }; @@ -4774,7 +4891,7 @@ namespace ranges { namespace views { template - class _Elements_fn : public range_adaptor_closure<_Elements_fn<_Index>> { + class _Elements_fn : public _Range_Adaptor_Base<_Elements_fn<_Index>> { public: template _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept( From a32c71da415ef783365c23b136f87863bb74e508 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 13:48:40 +0200 Subject: [PATCH 22/34] Fix typo Base -> _Base --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1f32097bec..78ef3c0388 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -816,7 +816,7 @@ namespace ranges { using _Range_Adaptor_Base = range_adaptor_closure<_Ty>; #else // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv template - using _Range_Adaptor_Base = _Pipe::Base<_Ty>; + using _Range_Adaptor_Base = _Pipe::_Base<_Ty>; #endif template From 1ba884e3e2b71e0e891e751c1d4161f5f491a08f Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 17:53:45 +0200 Subject: [PATCH 23/34] As __cpp_lib_ranges is language mode sensitive, move it to the corresponding area --- stl/inc/yvals_core.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index a686455ea6..de5ca1856e 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1358,15 +1358,6 @@ #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L - -#if defined(__cpp_lib_concepts) // TRANSITION, GH-395 -#if _HAS_CXX23 -#define __cpp_lib_ranges 202202L -#elif _HAS_CXX20 // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv -#define __cpp_lib_ranges 202110L -#endif // _HAS_CXX23 -#endif // defined(__cpp_lib_concepts) - #define __cpp_lib_remove_cvref 201711L #define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L @@ -1457,6 +1448,14 @@ #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 +#elif _HAS_CXX20 // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv +#define __cpp_lib_ranges 202110L +#endif // _HAS_CXX23 +#endif // defined(__cpp_lib_concepts) + #if _HAS_CXX20 #define __cpp_lib_shared_ptr_arrays 201707L // P0674R1 make_shared() For Arrays #else // _HAS_CXX20 From b03523e650c767bee6c4c6036e02ffd9bdbde12c Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 18:06:20 +0200 Subject: [PATCH 24/34] Merge content of _Pipe_cpp23 into _Pipe --- stl/inc/ranges | 186 +++++++++++++++++++------------------------------ 1 file changed, 71 insertions(+), 115 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 78ef3c0388..80fe13ef88 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -60,7 +60,22 @@ namespace ranges { template using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>; -#if !_HAS_CXX23 +#if _HAS_CXX23 + template + requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> + class range_adaptor_closure; + + template + using _Range_Adaptor_Base = range_adaptor_closure<_Ty>; +#else // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv + namespace _Pipe { + template + struct _Base; + } // namespace _Pipe + template + using _Range_Adaptor_Base = _Pipe::_Base<_Ty>; +#endif + namespace _Pipe { // clang-format off template @@ -73,9 +88,49 @@ namespace ranges { && constructible_from, _Right>; // clang-format on - template - struct _Pipeline; + template + struct _Pipeline : _Range_Adaptor_Base<_Pipeline<_ClosureLeft, _ClosureRight>> { + /* [[no_unique_address]] */ _ClosureLeft _Left; + /* [[no_unique_address]] */ _ClosureRight _Right; + + template + constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept( + 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(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires { + _Right(_Left(_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(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires { + _Right(_Left(_STD forward<_Ty>(_Val))); + } + { return _Right(_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>; +#if !_HAS_CXX23 template struct _Base { template @@ -136,36 +191,8 @@ namespace ranges { return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l)); } }; - - template - struct _Pipeline : _Base<_Pipeline<_Left, _Right>> { - /* [[no_unique_address]] */ _Left __l; - /* [[no_unique_address]] */ _Right __r; - - 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)) {} - - template - _NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept( - noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires { - __r(__l(static_cast<_Ty&&>(_Val))); - } - { return __r(__l(_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))); - } - { return __r(__l(_STD forward<_Ty>(_Val))); } - }; - - template - _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; - } // namespace _Pipe #endif //!_HAS_CXX23 + } // namespace _Pipe template class _Cached_position : public view_interface<_Derived> { @@ -807,18 +834,6 @@ namespace ranges { }; }; -#if _HAS_CXX23 - template - requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> - class range_adaptor_closure; - - template - using _Range_Adaptor_Base = range_adaptor_closure<_Ty>; -#else // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv - template - using _Range_Adaptor_Base = _Pipe::_Base<_Ty>; -#endif - template class _Range_closure : public _Range_Adaptor_Base<_Range_closure<_Fn, _Types...>> { public: @@ -1448,7 +1463,7 @@ namespace ranges { } // namespace views #if _HAS_CXX23 - namespace _Pipe_cpp23 { + namespace _Pipe { template void _Derived_from_range_adaptor_closure_impl(const volatile range_adaptor_closure<_Ty>&); @@ -1457,17 +1472,6 @@ namespace ranges { _Derived_from_range_adaptor_closure_impl(__obj); }; - // 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 concept _Is_range_adaptor_common = derived_from, range_adaptor_closure>> && _Derived_from_range_adaptor_closure<_Ty> && !range<_Ty>; @@ -1479,30 +1483,27 @@ namespace ranges { template concept _Are_composable_range_adaptor_closures = _Is_range_adaptor_common<_Left> && _Is_range_adaptor_common<_Right> && _Can_compose<_Left, _Right>; - - template - struct _Pipeline; - } // namespace _Pipe_cpp23 + } // namespace _Pipe template requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> class range_adaptor_closure { public: template - requires _Pipe_cpp23::_Are_composable_range_adaptor_closures<_Derived, _Other> + requires _Pipe::_Are_composable_range_adaptor_closures<_Derived, _Other> constexpr auto operator|(_Other&& _Right) && noexcept( - noexcept(_Pipe_cpp23::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) { - return _Pipe_cpp23::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)}; + noexcept(_Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) { + return _Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)}; } template - requires _Pipe_cpp23::_Are_composable_range_adaptor_closures + requires _Pipe::_Are_composable_range_adaptor_closures constexpr auto operator|(_Other&& _Right) const& noexcept( - noexcept(_Pipe_cpp23::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { - return _Pipe_cpp23::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)}; + noexcept(_Pipe::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { + return _Pipe::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)}; } - template <_Pipe_cpp23::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> + template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval<_Derived&>()(_STD forward<_Rng>(_Left)))) @@ -1513,7 +1514,7 @@ namespace ranges { return static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)); } - template <_Pipe_cpp23::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> + template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> friend constexpr auto operator|(_Rng&& _Left, _Derived&& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Rng>(_Left)))) @@ -1524,7 +1525,7 @@ namespace ranges { return static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)); } - template <_Pipe_cpp23::_Is_invocable_range_adaptor_closure _Rng> + template <_Pipe::_Is_invocable_range_adaptor_closure _Rng> friend constexpr auto operator|(_Rng&& _Left, const _Derived& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) @@ -1535,7 +1536,7 @@ namespace ranges { return static_cast(_Right)(_STD forward<_Rng>(_Left)); } - template <_Pipe_cpp23::_Is_invocable_range_adaptor_closure _Rng> + template <_Pipe::_Is_invocable_range_adaptor_closure _Rng> friend constexpr auto operator|(_Rng&& _Left, const _Derived&& _Right) #ifdef __EDG__ // TRANSITION, VSO-1222776 noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) @@ -1546,51 +1547,6 @@ namespace ranges { return static_cast(_Right)(_STD forward<_Rng>(_Left)); } }; - - namespace _Pipe_cpp23 { - template - struct _Pipeline : range_adaptor_closure<_Pipeline<_ClosureLeft, _ClosureRight>> { - /* [[no_unique_address]] */ _ClosureLeft _Left; - /* [[no_unique_address]] */ _ClosureRight _Right; - - template - constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept( - 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(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires { - _Right(_Left(_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(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires { - _Right(_Left(_STD forward<_Ty>(_Val))); - } - { return _Right(_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>; - - } // namespace _Pipe_cpp23 #endif //_HAS_CXX23 template From 5faa9125570ddb621e32bbe4ad8422ffacbdf7d5 Mon Sep 17 00:00:00 2001 From: Kilian Date: Fri, 22 Apr 2022 18:09:03 +0200 Subject: [PATCH 25/34] Add missing new line --- stl/inc/ranges | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/ranges b/stl/inc/ranges index 80fe13ef88..5e152b1534 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -72,6 +72,7 @@ namespace ranges { template struct _Base; } // namespace _Pipe + template using _Range_Adaptor_Base = _Pipe::_Base<_Ty>; #endif From f191335f611913e093fdba846ef14ce78a51688f Mon Sep 17 00:00:00 2001 From: Kilian Date: Sat, 23 Apr 2022 18:12:34 +0200 Subject: [PATCH 26/34] Add // to overcome clang-format and improve readability --- stl/inc/ranges | 5 +++-- .../test.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 5e152b1534..86021ff306 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1474,8 +1474,9 @@ namespace ranges { }; template - concept _Is_range_adaptor_common = derived_from, - range_adaptor_closure>> && _Derived_from_range_adaptor_closure<_Ty> && !range<_Ty>; + concept _Is_range_adaptor_common = + derived_from, range_adaptor_closure>> // + && _Derived_from_range_adaptor_closure<_Ty> && !range<_Ty>; template concept _Is_invocable_range_adaptor_closure = 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 index 282193e2c3..63e88b55b0 100644 --- 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 @@ -39,8 +39,8 @@ using TestRange = std::array; template constexpr bool does_not_satisfy_range_adaptor_closure() { - return !CanPipe && !CanPipe && !CanPipe && !CanPipe; + return !CanPipe && !CanPipe // + && !CanPipe && !CanPipe; } struct IdentityRangeAdaptorClosure : std::ranges::range_adaptor_closure { From bb1033df46c0b046f0decac6bb9203bbd0f60153 Mon Sep 17 00:00:00 2001 From: Kilian Date: Sat, 23 Apr 2022 18:18:53 +0200 Subject: [PATCH 27/34] Remove std:: because of using namespace std; --- .../test.cpp | 127 +++++++++--------- 1 file changed, 63 insertions(+), 64 deletions(-) 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 index 63e88b55b0..e38e3413a8 100644 --- 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 @@ -11,7 +11,7 @@ using namespace std; template concept CanInstantiateRangeAdaptorClosure = requires { - typename std::ranges::range_adaptor_closure; + typename ranges::range_adaptor_closure; }; class EmptyTestType {}; @@ -27,15 +27,15 @@ static_assert(CanInstantiateRangeAdaptorClosure); template concept CanPipe = requires(LHS lhs, RHS rhs) { - std::forward(lhs) | std::forward(rhs); + forward(lhs) | forward(rhs); }; template concept CanPipe_R = requires(LHS lhs, RHS rhs) { - { std::forward(lhs) | std::forward(rhs) } -> std::same_as; + { forward(lhs) | forward(rhs) } -> same_as; }; -using TestRange = std::array; +using TestRange = array; template constexpr bool does_not_satisfy_range_adaptor_closure() { @@ -43,19 +43,19 @@ constexpr bool does_not_satisfy_range_adaptor_closure() { && !CanPipe && !CanPipe; } -struct IdentityRangeAdaptorClosure : std::ranges::range_adaptor_closure { +struct IdentityRangeAdaptorClosure : ranges::range_adaptor_closure { template constexpr decltype(auto) operator()(T&& range) const { - return std::forward(range); + return forward(range); } }; // Is not a range adaptor closure, because it is not a function object. -struct NotCallable : std::ranges::range_adaptor_closure {}; +struct NotCallable : ranges::range_adaptor_closure {}; static_assert(does_not_satisfy_range_adaptor_closure()); // Is not a range adaptor closure, because it does not accept a range as argument. -struct NotCallableWithRange : std::ranges::range_adaptor_closure { +struct NotCallableWithRange : ranges::range_adaptor_closure { void operator()() {} }; static_assert(does_not_satisfy_range_adaptor_closure()); @@ -67,27 +67,27 @@ struct NotDerivedFrom { static_assert(does_not_satisfy_range_adaptor_closure()); // Is not a range adaptor closure, because it inherits privately from range_adaptor_closure. -struct DerivedPrivately : private std::ranges::range_adaptor_closure { +struct DerivedPrivately : private ranges::range_adaptor_closure { void operator()(const TestRange&) {} }; static_assert(does_not_satisfy_range_adaptor_closure()); // Is not a range adaptor closure, because it inherits from the wrong specialization of range_adaptor_closure. -struct DerivedFromWrongSpecialization : std::ranges::range_adaptor_closure { +struct DerivedFromWrongSpecialization : ranges::range_adaptor_closure { void operator()(const TestRange&) {} }; static_assert(does_not_satisfy_range_adaptor_closure()); // Is not a range adaptor closure, because it has two base classes which are specializations of // range_adaptor_closure. -struct DerivedFromTwoSpecializations : std::ranges::range_adaptor_closure, - std::ranges::range_adaptor_closure { +struct DerivedFromTwoSpecializations : ranges::range_adaptor_closure, + ranges::range_adaptor_closure { void operator()(const TestRange&) {} }; static_assert(does_not_satisfy_range_adaptor_closure()); -// Is not a range adaptor closure, because it models std::ranges::range. -struct ModelsRange : std::ranges::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() { @@ -105,50 +105,50 @@ struct ModelsRange : std::ranges::range_adaptor_closure { }; static_assert(does_not_satisfy_range_adaptor_closure()); -struct RangeAdaptorClosureMemberRefQualTest : std::ranges::range_adaptor_closure { - constexpr std::ranges::empty_view operator()(const auto&) & { - return std::views::empty; +struct RangeAdaptorClosureMemberRefQualTest : ranges::range_adaptor_closure { + constexpr ranges::empty_view operator()(const auto&) & { + return views::empty; } - constexpr std::ranges::empty_view operator()(const auto&) && { - return std::views::empty; + constexpr ranges::empty_view operator()(const auto&) && { + return views::empty; } - constexpr std::ranges::empty_view operator()(const auto&) const& { - return std::views::empty; + constexpr ranges::empty_view operator()(const auto&) const& { + return views::empty; } - constexpr std::ranges::empty_view operator()(const auto&) const&& { - return std::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>); +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>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); +static_assert(CanPipe_R>); using FirstTransformThenMemberRefQualTest = decltype( - std::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{} | std::views::all); -static_assert(CanPipe_R>); -static_assert(CanPipe_R>); -static_assert(CanPipe_R>); -static_assert(CanPipe_R>); + 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 - : std::ranges::range_adaptor_closure { + : ranges::range_adaptor_closure { constexpr char operator()(TestRange&) { return {}; } @@ -170,7 +170,7 @@ static_assert(CanPipe_R); static_assert(CanPipe_R); -struct MoveOnlyRangeAdaptorClosure : std::ranges::range_adaptor_closure { +struct MoveOnlyRangeAdaptorClosure : ranges::range_adaptor_closure { MoveOnlyRangeAdaptorClosure() = default; MoveOnlyRangeAdaptorClosure(const MoveOnlyRangeAdaptorClosure&) = delete; MoveOnlyRangeAdaptorClosure(MoveOnlyRangeAdaptorClosure&&) = default; @@ -184,29 +184,29 @@ static_assert(CanPipe) static_assert(!CanPipe); static_assert(!CanPipe); -class TimesTwoAdaptor : public std::ranges::range_adaptor_closure { +class TimesTwoAdaptor : public ranges::range_adaptor_closure { public: - template + template constexpr auto operator()(R&& range) const { - return std::forward(range) | std::views::transform([](auto x) { return x * 2; }); + return forward(range) | views::transform([](auto x) { return x * 2; }); } }; -class DividedByTwoAdaptor : public std::ranges::range_adaptor_closure { +class DividedByTwoAdaptor : public ranges::range_adaptor_closure { public: - template + template constexpr auto operator()(R&& range) const { - return std::forward(range) | std::views::transform([](auto x) { return x / 2; }); + return forward(range) | views::transform([](auto x) { return x / 2; }); } }; constexpr bool test_user_defined_adaptors() { - const std::array numbers{1, 2, 3}; - const std::array numbers_times_two{2, 4, 6}; - assert(std::ranges::equal(numbers_times_two, numbers | TimesTwoAdaptor{})); - assert(std::ranges::equal(numbers, (numbers | TimesTwoAdaptor{}) | DividedByTwoAdaptor{})); - assert(std::ranges::equal(numbers, numbers | (TimesTwoAdaptor{} | DividedByTwoAdaptor{}))); - assert(std::ranges::equal(numbers_times_two, + 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{}))); @@ -214,16 +214,15 @@ constexpr bool test_user_defined_adaptors() { } constexpr bool test_mixing_of_range_adaptors() { - const std::array numbers{1, 2, 3}; - assert(std::ranges::equal( - numbers, numbers | (TimesTwoAdaptor{} | std::views::transform([](int x) { return x / 2; })))); + const array numbers{1, 2, 3}; + assert(ranges::equal(numbers, numbers | (TimesTwoAdaptor{} | views::transform([](int x) { return x / 2; })))); - const auto mixed_pipeline = TimesTwoAdaptor{} | std::views::reverse | DividedByTwoAdaptor{} | std::views::reverse; - assert(std::ranges::equal(mixed_pipeline(numbers), numbers)); - assert(std::ranges::equal(numbers | mixed_pipeline, numbers)); + 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 = std::views::iota(1) | TimesTwoAdaptor{} | std::views::take(3); - assert(std::ranges::equal(factory_pipeline, std::array{2, 4, 6})); + auto factory_pipeline = views::iota(1) | TimesTwoAdaptor{} | views::take(3); + assert(ranges::equal(factory_pipeline, array{2, 4, 6})); return true; } From 327897925c0f5e4381ae9eae205609deaafc5835 Mon Sep 17 00:00:00 2001 From: nicole mazzuca Date: Thu, 14 Jul 2022 15:34:56 -0700 Subject: [PATCH 28/34] Nicole suggestions --- stl/inc/yvals_core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index b0bd4d9ca7..6cacacc5c6 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1514,10 +1514,10 @@ #if defined(__cpp_lib_concepts) // TRANSITION, GH-395 #if _HAS_CXX23 -#define __cpp_lib_ranges 202202L +#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 -#endif // _HAS_CXX23 +#define __cpp_lib_ranges 202110L // P2415R2 What Is A `view`? +#endif // _HAS_CXX20 #endif // defined(__cpp_lib_concepts) #if _HAS_CXX20 From 3c88b92d16f12c6ece9b49a927ca80e64c85d761 Mon Sep 17 00:00:00 2001 From: nicole mazzuca Date: Thu, 14 Jul 2022 15:43:06 -0700 Subject: [PATCH 29/34] fix formatting --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index e87d0ab593..18326c7f42 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -5078,7 +5078,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 } From f3cfad7b83ab8e95236e0c6a1b92059d3855f455 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 18 Aug 2022 14:44:52 -0700 Subject: [PATCH 30/34] Derive _As_rvalue_fn from _Range_Adaptor_base --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index f411188480..2b29c2afe0 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1826,7 +1826,7 @@ namespace ranges { as_rvalue_view{static_cast<_Rng&&>(__r)}; }; - class _As_rvalue_fn : public _Pipe::_Base<_As_rvalue_fn> { + class _As_rvalue_fn : public _Range_Adaptor_Base<_As_rvalue_fn> { private: enum class _St { _None, _All, _As_rvalue }; From 42752a1650ef2f7a6ded2ced5ca2ca3cbe763d3c Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 18 Aug 2022 14:45:47 -0700 Subject: [PATCH 31/34] Rename `_Range_Adaptor_base` => `_Range_adaptor_base` --- stl/inc/ranges | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 2b29c2afe0..454b373cc3 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -66,7 +66,7 @@ namespace ranges { class range_adaptor_closure; template - using _Range_Adaptor_Base = range_adaptor_closure<_Ty>; + using _Range_adaptor_Base = range_adaptor_closure<_Ty>; #else // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv namespace _Pipe { template @@ -74,7 +74,7 @@ namespace ranges { } // namespace _Pipe template - using _Range_Adaptor_Base = _Pipe::_Base<_Ty>; + using _Range_adaptor_Base = _Pipe::_Base<_Ty>; #endif namespace _Pipe { @@ -90,7 +90,7 @@ namespace ranges { // clang-format on template - struct _Pipeline : _Range_Adaptor_Base<_Pipeline<_ClosureLeft, _ClosureRight>> { + struct _Pipeline : _Range_adaptor_Base<_Pipeline<_ClosureLeft, _ClosureRight>> { /* [[no_unique_address]] */ _ClosureLeft _Left; /* [[no_unique_address]] */ _ClosureRight _Right; @@ -836,7 +836,7 @@ namespace ranges { }; template - class _Range_closure : public _Range_Adaptor_Base<_Range_closure<_Fn, _Types...>> { + class _Range_closure : public _Range_adaptor_Base<_Range_closure<_Fn, _Types...>> { public: // We assume that _Fn is the type of a customization point object. That means // 1. The behavior of operator() is independent of cvref qualifiers, so we can use `invocable<_Fn, ` without @@ -1697,7 +1697,7 @@ namespace ranges { owning_view{static_cast<_Rng&&>(__r)}; }; - class _All_fn : public _Range_Adaptor_Base<_All_fn> { + class _All_fn : public _Range_adaptor_Base<_All_fn> { private: enum class _St { _None, _View, _Ref, _Own }; @@ -1826,7 +1826,7 @@ namespace ranges { as_rvalue_view{static_cast<_Rng&&>(__r)}; }; - class _As_rvalue_fn : public _Range_Adaptor_Base<_As_rvalue_fn> { + class _As_rvalue_fn : public _Range_adaptor_Base<_As_rvalue_fn> { private: enum class _St { _None, _All, _As_rvalue }; @@ -3656,7 +3656,7 @@ namespace ranges { explicit join_view(_Rng&&) -> join_view>; namespace views { - class _Join_fn : public _Range_Adaptor_Base<_Join_fn> { + class _Join_fn : public _Range_adaptor_Base<_Join_fn> { public: // clang-format off template @@ -4886,7 +4886,7 @@ namespace ranges { inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Rng>; namespace views { - class _Common_fn : public _Range_Adaptor_Base<_Common_fn> { + class _Common_fn : public _Range_adaptor_Base<_Common_fn> { private: enum class _St { _None, _All, _Common }; @@ -5010,7 +5010,7 @@ namespace ranges { reverse_view{static_cast<_Rng&&>(__r)}; }; - class _Reverse_fn : public _Range_Adaptor_Base<_Reverse_fn> { + class _Reverse_fn : public _Range_adaptor_Base<_Reverse_fn> { private: enum class _St { _None, _Base, _Subrange_unsized, _Subrange_sized, _Reverse }; @@ -5462,7 +5462,7 @@ namespace ranges { namespace views { template - class _Elements_fn : public _Range_Adaptor_Base<_Elements_fn<_Index>> { + class _Elements_fn : public _Range_adaptor_Base<_Elements_fn<_Index>> { public: template _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept( From c6206c4498193858ec7f359419a0ea2447d2b075 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 19 Aug 2022 13:26:55 -0700 Subject: [PATCH 32/34] Address review comments Most notably, `range_adaptor_closure` now derives from `_Pipe::_Base`, and the standard range adaptor closure objects derive directly from `_Pipe::_Base` as they did previously. I also eliminated a ton of repetition by implementing `operator|` for `_Pipe::_Base` as a non-member. (Since the non-member operator is squirreled away in `std::ranges::_Pipe` it is still effectively only found by ADL for things derived from `_Pipe::_Base` despite no longer being a hidden friend.) --- stl/inc/ranges | 236 ++++-------------- .../test.cpp | 24 +- 2 files changed, 60 insertions(+), 200 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 454b373cc3..00dc0ed600 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -60,37 +60,23 @@ namespace ranges { template using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>; -#if _HAS_CXX23 - template - requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> - class range_adaptor_closure; - - template - using _Range_adaptor_Base = range_adaptor_closure<_Ty>; -#else // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv namespace _Pipe { template - struct _Base; - } // namespace _Pipe + struct _Base {}; - template - using _Range_adaptor_Base = _Pipe::_Base<_Ty>; -#endif + template + _Ty* _Derived_from_range_adaptor_closure(_Base<_Ty>&); // not defined - namespace _Pipe { - // clang-format off - template - concept _Can_pipe = requires(_Left&& __l, _Right&& __r) { - static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l)); + template + concept _Range_adaptor_closure_object = !range> && requires(remove_cvref_t<_Ty> & __t) { + { _Pipe::_Derived_from_range_adaptor_closure(__t) } -> same_as*>; }; - template - concept _Can_compose = constructible_from, _Left> - && constructible_from, _Right>; - // clang-format on - template - struct _Pipeline : _Range_adaptor_Base<_Pipeline<_ClosureLeft, _ClosureRight>> { + 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; @@ -106,13 +92,6 @@ namespace ranges { } { 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(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires { @@ -120,6 +99,13 @@ namespace ranges { } { 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 { @@ -131,68 +117,24 @@ namespace ranges { template _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; -#if !_HAS_CXX23 - 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)); - } + template + requires _Range_adaptor_closure_object<_Left> // + && _Range_adaptor_closure_object<_Right> // + && constructible_from, _Left> // + && constructible_from, _Right> + 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 <_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)); - } - }; -#endif //!_HAS_CXX23 + template + requires _Range_adaptor_closure_object<_Right> && range<_Left> + 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 @@ -835,8 +777,15 @@ 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 + template - class _Range_closure : public _Range_adaptor_Base<_Range_closure<_Fn, _Types...>> { + class _Range_closure : public _Pipe::_Base<_Range_closure<_Fn, _Types...>> { public: // We assume that _Fn is the type of a customization point object. That means // 1. The behavior of operator() is independent of cvref qualifiers, so we can use `invocable<_Fn, ` without @@ -1463,94 +1412,6 @@ namespace ranges { inline constexpr _Istream_fn<_Ty> istream; } // namespace views -#if _HAS_CXX23 - namespace _Pipe { - template - void _Derived_from_range_adaptor_closure_impl(const volatile range_adaptor_closure<_Ty>&); - - template - concept _Derived_from_range_adaptor_closure = requires(const volatile _Ty& __obj) { - _Derived_from_range_adaptor_closure_impl(__obj); - }; - - template - concept _Is_range_adaptor_common = - derived_from, range_adaptor_closure>> // - && _Derived_from_range_adaptor_closure<_Ty> && !range<_Ty>; - - template - concept _Is_invocable_range_adaptor_closure = - range<_Rng> && _Can_pipe<_Rng, _Ty> && _Is_range_adaptor_common<_Ty>; - - template - concept _Are_composable_range_adaptor_closures = - _Is_range_adaptor_common<_Left> && _Is_range_adaptor_common<_Right> && _Can_compose<_Left, _Right>; - } // namespace _Pipe - - template - requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> - class range_adaptor_closure { - public: - template - requires _Pipe::_Are_composable_range_adaptor_closures<_Derived, _Other> - constexpr auto operator|(_Other&& _Right) && noexcept( - noexcept(_Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) { - return _Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)}; - } - - template - requires _Pipe::_Are_composable_range_adaptor_closures - constexpr auto operator|(_Other&& _Right) const& noexcept( - noexcept(_Pipe::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)})) { - return _Pipe::_Pipeline{static_cast(*this), _STD forward<_Other>(_Right)}; - } - - template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng> - friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right) -#ifdef __EDG__ // TRANSITION, VSO-1222776 - noexcept(noexcept(_STD declval<_Derived&>()(_STD forward<_Rng>(_Left)))) -#else // ^^^ workaround / no workaround vvv - noexcept(noexcept(static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)))) -#endif // TRANSITION, VSO-1222776 - { - return static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left)); - } - - template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng> - friend constexpr auto operator|(_Rng&& _Left, _Derived&& _Right) -#ifdef __EDG__ // TRANSITION, VSO-1222776 - noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Rng>(_Left)))) -#else // ^^^ workaround / no workaround vvv - noexcept(noexcept(static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)))) -#endif // TRANSITION, VSO-1222776 - { - return static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left)); - } - - template <_Pipe::_Is_invocable_range_adaptor_closure _Rng> - friend constexpr auto operator|(_Rng&& _Left, const _Derived& _Right) -#ifdef __EDG__ // TRANSITION, VSO-1222776 - noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) -#else // ^^^ workaround / no workaround vvv - noexcept(noexcept(static_cast(_Right)(_STD forward<_Rng>(_Left)))) -#endif // TRANSITION, VSO-1222776 - { - return static_cast(_Right)(_STD forward<_Rng>(_Left)); - } - - template <_Pipe::_Is_invocable_range_adaptor_closure _Rng> - friend constexpr auto operator|(_Rng&& _Left, const _Derived&& _Right) -#ifdef __EDG__ // TRANSITION, VSO-1222776 - noexcept(noexcept(_STD declval()(_STD forward<_Rng>(_Left)))) -#else // ^^^ workaround / no workaround vvv - noexcept(noexcept(static_cast(_Right)(_STD forward<_Rng>(_Left)))) -#endif // TRANSITION, VSO-1222776 - { - return static_cast(_Right)(_STD forward<_Rng>(_Left)); - } - }; -#endif //_HAS_CXX23 - template requires is_object_v<_Rng> class ref_view : public view_interface> { @@ -1697,7 +1558,7 @@ namespace ranges { owning_view{static_cast<_Rng&&>(__r)}; }; - class _All_fn : public _Range_adaptor_Base<_All_fn> { + class _All_fn : public _Pipe::_Base<_All_fn> { private: enum class _St { _None, _View, _Ref, _Own }; @@ -1826,7 +1687,7 @@ namespace ranges { as_rvalue_view{static_cast<_Rng&&>(__r)}; }; - class _As_rvalue_fn : public _Range_adaptor_Base<_As_rvalue_fn> { + class _As_rvalue_fn : public _Pipe::_Base<_As_rvalue_fn> { private: enum class _St { _None, _All, _As_rvalue }; @@ -3656,7 +3517,7 @@ namespace ranges { explicit join_view(_Rng&&) -> join_view>; namespace views { - class _Join_fn : public _Range_adaptor_Base<_Join_fn> { + class _Join_fn : public _Pipe::_Base<_Join_fn> { public: // clang-format off template @@ -4407,7 +4268,6 @@ namespace ranges { } } - public: using iterator_concept = typename _Outer_iter<_Const>::iterator_concept; using value_type = range_value_t<_BaseTy>; @@ -4886,7 +4746,7 @@ namespace ranges { inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Rng>; namespace views { - class _Common_fn : public _Range_adaptor_Base<_Common_fn> { + class _Common_fn : public _Pipe::_Base<_Common_fn> { private: enum class _St { _None, _All, _Common }; @@ -5010,7 +4870,7 @@ namespace ranges { reverse_view{static_cast<_Rng&&>(__r)}; }; - class _Reverse_fn : public _Range_adaptor_Base<_Reverse_fn> { + class _Reverse_fn : public _Pipe::_Base<_Reverse_fn> { private: enum class _St { _None, _Base, _Subrange_unsized, _Subrange_sized, _Reverse }; @@ -5462,7 +5322,7 @@ namespace ranges { namespace views { template - class _Elements_fn : public _Range_adaptor_Base<_Elements_fn<_Index>> { + class _Elements_fn : public _Pipe::_Base<_Elements_fn<_Index>> { public: template _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept( 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 index e38e3413a8..ab0eac62d5 100644 --- 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 @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include using namespace std; @@ -24,7 +24,6 @@ static_assert(!CanInstantiateRangeAdaptorClosure); static_assert(!CanInstantiateRangeAdaptorClosure); static_assert(CanInstantiateRangeAdaptorClosure); - template concept CanPipe = requires(LHS lhs, RHS rhs) { forward(lhs) | forward(rhs); @@ -38,9 +37,9 @@ concept CanPipe_R = requires(LHS lhs, RHS rhs) { using TestRange = array; template -constexpr bool does_not_satisfy_range_adaptor_closure() { - return !CanPipe && !CanPipe // - && !CanPipe && !CanPipe; +constexpr bool is_range_adaptor_closure() { + return CanPipe || CanPipe // + || CanPipe || CanPipe; } struct IdentityRangeAdaptorClosure : ranges::range_adaptor_closure { @@ -52,31 +51,31 @@ struct IdentityRangeAdaptorClosure : ranges::range_adaptor_closure {}; -static_assert(does_not_satisfy_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(does_not_satisfy_range_adaptor_closure()); +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(does_not_satisfy_range_adaptor_closure()); +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(does_not_satisfy_range_adaptor_closure()); +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(does_not_satisfy_range_adaptor_closure()); +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. @@ -84,7 +83,7 @@ struct DerivedFromTwoSpecializations : ranges::range_adaptor_closure { void operator()(const TestRange&) {} }; -static_assert(does_not_satisfy_range_adaptor_closure()); +static_assert(!is_range_adaptor_closure()); // Is not a range adaptor closure, because it models ranges::range. struct ModelsRange : ranges::range_adaptor_closure { @@ -103,7 +102,8 @@ struct ModelsRange : ranges::range_adaptor_closure { return nullptr; } }; -static_assert(does_not_satisfy_range_adaptor_closure()); +static_assert(ranges::range); +static_assert(!is_range_adaptor_closure()); struct RangeAdaptorClosureMemberRefQualTest : ranges::range_adaptor_closure { constexpr ranges::empty_view operator()(const auto&) & { From 32e6cbb40c1652dfe119d3a0fcb17e413b97876a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Aug 2022 17:09:03 -0700 Subject: [PATCH 33/34] Code review nitpicks: Newline, comment. --- stl/inc/functional | 1 + stl/inc/ranges | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index af4a2486bc..5442e659d6 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2192,6 +2192,7 @@ public: _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>, diff --git a/stl/inc/ranges b/stl/inc/ranges index 00dc0ed600..545ac0610e 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -782,7 +782,7 @@ namespace ranges { requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> class range_adaptor_closure : public _Pipe::_Base<_Derived> { }; -#endif +#endif // _HAS_CXX23 template class _Range_closure : public _Pipe::_Base<_Range_closure<_Fn, _Types...>> { From 2aed2b54be755b69ea0926b3808129f61288fa0a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 19 Aug 2022 17:12:19 -0700 Subject: [PATCH 34/34] Add `_NODISCARD` to `operator|` overloads. --- stl/inc/ranges | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 545ac0610e..bbef590d24 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -118,19 +118,19 @@ namespace ranges { _Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>; template - requires _Range_adaptor_closure_object<_Left> // - && _Range_adaptor_closure_object<_Right> // - && constructible_from, _Left> // - && constructible_from, _Right> - constexpr auto operator|(_Left&& __l, _Right&& __r) noexcept( - noexcept(_Pipeline{static_cast<_Left&&>(__l), static_cast<_Right&&>(__r)})) { + 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> - constexpr auto operator|(_Left&& __l, _Right&& __r) noexcept( - noexcept(_STD forward<_Right>(__r)(_STD forward<_Left>(__l)))) // + 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)); }