diff --git a/stl/inc/memory b/stl/inc/memory index 369a730af50..ec08b778f0d 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -1199,6 +1199,24 @@ private: friend shared_ptr<_Ty0> allocate_shared(const _Alloc& _Al_arg, _Types&&... _Args); #endif // !_HAS_CXX20 +// Should this be in a separate ifdef _HAS_CXX20 control block since it is a +// different feature? +#if _HAS_CXX20 + template + friend enable_if_t, shared_ptr<_Ty0>> make_shared_for_overwrite(); + + template + friend enable_if_t, shared_ptr<_Ty0>> allocate_shared_for_overwrite( + const _Alloc& _Al_arg); + + template + friend enable_if_t, shared_ptr<_Ty0>> make_shared_for_overwrite(size_t _Count); + + template + friend enable_if_t, shared_ptr<_Ty0>> allocate_shared_for_overwrite( + const _Alloc& _Al_arg, size_t _Count); +#endif // !_HAS_CXX20 + template void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Px this->_Ptr = _Px; @@ -1419,13 +1437,19 @@ template _Dx* get_deleter(const shared_ptr<_Ty>&) noexcept = delete; // requires static RTTI #endif // _HAS_STATIC_RTTI +enum class _Init_form : bool { _Default_init, _Value_init }; + // CLASS TEMPLATE _Ref_count_obj2 -template +template class _Ref_count_obj2 : public _Ref_count_base { // handle reference counting for object in control block, no allocator public: template explicit _Ref_count_obj2(_Types&&... _Args) : _Ref_count_base() { - _Construct_in_place(_Storage._Value, _STD forward<_Types>(_Args)...); + if _CONSTEXPR_IF (_Init == _Init_form::_Default_init) { + _Construct_in_place_for_overwrite(_Storage._Value); + } else { + _Construct_in_place(_Storage._Value, _STD forward<_Types>(_Args)...); + } } ~_Ref_count_obj2() { @@ -1513,7 +1537,7 @@ void _Deallocate_flexible_array(_Refc* const _Ptr) noexcept { } } -template +template struct _Uninitialized_rev_destroying_backout { // struct to undo partially constructed ranges in _Uninitialized_xxx algorithms _NoThrowIt _First; @@ -1534,7 +1558,11 @@ struct _Uninitialized_rev_destroying_backout { template void _Emplace_back(_Types&&... _Vals) { // construct a new element at *_Last and increment - _Construct_in_place(*_Last, _STD forward<_Types>(_Vals)...); + if constexpr (_Init == _Init_form::_Default_init) { + _Construct_in_place_for_overwrite(*_Last); + } else { + _Construct_in_place(*_Last, _STD forward<_Types>(_Vals)...); + } ++_Last; } @@ -1579,7 +1607,9 @@ void _Uninitialized_copy_multidimensional(const _Ty (&_In)[_Size], _Ty (&_Out)[_ } _Guard._Target = nullptr; } else { - _Uninitialized_rev_destroying_backout _Backout{_Out}; + // I don't really like calling this value-init since it's my understanding this would be direct-init. + // However, I don't know how to rewrite it. Maybe changing the enum to add _Direct_init. + _Uninitialized_rev_destroying_backout<_Ty*, _Init_form::_Value_init> _Backout{_Out}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Backout._Emplace_back(_In[_Idx]); } @@ -1599,7 +1629,25 @@ void _Uninitialized_value_construct_multidimensional_n(_Ty* const _Out, const si } _Guard._Target = nullptr; } else { - _Uninitialized_rev_destroying_backout _Backout{_Out}; + _Uninitialized_rev_destroying_backout<_Ty*, _Init_form::_Value_init> _Backout{_Out}; + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { + _Backout._Emplace_back(); + } + _Backout._Release(); + } +} + +template +void _Uninitialized_default_construct_multidimensional_n(_Ty* const _Out, const size_t _Size) { + using _Item = remove_all_extents_t<_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<_Ty*, _Init_form::_Default_init> _Backout{_Out}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Backout._Emplace_back(); } @@ -1618,7 +1666,7 @@ void _Uninitialized_fill_multidimensional_n(_Ty* const _Out, const size_t _Size, } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty>) { _CSTD memset(_Out, static_cast(_Val), _Size); } else { - _Uninitialized_rev_destroying_backout _Backout{_Out}; + _Uninitialized_rev_destroying_backout<_Ty*, _Init_form::_Value_init> _Backout{_Out}; for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Backout._Emplace_back(_Val); } @@ -1627,7 +1675,7 @@ void _Uninitialized_fill_multidimensional_n(_Ty* const _Out, const size_t _Size, } // CLASS TEMPLATE _Ref_count_unbounded_array -template >> +template >> class _Ref_count_unbounded_array : public _Ref_count_base { // handle reference counting for unbounded array with trivial destruction in control block, no allocator public: @@ -1636,7 +1684,11 @@ public: using _Element_type = remove_extent_t<_Ty>; explicit _Ref_count_unbounded_array(const size_t _Count) : _Ref_count_base() { - _Uninitialized_value_construct_multidimensional_n(_Get_ptr(), _Count); + if constexpr (_Init == _Init_form::_Default_init) { + _Uninitialized_default_construct_multidimensional_n(_Get_ptr(), _Count); + } else { + _Uninitialized_value_construct_multidimensional_n(_Get_ptr(), _Count); + } } explicit _Ref_count_unbounded_array(const size_t _Count, const _Element_type& _Val) : _Ref_count_base() { @@ -1668,8 +1720,8 @@ private: } }; -template -class _Ref_count_unbounded_array<_Ty, false> : public _Ref_count_base { +template +class _Ref_count_unbounded_array<_Ty, _Init, false> : public _Ref_count_base { // handle reference counting for unbounded array with non-trivial destruction in control block, no allocator public: static_assert(is_unbounded_array_v<_Ty>); @@ -1677,7 +1729,11 @@ public: using _Element_type = remove_extent_t<_Ty>; explicit _Ref_count_unbounded_array(const size_t _Count) : _Ref_count_base(), _Size(_Count) { - _Uninitialized_value_construct_multidimensional_n(_Get_ptr(), _Size); + if constexpr (_Init == _Init_form::_Default_init) { + _Uninitialized_default_construct_multidimensional_n(_Get_ptr(), _Count); + } else { + _Uninitialized_value_construct_multidimensional_n(_Get_ptr(), _Count); + } } explicit _Ref_count_unbounded_array(const size_t _Count, const _Element_type& _Val) @@ -1713,13 +1769,19 @@ private: }; // CLASS TEMPLATE _Ref_count_bounded_array -template +template class _Ref_count_bounded_array : public _Ref_count_base { // handle reference counting for bounded array in control block, no allocator public: static_assert(is_bounded_array_v<_Ty>); - _Ref_count_bounded_array() : _Ref_count_base(), _Storage() {} // value-initializing _Storage is necessary here + _Ref_count_bounded_array() : _Ref_count_base() { + if constexpr (_Init == _Init_form::_Default_init) { + _Uninitialized_default_construct_multidimensional_n(_Storage._Value, extent_v<_Ty>); + } else { + _Uninitialized_value_construct_multidimensional_n(_Storage._Value, extent_v<_Ty>); + } + } explicit _Ref_count_bounded_array(const remove_extent_t<_Ty>& _Val) : _Ref_count_base() { // don't value-initialize _Storage @@ -1788,8 +1850,9 @@ protected: }; // CLASS TEMPLATE _Ref_count_obj_alloc3 -template -class _Ref_count_obj_alloc3 : public _Ebco_base<_Rebind_alloc_t<_Alloc, _Ty>>, public _Ref_count_base { +template +class __declspec(empty_bases) _Ref_count_obj_alloc3 : public _Ebco_base<_Rebind_alloc_t<_Alloc, _Ty>>, + public _Ref_count_base { // handle reference counting for object in control block, allocator private: static_assert(is_same_v<_Ty, remove_cv_t<_Ty>>, "allocate_shared should remove_cv_t"); @@ -1800,8 +1863,12 @@ public: template 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 _CONSTEXPR_IF (_Init == _Init_form::_Default_init) { + _Construct_in_place_for_overwrite(_Storage._Value); + } else { + allocator_traits<_Rebound>::construct( + this->_Get_val(), _STD addressof(_Storage._Value), _STD forward<_Types>(_Args)...); + } } union { @@ -1950,9 +2017,10 @@ void _Uninitialized_fill_multidimensional_n_al(_Ty* const _Out, const size_t _Si } // CLASS TEMPLATE _Ref_count_unbounded_array_alloc -template -class _Ref_count_unbounded_array_alloc : public _Ebco_base<_Rebind_alloc_t<_Alloc, remove_all_extents_t<_Ty>>>, - public _Ref_count_base { +template +class __declspec(empty_bases) _Ref_count_unbounded_array_alloc + : public _Ebco_base<_Rebind_alloc_t<_Alloc, remove_all_extents_t<_Ty>>>, + public _Ref_count_base { // handle reference counting for unbounded array in control block, allocator private: static_assert(is_unbounded_array_v<_Ty>); @@ -1966,7 +2034,11 @@ public: explicit _Ref_count_unbounded_array_alloc(const _Alloc& _Al_arg, const size_t _Count) : _Ebco_base<_Rebound>(_Al_arg), _Ref_count_base(), _Size(_Count) { - _Uninitialized_value_construct_multidimensional_n_al(_Get_ptr(), _Size, this->_Get_val()); + if constexpr (_Init == _Init_form::_Default_init) { + _Uninitialized_default_construct_multidimensional_n(_Get_ptr(), _Size); // the allocator isn't needed + } else { + _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) @@ -2013,9 +2085,10 @@ private: }; // CLASS TEMPLATE _Ref_count_bounded_array_alloc -template -class _Ref_count_bounded_array_alloc : public _Ebco_base<_Rebind_alloc_t<_Alloc, remove_all_extents_t<_Ty>>>, - public _Ref_count_base { +template +class __declspec(empty_bases) _Ref_count_bounded_array_alloc + : public _Ebco_base<_Rebind_alloc_t<_Alloc, remove_all_extents_t<_Ty>>>, + public _Ref_count_base { // handle reference counting for bounded array in control block, allocator private: static_assert(is_bounded_array_v<_Ty>); @@ -2027,7 +2100,12 @@ private: public: explicit _Ref_count_bounded_array_alloc(const _Alloc& _Al_arg) : _Ebco_base<_Rebound>(_Al_arg), _Ref_count_base() { // don't value-initialize _Storage - _Uninitialized_value_construct_multidimensional_n_al(_Storage._Value, extent_v<_Ty>, this->_Get_val()); + if constexpr (_Init == _Init_form::_Default_init) { + _Uninitialized_default_construct_multidimensional_n( + _Storage._Value, extent_v<_Ty>); // the allocator isn't needed + } else { + _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) @@ -2069,7 +2147,7 @@ _NODISCARD shared_ptr<_Ty> #endif // _HAS_CXX20 make_shared(_Types&&... _Args) { // make a shared_ptr to non-array object - const auto _Rx = new _Ref_count_obj2<_Ty>(_STD forward<_Types>(_Args)...); + const auto _Rx = new _Ref_count_obj2<_Ty, _Init_form::_Value_init>(_STD forward<_Types>(_Args)...); shared_ptr<_Ty> _Ret; _Ret._Set_ptr_rep_and_enable_shared(_STD addressof(_Rx->_Storage._Value), _Rx); return _Ret; @@ -2093,7 +2171,7 @@ struct _Global_delete_guard { template _NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared(const size_t _Count) { // make a shared_ptr to an unbounded array - using _Refc = _Ref_count_unbounded_array<_Ty>; + using _Refc = _Ref_count_unbounded_array<_Ty, _Init_form::_Value_init>; const auto _Rx = _Allocate_flexible_array<_Refc>(_Count); _Global_delete_guard<_Refc> _Guard{_Rx}; ::new (static_cast(_Rx)) _Refc(_Count); @@ -2107,7 +2185,7 @@ template _NODISCARD enable_if_t, 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>; + using _Refc = _Ref_count_unbounded_array<_Ty, _Init_form::_Value_init>; const auto _Rx = _Allocate_flexible_array<_Refc>(_Count); _Global_delete_guard<_Refc> _Guard{_Rx}; ::new (static_cast(_Rx)) _Refc(_Count, _Val); @@ -2120,7 +2198,7 @@ _NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared( template _NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared() { // make a shared_ptr to a bounded array - const auto _Rx = new _Ref_count_bounded_array<_Ty>(); + const auto _Rx = new _Ref_count_bounded_array<_Ty, _Init_form::_Value_init>(); shared_ptr<_Ty> _Ret; _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Storage._Value, _Rx); return _Ret; @@ -2129,7 +2207,7 @@ _NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared() { template _NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared(const remove_extent_t<_Ty>& _Val) { // make a shared_ptr to a bounded array - const auto _Rx = new _Ref_count_bounded_array<_Ty>(_Val); + const auto _Rx = new _Ref_count_bounded_array<_Ty, _Init_form::_Value_init>(_Val); shared_ptr<_Ty> _Ret; _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Storage._Value, _Rx); return _Ret; @@ -2147,7 +2225,7 @@ _NODISCARD allocate_shared(const _Alloc& _Al, _Types&&... _Args) { // make a shared_ptr to non-array object // Note: As of 2019-05-28, this implements the proposed resolution of LWG-3210 (which controls whether // allocator::construct sees T or const T when _Ty is const qualified) - using _Refoa = _Ref_count_obj_alloc3, _Alloc>; + using _Refoa = _Ref_count_obj_alloc3, _Alloc, _Init_form::_Value_init>; using _Alblock = _Rebind_alloc_t<_Alloc, _Refoa>; _Alblock _Rebound(_Al); _Alloc_construct_ptr<_Alblock> _Constructor{_Rebound}; @@ -2182,7 +2260,7 @@ template _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared( const _Alloc& _Al, const size_t _Count) { // make a shared_ptr to an unbounded array - using _Refc = _Ref_count_unbounded_array_alloc, _Alloc>; + using _Refc = _Ref_count_unbounded_array_alloc, _Alloc, _Init_form::_Value_init>; constexpr size_t _Align = alignof(_Refc); using _Storage = _Alignas_storage_unit<_Align>; _Rebind_alloc_t<_Alloc, _Storage> _Rebound(_Al); @@ -2201,7 +2279,7 @@ template _NODISCARD enable_if_t, 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, _Alloc>; + using _Refc = _Ref_count_unbounded_array_alloc, _Alloc, _Init_form::_Value_init>; constexpr size_t _Align = alignof(_Refc); using _Storage = _Alignas_storage_unit<_Align>; _Rebind_alloc_t<_Alloc, _Storage> _Rebound(_Al); @@ -2219,7 +2297,7 @@ _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shar template _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared(const _Alloc& _Al) { // make a shared_ptr to a bounded array - using _Refc = _Ref_count_bounded_array_alloc, _Alloc>; + using _Refc = _Ref_count_bounded_array_alloc, _Alloc, _Init_form::_Value_init>; using _Alblock = _Rebind_alloc_t<_Alloc, _Refc>; _Alblock _Rebound(_Al); _Alloc_construct_ptr _Constructor{_Rebound}; @@ -2235,7 +2313,7 @@ template _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared( const _Alloc& _Al, const remove_extent_t<_Ty>& _Val) { // make a shared_ptr to a bounded array - using _Refc = _Ref_count_bounded_array_alloc, _Alloc>; + using _Refc = _Ref_count_bounded_array_alloc, _Alloc, _Init_form::_Value_init>; using _Alblock = _Rebind_alloc_t<_Alloc, _Refc>; _Alblock _Rebound(_Al); _Alloc_construct_ptr _Constructor{_Rebound}; @@ -2248,6 +2326,88 @@ _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared } #endif // _HAS_CXX20 +// Once more, should this be in a separate #ifdef _HAS_CXX20 block? +#ifdef _HAS_CXX20 +// FUNCTION TEMPLATE make_shared_for_overwrite +template +_NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared_for_overwrite() { + shared_ptr<_Ty> _Ret; + if constexpr (!is_array_v<_Ty>) { + // make a shared_ptr to non-array object + const auto _Rx = new _Ref_count_obj2<_Ty, _Init_form::_Default_init>(); + _Ret._Set_ptr_rep_and_enable_shared(_STD addressof(_Rx->_Storage._Value), _Rx); + } else { + // make a shared_ptr to a bounded array + const auto _Rx = new _Ref_count_bounded_array<_Ty, _Init_form::_Default_init>(); + _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Storage._Value, _Rx); + } + return _Ret; +} + + +template +_NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared_for_overwrite(size_t _Count) { + // make a shared_ptr to an unbounded array + using _Refc = _Ref_count_unbounded_array<_Ty, _Init_form::_Default_init>; + const auto _Rx = _Allocate_flexible_array<_Refc>(_Count); + _Global_delete_guard<_Refc> _Guard{_Rx}; + ::new (static_cast(_Rx)) _Refc(_Count); + _Guard._Target = nullptr; + shared_ptr<_Ty> _Ret; + _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Get_ptr(), _Rx); + return _Ret; +} + +// FUNCTION TEMPLATE allocate_shared_for_overwrite +template +_NODISCARD enable_if_t, 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 non-array object + using _Refoa = _Ref_count_obj_alloc3, _Alloc, _Init_form::_Default_init>; + using _Alblock = _Rebind_alloc_t<_Alloc, _Refoa>; + _Alblock _Rebound(_Al); + _Alloc_construct_ptr<_Alblock> _Constructor{_Rebound}; + _Constructor._Allocate(); + _Construct_in_place(*_Constructor._Ptr, _Al); + const auto _Ptr = reinterpret_cast<_Ty*>(_STD addressof(_Constructor._Ptr->_Storage._Value)); + _Ret._Set_ptr_rep_and_enable_shared(_Ptr, _Unfancy(_Constructor._Release())); + } else { + // make a shared_ptr to a bounded array + using _Refc = _Ref_count_bounded_array_alloc, _Alloc, _Init_form::_Default_init>; + using _Alblock = _Rebind_alloc_t<_Alloc, _Refc>; + _Alblock _Rebound(_Al); + _Alloc_construct_ptr _Constructor{_Rebound}; + _Constructor._Allocate(); + ::new (static_cast(_Unfancy(_Constructor._Ptr))) _Refc(_Al); + const auto _Ptr = static_cast*>(_Constructor._Ptr->_Storage._Value); + _Ret._Set_ptr_rep_and_enable_shared(_Ptr, _Unfancy(_Constructor._Release())); + } + + return _Ret; +} + +template +_NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared_for_overwrite( + const _Alloc& _Al, const size_t _Count) { + // make a shared_ptr to an unbounded array + using _Refc = _Ref_count_unbounded_array_alloc, _Alloc, _Init_form::_Default_init>; + 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(_Rx)) _Refc(_Al, _Count); + _Guard._Ptr = nullptr; + shared_ptr<_Ty> _Ret; + _Ret._Set_ptr_rep_and_enable_shared(_Rx->_Get_ptr(), _Rx); + return _Ret; +} + +#endif // _HAS_CXX20 + // CLASS TEMPLATE weak_ptr template class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource @@ -2707,6 +2867,21 @@ _NODISCARD unique_ptr<_Ty> make_unique(size_t _Size) { // make a unique_ptr template != 0, int> = 0> void make_unique(_Types&&...) = delete; +// FUNCTION TEMPLATE make_unique_for_overwrite +template , int> = 0> +_NODISCARD unique_ptr<_Ty> make_unique_for_overwrite() { // make a unique_ptr with default initialization + return unique_ptr<_Ty>(new _Ty); +} + +template && extent_v<_Ty> == 0, int> = 0> +_NODISCARD unique_ptr<_Ty> make_unique_for_overwrite(size_t _Size) { // make a unique_ptr with default initialization + using _Elem = remove_extent_t<_Ty>; + return unique_ptr<_Ty>(new _Elem[_Size]); +} + +template != 0, int> = 0> +void make_unique_for_overwrite(_Types&&...) = delete; + template ::value, int> = 0> void swap(unique_ptr<_Ty, _Dx>& _Left, unique_ptr<_Ty, _Dx>& _Right) noexcept { _Left.swap(_Right); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index aa6cf79497a..efa51b34c9b 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -230,6 +230,12 @@ void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_const _Ty(_STD forward<_Types>(_Args)...); } +// FUNCTION TEMPLATE _Construct_in_place_for_overwrite +template +void _Construct_in_place_for_overwrite(_Ty& _Obj) noexcept(is_nothrow_constructible_v<_Ty>) { + ::new (const_cast(static_cast(_STD addressof(_Obj)))) _Ty; +} + // FUNCTION TEMPLATE _Global_new template _Ty* _Global_new(_Types&&... _Args) { // acts as "new" while disallowing user overload selection diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 53ed31b30c8..0c99aa098e2 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1162,6 +1162,7 @@ #define __cpp_lib_math_constants 201907L #define __cpp_lib_remove_cvref 201711L #define __cpp_lib_shift 201806L +#define __cpp_lib_smart_ptr_for_overwrite 201811L #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 9cdaa15d150..8f63856e48c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -254,6 +254,7 @@ tests\P0898R3_identity tests\P0919R3_heterogeneous_unordered_lookup tests\P0966R1_string_reserve_should_not_shrink tests\P1023R0_constexpr_for_array_comparisons +tests\P1020R1_smart_pointer_for_overwrite tests\P1165R1_consistently_propagating_stateful_allocators tests\P1423R3_char8_t_remediation tests\P1645R1_constexpr_numeric 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..846ad3c36f4 --- /dev/null +++ b/tests/std/tests/P1020R1_smart_pointer_for_overwrite/test.cpp @@ -0,0 +1,336 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +int allocationCount = 0; +int canCreate = 10; // Counter to force an exception when constructing a + // sufficiently large ReportAddress array + +struct ReportAddress; +vector ascendingAddressBuffer; +vector descendingAddressBuffer; + +// 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, 0xEE, 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(align)); + ++allocationCount; + if (result) { + memset(result, 0xEE, size); + } + + return result; +} + +// Helper struct to check if type T is default initable without arguments. +template +struct unique_is_for_overwritable : false_type {}; + +template +struct unique_is_for_overwritable())>> : true_type {}; + +template +constexpr bool unique_is_for_overwritable_v = unique_is_for_overwritable::value; + +struct DefaultInitableInt { + int value; + DefaultInitableInt() : value(106) {} +}; + +struct alignas(32) HighlyAligned { + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; +}; + +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(p); + for (unsigned int offset = 0; offset < size; ++offset) { + assert(*(chPtr + offset) == 0xEE); + } +} + +template +void assert_shared_use_get(const shared_ptr& sp) { + assert(sp.use_count() == 1); + assert(sp.get() != nullptr); +} + +template +shared_ptr make_shared_for_overwrite_assert(Args&&... vals) { + int count = allocationCount; + shared_ptr sp = make_shared_for_overwrite(forward(vals)...); + assert_shared_use_get(sp); + assert(count + 1 == allocationCount); + return sp; +} + +template +void test_make_init_destruct_order(Args&&... vals) { + try { + shared_ptr sp = make_shared_for_overwrite(forward(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); + static_assert(!unique_is_for_overwritable_v); + + auto p0 = make_unique_for_overwrite(); + assert_uninitialized(addressof(*p0), sizeof(int)); + + auto p1 = make_unique_for_overwrite(100u); + assert_uninitialized(addressof(p1[0]), sizeof(int) * 100u); + + auto p2 = make_unique_for_overwrite(); + assert(p2->value == 106); + + auto p3 = make_unique_for_overwrite(2u); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 89; ++j) { + assert(p3[i][j].value == 106); + } + } + + auto p4 = make_unique_for_overwrite(0u); // p4 cannot be dereferenced +} + +void test_make_shared_for_overwrite() { + auto p0 = make_shared_for_overwrite_assert(); + assert_uninitialized(addressof(*p0), sizeof(int)); + + auto p1 = make_shared_for_overwrite_assert(); + assert(p1->value == 106); + + auto p2 = make_shared_for_overwrite_assert(); + assert(reinterpret_cast(p2.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(*p2), sizeof(HighlyAligned)); + + auto p3 = make_shared_for_overwrite_assert(); + assert_uninitialized(addressof(p3[0]), sizeof(int) * 100u); + + auto p4 = make_shared_for_overwrite_assert(); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 8; ++j) { + assert(p4[i][j].value == 106); + } + } + + auto p5 = make_shared_for_overwrite_assert(); + assert(reinterpret_cast(p5.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(p5[0]), sizeof(HighlyAligned) * 10u); + + auto p6 = make_shared_for_overwrite_assert(100u); + for (int i = 0; i < 100; ++i) { + assert(p6[i].value == 106); + } + + auto p7 = make_shared_for_overwrite_assert(2u); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 8; ++j) { + for (int k = 0; k < 9; ++k) { + assert(p7[i][j][k].value == 106); + } + } + } + + auto p8 = make_shared_for_overwrite_assert(100u); + assert_uninitialized(addressof(p8[0]), sizeof(int) * 100u); + + auto p9 = make_shared_for_overwrite_assert(0u); // p9 cannot be dereferenced + + auto p10 = make_shared_for_overwrite_assert(10u); + assert(reinterpret_cast(p10.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(p10[0]), sizeof(HighlyAligned) * 10u); + + test_make_init_destruct_order(); // success one dimensional + + test_make_init_destruct_order(); // failure one dimensional + + test_make_init_destruct_order(); // success multidimensional + + test_make_init_destruct_order(); // failure multidimensional + + test_make_init_destruct_order(5u); // success one dimensional + + test_make_init_destruct_order(20u); // failure one dimensional + + test_make_init_destruct_order(2u); // success multidimensional + + test_make_init_destruct_order(3u); // failure multidimensional +} + +template +shared_ptr allocate_shared_for_overwrite_assert(Args&&... vals) { + int aCount = allocationCount; + shared_ptr sp = allocate_shared_for_overwrite(forward(vals)...); + assert_shared_use_get(sp); + assert(aCount + 1 == allocationCount); + return sp; +} + +template +void test_allocate_init_destruct_order(Args&&... vals) { + allocator> a{}; + + try { + shared_ptr sp = allocate_shared_for_overwrite(a, forward(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 a0{}; + auto p0 = allocate_shared_for_overwrite_assert(a0); + assert_uninitialized(addressof(*p0), sizeof(int)); + + allocator a1{}; + auto p1 = allocate_shared_for_overwrite_assert(a1); + assert(p1->value == 106); + + allocator a2{}; + auto p2 = allocate_shared_for_overwrite_assert(a2); + assert_uninitialized(addressof(*p2), sizeof(HighlyAligned)); + + auto p3 = allocate_shared_for_overwrite_assert(a0); + assert_uninitialized(addressof(p3[0]), sizeof(int) * 100u); + + auto p4 = allocate_shared_for_overwrite_assert(a1); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 8; ++j) { + assert(p4[i][j].value == 106); + } + } + + auto p5 = allocate_shared_for_overwrite_assert(a2); + assert(reinterpret_cast(p5.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(p5[0]), sizeof(HighlyAligned) * 10u); + + auto p6 = allocate_shared_for_overwrite_assert(a1, 100u); + for (int i = 0; i < 100; ++i) { + assert(p6[i].value == 106); + } + + auto p7 = allocate_shared_for_overwrite_assert(a1, 2u); + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 8; ++j) { + for (int k = 0; k < 9; ++k) { + assert(p7[i][j][k].value == 106); + } + } + } + + auto p8 = allocate_shared_for_overwrite_assert(a0, 100u); + assert_uninitialized(addressof(p8[0]), sizeof(int) * 100u); + + auto p9 = allocate_shared_for_overwrite_assert(a0, 0u); // p9 cannot be dereferenced + + auto p10 = allocate_shared_for_overwrite_assert(a2, 10u); + assert(reinterpret_cast(p10.get()) % alignof(HighlyAligned) == 0); + assert_uninitialized(addressof(p10[0]), sizeof(HighlyAligned) * 10u); + + test_allocate_init_destruct_order(); // success one dimensional + + test_allocate_init_destruct_order(); // failure one dimensional + + test_allocate_init_destruct_order(); // success multidimensional + + test_allocate_init_destruct_order(); // failure multidimensional + + test_allocate_init_destruct_order(5u); // success one dimensional + + test_allocate_init_destruct_order(20u); // failure one dimensional + + test_allocate_init_destruct_order(2u); // success multidimensional + + test_allocate_init_destruct_order(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 57134316e3d..01cfaab4d96 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.cpp @@ -1001,6 +1001,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 != 201811L +#error __cpp_lib_smart_ptr_for_overwrite is not 201811L +#else +STATIC_ASSERT(__cpp_lib_smart_ptr_for_overwrite == 201811L); +#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