diff --git a/README.md b/README.md index 70e3e9041e7..72576a20bee 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem The STL uses boost-math headers to provide P0226R1 Mathematical Special Functions. We recommend using [vcpkg][] to acquire this dependency. -1. Install Visual Studio 2019 16.8 Preview 3 or later. +1. Install Visual Studio 2019 16.8 Preview 4 or later. * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. This will ensure that you're using supported versions of CMake and Ninja. * Otherwise, install [CMake][] 3.17 or later, and [Ninja][] 1.8.2 or later. @@ -158,7 +158,7 @@ acquire this dependency. # How To Build With A Native Tools Command Prompt -1. Install Visual Studio 2019 16.8 Preview 3 or later. +1. Install Visual Studio 2019 16.8 Preview 4 or later. * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. This will ensure that you're using supported versions of CMake and Ninja. * Otherwise, install [CMake][] 3.17 or later, and [Ninja][] 1.8.2 or later. @@ -235,7 +235,7 @@ C:\Users\username\Desktop>dumpbin /IMPORTS .\example.exe | findstr msvcp 1. Follow either [How To Build With A Native Tools Command Prompt][] or [How To Build With The Visual Studio IDE][]. 2. Invoke `git submodule update --init llvm-project` at the root of the STL source tree. -3. Acquire [Python][] 3.8 or newer and have it on the `PATH` (or run it directly using its absolute or relative path). +3. Acquire [Python][] 3.9 or newer and have it on the `PATH` (or run it directly using its absolute or relative path). 4. Have LLVM's `bin` directory on the `PATH` (so `clang-cl.exe` is available). * We recommend selecting "C++ Clang tools for Windows" in the VS Installer. This will automatically add LLVM to the `PATH` of the x86 and x64 Native Tools Command Prompts, and will ensure that you're using a supported version. diff --git a/azure-devops/provision-image.ps1 b/azure-devops/provision-image.ps1 index fb09a22eeea..ea84e185ab1 100644 --- a/azure-devops/provision-image.ps1 +++ b/azure-devops/provision-image.ps1 @@ -97,7 +97,7 @@ $Workloads = @( $ReleaseInPath = 'Preview' $Sku = 'Enterprise' $VisualStudioBootstrapperUrl = 'https://aka.ms/vs/16/pre/vs_enterprise.exe' -$PythonUrl = 'https://www.python.org/ftp/python/3.8.5/python-3.8.5-amd64.exe' +$PythonUrl = 'https://www.python.org/ftp/python/3.9.0/python-3.9.0-amd64.exe' $CudaUrl = ` 'https://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda_10.1.243_426.00_win10.exe' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 49c9a55da32..bc4ce5d12c0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,7 +6,7 @@ variables: tmpDir: 'D:\Temp' -pool: 'StlBuild-2020-09-14' +pool: 'StlBuild-2020-10-13' stages: - stage: Code_Format diff --git a/stl/inc/condition_variable b/stl/inc/condition_variable index 1289183537c..7acd1e47a4e 100644 --- a/stl/inc/condition_variable +++ b/stl/inc/condition_variable @@ -130,7 +130,7 @@ public: template bool wait_for(_Lock& _Lck, const chrono::duration<_Rep, _Period>& _Rel_time, _Predicate _Pred) { // wait for signal with timeout and check predicate - return wait_until(_Lck, chrono::steady_clock::now() + _Rel_time, _STD move(_Pred)); + return wait_until(_Lck, _To_absolute_time(_Rel_time), _STD move(_Pred)); } template @@ -232,7 +232,7 @@ public: template bool wait_for(_Lock& _Lck, stop_token _Stoken, const chrono::duration<_Rep, _Period>& _Rel_time, _Predicate _Pred) { - return wait_until(_Lck, _STD move(_Stoken), chrono::steady_clock::now() + _Rel_time, _STD move(_Pred)); + return wait_until(_Lck, _STD move(_Stoken), _To_absolute_time(_Rel_time), _STD move(_Pred)); } #endif // _HAS_CXX20 diff --git a/stl/inc/experimental/generator b/stl/inc/experimental/generator index e19a80142d3..6a13039c5dd 100644 --- a/stl/inc/experimental/generator +++ b/stl/inc/experimental/generator @@ -22,7 +22,6 @@ #error requires /std:c++latest or /await compiler options #endif // ^^^ no coroutine support ^^^ - #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -33,18 +32,16 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN namespace experimental { - - template > + // NOTE WELL: _CPPUNWIND currently affects the ABI of generator. + template > struct generator { struct promise_type { - _Ty const* _CurrentValue; -#if 1 // TRANSITION, VSO-1172852 -#ifdef _CPPUNWIND - exception_ptr _Eptr; -#endif // _CPPUNWIND + const _Ty* _Value; +#ifdef _CPPUNWIND // TRANSITION, VSO-1172852 + exception_ptr _Exception; #endif // TRANSITION, VSO-1172852 - auto get_return_object() { + generator get_return_object() noexcept { return generator{*this}; } @@ -56,38 +53,38 @@ namespace experimental { return {}; } -#if defined(_CPPUNWIND) || defined(__cpp_impl_coroutine) - void unhandled_exception() noexcept { -#if 1 // TRANSITION, VSO-1172852 +#ifndef _KERNEL_MODE #ifdef _CPPUNWIND - _Eptr = _STD current_exception(); -#else // ^^^ _CPPUNWIND / !_CPPUNWIND vvv - abort(); -#endif // _CPPUNWIND +#if 1 // TRANSITION, VSO-1172852 + void unhandled_exception() noexcept { + _Exception = _STD current_exception(); + } #else // ^^^ workaround / no workaround vvv + void unhandled_exception() { throw; -#endif // TRANSITION, VSO-1172852 } -#endif // defined(_CPPUNWIND) || defined(__cpp_impl_coroutine) +#endif // TRANSITION, VSO-1172852 +#else // ^^^ defined(_CPPUNWIND) / !defined(_CPPUNWIND) vvv + void unhandled_exception() noexcept {} +#endif // _CPPUNWIND +#endif // _KERNEL_MODE -#if 1 // TRANSITION, VSO-1172852 +#ifdef _CPPUNWIND // TRANSITION, VSO-1172852 void _Rethrow_if_exception() { -#ifdef _CPPUNWIND - if (_Eptr) { - _STD rethrow_exception(_Eptr); + if (_Exception) { + _STD rethrow_exception(_Exception); } -#endif // _CPPUNWIND } #endif // TRANSITION, VSO-1172852 - auto yield_value(_Ty const& _Value) { - _CurrentValue = _STD addressof(_Value); - return suspend_always{}; + suspend_always yield_value(const _Ty& _Val) noexcept { + _Value = _STD addressof(_Val); + return {}; } - void return_void() {} + void return_void() noexcept {} - template + template _Uty&& await_transform(_Uty&& _Whatever) { static_assert(_Always_false<_Uty>, "co_await is not supported in coroutines of type std::experimental::generator"); @@ -98,15 +95,16 @@ namespace experimental { static_assert(is_same_v::pointer>, "generator does not support allocators with fancy pointer types"); static_assert( - allocator_traits<_Alloc_char>::is_always_equal::value, "generator only supports stateless allocators"); + allocator_traits<_Alloc_char>::is_always_equal::value && is_default_constructible_v<_Alloc_char>, + "generator supports only stateless allocators"); static void* operator new(size_t _Size) { - _Alloc_char _Al; + _Alloc_char _Al{}; return allocator_traits<_Alloc_char>::allocate(_Al, _Size); } static void operator delete(void* _Ptr, size_t _Size) noexcept { - _Alloc_char _Al; + _Alloc_char _Al{}; return allocator_traits<_Alloc_char>::deallocate(_Al, static_cast(_Ptr), _Size); } }; @@ -115,21 +113,19 @@ namespace experimental { using iterator_category = input_iterator_tag; using difference_type = ptrdiff_t; using value_type = _Ty; - using reference = _Ty const&; - using pointer = _Ty const*; + using reference = const _Ty&; + using pointer = const _Ty*; coroutine_handle _Coro = nullptr; iterator() = default; - iterator(nullptr_t) : _Coro(nullptr) {} - - iterator(coroutine_handle _CoroArg) : _Coro(_CoroArg) {} + explicit iterator(coroutine_handle _Coro_) noexcept : _Coro(_Coro_) {} iterator& operator++() { _Coro.resume(); if (_Coro.done()) { -#if 1 // TRANSITION, VSO-1172852 - _STD exchange(_Coro, {}).promise()._Rethrow_if_exception(); +#ifdef _CPPUNWIND // TRANSITION, VSO-1172852 + _STD exchange(_Coro, nullptr).promise()._Rethrow_if_exception(); #else // ^^^ workaround / no workaround vvv _Coro = nullptr; #endif // TRANSITION, VSO-1172852 @@ -139,25 +135,25 @@ namespace experimental { } void operator++(int) { - // This postincrement operator meets the requirements of the Ranges TS - // InputIterator concept, but not those of Standard C++ InputIterator. + // This operator meets the requirements of the C++20 input_iterator concept, + // but not the Cpp17InputIterator requirements. ++*this; } - _NODISCARD bool operator==(iterator const& _Right) const { + _NODISCARD bool operator==(const iterator& _Right) const noexcept { return _Coro == _Right._Coro; } - _NODISCARD bool operator!=(iterator const& _Right) const { + _NODISCARD bool operator!=(const iterator& _Right) const noexcept { return !(*this == _Right); } - _NODISCARD reference operator*() const { - return *_Coro.promise()._CurrentValue; + _NODISCARD reference operator*() const noexcept { + return *_Coro.promise()._Value; } - _NODISCARD pointer operator->() const { - return _Coro.promise()._CurrentValue; + _NODISCARD pointer operator->() const noexcept { + return _Coro.promise()._Value; } }; @@ -165,35 +161,28 @@ namespace experimental { if (_Coro) { _Coro.resume(); if (_Coro.done()) { -#if 1 // TRANSITION, VSO-1172852 +#ifdef _CPPUNWIND // TRANSITION, VSO-1172852 _Coro.promise()._Rethrow_if_exception(); #endif // TRANSITION, VSO-1172852 - return {nullptr}; + return {}; } } - return {_Coro}; + return iterator{_Coro}; } - _NODISCARD iterator end() { - return {nullptr}; + _NODISCARD iterator end() noexcept { + return {}; } - explicit generator(promise_type& _Prom) : _Coro(coroutine_handle::from_promise(_Prom)) {} + explicit generator(promise_type& _Prom) noexcept : _Coro(coroutine_handle::from_promise(_Prom)) {} - generator() = default; - generator(generator const&) = delete; - generator& operator=(generator const&) = delete; + generator() = default; - generator(generator&& _Right) : _Coro(_Right._Coro) { - _Right._Coro = nullptr; - } + generator(generator&& _Right) noexcept : _Coro(_STD exchange(_Right._Coro, nullptr)) {} - generator& operator=(generator&& _Right) { - if (this != _STD addressof(_Right)) { - _Coro = _Right._Coro; - _Right._Coro = nullptr; - } + generator& operator=(generator&& _Right) noexcept { + _Coro = _STD exchange(_Right._Coro, nullptr); return *this; } @@ -206,7 +195,6 @@ namespace experimental { private: coroutine_handle _Coro = nullptr; }; - } // namespace experimental _STD_END diff --git a/stl/inc/memory b/stl/inc/memory index eafeefd38b9..d168f49e698 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 -_NODISCARD void* _Voidify_iter(_Iter _It) noexcept { - return const_cast(static_cast(_STD addressof(*_It))); -} - template 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()); @@ -858,19 +853,11 @@ namespace ranges { using _Ty = remove_reference_t>; if constexpr (is_trivially_default_constructible_v<_Ty>) { _RANGES advance(_First, _Count); - } else if constexpr (is_nothrow_default_constructible_v<_Ty>) { - auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); - - for (; _Count > 0; --_Count, (void) ++_UFirst) { - ::new (_Voidify_iter(_UFirst)) _Ty; - } - - _Seek_wrapped(_First, _STD move(_UFirst)); } else { _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 +1895,6 @@ private: template friend enable_if_t, shared_ptr<_Ty0>> allocate_shared(const _Alloc& _Al_arg, _Types&&... _Args); - template - friend enable_if_t, shared_ptr<_Ty0>> make_shared(size_t _Count); - - template - friend enable_if_t, shared_ptr<_Ty0>> allocate_shared( - const _Alloc& _Al_arg, size_t _Count); - template friend enable_if_t, shared_ptr<_Ty0>> make_shared(); @@ -1922,19 +1902,25 @@ private: friend enable_if_t, shared_ptr<_Ty0>> allocate_shared(const _Alloc& _Al_arg); template - friend enable_if_t, shared_ptr<_Ty0>> make_shared( - size_t _Count, const remove_extent_t<_Ty0>& _Val); + friend enable_if_t, shared_ptr<_Ty0>> make_shared(const remove_extent_t<_Ty0>& _Val); template - friend enable_if_t, shared_ptr<_Ty0>> allocate_shared( - const _Alloc& _Al_arg, size_t _Count, const remove_extent_t<_Ty0>& _Val); + friend enable_if_t, shared_ptr<_Ty0>> allocate_shared( + const _Alloc& _Al_arg, const remove_extent_t<_Ty0>& _Val); template - friend enable_if_t, shared_ptr<_Ty0>> make_shared(const remove_extent_t<_Ty0>& _Val); + friend enable_if_t, shared_ptr<_Ty0>> make_shared_for_overwrite(); template - friend enable_if_t, shared_ptr<_Ty0>> allocate_shared( - const _Alloc& _Al_arg, const remove_extent_t<_Ty0>& _Val); + friend enable_if_t, shared_ptr<_Ty0>> allocate_shared_for_overwrite( + const _Alloc& _Al_arg); + + template + friend shared_ptr<_Ty0> _Make_shared_unbounded_array(size_t _Count, const _ArgTypes&... _Args); + + template + friend shared_ptr<_Ty0> _Allocate_shared_unbounded_array( + const _Alloc& _Al, size_t _Count, const _ArgTypes&... _Args); #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template friend shared_ptr<_Ty0> make_shared(_Types&&... _Args); @@ -2163,13 +2149,27 @@ template _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 _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 _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 +2282,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 +2356,25 @@ void _Uninitialized_value_construct_multidimensional_n(_Ty* const _Out, const si } } +template +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 void _Uninitialized_fill_multidimensional_n(_Ty* const _Out, const size_t _Size, const _Ty& _Val) { if constexpr (is_array_v<_Ty>) { @@ -2383,8 +2407,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 + 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 +2453,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 + 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 +2498,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 + 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 +2581,16 @@ 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 _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 +2758,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 + 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 +2824,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 + 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 +2890,29 @@ struct _Global_delete_guard { } }; -template -_NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared(const size_t _Count) { +template +_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(_Rx)) _Refc(_Count); + ::new (static_cast(_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 +_NODISCARD enable_if_t, shared_ptr<_Ty>> make_shared(const size_t _Count) { + return _Make_shared_unbounded_array<_Ty>(_Count); +} + 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>; - const auto _Rx = _Allocate_flexible_array<_Refc>(_Count); - _Global_delete_guard<_Refc> _Guard{_Rx}; - ::new (static_cast(_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 @@ -2878,6 +2932,27 @@ _NODISCARD enable_if_t, 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 +_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 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 +_NODISCARD enable_if_t, 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 +2997,11 @@ struct _Allocate_n_ptr { _Allocate_n_ptr& operator=(const _Allocate_n_ptr&) = delete; }; -template -_NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared( - const _Alloc& _Al, const size_t _Count) { +template +_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, _Alloc>; constexpr size_t _Align = alignof(_Refc); using _Storage = _Alignas_storage_unit<_Align>; @@ -2934,30 +3010,23 @@ _NODISCARD enable_if_t, 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(_Rx)) _Refc(_Al, _Count); + ::new (static_cast(_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 +_NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared( + const _Alloc& _Al, const size_t _Count) { + return _Allocate_shared_unbounded_array<_Ty>(_Al, _Count); +} + 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>; - 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, _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 @@ -2990,6 +3059,41 @@ _NODISCARD enable_if_t, 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 +_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 a bounded array + using _Refc = _Ref_count_bounded_array_alloc, _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*>(_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, _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 +_NODISCARD enable_if_t, 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 +3546,7 @@ _NODISCARD unique_ptr<_Ty> make_unique(_Types&&... _Args) { // make a unique_ptr } template && 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 +3554,24 @@ _NODISCARD unique_ptr<_Ty> make_unique(size_t _Size) { // make a unique_ptr template != 0, int> = 0> void make_unique(_Types&&...) = delete; +#if _HAS_CXX20 +// 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 , 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 , int> = 0> +void make_unique_for_overwrite(_Types&&...) = delete; +#endif // _HAS_CXX20 + 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/mutex b/stl/inc/mutex index 1811ccc021b..501fa02c01f 100644 --- a/stl/inc/mutex +++ b/stl/inc/mutex @@ -628,7 +628,7 @@ public: template bool wait_for(unique_lock& _Lck, const chrono::duration<_Rep, _Period>& _Rel_time, _Predicate _Pred) { // wait for signal with timeout and check predicate - return _Wait_until1(_Lck, chrono::steady_clock::now() + _Rel_time, _Pred); + return _Wait_until1(_Lck, _To_absolute_time(_Rel_time), _Pred); } template @@ -776,7 +776,7 @@ public: template _NODISCARD bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time) { // try to lock for duration - return try_lock_until(chrono::steady_clock::now() + _Rel_time); + return try_lock_until(_To_absolute_time(_Rel_time)); } template @@ -875,7 +875,7 @@ public: template _NODISCARD bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time) { // try to lock for duration - return try_lock_until(chrono::steady_clock::now() + _Rel_time); + return try_lock_until(_To_absolute_time(_Rel_time)); } template diff --git a/stl/inc/optional b/stl/inc/optional index e852cda7b38..04e36c0c10b 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -338,22 +338,40 @@ public: // observers [optional.object.observe] _NODISCARD constexpr const _Ty* operator->() const { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _STD addressof(this->_Value); } _NODISCARD constexpr _Ty* operator->() { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _STD addressof(this->_Value); } _NODISCARD constexpr const _Ty& operator*() const& { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return this->_Value; } _NODISCARD constexpr _Ty& operator*() & { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return this->_Value; } _NODISCARD constexpr _Ty&& operator*() && { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _STD move(this->_Value); } _NODISCARD constexpr const _Ty&& operator*() const&& { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(this->_Has_value, "Cannot access value of empty optional"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _STD move(this->_Value); } diff --git a/stl/inc/shared_mutex b/stl/inc/shared_mutex index 705b1d30f18..06c921d8e76 100644 --- a/stl/inc/shared_mutex +++ b/stl/inc/shared_mutex @@ -110,7 +110,7 @@ public: template _NODISCARD bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time) { // try to lock for duration - return try_lock_until(chrono::steady_clock::now() + _Rel_time); + return try_lock_until(_To_absolute_time(_Rel_time)); } template @@ -168,7 +168,7 @@ public: template _NODISCARD bool try_lock_shared_for( const chrono::duration<_Rep, _Period>& _Rel_time) { // try to lock non-exclusive for relative time - return try_lock_shared_until(_Rel_time + chrono::steady_clock::now()); + return try_lock_shared_until(_To_absolute_time(_Rel_time)); } template diff --git a/stl/inc/thread b/stl/inc/thread index 20d6ee84272..583cdcf6631 100644 --- a/stl/inc/thread +++ b/stl/inc/thread @@ -156,6 +156,22 @@ private: _Thrd_t _Thr; }; +template +_NODISCARD auto _To_absolute_time(const chrono::duration<_Rep, _Period>& _Rel_time) noexcept { + constexpr auto _Zero = chrono::duration<_Rep, _Period>::zero(); + const auto _Now = chrono::steady_clock::now(); + decltype(_Now + _Rel_time) _Abs_time = _Now; // return common type + if (_Rel_time > _Zero) { + constexpr auto _Forever = (chrono::steady_clock::time_point::max)(); + if (_Abs_time < _Forever - _Rel_time) { + _Abs_time += _Rel_time; + } else { + _Abs_time = _Forever; + } + } + return _Abs_time; +} + namespace this_thread { _NODISCARD thread::id get_id() noexcept; @@ -183,7 +199,7 @@ namespace this_thread { template void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time) { - sleep_until(chrono::steady_clock::now() + _Rel_time); + sleep_until(_To_absolute_time(_Rel_time)); } } // namespace this_thread 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; }; +// FUNCTION TEMPLATE _Voidify_iter +template +_NODISCARD void* _Voidify_iter(_Iter _It) noexcept { +#if _HAS_IF_CONSTEXPR + if constexpr (is_pointer_v<_Iter>) { + return const_cast(static_cast(_It)); + } else +#endif // _HAS_IF_CONSTEXPR + { + return const_cast(static_cast(_STD addressof(*_It))); + } +} + // FUNCTION TEMPLATE _Construct_in_place template void _Construct_in_place(_Ty& _Obj, _Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { - ::new (const_cast(static_cast(_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 +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::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/CMakeLists.txt b/tests/CMakeLists.txt index b8059d68cd6..1103c9dd3bc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,7 +18,7 @@ add_subdirectory(tr1) # chance to add to the config map and test directory global properties. add_subdirectory(utils/stl-lit) -find_package(Python "3.8" REQUIRED COMPONENTS Interpreter) +find_package(Python "3.9" REQUIRED COMPONENTS Interpreter) if(NOT DEFINED LIT_FLAGS) list(APPEND LIT_FLAGS "-o" "${CMAKE_CURRENT_BINARY_DIR}/test_results.json") diff --git a/tests/std/include/test_death.hpp b/tests/std/include/test_death.hpp index 801f813bcb1..943b0f28a02 100644 --- a/tests/std/include/test_death.hpp +++ b/tests/std/include/test_death.hpp @@ -12,7 +12,6 @@ #include namespace std_testing { - constexpr int internal_failure = 103; using normal_function_t = void (*)(); using death_function_t = void (*)(); @@ -23,7 +22,6 @@ namespace std_testing { abort(); } - class death_test_executive { const normal_function_t run_normal_tests; std::vector death_tests; @@ -98,10 +96,11 @@ namespace std_testing { } public: + death_test_executive() : run_normal_tests(nullptr) {} + explicit death_test_executive(const normal_function_t normal_tests_function) : run_normal_tests(normal_tests_function) {} - template void add_death_tests(const death_function_t (&tests)[TestsCount]) { death_tests.insert(death_tests.end(), tests, std::end(tests)); @@ -111,7 +110,9 @@ namespace std_testing { if (argc == 1) { // first pass, run normal tests and sub-process loop printf("running normal tests..."); - run_normal_tests(); + if (run_normal_tests != nullptr) { + run_normal_tests(); + } puts(" passed!"); ::SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); diff --git a/tests/std/test.lst b/tests/std/test.lst index 3855902a344..2ac454e53c6 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -215,6 +215,7 @@ tests\P0202R3_constexpr_algorithm_and_exchange tests\P0218R1_filesystem tests\P0220R1_any tests\P0220R1_optional +tests\P0220R1_optional_death tests\P0220R1_polymorphic_memory_resources tests\P0220R1_sample tests\P0220R1_searchers @@ -353,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/P0220R1_optional_death/env.lst b/tests/std/tests/P0220R1_optional_death/env.lst new file mode 100644 index 00000000000..46a3592c2e6 --- /dev/null +++ b/tests/std/tests/P0220R1_optional_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_17_winsdk_matrix.lst diff --git a/tests/std/tests/P0220R1_optional_death/test.cpp b/tests/std/tests/P0220R1_optional_death/test.cpp new file mode 100644 index 00000000000..bdc284f0028 --- /dev/null +++ b/tests/std/tests/P0220R1_optional_death/test.cpp @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include + +#include + +using namespace std; + +struct S { + int value; +}; + +void test_nullopt_operator_arrow() { + optional o; + (void) o->value; +} + +void test_nullopt_operator_arrow_const() { + const optional o; + (void) o->value; +} + +void test_nullopt_operator_star_lvalue() { + optional o; + (void) *o; +} + +void test_nullopt_operator_star_const_lvalue() { + const optional o; + (void) *o; +} + +void test_nullopt_operator_star_rvalue() { + optional o; + (void) *move(o); +} + +void test_nullopt_operator_star_const_rvalue() { + const optional o; + (void) *move(o); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + + exec.add_death_tests({ + test_nullopt_operator_arrow, + test_nullopt_operator_arrow_const, + test_nullopt_operator_star_lvalue, + test_nullopt_operator_star_const_lvalue, + test_nullopt_operator_star_rvalue, + test_nullopt_operator_star_const_rvalue, + }); + + return exec.run(argc, argv); +} diff --git a/tests/std/tests/P0660R10_jthread_and_cv_any/test.cpp b/tests/std/tests/P0660R10_jthread_and_cv_any/test.cpp index 1495227aacd..dfe79d60dde 100644 --- a/tests/std/tests/P0660R10_jthread_and_cv_any/test.cpp +++ b/tests/std/tests/P0660R10_jthread_and_cv_any/test.cpp @@ -180,7 +180,7 @@ int main() { jthread worker([&](stop_token token) { unique_lock lck{m}; assert(cv.wait(lck, move(token), [] { return true; }) == true); - assert(cv.wait(lck, move(token), [&] { return b; }) == true); + assert(cv.wait(lck, move(token), [&] { return b; }) == true); // Intentionally uses moved-from token }); { @@ -198,7 +198,8 @@ int main() { jthread worker([&](stop_token token) { unique_lock lck{m}; assert(cv.wait_until(lck, move(token), infinity, [] { return true; }) == true); - assert(cv.wait_until(lck, move(token), infinity, [&] { return b; }) == true); + assert(cv.wait_until(lck, move(token), infinity, [&] { return b; }) + == true); // Intentionally uses moved-from token }); { @@ -216,7 +217,8 @@ int main() { jthread worker([&](stop_token token) { unique_lock lck{m}; assert(cv.wait_for(lck, move(token), forever, [] { return true; }) == true); - assert(cv.wait_for(lck, move(token), forever, [&] { return b; }) == true); + assert(cv.wait_for(lck, move(token), forever, [&] { return b; }) + == true); // Intentionally uses moved-from token }); { diff --git a/tests/std/tests/P0660R10_stop_token_death/test.cpp b/tests/std/tests/P0660R10_stop_token_death/test.cpp index e9075218b7a..ef74ee52326 100644 --- a/tests/std/tests/P0660R10_stop_token_death/test.cpp +++ b/tests/std/tests/P0660R10_stop_token_death/test.cpp @@ -36,7 +36,7 @@ void test_case_throw_during_request_stop() { } int main(int argc, char* argv[]) { - std_testing::death_test_executive exec([] {}); + std_testing::death_test_executive exec; exec.add_death_tests({ test_case_throw_during_request_stop, diff --git a/tests/std/tests/P0896R4_common_iterator_death/test.cpp b/tests/std/tests/P0896R4_common_iterator_death/test.cpp index 131b1fdf6cf..9409bcfb9a2 100644 --- a/tests/std/tests/P0896R4_common_iterator_death/test.cpp +++ b/tests/std/tests/P0896R4_common_iterator_death/test.cpp @@ -156,7 +156,7 @@ void test_case_iter_swap_sentinel_right_valueless() { } int main(int argc, char* argv[]) { - std_testing::death_test_executive exec([] {}); + std_testing::death_test_executive exec; #if _ITERATOR_DEBUG_LEVEL != 0 exec.add_death_tests({ diff --git a/tests/std/tests/P0896R4_counted_iterator_death/test.cpp b/tests/std/tests/P0896R4_counted_iterator_death/test.cpp index 3c0157fddef..523490ee19f 100644 --- a/tests/std/tests/P0896R4_counted_iterator_death/test.cpp +++ b/tests/std/tests/P0896R4_counted_iterator_death/test.cpp @@ -206,7 +206,7 @@ void test_case_operator_spaceship_incompatible_value_initialized() { } int main(int argc, char* argv[]) { - std_testing::death_test_executive exec([] {}); + std_testing::death_test_executive exec; #if _ITERATOR_DEBUG_LEVEL != 0 exec.add_death_tests({ diff --git a/tests/std/tests/P0896R4_views_filter_death/test.cpp b/tests/std/tests/P0896R4_views_filter_death/test.cpp index 01c4017ca98..d784bedca19 100644 --- a/tests/std/tests/P0896R4_views_filter_death/test.cpp +++ b/tests/std/tests/P0896R4_views_filter_death/test.cpp @@ -131,7 +131,7 @@ void test_iter_swap_value_initialized_iterator_right() { } int main(int argc, char* argv[]) { - std_testing::death_test_executive exec([] {}); + std_testing::death_test_executive exec; #if _ITERATOR_DEBUG_LEVEL != 0 exec.add_death_tests({ diff --git a/tests/std/tests/P0896R4_views_transform_death/test.cpp b/tests/std/tests/P0896R4_views_transform_death/test.cpp index 5dfa72da1b3..edb51eb5a62 100644 --- a/tests/std/tests/P0896R4_views_transform_death/test.cpp +++ b/tests/std/tests/P0896R4_views_transform_death/test.cpp @@ -334,7 +334,7 @@ void test_flipped_sentinel_difference_value_initialized() { } int main(int argc, char* argv[]) { - std_testing::death_test_executive exec([] {}); + std_testing::death_test_executive exec; #if _ITERATOR_DEBUG_LEVEL != 0 exec.add_death_tests({ 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +constexpr int uninitializedValue = 0xEE; +constexpr int initializedValue = 106; +size_t allocationCount = 0; + +struct ReportAddress; +vector ascendingAddressBuffer; +vector 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(align)); + ++allocationCount; + if (result) { + memset(result, uninitializedValue, size); + } + + return result; +} + +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 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(p); + for (size_t offset = 0; offset < size; ++offset) { + assert(chPtr[offset] == uninitializedValue); + } +} + +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) { + size_t aCount = allocationCount; + shared_ptr sp = make_shared_for_overwrite(forward(vals)...); + assert_shared_use_get(sp); + assert(aCount + 1 == allocationCount); + return sp; +} + +template +void test_make_shared_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(p0.get(), sizeof(int)); + + auto p1 = make_unique_for_overwrite(100u); + assert_uninitialized(p1.get(), sizeof(int) * 100u); + + auto p2 = make_unique_for_overwrite(); + assert(p2->value == initializedValue); + + auto p3 = make_unique_for_overwrite(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(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 == initializedValue); + + 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 (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(); + 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 (ptrdiff_t i = 0; i < 100; ++i) { + assert(p6[i].value == initializedValue); + } + + auto p7 = make_shared_for_overwrite_assert(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(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_shared_init_destruct_order(); // success one dimensional + + test_make_shared_init_destruct_order(); // failure one dimensional + + test_make_shared_init_destruct_order(); // success multidimensional + + test_make_shared_init_destruct_order(); // failure multidimensional + + test_make_shared_init_destruct_order(5u); // success one dimensional + + test_make_shared_init_destruct_order(20u); // failure one dimensional + + test_make_shared_init_destruct_order(2u); // success multidimensional + + test_make_shared_init_destruct_order(3u); // failure multidimensional +} + +template +shared_ptr allocate_shared_for_overwrite_assert(Args&&... vals) { + size_t 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_shared_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 == initializedValue); + + allocator a2{}; + auto p2 = allocate_shared_for_overwrite_assert(a2); + assert(reinterpret_cast(p2.get()) % alignof(HighlyAligned) == 0); + 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 (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(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 (ptrdiff_t i = 0; i < 100; ++i) { + assert(p6[i].value == initializedValue); + } + + auto p7 = allocate_shared_for_overwrite_assert(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(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_shared_init_destruct_order(); // success one dimensional + + test_allocate_shared_init_destruct_order(); // failure one dimensional + + test_allocate_shared_init_destruct_order(); // success multidimensional + + test_allocate_shared_init_destruct_order(); // failure multidimensional + + test_allocate_shared_init_destruct_order(5u); // success one dimensional + + test_allocate_shared_init_destruct_order(20u); // failure one dimensional + + test_allocate_shared_init_destruct_order(2u); // success multidimensional + + test_allocate_shared_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 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