diff --git a/stl/inc/memory b/stl/inc/memory index eafeefd38b9..915acf7c89b 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -736,11 +736,6 @@ namespace ranges { #endif // __cpp_lib_concepts // FUNCTION TEMPLATE uninitialized_default_construct -template <class _Iter> -_NODISCARD void* _Voidify_iter(_Iter _It) noexcept { - return const_cast<void*>(static_cast<const volatile void*>(_STD addressof(*_It))); -} - template <class _NoThrowFwdIt> void uninitialized_default_construct(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last) { // default-initialize all elements in [_First, _Last) @@ -750,7 +745,7 @@ void uninitialized_default_construct(const _NoThrowFwdIt _First, const _NoThrowF _Uninitialized_backout _Backout{_Get_unwrapped(_First)}; for (const auto _ULast = _Get_unwrapped(_Last); _Backout._Last != _ULast; ++_Backout._Last) { - ::new (_Voidify_iter(_Backout._Last)) _Ty; + _Default_construct_in_place(*_Backout._Last); } _Backout._Release(); @@ -802,7 +797,7 @@ namespace ranges { _Uninitialized_backout _Backout{_STD move(_OFirst)}; for (; _Backout._Last != _OLast; ++_Backout._Last) { - ::new (_Voidify_iter(_Backout._Last)) _Ty; + _Default_construct_in_place(*_Backout._Last); } return _Backout._Release(); @@ -831,7 +826,7 @@ _NoThrowFwdIt uninitialized_default_construct_n(_NoThrowFwdIt _First, const _Dif _Uninitialized_backout _Backout{_Get_unwrapped_n(_First, _Count)}; for (; _Count > 0; ++_Backout._Last, (void) --_Count) { - ::new (_Voidify_iter(_Backout._Last)) _Ty; + _Default_construct_in_place(*_Backout._Last); } _Seek_wrapped(_First, _Backout._Release()); @@ -862,7 +857,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); for (; _Count > 0; --_Count, (void) ++_UFirst) { - ::new (_Voidify_iter(_UFirst)) _Ty; + _Default_construct_in_place(*_UFirst); } _Seek_wrapped(_First, _STD move(_UFirst)); @@ -870,7 +865,7 @@ namespace ranges { _Uninitialized_backout _Backout{_Get_unwrapped_n(_STD move(_First), _Count)}; for (; _Count > 0; --_Count, (void) ++_Backout._Last) { - ::new (_Voidify_iter(_Backout._Last)) _Ty; + _Default_construct_in_place(*_Backout._Last); } _Seek_wrapped(_First, _Backout._Release()); @@ -1908,13 +1903,6 @@ private: template <class _Ty0, class _Alloc, class... _Types> friend enable_if_t<!is_array_v<_Ty0>, shared_ptr<_Ty0>> allocate_shared(const _Alloc& _Al_arg, _Types&&... _Args); - template <class _Ty0> - friend enable_if_t<is_unbounded_array_v<_Ty0>, shared_ptr<_Ty0>> make_shared(size_t _Count); - - template <class _Ty0, class _Alloc> - friend enable_if_t<is_unbounded_array_v<_Ty0>, shared_ptr<_Ty0>> allocate_shared( - const _Alloc& _Al_arg, size_t _Count); - template <class _Ty0> friend enable_if_t<is_bounded_array_v<_Ty0>, shared_ptr<_Ty0>> make_shared(); @@ -1922,19 +1910,25 @@ private: friend enable_if_t<is_bounded_array_v<_Ty0>, shared_ptr<_Ty0>> allocate_shared(const _Alloc& _Al_arg); template <class _Ty0> - friend enable_if_t<is_unbounded_array_v<_Ty0>, shared_ptr<_Ty0>> make_shared( - size_t _Count, const remove_extent_t<_Ty0>& _Val); + friend enable_if_t<is_bounded_array_v<_Ty0>, shared_ptr<_Ty0>> make_shared(const remove_extent_t<_Ty0>& _Val); template <class _Ty0, class _Alloc> - friend enable_if_t<is_unbounded_array_v<_Ty0>, shared_ptr<_Ty0>> allocate_shared( - const _Alloc& _Al_arg, size_t _Count, const remove_extent_t<_Ty0>& _Val); + friend enable_if_t<is_bounded_array_v<_Ty0>, shared_ptr<_Ty0>> allocate_shared( + const _Alloc& _Al_arg, const remove_extent_t<_Ty0>& _Val); template <class _Ty0> - friend enable_if_t<is_bounded_array_v<_Ty0>, shared_ptr<_Ty0>> make_shared(const remove_extent_t<_Ty0>& _Val); + friend enable_if_t<!is_unbounded_array_v<_Ty0>, shared_ptr<_Ty0>> make_shared_for_overwrite(); template <class _Ty0, class _Alloc> - friend enable_if_t<is_bounded_array_v<_Ty0>, shared_ptr<_Ty0>> allocate_shared( - const _Alloc& _Al_arg, const remove_extent_t<_Ty0>& _Val); + friend enable_if_t<!is_unbounded_array_v<_Ty0>, shared_ptr<_Ty0>> allocate_shared_for_overwrite( + const _Alloc& _Al_arg); + + template <class _Ty0, class... _ArgTypes> + friend shared_ptr<_Ty0> _Make_shared_unbounded_array(size_t _Count, const _ArgTypes&... _Args); + + template <class _Ty0, class _Alloc, class... _ArgTypes> + friend shared_ptr<_Ty0> _Allocate_shared_unbounded_array( + const _Alloc& _Al, size_t _Count, const _ArgTypes&... _Args); #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template <class _Ty0, class... _Types> friend shared_ptr<_Ty0> make_shared(_Types&&... _Args); @@ -2163,13 +2157,27 @@ template <class _Dx, class _Ty> _Dx* get_deleter(const shared_ptr<_Ty>&) noexcept = delete; // requires static RTTI #endif // _HAS_STATIC_RTTI +#if _HAS_CXX20 +struct _For_overwrite_tag { + explicit _For_overwrite_tag() = default; +}; +#endif // _HAS_CXX20 + // CLASS TEMPLATE _Ref_count_obj2 template <class _Ty> class _Ref_count_obj2 : public _Ref_count_base { // handle reference counting for object in control block, no allocator public: template <class... _Types> explicit _Ref_count_obj2(_Types&&... _Args) : _Ref_count_base() { - _Construct_in_place(_Storage._Value, _STD forward<_Types>(_Args)...); +#if _HAS_CXX20 + if constexpr (sizeof...(_Types) == 1 && (is_same_v<_For_overwrite_tag, remove_cvref_t<_Types>> && ...)) { + _Default_construct_in_place(_Storage._Value); + ((void) _Args, ...); + } else +#endif // _HAS_CXX20 + { + _Construct_in_place(_Storage._Value, _STD forward<_Types>(_Args)...); + } } ~_Ref_count_obj2() { @@ -2282,6 +2290,11 @@ struct _Uninitialized_rev_destroying_backout { ++_Last; } + void _Emplace_back_for_overwrite() { + _Default_construct_in_place(*_Last); + ++_Last; + } + _NoThrowIt _Release() noexcept { // suppress any exception handling backout and return _Last _First = _Last; return _Last; @@ -2351,6 +2364,25 @@ void _Uninitialized_value_construct_multidimensional_n(_Ty* const _Out, const si } } +template <class _Ty> +void _Uninitialized_default_construct_multidimensional_n(_Ty* const _Out, const size_t _Size) { + if constexpr (!is_trivially_default_constructible_v<_Ty>) { + if constexpr (is_array_v<_Ty>) { + _Reverse_destroy_multidimensional_n_guard<_Ty> _Guard{_Out, 0}; + for (size_t& _Idx = _Guard._Index; _Idx < _Size; ++_Idx) { + _Uninitialized_default_construct_multidimensional_n(_Out[_Idx], extent_v<_Ty>); + } + _Guard._Target = nullptr; + } else { + _Uninitialized_rev_destroying_backout _Backout{_Out}; + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { + _Backout._Emplace_back_for_overwrite(); + } + _Backout._Release(); + } + } +} + template <class _Ty> void _Uninitialized_fill_multidimensional_n(_Ty* const _Out, const size_t _Size, const _Ty& _Val) { if constexpr (is_array_v<_Ty>) { @@ -2383,8 +2415,13 @@ public: _Uninitialized_value_construct_multidimensional_n(_Get_ptr(), _Count); } - explicit _Ref_count_unbounded_array(const size_t _Count, const _Element_type& _Val) : _Ref_count_base() { - _Uninitialized_fill_multidimensional_n(_Get_ptr(), _Count, _Val); + template <class _Arg> + explicit _Ref_count_unbounded_array(const size_t _Count, const _Arg& _Val) : _Ref_count_base() { + if constexpr (is_same_v<_For_overwrite_tag, _Arg>) { + _Uninitialized_default_construct_multidimensional_n(_Get_ptr(), _Count); + } else { + _Uninitialized_fill_multidimensional_n(_Get_ptr(), _Count, _Val); + } } _NODISCARD auto _Get_ptr() noexcept { @@ -2424,9 +2461,13 @@ public: _Uninitialized_value_construct_multidimensional_n(_Get_ptr(), _Size); } - explicit _Ref_count_unbounded_array(const size_t _Count, const _Element_type& _Val) - : _Ref_count_base(), _Size(_Count) { - _Uninitialized_fill_multidimensional_n(_Get_ptr(), _Size, _Val); + template <class _Arg> + explicit _Ref_count_unbounded_array(const size_t _Count, const _Arg& _Val) : _Ref_count_base(), _Size(_Count) { + if constexpr (is_same_v<_For_overwrite_tag, _Arg>) { + _Uninitialized_default_construct_multidimensional_n(_Get_ptr(), _Size); + } else { + _Uninitialized_fill_multidimensional_n(_Get_ptr(), _Size, _Val); + } } _NODISCARD auto _Get_ptr() noexcept { @@ -2465,9 +2506,13 @@ public: _Ref_count_bounded_array() : _Ref_count_base(), _Storage() {} // value-initializing _Storage is necessary here - explicit _Ref_count_bounded_array(const remove_extent_t<_Ty>& _Val) - : _Ref_count_base() { // don't value-initialize _Storage - _Uninitialized_fill_multidimensional_n(_Storage._Value, extent_v<_Ty>, _Val); + template <class _Arg> + explicit _Ref_count_bounded_array(const _Arg& _Val) : _Ref_count_base() { // don't value-initialize _Storage + if constexpr (is_same_v<_For_overwrite_tag, _Arg>) { + _Uninitialized_default_construct_multidimensional_n(_Storage._Value, extent_v<_Ty>); + } else { + _Uninitialized_fill_multidimensional_n(_Storage._Value, extent_v<_Ty>, _Val); + } } union { @@ -2544,8 +2589,16 @@ public: template <class... _Types> explicit _Ref_count_obj_alloc3(const _Alloc& _Al_arg, _Types&&... _Args) : _Ebco_base<_Rebound>(_Al_arg), _Ref_count_base() { - allocator_traits<_Rebound>::construct( - this->_Get_val(), _STD addressof(_Storage._Value), _STD forward<_Types>(_Args)...); +#if _HAS_CXX20 + if constexpr (sizeof...(_Types) == 1 && (is_same_v<_For_overwrite_tag, remove_cvref_t<_Types>> && ...)) { + _Default_construct_in_place(_Storage._Value); + ((void) _Args, ...); + } else +#endif // _HAS_CXX20 + { + allocator_traits<_Rebound>::construct( + this->_Get_val(), _STD addressof(_Storage._Value), _STD forward<_Types>(_Args)...); + } } union { @@ -2713,9 +2766,14 @@ public: _Uninitialized_value_construct_multidimensional_n_al(_Get_ptr(), _Size, this->_Get_val()); } - explicit _Ref_count_unbounded_array_alloc(const _Alloc& _Al_arg, const size_t _Count, const _Element_type& _Val) + template <class _Arg> + explicit _Ref_count_unbounded_array_alloc(const _Alloc& _Al_arg, const size_t _Count, const _Arg& _Val) : _Ebco_base<_Rebound>(_Al_arg), _Ref_count_base(), _Size(_Count) { - _Uninitialized_fill_multidimensional_n_al(_Get_ptr(), _Size, _Val, this->_Get_val()); + if constexpr (is_same_v<_For_overwrite_tag, _Arg>) { + _Uninitialized_default_construct_multidimensional_n(_Get_ptr(), _Size); // the allocator isn't needed + } else { + _Uninitialized_fill_multidimensional_n_al(_Get_ptr(), _Size, _Val, this->_Get_val()); + } } _NODISCARD auto _Get_ptr() noexcept { @@ -2774,9 +2832,15 @@ public: _Uninitialized_value_construct_multidimensional_n_al(_Storage._Value, extent_v<_Ty>, this->_Get_val()); } - explicit _Ref_count_bounded_array_alloc(const _Alloc& _Al_arg, const remove_extent_t<_Ty>& _Val) + template <class _Arg> + explicit _Ref_count_bounded_array_alloc(const _Alloc& _Al_arg, const _Arg& _Val) : _Ebco_base<_Rebound>(_Al_arg), _Ref_count_base() { // don't value-initialize _Storage - _Uninitialized_fill_multidimensional_n_al(_Storage._Value, extent_v<_Ty>, _Val, this->_Get_val()); + if constexpr (is_same_v<_For_overwrite_tag, _Arg>) { + _Uninitialized_default_construct_multidimensional_n( + _Storage._Value, extent_v<_Ty>); // the allocator isn't needed + } else { + _Uninitialized_fill_multidimensional_n_al(_Storage._Value, extent_v<_Ty>, _Val, this->_Get_val()); + } } union { @@ -2834,31 +2898,29 @@ struct _Global_delete_guard { } }; -template <class _Ty> -_NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> make_shared(const size_t _Count) { +template <class _Ty, class... _ArgTypes> +_NODISCARD shared_ptr<_Ty> _Make_shared_unbounded_array(const size_t _Count, const _ArgTypes&... _Args) { // make a shared_ptr to an unbounded array + static_assert(is_unbounded_array_v<_Ty>); using _Refc = _Ref_count_unbounded_array<_Ty>; const auto _Rx = _Allocate_flexible_array<_Refc>(_Count); _Global_delete_guard<_Refc> _Guard{_Rx}; - ::new (static_cast<void*>(_Rx)) _Refc(_Count); + ::new (static_cast<void*>(_Rx)) _Refc(_Count, _Args...); _Guard._Target = nullptr; shared_ptr<_Ty> _Ret; _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Get_ptr(), _Rx); return _Ret; } +template <class _Ty> +_NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> make_shared(const size_t _Count) { + return _Make_shared_unbounded_array<_Ty>(_Count); +} + template <class _Ty> _NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> make_shared( const size_t _Count, const remove_extent_t<_Ty>& _Val) { - // make a shared_ptr to an unbounded array - using _Refc = _Ref_count_unbounded_array<_Ty>; - const auto _Rx = _Allocate_flexible_array<_Refc>(_Count); - _Global_delete_guard<_Refc> _Guard{_Rx}; - ::new (static_cast<void*>(_Rx)) _Refc(_Count, _Val); - _Guard._Target = nullptr; - shared_ptr<_Ty> _Ret; - _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Get_ptr(), _Rx); - return _Ret; + return _Make_shared_unbounded_array<_Ty>(_Count, _Val); } template <class _Ty> @@ -2878,6 +2940,27 @@ _NODISCARD enable_if_t<is_bounded_array_v<_Ty>, shared_ptr<_Ty>> make_shared(con _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Storage._Value, _Rx); return _Ret; } + +// FUNCTION TEMPLATE make_shared_for_overwrite +template <class _Ty> +_NODISCARD enable_if_t<!is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> make_shared_for_overwrite() { + shared_ptr<_Ty> _Ret; + if constexpr (is_array_v<_Ty>) { + // make a shared_ptr to a bounded array + const auto _Rx = new _Ref_count_bounded_array<_Ty>(_For_overwrite_tag{}); + _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Storage._Value, _Rx); + } else { + // make a shared_ptr to non-array object + const auto _Rx = new _Ref_count_obj2<_Ty>(_For_overwrite_tag{}); + _Ret._Set_ptr_rep_and_enable_shared(_STD addressof(_Rx->_Storage._Value), _Rx); + } + return _Ret; +} + +template <class _Ty> +_NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> make_shared_for_overwrite(const size_t _Count) { + return _Make_shared_unbounded_array<_Ty>(_Count, _For_overwrite_tag{}); +} #endif // _HAS_CXX20 // FUNCTION TEMPLATE allocate_shared @@ -2922,10 +3005,11 @@ struct _Allocate_n_ptr { _Allocate_n_ptr& operator=(const _Allocate_n_ptr&) = delete; }; -template <class _Ty, class _Alloc> -_NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> allocate_shared( - const _Alloc& _Al, const size_t _Count) { +template <class _Ty, class _Alloc, class... _ArgTypes> +_NODISCARD shared_ptr<_Ty> _Allocate_shared_unbounded_array( + const _Alloc& _Al, const size_t _Count, const _ArgTypes&... _Args) { // make a shared_ptr to an unbounded array + static_assert(is_unbounded_array_v<_Ty>); using _Refc = _Ref_count_unbounded_array_alloc<remove_cv_t<_Ty>, _Alloc>; constexpr size_t _Align = alignof(_Refc); using _Storage = _Alignas_storage_unit<_Align>; @@ -2934,30 +3018,23 @@ _NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> allocate_shar const size_t _Storage_units = _Bytes / sizeof(_Storage); _Allocate_n_ptr _Guard{_Rebound, _Storage_units}; const auto _Rx = reinterpret_cast<_Refc*>(_Unfancy(_Guard._Ptr)); - ::new (static_cast<void*>(_Rx)) _Refc(_Al, _Count); + ::new (static_cast<void*>(_Rx)) _Refc(_Al, _Count, _Args...); _Guard._Ptr = nullptr; shared_ptr<_Ty> _Ret; _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Get_ptr(), _Rx); return _Ret; } +template <class _Ty, class _Alloc> +_NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> allocate_shared( + const _Alloc& _Al, const size_t _Count) { + return _Allocate_shared_unbounded_array<_Ty>(_Al, _Count); +} + template <class _Ty, class _Alloc> _NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> allocate_shared( const _Alloc& _Al, const size_t _Count, const remove_extent_t<_Ty>& _Val) { - // make a shared_ptr to an unbounded array - using _Refc = _Ref_count_unbounded_array_alloc<remove_cv_t<_Ty>, _Alloc>; - constexpr size_t _Align = alignof(_Refc); - using _Storage = _Alignas_storage_unit<_Align>; - _Rebind_alloc_t<_Alloc, _Storage> _Rebound(_Al); - const size_t _Bytes = _Calculate_bytes_for_flexible_array<_Refc, _Check_overflow::_Yes>(_Count); - const size_t _Storage_units = _Bytes / sizeof(_Storage); - _Allocate_n_ptr _Guard{_Rebound, _Storage_units}; - const auto _Rx = reinterpret_cast<_Refc*>(_Unfancy(_Guard._Ptr)); - ::new (static_cast<void*>(_Rx)) _Refc(_Al, _Count, _Val); - _Guard._Ptr = nullptr; - shared_ptr<_Ty> _Ret; - _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Get_ptr(), _Rx); - return _Ret; + return _Allocate_shared_unbounded_array<_Ty>(_Al, _Count, _Val); } template <class _Ty, class _Alloc> @@ -2990,6 +3067,41 @@ _NODISCARD enable_if_t<is_bounded_array_v<_Ty>, shared_ptr<_Ty>> allocate_shared _Ret._Set_ptr_rep_and_enable_shared(_Ptr, _Unfancy(_Constructor._Release())); return _Ret; } + +// FUNCTION TEMPLATE allocate_shared_for_overwrite +template <class _Ty, class _Alloc> +_NODISCARD enable_if_t<!is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> allocate_shared_for_overwrite(const _Alloc& _Al) { + shared_ptr<_Ty> _Ret; + if constexpr (is_array_v<_Ty>) { + // make a shared_ptr to a bounded array + using _Refc = _Ref_count_bounded_array_alloc<remove_cv_t<_Ty>, _Alloc>; + using _Alblock = _Rebind_alloc_t<_Alloc, _Refc>; + _Alblock _Rebound(_Al); + _Alloc_construct_ptr _Constructor{_Rebound}; + _Constructor._Allocate(); + ::new (_Voidify_iter(_Constructor._Ptr)) _Refc(_Al, _For_overwrite_tag{}); + const auto _Ptr = static_cast<remove_extent_t<_Ty>*>(_Constructor._Ptr->_Storage._Value); + _Ret._Set_ptr_rep_and_enable_shared(_Ptr, _Unfancy(_Constructor._Release())); + } else { + // make a shared_ptr to non-array object + using _Refoa = _Ref_count_obj_alloc3<remove_cv_t<_Ty>, _Alloc>; + using _Alblock = _Rebind_alloc_t<_Alloc, _Refoa>; + _Alblock _Rebound(_Al); + _Alloc_construct_ptr<_Alblock> _Constructor{_Rebound}; + _Constructor._Allocate(); + _Construct_in_place(*_Constructor._Ptr, _Al, _For_overwrite_tag{}); + const auto _Ptr = reinterpret_cast<_Ty*>(_STD addressof(_Constructor._Ptr->_Storage._Value)); + _Ret._Set_ptr_rep_and_enable_shared(_Ptr, _Unfancy(_Constructor._Release())); + } + + return _Ret; +} + +template <class _Ty, class _Alloc> +_NODISCARD enable_if_t<is_unbounded_array_v<_Ty>, shared_ptr<_Ty>> allocate_shared_for_overwrite( + const _Alloc& _Al, const size_t _Count) { + return _Allocate_shared_unbounded_array<_Ty>(_Al, _Count, _For_overwrite_tag{}); +} #endif // _HAS_CXX20 // CLASS TEMPLATE weak_ptr @@ -3442,7 +3554,7 @@ _NODISCARD unique_ptr<_Ty> make_unique(_Types&&... _Args) { // make a unique_ptr } template <class _Ty, enable_if_t<is_array_v<_Ty> && extent_v<_Ty> == 0, int> = 0> -_NODISCARD unique_ptr<_Ty> make_unique(size_t _Size) { // make a unique_ptr +_NODISCARD unique_ptr<_Ty> make_unique(const size_t _Size) { // make a unique_ptr using _Elem = remove_extent_t<_Ty>; return unique_ptr<_Ty>(new _Elem[_Size]()); } @@ -3450,6 +3562,24 @@ _NODISCARD unique_ptr<_Ty> make_unique(size_t _Size) { // make a unique_ptr template <class _Ty, class... _Types, enable_if_t<extent_v<_Ty> != 0, int> = 0> void make_unique(_Types&&...) = delete; +#if _HAS_CXX20 +// FUNCTION TEMPLATE make_unique_for_overwrite +template <class _Ty, enable_if_t<!is_array_v<_Ty>, int> = 0> +_NODISCARD unique_ptr<_Ty> make_unique_for_overwrite() { // make a unique_ptr with default initialization + return unique_ptr<_Ty>(new _Ty); +} + +template <class _Ty, enable_if_t<is_unbounded_array_v<_Ty>, int> = 0> +_NODISCARD unique_ptr<_Ty> make_unique_for_overwrite( + const size_t _Size) { // make a unique_ptr with default initialization + using _Elem = remove_extent_t<_Ty>; + return unique_ptr<_Ty>(new _Elem[_Size]); +} + +template <class _Ty, class... _Types, enable_if_t<is_bounded_array_v<_Ty>, int> = 0> +void make_unique_for_overwrite(_Types&&...) = delete; +#endif // _HAS_CXX20 + template <class _Ty, class _Dx, enable_if_t<_Is_swappable<_Dx>::value, int> = 0> void swap(unique_ptr<_Ty, _Dx>& _Left, unique_ptr<_Ty, _Dx>& _Right) noexcept { _Left.swap(_Right); diff --git a/stl/inc/xutility b/stl/inc/xutility index 7dbdc5aa5b6..fa244555d08 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -119,11 +119,29 @@ struct _Get_rebind_alias<_Ty, _Other, void_t<typename _Ty::template rebind<_Othe using type = typename _Ty::template rebind<_Other>; }; +// FUNCTION TEMPLATE _Voidify_iter +template <class _Iter> +_NODISCARD void* _Voidify_iter(_Iter _It) noexcept { +#if _HAS_IF_CONSTEXPR + if constexpr (is_pointer_v<_Iter>) { + return const_cast<void*>(static_cast<const volatile void*>(_It)); + } else +#endif // _HAS_IF_CONSTEXPR + { + return const_cast<void*>(static_cast<const volatile void*>(_STD addressof(*_It))); + } +} + // FUNCTION TEMPLATE _Construct_in_place template <class _Ty, class... _Types> void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { - ::new (const_cast<void*>(static_cast<const volatile void*>(_STD addressof(_Obj)))) - _Ty(_STD forward<_Types>(_Args)...); + ::new (_Voidify_iter(_STD addressof(_Obj))) _Ty(_STD forward<_Types>(_Args)...); +} + +// FUNCTION TEMPLATE _Default_construct_in_place +template <class _Ty> +void _Default_construct_in_place(_Ty& _Obj) noexcept(is_nothrow_default_constructible_v<_Ty>) { + ::new (_Voidify_iter(_STD addressof(_Obj))) _Ty; } // STRUCT TEMPLATE pointer_traits diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index dcfd6744b1a..afb1523b28f 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -179,6 +179,7 @@ // P1001R2 execution::unseq // P1006R1 constexpr For pointer_traits<T*>::pointer_to() // P1007R3 assume_aligned() +// P1020R1 Smart Pointer Creation With Default Initialization // P1023R0 constexpr For std::array Comparisons // P1024R3 Enhancing span Usability // P1032R1 Miscellaneous constexpr @@ -219,6 +220,7 @@ // P1959R0 Removing weak_equality And strong_equality // P1960R0 atomic_ref Cleanup // P1964R2 Replacing boolean With boolean-testable +// P1973R1 Renaming default_init To for_overwrite // P1976R2 Explicit Constructors For Fixed-Extent span From Dynamic-Extent Ranges // P2091R0 Fixing Issues With Range Access CPOs // P2102R0 Making "Implicit Expression Variations" More Explicit @@ -1208,6 +1210,7 @@ #define __cpp_lib_remove_cvref 201711L #define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L +#define __cpp_lib_smart_ptr_for_overwrite 202002L #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L #define __cpp_lib_starts_ends_with 201711L diff --git a/tests/std/test.lst b/tests/std/test.lst index ce4aff5bb8d..2ac454e53c6 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -354,6 +354,7 @@ tests\P0912R5_coroutine tests\P0919R3_heterogeneous_unordered_lookup tests\P0966R1_string_reserve_should_not_shrink tests\P1007R3_assume_aligned +tests\P1020R1_smart_pointer_for_overwrite tests\P1023R0_constexpr_for_array_comparisons tests\P1032R1_miscellaneous_constexpr tests\P1135R6_atomic_flag_test diff --git a/tests/std/tests/P1020R1_smart_pointer_for_overwrite/env.lst b/tests/std/tests/P1020R1_smart_pointer_for_overwrite/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1020R1_smart_pointer_for_overwrite/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/P1020R1_smart_pointer_for_overwrite/test.cpp b/tests/std/tests/P1020R1_smart_pointer_for_overwrite/test.cpp new file mode 100644 index 00000000000..c6ee3f4c3c9 --- /dev/null +++ b/tests/std/tests/P1020R1_smart_pointer_for_overwrite/test.cpp @@ -0,0 +1,344 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include <assert.h> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <memory> +#include <new> +#include <stdexcept> +#include <string_view> +#include <type_traits> +#include <utility> +#include <vector> + +using namespace std; + +constexpr int uninitializedValue = 0xEE; +constexpr int initializedValue = 106; +size_t allocationCount = 0; + +struct ReportAddress; +vector<ReportAddress*> ascendingAddressBuffer; +vector<ReportAddress*> descendingAddressBuffer; + +#pragma warning(disable : 28251) // Inconsistent annotation for 'new' + +// According to N4849, the default behavior of operator new[](size) is to return +// operator new(size), so only the latter needs to be replaced. +void* operator new(size_t size) { + void* const p = ::operator new(size, nothrow); + + if (p) { + return p; + } else { + throw bad_alloc(); + } +} + +void* operator new(size_t size, const nothrow_t&) noexcept { + void* const result = malloc(size == 0 ? 1 : size); + ++allocationCount; + if (result) { + memset(result, uninitializedValue, size); + } + + return result; +} + +void* operator new(size_t size, align_val_t align) { + void* const p = ::operator new(size, align, nothrow); + + if (p) { + return p; + } else { + throw bad_alloc(); + } +} + +void* operator new(size_t size, align_val_t align, const nothrow_t&) noexcept { + void* const result = ::_aligned_malloc(size, static_cast<size_t>(align)); + ++allocationCount; + if (result) { + memset(result, uninitializedValue, size); + } + + return result; +} + +template <typename T, typename = void> +struct unique_is_for_overwritable : false_type {}; + +template <typename T> +struct unique_is_for_overwritable<T, void_t<decltype(make_unique_for_overwrite<T>())>> : true_type {}; + +template <typename T> +constexpr bool unique_is_for_overwritable_v = unique_is_for_overwritable<T>::value; + +struct DefaultInitializableInt { + int value; + DefaultInitializableInt() : value(initializedValue) {} +}; + +struct alignas(32) HighlyAligned { + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; +}; + +size_t canCreate = 10; // Counter to force an exception when constructing a sufficiently large ReportAddress array + +struct ReportAddress { + ReportAddress() { + if (canCreate > 0) { + ascendingAddressBuffer.push_back(this); + --canCreate; + } else { + throw runtime_error("Can't create more ReportAddress objects."); + } + } + + ~ReportAddress() { + ++canCreate; + descendingAddressBuffer.push_back(this); + } +}; + +void assert_ascending_init() { + for (size_t i = 1; i < ascendingAddressBuffer.size(); ++i) { + assert(ascendingAddressBuffer[i - 1] < ascendingAddressBuffer[i]); + } + + ascendingAddressBuffer.clear(); +} + +void assert_descending_destruct() { + for (size_t i = 1; i < descendingAddressBuffer.size(); ++i) { + assert(descendingAddressBuffer[i - 1] > descendingAddressBuffer[i]); + } + + descendingAddressBuffer.clear(); +} + +void assert_uninitialized(void* p, size_t size) { + unsigned char* chPtr = reinterpret_cast<unsigned char*>(p); + for (size_t offset = 0; offset < size; ++offset) { + assert(chPtr[offset] == uninitializedValue); + } +} + +template <class T> +void assert_shared_use_get(const shared_ptr<T>& sp) { + assert(sp.use_count() == 1); + assert(sp.get() != nullptr); +} + +template <class T, class... Args> +shared_ptr<T> make_shared_for_overwrite_assert(Args&&... vals) { + size_t aCount = allocationCount; + shared_ptr<T> sp = make_shared_for_overwrite<T>(forward<Args>(vals)...); + assert_shared_use_get(sp); + assert(aCount + 1 == allocationCount); + return sp; +} + +template <class T, class... Args> +void test_make_shared_init_destruct_order(Args&&... vals) { + try { + shared_ptr<T> sp = make_shared_for_overwrite<T>(forward<Args>(vals)...); + assert_shared_use_get(sp); + } catch (const runtime_error& exc) { + assert(exc.what() == "Can't create more ReportAddress objects."sv); + } + + assert_ascending_init(); + assert_descending_destruct(); +} + +void test_make_unique_for_overwrite() { + static_assert(unique_is_for_overwritable_v<char>); + static_assert(!unique_is_for_overwritable_v<int[100]>); + + auto p0 = make_unique_for_overwrite<int>(); + assert_uninitialized(p0.get(), sizeof(int)); + + auto p1 = make_unique_for_overwrite<int[]>(100u); + assert_uninitialized(p1.get(), sizeof(int) * 100u); + + auto p2 = make_unique_for_overwrite<DefaultInitializableInt>(); + assert(p2->value == initializedValue); + + auto p3 = make_unique_for_overwrite<DefaultInitializableInt[][89]>(2u); + for (size_t i = 0; i < 2; ++i) { + for (size_t j = 0; j < 89; ++j) { + assert(p3[i][j].value == initializedValue); + } + } + + auto p4 = make_unique_for_overwrite<int[]>(0u); // p4 cannot be dereferenced +} + +void test_make_shared_for_overwrite() { + auto p0 = make_shared_for_overwrite_assert<int>(); + assert_uninitialized(addressof(*p0), sizeof(int)); + + auto p1 = make_shared_for_overwrite_assert<DefaultInitializableInt>(); + assert(p1->value == initializedValue); + + auto p2 = make_shared_for_overwrite_assert<HighlyAligned>(); + assert(reinterpret_cast<uintptr_t>(p2.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(*p2), sizeof(HighlyAligned)); + + auto p3 = make_shared_for_overwrite_assert<int[100]>(); + assert_uninitialized(addressof(p3[0]), sizeof(int) * 100u); + + auto p4 = make_shared_for_overwrite_assert<DefaultInitializableInt[2][8]>(); + for (ptrdiff_t i = 0; i < 2; ++i) { + for (ptrdiff_t j = 0; j < 8; ++j) { + assert(p4[i][j].value == initializedValue); + } + } + + auto p5 = make_shared_for_overwrite_assert<HighlyAligned[10]>(); + assert(reinterpret_cast<uintptr_t>(p5.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(p5[0]), sizeof(HighlyAligned) * 10u); + + auto p6 = make_shared_for_overwrite_assert<DefaultInitializableInt[]>(100u); + for (ptrdiff_t i = 0; i < 100; ++i) { + assert(p6[i].value == initializedValue); + } + + auto p7 = make_shared_for_overwrite_assert<DefaultInitializableInt[][8][9]>(2u); + for (ptrdiff_t i = 0; i < 2; ++i) { + for (ptrdiff_t j = 0; j < 8; ++j) { + for (ptrdiff_t k = 0; k < 9; ++k) { + assert(p7[i][j][k].value == initializedValue); + } + } + } + + auto p8 = make_shared_for_overwrite_assert<int[]>(100u); + assert_uninitialized(addressof(p8[0]), sizeof(int) * 100u); + + auto p9 = make_shared_for_overwrite_assert<int[]>(0u); // p9 cannot be dereferenced + + auto p10 = make_shared_for_overwrite_assert<HighlyAligned[]>(10u); + assert(reinterpret_cast<uintptr_t>(p10.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(p10[0]), sizeof(HighlyAligned) * 10u); + + test_make_shared_init_destruct_order<ReportAddress[5]>(); // success one dimensional + + test_make_shared_init_destruct_order<ReportAddress[20]>(); // failure one dimensional + + test_make_shared_init_destruct_order<ReportAddress[2][2][2]>(); // success multidimensional + + test_make_shared_init_destruct_order<ReportAddress[3][3][3]>(); // failure multidimensional + + test_make_shared_init_destruct_order<ReportAddress[]>(5u); // success one dimensional + + test_make_shared_init_destruct_order<ReportAddress[]>(20u); // failure one dimensional + + test_make_shared_init_destruct_order<ReportAddress[][2][2]>(2u); // success multidimensional + + test_make_shared_init_destruct_order<ReportAddress[][3][3]>(3u); // failure multidimensional +} + +template <class T, class... Args> +shared_ptr<T> allocate_shared_for_overwrite_assert(Args&&... vals) { + size_t aCount = allocationCount; + shared_ptr<T> sp = allocate_shared_for_overwrite<T>(forward<Args>(vals)...); + assert_shared_use_get(sp); + assert(aCount + 1 == allocationCount); + return sp; +} + +template <class T, class... Args> +void test_allocate_shared_init_destruct_order(Args&&... vals) { + allocator<remove_all_extents_t<T>> a{}; + + try { + shared_ptr<T> sp = allocate_shared_for_overwrite<T>(a, forward<Args>(vals)...); + assert_shared_use_get(sp); + } catch (const runtime_error& exc) { + assert(exc.what() == "Can't create more ReportAddress objects."sv); + } + + assert_ascending_init(); + assert_descending_destruct(); +} + +void test_allocate_shared_for_overwrite() { + allocator<int> a0{}; + auto p0 = allocate_shared_for_overwrite_assert<int>(a0); + assert_uninitialized(addressof(*p0), sizeof(int)); + + allocator<DefaultInitializableInt> a1{}; + auto p1 = allocate_shared_for_overwrite_assert<DefaultInitializableInt>(a1); + assert(p1->value == initializedValue); + + allocator<HighlyAligned> a2{}; + auto p2 = allocate_shared_for_overwrite_assert<HighlyAligned>(a2); + assert(reinterpret_cast<uintptr_t>(p2.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(*p2), sizeof(HighlyAligned)); + + auto p3 = allocate_shared_for_overwrite_assert<int[100]>(a0); + assert_uninitialized(addressof(p3[0]), sizeof(int) * 100u); + + auto p4 = allocate_shared_for_overwrite_assert<DefaultInitializableInt[2][8]>(a1); + for (ptrdiff_t i = 0; i < 2; ++i) { + for (ptrdiff_t j = 0; j < 8; ++j) { + assert(p4[i][j].value == initializedValue); + } + } + + auto p5 = allocate_shared_for_overwrite_assert<HighlyAligned[10]>(a2); + assert(reinterpret_cast<uintptr_t>(p5.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(p5[0]), sizeof(HighlyAligned) * 10u); + + auto p6 = allocate_shared_for_overwrite_assert<DefaultInitializableInt[]>(a1, 100u); + for (ptrdiff_t i = 0; i < 100; ++i) { + assert(p6[i].value == initializedValue); + } + + auto p7 = allocate_shared_for_overwrite_assert<DefaultInitializableInt[][8][9]>(a1, 2u); + for (ptrdiff_t i = 0; i < 2; ++i) { + for (ptrdiff_t j = 0; j < 8; ++j) { + for (ptrdiff_t k = 0; k < 9; ++k) { + assert(p7[i][j][k].value == initializedValue); + } + } + } + + auto p8 = allocate_shared_for_overwrite_assert<int[]>(a0, 100u); + assert_uninitialized(addressof(p8[0]), sizeof(int) * 100u); + + auto p9 = allocate_shared_for_overwrite_assert<int[]>(a0, 0u); // p9 cannot be dereferenced + + auto p10 = allocate_shared_for_overwrite_assert<HighlyAligned[]>(a2, 10u); + assert(reinterpret_cast<uintptr_t>(p10.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(p10[0]), sizeof(HighlyAligned) * 10u); + + test_allocate_shared_init_destruct_order<ReportAddress[5]>(); // success one dimensional + + test_allocate_shared_init_destruct_order<ReportAddress[20]>(); // failure one dimensional + + test_allocate_shared_init_destruct_order<ReportAddress[2][2][2]>(); // success multidimensional + + test_allocate_shared_init_destruct_order<ReportAddress[3][3][3]>(); // failure multidimensional + + test_allocate_shared_init_destruct_order<ReportAddress[]>(5u); // success one dimensional + + test_allocate_shared_init_destruct_order<ReportAddress[]>(20u); // failure one dimensional + + test_allocate_shared_init_destruct_order<ReportAddress[][2][2]>(2u); // success multidimensional + + test_allocate_shared_init_destruct_order<ReportAddress[][3][3]>(3u); // failure multidimensional +} + +int main() { + test_make_unique_for_overwrite(); + test_make_shared_for_overwrite(); + test_allocate_shared_for_overwrite(); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp index 153a5e821fc..216ac473b68 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp @@ -1266,6 +1266,20 @@ STATIC_ASSERT(__cpp_lib_shift == 201806L); #endif #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_smart_ptr_for_overwrite +#error __cpp_lib_smart_ptr_for_overwrite is not defined +#elif __cpp_lib_smart_ptr_for_overwrite != 202002L +#error __cpp_lib_smart_ptr_for_overwrite is not 202002L +#else +STATIC_ASSERT(__cpp_lib_smart_ptr_for_overwrite == 202002L); +#endif +#else +#ifdef __cpp_lib_smart_ptr_for_overwrite +#error __cpp_lib_smart_ptr_for_overwrite is defined +#endif +#endif + #if _HAS_CXX20 #ifndef __cpp_lib_span #error __cpp_lib_span is not defined