diff --git a/stl/inc/memory b/stl/inc/memory index 42ad7303c0e..782a50c2d37 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -284,27 +284,84 @@ _NoThrowFwdIt uninitialized_fill_n(_NoThrowFwdIt _First, const _Diff _Count_raw, } #endif // _HAS_IF_CONSTEXPR +// FUNCTION TEMPLATE construct_at #if _HAS_CXX20 template -auto construct_at(_Ty* const _Location, _Types&&... _Args) noexcept(noexcept(::new (const_cast( - static_cast(_Location))) _Ty(_STD forward<_Types>(_Args)...))) // strengthened +_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 { - using _STD construct_at; + // VARIABLE ranges::construct_at + class _Construct_at_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template + requires requires(void* _Void_ptr, _Types&&... _Args) { + ::new (_Void_ptr) _Ty(static_cast<_Types&&>(_Args)...); + } + _CONSTEXPR20_DYNALLOC _Ty* operator()(_Ty* _Location, _Types&&... _Args) const + noexcept(noexcept(::new (const_cast(static_cast(_Location))) + _Ty(_STD forward<_Types>(_Args)...))) /* strengthened */ { + // clang-format on + return ::new (const_cast(static_cast(_Location))) + _Ty(_STD forward<_Types>(_Args)...); + } + }; + + 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 template -void destroy_at(_Ty* const _Location) noexcept /* strengthened */ { - _Location->~_Ty(); +_CONSTEXPR20_DYNALLOC void destroy_at(_Ty* const _Location) noexcept /* strengthened */ { +#if _HAS_CXX20 + if constexpr (is_array_v<_Ty>) { + _Destroy_range(_STD begin(*_Location), _STD end(*_Location)); + } else +#endif // _HAS_CXX20 + { + _Location->~_Ty(); + } } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::destroy_at + // clang-format off + template <_No_throw_input_iterator _It, _No_throw_sentinel_for<_It> _Se> + requires destructible> + _NODISCARD _CONSTEXPR20_DYNALLOC _It _Destroy_unchecked(_It _First, _Se _Last) noexcept; + // clang-format on + + class _Destroy_at_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template + _CONSTEXPR20_DYNALLOC void operator()(_Ty* const _Location) const noexcept { + if constexpr (is_array_v<_Ty>) { + (void) _RANGES _Destroy_unchecked(_RANGES begin(*_Location), _RANGES end(*_Location)); + } else { + _Location->~_Ty(); + } + } + }; + + inline constexpr _Destroy_at_fn destroy_at{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE destroy template void destroy(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) { // destroy all elements in [_First, _Last) @@ -312,6 +369,51 @@ void destroy(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) { // destroy _Destroy_range(_Get_unwrapped(_First), _Get_unwrapped(_Last)); } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::destroy + // clang-format off + template <_No_throw_input_iterator _It, _No_throw_sentinel_for<_It> _Se> + requires destructible> + _NODISCARD _CONSTEXPR20_DYNALLOC _It _Destroy_unchecked(_It _First, const _Se _Last) noexcept { + // clang-format on + for (; _First != _Last; ++_First) { + _RANGES destroy_at(_STD addressof(*_First)); + } + + return _First; + } + + class _Destroy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template <_No_throw_input_iterator _It, _No_throw_sentinel_for<_It> _Se> + requires destructible> + /* _CONSTEXPR20_DYNALLOC */ _It operator()(_It _First, _Se _Last) const noexcept { + // clang-format on + _Adl_verify_range(_First, _Last); + _Seek_wrapped(_First, + _RANGES _Destroy_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)))); + return _First; + } + + // clang-format off + template <_No_throw_input_range _Rng> + requires destructible> + /* _CONSTEXPR20_DYNALLOC */ borrowed_iterator_t<_Rng> operator()(_Rng&& _Range) const noexcept { + // clang-format on + auto _First = _RANGES begin(_Range); + _Seek_wrapped(_First, _RANGES _Destroy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range))); + return _First; + } + }; + + inline constexpr _Destroy_fn destroy{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE destroy_n template _NoThrowFwdIt destroy_n(_NoThrowFwdIt _First, const _Diff _Count_raw) { @@ -335,6 +437,42 @@ _NoThrowFwdIt destroy_n(_NoThrowFwdIt _First, const _Diff _Count_raw) { return _First; } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::destroy_n + class _Destroy_n_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template <_No_throw_input_iterator _It> + requires destructible> + /* _CONSTEXPR20_DYNALLOC */ _It operator()(_It _First, const iter_difference_t<_It> _Count) const noexcept { + // clang-format on + if (_Count <= 0) { + return _First; + } + + auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + if constexpr (is_trivially_destructible_v>) { + _RANGES advance(_UFirst, _Count); + } else { + do { + _RANGES destroy_at(_STD addressof(*_UFirst)); + ++_UFirst; + --_Count; + } while (_Count > 0); + } + + _Seek_wrapped(_First, _STD move(_UFirst)); + return _First; + } + }; + + inline constexpr _Destroy_n_fn destroy_n{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE uninitialized_default_construct template void uninitialized_default_construct(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) { diff --git a/stl/inc/xmemory b/stl/inc/xmemory index c723ab4d262..7ad649cf512 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -257,10 +257,10 @@ _Pointer _Refancy(_Pointer _Ptr) noexcept { // FUNCTION TEMPLATE _Destroy_in_place template -/* _CONSTEXPR20_DYNALLOC */ void _Destroy_range(_NoThrowFwdIt _First, _NoThrowSentinel _Last) noexcept; +_CONSTEXPR20_DYNALLOC void _Destroy_range(_NoThrowFwdIt _First, _NoThrowSentinel _Last) noexcept; template -/* _CONSTEXPR20_DYNALLOC */ void _Destroy_in_place(_Ty& _Obj) noexcept { +_CONSTEXPR20_DYNALLOC void _Destroy_in_place(_Ty& _Obj) noexcept { #if _HAS_IF_CONSTEXPR if constexpr (is_array_v<_Ty>) { _Destroy_range(_Obj, _Obj + extent_v<_Ty>); @@ -951,8 +951,7 @@ void _Pocs(_Alloc& _Left, _Alloc& _Right) noexcept { // FUNCTION TEMPLATE _Destroy_range WITH ALLOC template -/* _CONSTEXPR20_DYNALLOC */ void _Destroy_range( - _Alloc_ptr_t<_Alloc> _First, const _Alloc_ptr_t<_Alloc> _Last, _Alloc& _Al) noexcept { +void _Destroy_range(_Alloc_ptr_t<_Alloc> _First, const _Alloc_ptr_t<_Alloc> _Last, _Alloc& _Al) noexcept { // note that this is an optimization for debug mode codegen; in release mode the BE removes all of this using _Ty = typename _Alloc::value_type; if _CONSTEXPR_IF (!conjunction_v, _Uses_default_destroy<_Alloc, _Ty*>>) { @@ -964,7 +963,7 @@ template // FUNCTION TEMPLATE _Destroy_range template -/* _CONSTEXPR20_DYNALLOC */ void _Destroy_range(_NoThrowFwdIt _First, const _NoThrowSentinel _Last) noexcept { +_CONSTEXPR20_DYNALLOC void _Destroy_range(_NoThrowFwdIt _First, const _NoThrowSentinel _Last) noexcept { // note that this is an optimization for debug mode codegen; in release mode the BE removes all of this if _CONSTEXPR_IF (!is_trivially_destructible_v<_Iter_value_t<_NoThrowFwdIt>>) { for (; _First != _Last; ++_First) { @@ -1425,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; - /* _CONSTEXPR20_DYNALLOC */ ~_Uninitialized_backout() { + ~_Uninitialized_backout() { _Destroy_range(_First, _Last); } template - /* _CONSTEXPR20_DYNALLOC */ void _Emplace_back(_Types&&... _Vals) { + void _Emplace_back(_Types&&... _Vals) { // construct a new element at *_Last and increment _Construct_in_place(*_Last, _STD forward<_Types>(_Vals)...); ++_Last; } - /* _CONSTEXPR20_DYNALLOC */ _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last + _NoThrowFwdIt _Release() { // suppress any exception handling backout and return _Last _First = _Last; return _Last; } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 0d62a90da3e..b0e5587eb0a 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -541,6 +541,13 @@ #define _CONSTEXPR20 inline #endif // ^^^ inline (not constexpr) in C++17 and earlier ^^^ +// Functions that became constexpr in C++20 via P0784R7 +#if _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) +#define _CONSTEXPR20_DYNALLOC constexpr +#else +#define _CONSTEXPR20_DYNALLOC inline +#endif + // P0607R0 Inline Variables For The STL #if _HAS_CXX17 #define _INLINE_VAR inline diff --git a/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp b/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp index 733265ddd20..7cf1685a980 100644 --- a/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp +++ b/tests/std/tests/P0784R7_library_support_for_more_constexpr_containers/test.cpp @@ -9,8 +9,50 @@ #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; +#ifdef __cpp_lib_concepts +template +concept can_std_construct_at = requires(Ty* ptr, Types&&... args) { + construct_at(ptr, forward(args)...); +}; + +template +concept can_ranges_construct_at = requires(Ty* ptr, Types&&... args) { + ranges::construct_at(ptr, forward(args)...); +}; + +template +concept can_ranges_destroy_at = requires(Ty* ptr) { + ranges::destroy_at(ptr); +}; + +template +inline constexpr bool can_construct_at = [] { + constexpr bool result = can_std_construct_at; + static_assert(can_ranges_construct_at == result); + return result; +}(); + +template +constexpr bool construct_at_noexcept() { + constexpr bool result = noexcept(construct_at(declval(), declval()...)); + static_assert(noexcept(ranges::construct_at(declval(), declval()...)) == result); + return result; +} + +template +constexpr bool destroy_at_noexcept() { + static_assert(noexcept(destroy_at(declval()))); + if constexpr (can_ranges_destroy_at) { + static_assert(noexcept(ranges::destroy_at(declval()))); + } + return true; +} +#else // ^^^ Concepts and Ranges / No Concepts or Ranges vvv template inline constexpr bool can_construct_at_impl = false; @@ -21,6 +63,17 @@ inline constexpr bool template inline constexpr bool can_construct_at = can_construct_at_impl; +template +constexpr bool construct_at_noexcept() { + return noexcept(construct_at(declval(), declval()...)); +} + +template +constexpr bool destroy_at_noexcept() { + return noexcept(destroy_at(declval())); +} +#endif // __cpp_lib_concepts + static_assert(can_construct_at); static_assert(can_construct_at); static_assert(can_construct_at); @@ -70,53 +123,157 @@ static_assert(!can_construct_at); // The following static_asserts test our strengthening of noexcept #ifndef __EDG__ // TRANSITION, VSO-1075296 -static_assert(noexcept(construct_at(declval(), 42))); -static_assert(noexcept(construct_at(declval(), 42))); -static_assert(noexcept(construct_at(declval(), 42))); -static_assert(noexcept(construct_at(declval(), 42))); +static_assert(construct_at_noexcept()); +static_assert(construct_at_noexcept()); +static_assert(construct_at_noexcept()); +static_assert(construct_at_noexcept()); #endif // __EDG__ -static_assert(!noexcept(construct_at(declval(), "hello"))); -static_assert(!noexcept(construct_at(declval(), "hello"))); -static_assert(!noexcept(construct_at(declval(), "hello"))); -static_assert(!noexcept(construct_at(declval(), "hello"))); +static_assert(!construct_at_noexcept()); +static_assert(!construct_at_noexcept()); +static_assert(!construct_at_noexcept()); +static_assert(!construct_at_noexcept()); + +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); -static_assert(noexcept(destroy_at(declval()))); -static_assert(noexcept(destroy_at(declval()))); -static_assert(noexcept(destroy_at(declval()))); -static_assert(noexcept(destroy_at(declval()))); -static_assert(noexcept(destroy_at(declval()))); -static_assert(noexcept(destroy_at(declval()))); -static_assert(noexcept(destroy_at(declval()))); -static_assert(noexcept(destroy_at(declval()))); +#if _HAS_CXX20 +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +static_assert(destroy_at_noexcept()); +#endif // _HAS_CXX20 struct throwing_dtor { ~throwing_dtor() noexcept(false) {} }; -static_assert(noexcept(destroy_at(declval()))); +static_assert(destroy_at_noexcept()); +#if _HAS_CXX20 +static_assert(destroy_at_noexcept()); +#endif // _HAS_CXX20 + +#ifdef __cpp_lib_concepts +static_assert(!can_ranges_destroy_at); +static_assert(!can_ranges_destroy_at); +#endif // __cpp_lib_concepts template void test_runtime(const Ty& val) { alignas(Ty) unsigned char storage[sizeof(Ty)]; - memset(storage, 42, sizeof(Ty)); const auto asPtrTy = reinterpret_cast(&storage); + memset(storage, 42, sizeof(Ty)); assert(asPtrTy == construct_at(asPtrTy, val)); assert(*asPtrTy == val); destroy_at(asPtrTy); +#ifdef __cpp_lib_concepts // test ranges: + memset(storage, 42, sizeof(Ty)); assert(asPtrTy == ranges::construct_at(asPtrTy, val)); assert(*asPtrTy == val); - destroy_at(asPtrTy); + ranges::destroy_at(asPtrTy); +#endif // __cpp_lib_concepts // test voidify: const auto asCv = static_cast(asPtrTy); + memset(storage, 42, sizeof(Ty)); assert(asPtrTy == construct_at(asCv, val)); assert(const_cast(*asCv) == val); destroy_at(asCv); + +#ifdef __cpp_lib_concepts + memset(storage, 42, sizeof(Ty)); + assert(asPtrTy == ranges::construct_at(asCv, val)); + assert(const_cast(*asCv) == val); + ranges::destroy_at(asCv); +#endif // __cpp_lib_concepts } +template +void test_array(const T& val) { + constexpr int N = 42; + (void) val; + +#if _HAS_CXX20 + alignas(T) unsigned char storage[sizeof(T) * N]; + using U = conditional_t, const volatile T, T>; + const auto ptr = reinterpret_cast(storage); + + for (auto i = 0; i < N; ++i) { + construct_at(ptr + i, val); + } + + destroy_at(reinterpret_cast(ptr)); + +#ifdef __cpp_lib_concepts + for (auto i = 0; i < N; ++i) { + ranges::construct_at(ptr + i, val); + } + +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1049320 + ranges::destroy_at(reinterpret_cast(ptr)); +#else // ^^^ no workaround / workaround vvv + ranges::destroy_at(reinterpret_cast(const_cast(ptr))); +#endif // TRANSITION, VSO-1049320 +#endif // __cpp_lib_concepts +#endif // _HAS_CXX20 +} + +#if _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) +template +struct storage_for { + union { + T object; + }; + + constexpr storage_for() noexcept {} + constexpr ~storage_for() {} +}; + +constexpr void test_compiletime() { + { + storage_for s; + construct_at(&s.object, 42); + assert(s.object == 42); + destroy_at(&s.object); + + ranges::construct_at(&s.object, 1729); + assert(s.object == 1729); + ranges::destroy_at(&s.object); + } + + struct nontrivial { + constexpr nontrivial(int i = 42) noexcept : x{i} {} + constexpr ~nontrivial() {} + + int x = 42; + }; + + { + storage_for s; + construct_at(&s.object, 42); + assert(s.object.x == 42); + destroy_at(&s.object); + + ranges::construct_at(&s.object, 1729); + assert(s.object.x == 1729); + ranges::destroy_at(&s.object); + } +} +static_assert((test_compiletime(), true)); +#endif // _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) + int main() { test_runtime(1234); test_runtime(string("hello world")); @@ -127,5 +284,14 @@ int main() { const auto ptr = reinterpret_cast(storage); construct_at(ptr); ptr->destroy(); + +#ifdef __cpp_lib_concepts + ranges::construct_at(ptr); + ptr->destroy(); +#endif // __cpp_lib_concepts } + + test_array(1234); + test_array(string("hello world")); + test_array(string("hello to some really long world that certainly doesn't fit in SSO")); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp index 773378f42f4..10bd044bbbf 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp @@ -80,21 +80,14 @@ struct holder { } }; -template -void not_ranges_destroy(R&& r) { // TRANSITION, ranges::destroy - for (auto& e : r) { - destroy_at(&e); - } -} - struct instantiator { static constexpr int expected_output[] = {13, 55, 12345}; static constexpr int expected_input[] = {-1, -1, -1}; template static void call() { - using ranges::uninitialized_move, ranges::uninitialized_move_result, ranges::equal, ranges::equal_to, - ranges::iterator_t; + using ranges::uninitialized_move, ranges::uninitialized_move_result, ranges::destroy, ranges::equal, + ranges::equal_to, ranges::iterator_t; { // Validate range overload int_wrapper input[3] = {13, 55, 12345}; @@ -111,7 +104,7 @@ struct instantiator { assert(result.out == wrapped_output.end()); assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); + destroy(wrapped_output); assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); } @@ -131,7 +124,7 @@ struct instantiator { assert(result.out == wrapped_output.end()); assert(equal(wrapped_output, expected_output, equal_to{}, &int_wrapper::val)); assert(equal(input, expected_input, equal_to{}, &int_wrapper::val)); - not_ranges_destroy(wrapped_output); + destroy(wrapped_output); assert(int_wrapper::constructions == 3); assert(int_wrapper::destructions == 3); }