From 256197425ba69c6add199974d13d92bafd0e2db4 Mon Sep 17 00:00:00 2001 From: "Michael S. Rizkalla" Date: Fri, 8 Jan 2021 03:01:52 +0000 Subject: [PATCH] P0784R7 Library Support For More constexpr Containers (#1369) Co-authored-by: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Co-authored-by: Curtis Jacques Bezault Co-authored-by: Michael Schellenberger Costa Co-authored-by: mnatsuhara <46756417+mnatsuhara@users.noreply.github.com> Co-authored-by: Stephan T. Lavavej --- stl/inc/memory | 47 +-- stl/inc/xmemory | 204 +++++++++---- stl/inc/xutility | 12 +- stl/inc/yvals_core.h | 25 +- tests/libcxx/expected_results.txt | 19 +- tests/libcxx/skipped_tests.txt | 1 - .../test.cpp | 267 +++++++++++++++++- .../test.compile.pass.cpp | 15 + 8 files changed, 472 insertions(+), 118 deletions(-) diff --git a/stl/inc/memory b/stl/inc/memory index 56ce70b616..91e7860b9c 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -496,22 +496,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: @@ -532,26 +517,7 @@ 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 -template -_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> @@ -577,9 +543,11 @@ namespace ranges { } // namespace ranges #endif // __cpp_lib_concepts +#if _HAS_CXX17 // FUNCTION TEMPLATE destroy template -void destroy(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) { // destroy all elements in [_First, _Last) +_CONSTEXPR20_DYNALLOC void destroy(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) { + // destroy all elements in [_First, _Last) _Adl_verify_range(_First, _Last); _Destroy_range(_Get_unwrapped(_First), _Get_unwrapped(_Last)); } @@ -606,7 +574,7 @@ namespace ranges { // 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 { + _CONSTEXPR20_DYNALLOC _It operator()(_It _First, _Se _Last) const noexcept { // clang-format on _Adl_verify_range(_First, _Last); _Seek_wrapped(_First, @@ -617,7 +585,7 @@ namespace ranges { // clang-format off template <_No_throw_input_range _Rng> requires destructible> - /* _CONSTEXPR20_DYNALLOC */ borrowed_iterator_t<_Rng> operator()(_Rng&& _Range) const noexcept { + _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))); @@ -631,7 +599,7 @@ namespace ranges { // FUNCTION TEMPLATE destroy_n template -_NoThrowFwdIt destroy_n(_NoThrowFwdIt _First, const _Diff _Count_raw) { +_CONSTEXPR20_DYNALLOC _NoThrowFwdIt destroy_n(_NoThrowFwdIt _First, const _Diff _Count_raw) { // destroy all elements in [_First, _First + _Count) _Algorithm_int_t<_Diff> _Count = _Count_raw; if (_Count <= 0) { @@ -661,8 +629,9 @@ namespace ranges { // 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 { + _CONSTEXPR20_DYNALLOC _It operator()(_It _First, const iter_difference_t<_It> _Count_raw) const noexcept { // clang-format on + _Algorithm_int_t> _Count = _Count_raw; if (_Count <= 0) { return _First; } diff --git a/stl/inc/xmemory b/stl/inc/xmemory index f6f91f3929..38209d5c9c 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -73,13 +73,30 @@ _INLINE_VAR constexpr size_t _New_alignof = (_STD max)(alignof(_Ty), // STRUCT _Default_allocate_traits struct _Default_allocate_traits { - __declspec(allocator) static void* _Allocate(const size_t _Bytes) { + __declspec(allocator) static +#ifdef __clang__ // Clang and MSVC implement P0784R7 differently; see GH-1532 + _CONSTEXPR20_DYNALLOC +#endif // __clang__ + void* _Allocate(const size_t _Bytes) { return ::operator new(_Bytes); } #ifdef __cpp_aligned_new - __declspec(allocator) static void* _Allocate_aligned(const size_t _Bytes, const size_t _Align) { - return ::operator new (_Bytes, align_val_t{_Align}); + __declspec(allocator) static +#ifdef __clang__ // Clang and MSVC implement P0784R7 differently; see GH-1532 + _CONSTEXPR20_DYNALLOC +#endif // __clang__ + void* _Allocate_aligned(const size_t _Bytes, const size_t _Align) { +#ifdef __clang__ // Clang and MSVC implement P0784R7 differently; see GH-1532 +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + return ::operator new(_Bytes); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc +#endif // __clang__ + { + return ::operator new (_Bytes, align_val_t{_Align}); + } } #endif // __cpp_aligned_new }; @@ -157,34 +174,46 @@ inline void _Adjust_manually_vector_aligned(void*& _Ptr, size_t& _Bytes) { #ifdef __cpp_aligned_new template __STDCPP_DEFAULT_NEW_ALIGNMENT__), int> = 0> -__declspec(allocator) void* _Allocate(const size_t _Bytes) { +__declspec(allocator) _CONSTEXPR20_DYNALLOC void* _Allocate(const size_t _Bytes) { // allocate _Bytes when __cpp_aligned_new && _Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__ if (_Bytes == 0) { return nullptr; } - size_t _Passed_align = _Align; +#ifdef __cpp_lib_constexpr_dynamic_alloc // TRANSITION, GH-1532 + if (_STD is_constant_evaluated()) { + return _Traits::_Allocate(_Bytes); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + size_t _Passed_align = _Align; #if defined(_M_IX86) || defined(_M_X64) - if (_Bytes >= _Big_allocation_threshold) { - // boost the alignment of big allocations to help autovectorization - _Passed_align = (_STD max)(_Align, _Big_allocation_alignment); - } + if (_Bytes >= _Big_allocation_threshold) { + // boost the alignment of big allocations to help autovectorization + _Passed_align = (_STD max)(_Align, _Big_allocation_alignment); + } #endif // defined(_M_IX86) || defined(_M_X64) - - return _Traits::_Allocate_aligned(_Bytes, _Passed_align); + return _Traits::_Allocate_aligned(_Bytes, _Passed_align); + } } template __STDCPP_DEFAULT_NEW_ALIGNMENT__), int> = 0> -void _Deallocate(void* _Ptr, const size_t _Bytes) noexcept { +_CONSTEXPR20_DYNALLOC void _Deallocate(void* _Ptr, const size_t _Bytes) noexcept { // deallocate storage allocated by _Allocate when __cpp_aligned_new && _Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__ - size_t _Passed_align = _Align; +#ifdef __cpp_lib_constexpr_dynamic_alloc // TRANSITION, GH-1532 + if (_STD is_constant_evaluated()) { + ::operator delete(_Ptr); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + size_t _Passed_align = _Align; #if defined(_M_IX86) || defined(_M_X64) - if (_Bytes >= _Big_allocation_threshold) { // boost the alignment of big allocations to help autovectorization - _Passed_align = (_STD max)(_Align, _Big_allocation_alignment); - } + if (_Bytes >= _Big_allocation_threshold) { // boost the alignment of big allocations to help autovectorization + _Passed_align = (_STD max)(_Align, _Big_allocation_alignment); + } #endif // defined(_M_IX86) || defined(_M_X64) - - ::operator delete (_Ptr, _Bytes, align_val_t{_Passed_align}); + ::operator delete (_Ptr, _Bytes, align_val_t{_Passed_align}); + } } #define _HAS_ALIGNED_NEW 1 @@ -194,11 +223,16 @@ void _Deallocate(void* _Ptr, const size_t _Bytes) noexcept { template = 0> -__declspec(allocator) void* _Allocate(const size_t _Bytes) { +__declspec(allocator) _CONSTEXPR20_DYNALLOC void* _Allocate(const size_t _Bytes) { // allocate _Bytes when !_HAS_ALIGNED_NEW || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__ #if defined(_M_IX86) || defined(_M_X64) - if (_Bytes >= _Big_allocation_threshold) { // boost the alignment of big allocations to help autovectorization - return _Allocate_manually_vector_aligned<_Traits>(_Bytes); +#ifdef __cpp_lib_constexpr_dynamic_alloc // TRANSITION, GH-1532 + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_dynamic_alloc + { + if (_Bytes >= _Big_allocation_threshold) { // boost the alignment of big allocations to help autovectorization + return _Allocate_manually_vector_aligned<_Traits>(_Bytes); + } } #endif // defined(_M_IX86) || defined(_M_X64) @@ -210,15 +244,21 @@ __declspec(allocator) void* _Allocate(const size_t _Bytes) { } template = 0> -void _Deallocate(void* _Ptr, size_t _Bytes) noexcept { +_CONSTEXPR20_DYNALLOC void _Deallocate(void* _Ptr, size_t _Bytes) noexcept { // deallocate storage allocated by _Allocate when !_HAS_ALIGNED_NEW || _Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__ +#ifdef __cpp_lib_constexpr_dynamic_alloc // TRANSITION, GH-1532 + if (_STD is_constant_evaluated()) { + ::operator delete(_Ptr); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { #if defined(_M_IX86) || defined(_M_X64) - if (_Bytes >= _Big_allocation_threshold) { // boost the alignment of big allocations to help autovectorization - _Adjust_manually_vector_aligned(_Ptr, _Bytes); - } + if (_Bytes >= _Big_allocation_threshold) { // boost the alignment of big allocations to help autovectorization + _Adjust_manually_vector_aligned(_Ptr, _Bytes); + } #endif // defined(_M_IX86) || defined(_M_X64) - - ::operator delete(_Ptr, _Bytes); + ::operator delete(_Ptr, _Bytes); + } } #undef _HAS_ALIGNED_NEW @@ -268,6 +308,21 @@ _CONSTEXPR20_DYNALLOC void _Destroy_in_place(_Ty& _Obj) noexcept { } } +#if _HAS_CXX17 +// FUNCTION TEMPLATE destroy_at +template +_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(); + } +} +#endif // _HAS_CXX17 + // FUNCTION TEMPLATE _Const_cast template auto _Const_cast(_Ptrty _Ptr) noexcept { // remove constness from a fancy pointer @@ -516,11 +571,12 @@ struct _Normal_allocator_traits { // defines traits for allocators template using rebind_traits = allocator_traits>; - _NODISCARD static __declspec(allocator) pointer allocate(_Alloc& _Al, _CRT_GUARDOVERFLOW const size_type _Count) { + _NODISCARD static _CONSTEXPR20_DYNALLOC __declspec(allocator) pointer + allocate(_Alloc& _Al, _CRT_GUARDOVERFLOW const size_type _Count) { return _Al.allocate(_Count); } - _NODISCARD static __declspec(allocator) pointer + _NODISCARD static _CONSTEXPR20_DYNALLOC __declspec(allocator) pointer allocate(_Alloc& _Al, _CRT_GUARDOVERFLOW const size_type _Count, const const_void_pointer _Hint) { if constexpr (_Has_allocate_hint<_Alloc, size_type, const_void_pointer>::value) { return _Al.allocate(_Count, _Hint); @@ -529,30 +585,38 @@ struct _Normal_allocator_traits { // defines traits for allocators } } - static void deallocate(_Alloc& _Al, pointer _Ptr, size_type _Count) { + static _CONSTEXPR20_DYNALLOC void deallocate(_Alloc& _Al, pointer _Ptr, size_type _Count) { _Al.deallocate(_Ptr, _Count); } template - static void construct(_Alloc& _Al, _Ty* _Ptr, _Types&&... _Args) { + static _CONSTEXPR20_DYNALLOC void construct(_Alloc& _Al, _Ty* _Ptr, _Types&&... _Args) { if constexpr (_Uses_default_construct<_Alloc, _Ty*, _Types...>::value) { (void) _Al; // TRANSITION, DevCom-1004719 +#ifdef __cpp_lib_constexpr_dynamic_alloc + _STD construct_at(_Ptr, _STD forward<_Types>(_Args)...); +#else // __cpp_lib_constexpr_dynamic_alloc ::new (static_cast(_Ptr)) _Ty(_STD forward<_Types>(_Args)...); +#endif // __cpp_lib_constexpr_dynamic_alloc } else { _Al.construct(_Ptr, _STD forward<_Types>(_Args)...); } } template - static void destroy(_Alloc& _Al, _Ty* _Ptr) { + static _CONSTEXPR20_DYNALLOC void destroy(_Alloc& _Al, _Ty* _Ptr) { if constexpr (_Uses_default_destroy<_Alloc, _Ty*>::value) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + _STD destroy_at(_Ptr); +#else // __cpp_lib_constexpr_dynamic_alloc _Ptr->~_Ty(); +#endif // __cpp_lib_constexpr_dynamic_alloc } else { _Al.destroy(_Ptr); } } - _NODISCARD static size_type max_size(const _Alloc& _Al) noexcept { + _NODISCARD static _CONSTEXPR20_DYNALLOC size_type max_size(const _Alloc& _Al) noexcept { if constexpr (_Has_max_size<_Alloc>::value) { return _Al.max_size(); } else { @@ -560,7 +624,7 @@ struct _Normal_allocator_traits { // defines traits for allocators } } - _NODISCARD static _Alloc select_on_container_copy_construction(const _Alloc& _Al) { + _NODISCARD static _CONSTEXPR20_DYNALLOC _Alloc select_on_container_copy_construction(const _Alloc& _Al) { if constexpr (_Has_select_on_container_copy_construction<_Alloc>::value) { return _Al.select_on_container_copy_construction(); } else { @@ -594,35 +658,73 @@ struct _Default_allocator_traits { // traits for std::allocator template using rebind_traits = allocator_traits>; - _NODISCARD static __declspec(allocator) pointer allocate(_Alloc&, _CRT_GUARDOVERFLOW const size_type _Count) { - return static_cast(_Allocate<_New_alignof>(_Get_size_of_n(_Count))); + _NODISCARD static _CONSTEXPR20_DYNALLOC __declspec(allocator) pointer + allocate(_Alloc& _Al, _CRT_GUARDOVERFLOW const size_type _Count) { +#ifdef __cpp_lib_constexpr_dynamic_alloc // TRANSITION, GH-1532 + if (_STD is_constant_evaluated()) { + return _Al.allocate(_Count); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + (void) _Al; + return static_cast( + _Allocate<_New_alignof>(_Get_size_of_n(_Count))); + } } - _NODISCARD static __declspec(allocator) pointer - allocate(_Alloc&, _CRT_GUARDOVERFLOW const size_type _Count, const_void_pointer) { - return static_cast(_Allocate<_New_alignof>(_Get_size_of_n(_Count))); + _NODISCARD static _CONSTEXPR20_DYNALLOC __declspec(allocator) pointer + allocate(_Alloc& _Al, _CRT_GUARDOVERFLOW const size_type _Count, const_void_pointer) { +#ifdef __cpp_lib_constexpr_dynamic_alloc // TRANSITION, GH-1532 + if (_STD is_constant_evaluated()) { + return _Al.allocate(_Count); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + (void) _Al; + return static_cast( + _Allocate<_New_alignof>(_Get_size_of_n(_Count))); + } } - static void deallocate(_Alloc&, const pointer _Ptr, const size_type _Count) { + static _CONSTEXPR20_DYNALLOC void deallocate(_Alloc& _Al, const pointer _Ptr, const size_type _Count) { // no overflow check on the following multiply; we assume _Allocate did that check - _Deallocate<_New_alignof>(_Ptr, sizeof(value_type) * _Count); +#ifdef __cpp_lib_constexpr_dynamic_alloc // TRANSITION, GH-1532 + if (_STD is_constant_evaluated()) { + _Al.deallocate(_Ptr, _Count); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + (void) _Al; + _Deallocate<_New_alignof>(_Ptr, sizeof(value_type) * _Count); + } } template - static void construct(_Alloc&, _Objty* const _Ptr, _Types&&... _Args) { - ::new (const_cast(static_cast(_Ptr))) _Objty(_STD forward<_Types>(_Args)...); + static _CONSTEXPR20_DYNALLOC void construct(_Alloc&, _Objty* const _Ptr, _Types&&... _Args) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + if (_STD is_constant_evaluated()) { + _STD construct_at(_Ptr, _STD forward<_Types>(_Args)...); + } else +#endif // __cpp_lib_constexpr_dynamic_alloc + { + ::new (_Voidify_iter(_Ptr)) _Objty(_STD forward<_Types>(_Args)...); + } } template - static void destroy(_Alloc&, _Uty* const _Ptr) { + static _CONSTEXPR20_DYNALLOC void destroy(_Alloc&, _Uty* const _Ptr) { +#ifdef __cpp_lib_constexpr_dynamic_alloc + _STD destroy_at(_Ptr); +#else // __cpp_lib_constexpr_dynamic_alloc _Ptr->~_Uty(); +#endif // __cpp_lib_constexpr_dynamic_alloc } - _NODISCARD static size_type max_size(const _Alloc&) noexcept { + _NODISCARD static _CONSTEXPR20_DYNALLOC size_type max_size(const _Alloc&) noexcept { return static_cast(-1) / sizeof(value_type); } - _NODISCARD static _Alloc select_on_container_copy_construction(const _Alloc& _Al) { + _NODISCARD static _CONSTEXPR20_DYNALLOC _Alloc select_on_container_copy_construction(const _Alloc& _Al) { return _Al; } }; @@ -716,13 +818,15 @@ public: constexpr allocator(const allocator&) noexcept = default; template constexpr allocator(const allocator<_Other>&) noexcept {} + _CONSTEXPR20_DYNALLOC ~allocator() = default; + _CONSTEXPR20_DYNALLOC allocator& operator=(const allocator&) = default; - void deallocate(_Ty* const _Ptr, const size_t _Count) { + _CONSTEXPR20_DYNALLOC void deallocate(_Ty* const _Ptr, const size_t _Count) { // no overflow check on the following multiply; we assume _Allocate did that check _Deallocate<_New_alignof<_Ty>>(_Ptr, sizeof(_Ty) * _Count); } - _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + _NODISCARD _CONSTEXPR20_DYNALLOC __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { return static_cast<_Ty*>(_Allocate<_New_alignof<_Ty>>(_Get_size_of_n(_Count))); } @@ -767,12 +871,12 @@ public: }; template -_NODISCARD bool operator==(const allocator<_Ty>&, const allocator<_Other>&) noexcept { +_NODISCARD _CONSTEXPR20_DYNALLOC bool operator==(const allocator<_Ty>&, const allocator<_Other>&) noexcept { return true; } template -_NODISCARD bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { +_NODISCARD _CONSTEXPR20_DYNALLOC bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { return false; } diff --git a/stl/inc/xutility b/stl/inc/xutility index d817785ee8..240171141e 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -121,7 +121,7 @@ struct _Get_rebind_alias<_Ty, _Other, void_t -_NODISCARD void* _Voidify_iter(_Iter _It) noexcept { +_NODISCARD constexpr void* _Voidify_iter(_Iter _It) noexcept { if constexpr (is_pointer_v<_Iter>) { return const_cast(static_cast(_It)); } else { @@ -129,6 +129,16 @@ _NODISCARD void* _Voidify_iter(_Iter _It) noexcept { } } +// FUNCTION TEMPLATE construct_at +#if _HAS_CXX20 +template +_CONSTEXPR20_DYNALLOC auto construct_at(_Ty* const _Location, _Types&&... _Args) noexcept( + noexcept(::new (_Voidify_iter(_Location)) _Ty(_STD forward<_Types>(_Args)...))) // strengthened + -> decltype(::new (_Voidify_iter(_Location)) _Ty(_STD forward<_Types>(_Args)...)) { + return ::new (_Voidify_iter(_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...>) { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 65e0e288a9..28011748cb 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -166,6 +166,7 @@ // P0758R1 is_nothrow_convertible // P0768R1 Library Support For The Spaceship Comparison Operator <=> // P0769R2 shift_left(), shift_right() +// P0784R7 Library Support For More constexpr Containers // P0811R3 midpoint(), lerp() // P0879R0 constexpr For Swapping Functions // P0887R1 type_identity @@ -550,13 +551,6 @@ #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 @@ -1185,8 +1179,14 @@ #define __cpp_lib_concepts 201907L #endif // !defined(__EDG__) || defined(__INTELLISENSE__) -#define __cpp_lib_constexpr_algorithms 201806L -#define __cpp_lib_constexpr_complex 201711L +#define __cpp_lib_constexpr_algorithms 201806L +#define __cpp_lib_constexpr_complex 201711L + +#if defined(__cpp_constexpr_dynamic_alloc) \ + && defined(__clang__) // TRANSITION, MSVC support for constexpr dynamic allocation +#define __cpp_lib_constexpr_dynamic_alloc 201907L +#endif // defined(__cpp_constexpr_dynamic_alloc) && defined(__clang__) + #define __cpp_lib_constexpr_functional 201907L #define __cpp_lib_constexpr_iterator 201811L #define __cpp_lib_constexpr_memory 201811L @@ -1255,6 +1255,13 @@ #define __cpp_lib_experimental_erase_if 201411L #define __cpp_lib_experimental_filesystem 201406L +// Functions that became constexpr in C++20 via P0784R7 +#ifdef __cpp_lib_constexpr_dynamic_alloc +#define _CONSTEXPR20_DYNALLOC constexpr +#else +#define _CONSTEXPR20_DYNALLOC inline +#endif + #ifdef _RTC_CONVERSION_CHECKS_ENABLED #ifndef _ALLOW_RTCc_IN_STL #error /RTCc rejects conformant code, so it is not supported by the C++ Standard Library. Either remove this \ diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 3996fe4e0e..8961a1cc34 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -493,19 +493,18 @@ std/utilities/variant/variant.variant/variant.ctor/conv.pass.cpp FAIL std/utilities/variant/variant.variant/variant.ctor/T.pass.cpp FAIL # C++20 P0784R7 "More constexpr containers" -std/utilities/memory/allocator.traits/allocator.traits.members/allocate.pass.cpp FAIL -std/utilities/memory/allocator.traits/allocator.traits.members/allocate_hint.pass.cpp FAIL +std/utilities/memory/allocator.traits/allocator.traits.members/allocate.pass.cpp:0 FAIL +std/utilities/memory/allocator.traits/allocator.traits.members/allocate_hint.pass.cpp:0 FAIL std/utilities/memory/allocator.traits/allocator.traits.members/construct.pass.cpp FAIL -std/utilities/memory/allocator.traits/allocator.traits.members/deallocate.pass.cpp FAIL +std/utilities/memory/allocator.traits/allocator.traits.members/deallocate.pass.cpp:0 FAIL std/utilities/memory/allocator.traits/allocator.traits.members/destroy.pass.cpp FAIL -std/utilities/memory/allocator.traits/allocator.traits.members/max_size.pass.cpp FAIL -std/utilities/memory/allocator.traits/allocator.traits.members/select_on_container_copy_construction.pass.cpp FAIL -std/utilities/memory/default.allocator/allocator.globals/eq.pass.cpp FAIL -std/utilities/memory/default.allocator/allocator.members/allocate.pass.cpp:1 FAIL +std/utilities/memory/allocator.traits/allocator.traits.members/max_size.pass.cpp:0 FAIL +std/utilities/memory/allocator.traits/allocator.traits.members/select_on_container_copy_construction.pass.cpp:0 FAIL +std/utilities/memory/default.allocator/allocator.globals/eq.pass.cpp:0 FAIL std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp FAIL -std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp FAIL -std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp FAIL -std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp FAIL +std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp:0 FAIL +std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp:0 FAIL +std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp:0 FAIL # C++20 P0896R4 "" std/language.support/support.limits/support.limits.general/algorithm.version.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index b5172f6cdb..61b46acc24 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -501,7 +501,6 @@ utilities\memory\allocator.traits\allocator.traits.members\destroy.pass.cpp utilities\memory\allocator.traits\allocator.traits.members\max_size.pass.cpp utilities\memory\allocator.traits\allocator.traits.members\select_on_container_copy_construction.pass.cpp utilities\memory\default.allocator\allocator.globals\eq.pass.cpp -utilities\memory\default.allocator\allocator.members\allocate.pass.cpp utilities\memory\specialized.algorithms\specialized.construct\construct_at.pass.cpp utilities\memory\specialized.algorithms\specialized.destroy\destroy.pass.cpp utilities\memory\specialized.algorithms\specialized.destroy\destroy_at.pass.cpp 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 7cf1685a98..52da34b9f9 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 @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include #include +#include #include #include #include @@ -143,7 +145,6 @@ static_assert(destroy_at_noexcept()); static_assert(destroy_at_noexcept()); static_assert(destroy_at_noexcept()); -#if _HAS_CXX20 static_assert(destroy_at_noexcept()); static_assert(destroy_at_noexcept()); static_assert(destroy_at_noexcept()); @@ -152,16 +153,13 @@ 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(destroy_at_noexcept()); -#if _HAS_CXX20 static_assert(destroy_at_noexcept()); -#endif // _HAS_CXX20 #ifdef __cpp_lib_concepts static_assert(!can_ranges_destroy_at); @@ -205,7 +203,6 @@ 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); @@ -227,10 +224,9 @@ void test_array(const T& val) { 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) +#ifdef __cpp_lib_constexpr_dynamic_alloc template struct storage_for { union { @@ -272,7 +268,262 @@ constexpr void test_compiletime() { } } static_assert((test_compiletime(), true)); -#endif // _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) + +template +struct A { + T value; + + constexpr A() noexcept = default; + constexpr ~A() = default; +}; + +template +struct nontrivial_A { + T value; + + constexpr nontrivial_A(T in = T{}) noexcept : value(in) {} + constexpr ~nontrivial_A() {} +}; + +constexpr void test_compiletime_destroy_variants() { + { + allocator> alloc{}; + A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + construct_at(a + i); + } + destroy(a, a + 10); + alloc.deallocate(a, 10); + } + { + allocator> alloc{}; + nontrivial_A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + construct_at(a + i); + } + destroy(a, a + 10); + alloc.deallocate(a, 10); + } +#ifdef __cpp_lib_concepts + { + allocator> alloc{}; + A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + ranges::construct_at(a + i); + } + ranges::destroy(a, a + 10); + alloc.deallocate(a, 10); + } + { + allocator> alloc{}; + nontrivial_A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + ranges::construct_at(a + i); + } + ranges::destroy(a, a + 10); + alloc.deallocate(a, 10); + } + { + allocator> alloc{}; + A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + ranges::construct_at(a + i); + } + span s{a, 10}; + ranges::destroy(s); + alloc.deallocate(a, 10); + } + { + allocator> alloc{}; + nontrivial_A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + ranges::construct_at(a + i); + } + span s{a, 10}; + ranges::destroy(s); + alloc.deallocate(a, 10); + } +#endif // __cpp_lib_concepts + { + allocator> alloc{}; + A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + construct_at(a + i); + } + destroy_n(a, 10); + alloc.deallocate(a, 10); + } + { + allocator> alloc{}; + nontrivial_A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + construct_at(a + i); + } + destroy_n(a, 10); + alloc.deallocate(a, 10); + } +#ifdef __cpp_lib_concepts + { + allocator> alloc{}; + A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + ranges::construct_at(a + i); + } + ranges::destroy_n(a, 10); + alloc.deallocate(a, 10); + } + { + allocator> alloc{}; + nontrivial_A* a = alloc.allocate(10); + for (int i = 0; i < 10; ++i) { + ranges::construct_at(a + i); + } + ranges::destroy_n(a, 10); + alloc.deallocate(a, 10); + } +#endif // __cpp_lib_concepts +} +static_assert((test_compiletime_destroy_variants(), true)); + +template +struct Alloc { + using value_type = T; + using size_type = size_t; + + template + struct rebind { + using other = Alloc; + }; + + constexpr Alloc(int id_) noexcept : id(id_) {} + + template + constexpr Alloc(const Alloc& al) noexcept : id(al.id) {} + + constexpr value_type* allocate(size_t n) { + assert(n == 10); + return allocator{}.allocate(n); + } + + constexpr void deallocate(value_type* ptr, size_t n) { + assert(n == 10); + allocator{}.deallocate(ptr, n); + } + + constexpr void construct(value_type* ptr, value_type n) requires(Construct) { + construct_at(ptr, n); + } + + constexpr void destroy(value_type* ptr) requires(Destroy) { + destroy_at(ptr); + } + + constexpr Alloc select_on_container_copy_construction() const noexcept { + return Alloc{id + 1}; + } + + constexpr size_type max_size() const noexcept { + return numeric_limits::max() / sizeof(value_type); + } + + template + constexpr bool operator==(const Alloc&) const noexcept { + return true; + } + + int id; +}; + +constexpr void test_compiletime_allocator_traits() { + { + storage_for> a; + Alloc> alloc{10}; + assert(alloc.id == 10); + + auto result = allocator_traits>>::allocate(alloc, 10); + assert(result != nullptr); + allocator_traits>>::deallocate(alloc, result, 10); + + allocator_traits>>::construct(alloc, &a.object); + assert(a.object.value == 0); + allocator_traits>>::destroy(alloc, &a.object); + + assert(allocator_traits>>::select_on_container_copy_construction(alloc).id == 11); + + assert(allocator_traits>>::max_size(alloc) + == numeric_limits>::size_type>::max() / sizeof(Alloc>::value_type)); + } + { + storage_for> a; + Alloc> alloc{10}; + assert(alloc.id == 10); + + auto result = allocator_traits>>::allocate(alloc, 10); + assert(result != nullptr); + allocator_traits>>::deallocate(alloc, result, 10); + + allocator_traits>>::construct(alloc, &a.object, 10); + assert(a.object.value == 10); + allocator_traits>>::destroy(alloc, &a.object); + + assert(allocator_traits>>::select_on_container_copy_construction(alloc).id == 11); + + assert(allocator_traits>>::max_size(alloc) + == numeric_limits>::size_type>::max() + / sizeof(Alloc>::value_type)); + } + { + storage_for> a; + Alloc, true> alloc{10}; + + allocator_traits, true>>::construct(alloc, &a.object, 10); + assert(a.object.value == 10); + allocator_traits, true>>::destroy(alloc, &a.object); + } + { + storage_for> a; + Alloc, false, true> alloc{10}; + + allocator_traits, false, true>>::construct(alloc, &a.object, 10); + assert(a.object.value == 10); + allocator_traits, false, true>>::destroy(alloc, &a.object); + } + { + storage_for> a; + Alloc, true, true> alloc{10}; + + allocator_traits, true, true>>::construct(alloc, &a.object, 10); + assert(a.object.value == 10); + allocator_traits, true, true>>::destroy(alloc, &a.object); + } +} +static_assert((test_compiletime_allocator_traits(), true)); + +constexpr void test_compiletime_allocator() { + { + auto result = allocator>{}.allocate(10); + allocator>{}.deallocate(result, 10); + } + { + auto result = allocator>{}.allocate(10); + allocator>{}.deallocate(result, 10); + } +} +static_assert((test_compiletime_allocator(), true)); + +constexpr void test_compiletime_operators() { + { + allocator allocatorA{}; + allocator allocatorB{}; + constexpr auto allocatorC = allocatorA; + + static_assert(allocatorA == allocatorB); + static_assert(!(allocatorA != allocatorB)); + static_assert(allocatorA == allocatorC); + } +} +static_assert((test_compiletime_operators(), true)); +#endif // __cpp_lib_constexpr_dynamic_alloc int main() { test_runtime(1234); 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 a513a4f533..9a6cd10c97 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 @@ -411,6 +411,21 @@ STATIC_ASSERT(__cpp_lib_constexpr_complex == 201711L); #endif #endif +#if _HAS_CXX20 && defined(__cpp_constexpr_dynamic_alloc) \ + && defined(__clang__) // TRANSITION, MSVC support for constexpr dynamic allocation +#ifndef __cpp_lib_constexpr_dynamic_alloc +#error __cpp_lib_constexpr_dynamic_alloc is not defined +#elif __cpp_lib_constexpr_dynamic_alloc != 201907L +#error __cpp_lib_constexpr_dynamic_alloc is not 201907L +#else +STATIC_ASSERT(__cpp_lib_constexpr_dynamic_alloc == 201907L); +#endif +#else +#ifdef __cpp_lib_constexpr_dynamic_alloc +#error __cpp_lib_constexpr_dynamic_alloc is defined +#endif +#endif + #if _HAS_CXX20 #ifndef __cpp_lib_constexpr_functional #error __cpp_lib_constexpr_functional is not defined