From a3e3dc864a08258ca724c1f429b49bef24fbdea8 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 5 Oct 2020 22:19:30 +0200 Subject: [PATCH] Implement constexpr versions of the meow_memmove helpers --- stl/inc/memory | 16 -- stl/inc/xmemory | 62 +++++--- stl/inc/xutility | 19 ++- tests/std/test.lst | 1 + .../tests/P0784R7_library_machinery/env.lst | 4 + .../tests/P0784R7_library_machinery/test.cpp | 141 ++++++++++++++++++ 6 files changed, 203 insertions(+), 40 deletions(-) create mode 100644 tests/std/tests/P0784R7_library_machinery/env.lst create mode 100644 tests/std/tests/P0784R7_library_machinery/test.cpp diff --git a/stl/inc/memory b/stl/inc/memory index bc16c897de..d49d6f5da6 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -545,22 +545,7 @@ namespace ranges { }; inline constexpr _Uninitialized_fill_n_fn uninitialized_fill_n{_Not_quite_object::_Construct_tag{}}; -} // namespace ranges -#endif // __cpp_lib_concepts -// FUNCTION TEMPLATE construct_at -#if _HAS_CXX20 -template -_CONSTEXPR20_DYNALLOC auto construct_at(_Ty* const _Location, _Types&&... _Args) noexcept( - noexcept(::new (const_cast(static_cast(_Location))) - _Ty(_STD forward<_Types>(_Args)...))) // strengthened - -> decltype( - ::new (const_cast(static_cast(_Location))) _Ty(_STD forward<_Types>(_Args)...)) { - return ::new (const_cast(static_cast(_Location))) _Ty(_STD forward<_Types>(_Args)...); -} - -#ifdef __cpp_lib_concepts -namespace ranges { // VARIABLE ranges::construct_at class _Construct_at_fn : private _Not_quite_object { public: @@ -583,7 +568,6 @@ namespace ranges { inline constexpr _Construct_at_fn construct_at{_Not_quite_object::_Construct_tag{}}; } // namespace ranges #endif // __cpp_lib_concepts -#endif // _HAS_CXX20 #if _HAS_CXX17 // FUNCTION TEMPLATE destroy_at diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 19bcb23411..be828abe29 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1424,18 +1424,18 @@ struct _Uninitialized_backout { // struct to undo partially constructed ranges i _Uninitialized_backout(const _Uninitialized_backout&) = delete; _Uninitialized_backout& operator=(const _Uninitialized_backout&) = delete; - ~_Uninitialized_backout() { + _CONSTEXPR20_DYNALLOC ~_Uninitialized_backout() { _Destroy_range(_First, _Last); } template - void _Emplace_back(_Types&&... _Vals) { + _CONSTEXPR20_DYNALLOC void _Emplace_back(_Types&&... _Vals) { // construct a new element at *_Last and increment _Construct_in_place(*_Last, _STD forward<_Types>(_Vals)...); ++_Last; } - _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last + constexpr _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last _First = _Last; return _Last; } @@ -1478,18 +1478,23 @@ namespace ranges { // FUNCTION TEMPLATE _Uninitialized_move_unchecked #if _HAS_IF_CONSTEXPR template -_NoThrowFwdIt _Uninitialized_move_unchecked(_InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { +_CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_move_unchecked( + _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { // move [_First, _Last) to raw [_Dest, ...) if constexpr (_Memmove_in_uninitialized_move_is_safe<_InIt, _NoThrowFwdIt>) { - return _Memmove_forward(_First, _Last, _Dest); - } else { - _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; - for (; _First != _Last; ++_First) { - _Backout._Emplace_back(_STD move(*_First)); +#ifdef __cpp_lib_is_constant_evaluated + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_is_constant_evaluated + { + return _Memmove_forward(_First, _Last, _Dest); } - - return _Backout._Release(); } + _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; + for (; _First != _Last; ++_First) { + _Backout._Emplace_back(_STD move(*_First)); + } + + return _Backout._Release(); } #else // ^^^ _HAS_IF_CONSTEXPR ^^^ // vvv !_HAS_IF_CONSTEXPR vvv template @@ -1617,6 +1622,28 @@ _Alloc_ptr_t<_Alloc> _Uninitialized_copy( // FUNCTION TEMPLATE uninitialized_copy #if _HAS_IF_CONSTEXPR +template +_CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_copy_unchecked( + _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { + // copy [_First, _Last) to raw [_Dest, ...) + if constexpr (_Memmove_in_uninitialized_copy_is_safe<_InIt, _NoThrowFwdIt>) { +#ifdef __cpp_lib_is_constant_evaluated + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_is_constant_evaluated + { + return _Memmove_forward(_First, _Last, _Dest); + } + } + _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; + for (; _First != _Last; ++_First) { + _Backout._Emplace_back(*_First); + } + + _Dest = _Backout._Release(); + + return _Dest; +} + template _NoThrowFwdIt uninitialized_copy(const _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { // copy [_First, _Last) to raw [_Dest, ...) @@ -1624,18 +1651,7 @@ _NoThrowFwdIt uninitialized_copy(const _InIt _First, const _InIt _Last, _NoThrow auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_InIt>(_UFirst, _ULast)); - if constexpr (_Memmove_in_uninitialized_copy_is_safe) { - _UDest = _Memmove_forward(_UFirst, _ULast, _UDest); - } else { - _Uninitialized_backout _Backout{_UDest}; - for (; _UFirst != _ULast; ++_UFirst) { - _Backout._Emplace_back(*_UFirst); - } - - _UDest = _Backout._Release(); - } - - _Seek_wrapped(_Dest, _UDest); + _Seek_wrapped(_Dest, _Uninitialized_copy_unchecked(_UFirst, _ULast, _UDest)); return _Dest; } #else // ^^^ _HAS_IF_CONSTEXPR / !_HAS_IF_CONSTEXPR vvv diff --git a/stl/inc/xutility b/stl/inc/xutility index 0ac84fcee8..a9e6099d12 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -119,11 +119,28 @@ struct _Get_rebind_alias<_Ty, _Other, void_t; }; +// FUNCTION TEMPLATE construct_at +#if _HAS_CXX20 +template +_CONSTEXPR20_DYNALLOC auto construct_at(_Ty* const _Location, _Types&&... _Args) noexcept( + noexcept(::new (const_cast(static_cast(_Location))) + _Ty(_STD forward<_Types>(_Args)...))) // strengthened + -> decltype( + ::new (const_cast(static_cast(_Location))) _Ty(_STD forward<_Types>(_Args)...)) { + return ::new (const_cast(static_cast(_Location))) _Ty(_STD forward<_Types>(_Args)...); +} +#endif // _HAS_CXX20 + // FUNCTION TEMPLATE _Construct_in_place template -void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { +_CONSTEXPR20_DYNALLOC void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept( + is_nothrow_constructible_v<_Ty, _Types...>) { +#if _HAS_CXX20 + _STD construct_at(_STD addressof(_Obj), _STD forward<_Types>(_Args)...); +#else // ^^^_HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv ::new (const_cast(static_cast(_STD addressof(_Obj)))) _Ty(_STD forward<_Types>(_Args)...); +#endif // !_HAS_CXX20 } // STRUCT TEMPLATE pointer_traits diff --git a/tests/std/test.lst b/tests/std/test.lst index c681576094..08bb5af506 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -246,6 +246,7 @@ tests\P0718R2_atomic_smart_ptrs tests\P0758R1_is_nothrow_convertible tests\P0768R1_spaceship_operator tests\P0769R2_shift_left_shift_right +tests\P0784R7_library_machinery tests\P0784R7_library_support_for_more_constexpr_containers tests\P0811R3_midpoint_lerp tests\P0896R4_common_iterator diff --git a/tests/std/tests/P0784R7_library_machinery/env.lst b/tests/std/tests/P0784R7_library_machinery/env.lst new file mode 100644 index 0000000000..642f530ffa --- /dev/null +++ b/tests/std/tests/P0784R7_library_machinery/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/P0784R7_library_machinery/test.cpp b/tests/std/tests/P0784R7_library_machinery/test.cpp new file mode 100644 index 0000000000..a1aa0d52a5 --- /dev/null +++ b/tests/std/tests/P0784R7_library_machinery/test.cpp @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +#pragma warning(disable : 4582) // '%s': constructor is not implicitly called +#pragma warning(disable : 4583) // '%s': destructor is not implicitly called + +using namespace std; + +struct int_wrapper_copy { + constexpr int_wrapper_copy() = default; + constexpr int_wrapper_copy(const int v) : _val(v){}; + + constexpr int_wrapper_copy(const int_wrapper_copy& other) : _val(other._val) {} + constexpr int_wrapper_copy& operator=(const int_wrapper_copy& other) { + _val = other._val; + return *this; + } + + constexpr int_wrapper_copy(int_wrapper_copy&&) = delete; + constexpr int_wrapper_copy& operator=(int_wrapper_copy&&) = delete; + + constexpr bool operator==(const int_wrapper_copy&) const = default; + + int _val = 0; +}; + +struct int_wrapper_move { + constexpr int_wrapper_move() = default; + constexpr int_wrapper_move(const int v) : _val(v){}; + + constexpr int_wrapper_move(const int_wrapper_move&) = delete; + constexpr int_wrapper_move& operator=(const int_wrapper_move&) = delete; + + constexpr int_wrapper_move(int_wrapper_move&& other) : _val(exchange(other._val, -1)) {} + constexpr int_wrapper_move& operator=(int_wrapper_move&& other) { + _val = exchange(other._val, -1); + return *this; + } + + constexpr bool operator==(const int_wrapper_move&) const = default; + + int _val = 0; +}; + +static constexpr int_wrapper_copy expected_copy[] = {1, 2, 3, 4}; +static constexpr int_wrapper_move expected_move[] = {1, 2, 3, 4}; +static constexpr int_wrapper_move expected_after_move[] = {-1, -1, -1, -1}; + +constexpr bool test() { + { // _Copy_unchecked + int_wrapper_copy input[] = {1, 2, 3, 4}; + int_wrapper_copy output[4] = {5, 6, 7, 8}; + + const same_as auto result = _Copy_unchecked(begin(input), end(input), begin(output)); + assert(result == end(output)); + assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output))); + } + + { // _Copy_backward_unchecked + int_wrapper_copy input[] = {1, 2, 3, 4}; + int_wrapper_copy output[4] = {5, 6, 7, 8}; + + const same_as auto result = _Copy_backward_unchecked(begin(input), end(input), end(output)); + assert(result == begin(output)); + assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output))); + } + +#if _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) + { // _Uninitialized_copy_unchecked + int_wrapper_copy input[] = {1, 2, 3, 4}; + int_wrapper_copy output[4]; + + const same_as auto result = + _Uninitialized_copy_unchecked(begin(input), end(input), begin(output)); + assert(result == end(output)); + assert(equal(begin(expected_copy), end(expected_copy), begin(output), end(output))); + } +#endif // _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) + + { // _Move_unchecked + int_wrapper_move input[] = {1, 2, 3, 4}; + int_wrapper_move output[4] = {5, 6, 7, 8}; + + const same_as auto result = _Move_unchecked(begin(input), end(input), begin(output)); + assert(result == end(output)); + assert(equal(begin(expected_move), end(expected_move), begin(output), end(output))); + if (is_constant_evaluated()) { + assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); + } + } + + { // _Move_backward_unchecked + int_wrapper_move input[] = {1, 2, 3, 4}; + int_wrapper_move output[4] = {5, 6, 7, 8}; + + const same_as auto result = _Move_backward_unchecked(begin(input), end(input), end(output)); + assert(result == begin(output)); + assert(equal(begin(expected_move), end(expected_move), begin(output), end(output))); + if (is_constant_evaluated()) { + assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); + } + } + + { // _Move_backward_common + int_wrapper_move input[] = {1, 2, 3, 4}; + int_wrapper_move output[4] = {5, 6, 7, 8}; + + const same_as auto result = + ranges::_Move_backward_common(begin(input), end(input), end(output)); + assert(result == begin(output)); + assert(equal(begin(expected_move), end(expected_move), begin(output), end(output))); + if (is_constant_evaluated()) { + assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); + } + } + +#if _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) + { // _Uninitialized_move_unchecked + int_wrapper_move input[] = {1, 2, 3, 4}; + int_wrapper_move output[4]; + + const same_as auto result = + _Uninitialized_move_unchecked(begin(input), end(input), begin(output)); + assert(result == end(output)); + assert(equal(begin(expected_move), end(expected_move), begin(output), end(output))); + if (is_constant_evaluated()) { + assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); + } + } +#endif // _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) + return true; +} + +int main() { + test(); + static_assert(test()); +}