diff options
author | Hui <hui.xie0621@gmail.com> | 2023-01-03 18:51:34 +0000 |
---|---|---|
committer | Hui <hui.xie0621@gmail.com> | 2023-01-23 21:07:22 +0000 |
commit | 9af9d39a47dc634b7de17708a4daf226ca699751 (patch) | |
tree | 79a769de6e5ca172a93e664eb054e265d1165526 | |
parent | 38a9e1d41264b551f659a5d9215109a96e74cadd (diff) | |
download | llvm-9af9d39a47dc634b7de17708a4daf226ca699751.zip llvm-9af9d39a47dc634b7de17708a4daf226ca699751.tar.gz llvm-9af9d39a47dc634b7de17708a4daf226ca699751.tar.bz2 |
[libc++] implement P1020R1 P1973R1 make_unique[shared]_for_overwrite
Differential Revision: https://reviews.llvm.org/D140913
11 files changed, 666 insertions, 14 deletions
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst index c0e5542..345dd53 100644 --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -69,6 +69,7 @@ Implemented Papers - P1035R7 - Input Range Adaptors - P2325R3 - Views should not be required to be default constructible - P2446R2 - ``views::as_rvalue`` +- P1020 - Smart pointer creation with default initialization Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv index 48405b8..3999b01 100644 --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -67,7 +67,7 @@ "`P0972R0 <https://wg21.link/P0972R0>`__","LWG","<chrono> ``zero()``\ , ``min()``\ , and ``max()``\ should be noexcept","San Diego","|Complete|","8.0" "`P1006R1 <https://wg21.link/P1006R1>`__","LWG","Constexpr in std::pointer_traits","San Diego","|Complete|","8.0" "`P1007R3 <https://wg21.link/P1007R3>`__","LWG","``std::assume_aligned``\ ","San Diego","|Complete|","15.0" -"`P1020R1 <https://wg21.link/P1020R1>`__","LWG","Smart pointer creation with default initialization","San Diego","* *","" +"`P1020R1 <https://wg21.link/P1020R1>`__","LWG","Smart pointer creation with default initialization","San Diego","|Complete|","16.0" "`P1032R1 <https://wg21.link/P1032R1>`__","LWG","Misc constexpr bits","San Diego","|Complete|","13.0" "`P1085R2 <https://wg21.link/P1085R2>`__","LWG","Should Span be Regular?","San Diego","|Complete|","8.0" "`P1123R0 <https://wg21.link/P1123R0>`__","LWG","Editorial Guidance for merging P0019r8 and P0528r3","San Diego","* *","" @@ -177,7 +177,7 @@ "`P1963R0 <https://wg21.link/P1963R0>`__","LWG","Fixing US 313","Prague","* *","","" "`P1964R2 <https://wg21.link/P1964R2>`__","LWG","Wording for boolean-testable","Prague","|Complete|","13.0" "`P1970R2 <https://wg21.link/P1970R2>`__","LWG","Consistency for size() functions: Add ranges::ssize","Prague","|Complete|","15.0","|ranges|" -"`P1973R1 <https://wg21.link/P1973R1>`__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","* *","" +"`P1973R1 <https://wg21.link/P1973R1>`__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","|Complete|","16.0" "`P1976R2 <https://wg21.link/P1976R2>`__","LWG","Fixed-size span construction from dynamic range","Prague","|Complete|","11.0","|ranges|" "`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","* *","" "`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","* *","" diff --git a/libcxx/docs/Status/Cxx2b.rst b/libcxx/docs/Status/Cxx2b.rst index 50d9a02..e79ed28 100644 --- a/libcxx/docs/Status/Cxx2b.rst +++ b/libcxx/docs/Status/Cxx2b.rst @@ -39,7 +39,6 @@ Paper Status .. note:: - .. [#note-P2273] P2273: ``make_unique_for_overwrite`` isn't done yet since `P1020` hasn't been implemented yet. .. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented. .. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations. diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv index ce5e96d..ffc9bed 100644 --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -44,7 +44,7 @@ "`P1206R7 <https://wg21.link/P1206R7>`__","LWG","``ranges::to``: A function to convert any range to a container","February 2022","","","|ranges|" "`P1413R3 <https://wg21.link/P1413R3>`__","LWG","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","February 2022","|Complete| [#note-P1413R3]_","" "`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","","" -"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Partial| [#note-P2273]_","16.0" +"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0" "`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|" "`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|" "`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","","","|ranges|" diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h index a76e262..b77ce92 100644 --- a/libcxx/include/__memory/shared_ptr.h +++ b/libcxx/include/__memory/shared_ptr.h @@ -260,6 +260,8 @@ __shared_ptr_pointer<_Tp, _Dp, _Alloc>::__on_zero_shared_weak() _NOEXCEPT __a.deallocate(_PTraits::pointer_to(*this), 1); } +struct __default_initialize_tag {}; + template <class _Tp, class _Alloc> struct __shared_ptr_emplace : __shared_weak_count @@ -278,6 +280,16 @@ struct __shared_ptr_emplace #endif } + +#if _LIBCPP_STD_VER >= 20 + _LIBCPP_HIDE_FROM_ABI + explicit __shared_ptr_emplace(__default_initialize_tag, _Alloc __a) + : __storage_(std::move(__a)) + { + ::new ((void*)__get_elem()) _Tp; + } +#endif + _LIBCPP_HIDE_FROM_ABI _Alloc* __get_alloc() _NOEXCEPT { return __storage_.__get_alloc(); } @@ -945,6 +957,29 @@ shared_ptr<_Tp> make_shared(_Args&& ...__args) return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...); } +#if _LIBCPP_STD_VER >= 20 + +template<class _Tp, class _Alloc, __enable_if_t<!is_array<_Tp>::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) +{ + using _ControlBlock = __shared_ptr_emplace<_Tp, _Alloc>; + using _ControlBlockAllocator = typename __allocator_traits_rebind<_Alloc, _ControlBlock>::type; + __allocation_guard<_ControlBlockAllocator> __guard(__a, 1); + ::new ((void*)_VSTD::addressof(*__guard.__get())) _ControlBlock(__default_initialize_tag{}, __a); + auto __control_block = __guard.__release_ptr(); + return shared_ptr<_Tp>::__create_with_control_block((*__control_block).__get_elem(), _VSTD::addressof(*__control_block)); +} + +template<class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> make_shared_for_overwrite() +{ + return std::allocate_shared_for_overwrite<_Tp>(allocator<_Tp>()); +} + +#endif // _LIBCPP_STD_VER >= 20 + #if _LIBCPP_STD_VER > 14 template <size_t _Alignment> @@ -975,6 +1010,17 @@ struct __unbounded_array_control_block<_Tp[], _Alloc> : __shared_weak_count std::__uninitialized_allocator_value_construct_n(__alloc_, std::begin(__data_), __count_); } +#if _LIBCPP_STD_VER >= 20 + _LIBCPP_HIDE_FROM_ABI + explicit __unbounded_array_control_block(_Alloc const& __alloc, size_t __count, __default_initialize_tag) + : __alloc_(__alloc), __count_(__count) + { + // We are purposefully not using an allocator-aware default construction because the spec says so. + // There's currently no way of expressing default initialization in an allocator-aware manner anyway. + std::uninitialized_default_construct_n(std::begin(__data_), __count_); + } +#endif + // Returns the number of bytes required to store a control block followed by the given number // of elements of _Tp, with the whole storage being aligned to a multiple of _Tp's alignment. _LIBCPP_HIDE_FROM_ABI @@ -1058,6 +1104,15 @@ struct __bounded_array_control_block<_Tp[_Count], _Alloc> std::__uninitialized_allocator_value_construct_n(__alloc_, std::addressof(__data_[0]), _Count); } +#if _LIBCPP_STD_VER >= 20 + _LIBCPP_HIDE_FROM_ABI + explicit __bounded_array_control_block(_Alloc const& __alloc, __default_initialize_tag) : __alloc_(__alloc) { + // We are purposefully not using an allocator-aware default construction because the spec says so. + // There's currently no way of expressing default initialization in an allocator-aware manner anyway. + std::uninitialized_default_construct_n(std::addressof(__data_[0]), _Count); + } +#endif + _LIBCPP_HIDE_FROM_ABI_VIRTUAL ~__bounded_array_control_block() override { } // can't be `= default` because of the sometimes-non-trivial union member __data_ @@ -1101,6 +1156,7 @@ shared_ptr<_Array> __allocate_shared_bounded_array(const _Alloc& __a, _Arg&& ... #if _LIBCPP_STD_VER > 17 +// bounded array variants template<class _Tp, class _Alloc, class = __enable_if_t<is_bounded_array<_Tp>::value>> _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a) @@ -1115,18 +1171,11 @@ shared_ptr<_Tp> allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& _ return std::__allocate_shared_bounded_array<_Tp>(__a, __u); } -template<class _Tp, class _Alloc, class = __enable_if_t<is_unbounded_array<_Tp>::value>> -_LIBCPP_HIDE_FROM_ABI -shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) -{ - return std::__allocate_shared_unbounded_array<_Tp>(__a, __n); -} - -template<class _Tp, class _Alloc, class = __enable_if_t<is_unbounded_array<_Tp>::value>> +template<class _Tp, class _Alloc, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0> _LIBCPP_HIDE_FROM_ABI -shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u) +shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a) { - return std::__allocate_shared_unbounded_array<_Tp>(__a, __n, __u); + return std::__allocate_shared_bounded_array<_Tp>(__a, __default_initialize_tag{}); } template<class _Tp, class = __enable_if_t<is_bounded_array<_Tp>::value>> @@ -1143,6 +1192,35 @@ shared_ptr<_Tp> make_shared(const remove_extent_t<_Tp>& __u) return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>(), __u); } +template<class _Tp, __enable_if_t<is_bounded_array<_Tp>::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> make_shared_for_overwrite() +{ + return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>(), __default_initialize_tag{}); +} + +// unbounded array variants +template<class _Tp, class _Alloc, class = __enable_if_t<is_unbounded_array<_Tp>::value>> +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n) +{ + return std::__allocate_shared_unbounded_array<_Tp>(__a, __n); +} + +template<class _Tp, class _Alloc, class = __enable_if_t<is_unbounded_array<_Tp>::value>> +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared(const _Alloc& __a, size_t __n, const remove_extent_t<_Tp>& __u) +{ + return std::__allocate_shared_unbounded_array<_Tp>(__a, __n, __u); +} + +template<class _Tp, class _Alloc, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc& __a, size_t __n) +{ + return std::__allocate_shared_unbounded_array<_Tp>(__a, __n, __default_initialize_tag{}); +} + template<class _Tp, class = __enable_if_t<is_unbounded_array<_Tp>::value>> _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n) @@ -1157,6 +1235,13 @@ shared_ptr<_Tp> make_shared(size_t __n, const remove_extent_t<_Tp>& __u) return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n, __u); } +template<class _Tp, __enable_if_t<is_unbounded_array<_Tp>::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI +shared_ptr<_Tp> make_shared_for_overwrite(size_t __n) +{ + return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n, __default_initialize_tag{}); +} + #endif // _LIBCPP_STD_VER > 17 template<class _Tp, class _Up> diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h index bb5399d..9cdbda8 100644 --- a/libcxx/include/__memory/unique_ptr.h +++ b/libcxx/include/__memory/unique_ptr.h @@ -699,6 +699,25 @@ template<class _Tp, class... _Args> #endif // _LIBCPP_STD_VER > 11 +#if _LIBCPP_STD_VER >= 20 + +template <class _Tp> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_single +make_unique_for_overwrite() { + return unique_ptr<_Tp>(new _Tp); +} + +template <class _Tp> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound +make_unique_for_overwrite(size_t __n) { + return unique_ptr<_Tp>(new __remove_extent_t<_Tp>[__n]); +} + +template<class _Tp, class... _Args> +typename __unique_if<_Tp>::__unique_array_known_bound make_unique_for_overwrite(_Args&&...) = delete; + +#endif // _LIBCPP_STD_VER >= 20 + template <class _Tp> struct _LIBCPP_TEMPLATE_VIS hash; template <class _Tp, class _Dp> diff --git a/libcxx/include/memory b/libcxx/include/memory index 48e808e..0a7787a 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -569,6 +569,13 @@ template<class T> constexpr unique_ptr<T> make_unique(size_t n); // C++14, constexpr since C++23 template<class T, class... Args> unspecified make_unique(Args&&...) = delete; // C++14, T == U[N] +template<class T> + constexpr unique_ptr<T> make_unique_for_overwrite(); // T is not array, C++20, constexpr since C++23 +template<class T> + constexpr unique_ptr<T> make_unique_for_overwrite(size_t n); // T is U[], C++20, constexpr since C++23 +template<class T, class... Args> + unspecified make_unique_for_overwrite(Args&&...) = delete; // T is U[N], C++20 + template<class E, class T, class Y, class D> basic_ostream<E, T>& operator<< (basic_ostream<E, T>& os, unique_ptr<Y, D> const& p); @@ -718,6 +725,16 @@ template<class T, class A> shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& u); // T is U[N] (since C++20) template<class T> + shared_ptr<T> make_shared_for_overwrite(); // T is not U[], C++20 +template<class T, class A> + shared_ptr<T> allocate_shared_for_overwrite(const A& a); // T is not U[], C++20 + +template<class T> + shared_ptr<T> make_shared_for_overwrite(size_t N); // T is U[], C++20 +template<class T, class A> + shared_ptr<T> allocate_shared_for_overwrite(const A& a, size_t N); // T is U[], C++20 + +template<class T> class weak_ptr { public: diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_for_overwrite.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_for_overwrite.pass.cpp new file mode 100644 index 0000000..e69adee --- /dev/null +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_for_overwrite.pass.cpp @@ -0,0 +1,190 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// template<class T, class A> +// shared_ptr<T> make_shared_for_overwrite(const A& a); // T is not U[] +// +// template<class T, class A> +// shared_ptr<T> make_shared_for_overwrite(const A& a, size_t N); // T is U[] + +#include <cassert> +#include <concepts> +#include <cstring> +#include <memory> +#include <utility> + +#include "min_allocator.h" +#include "test_allocator.h" +#include "test_macros.h" + +template <class T, class... Args> +concept HasAllocateSharedForOverwrite = + requires(Args&&... args) { std::allocate_shared_for_overwrite<T>(std::forward<Args>(args)...); }; + +struct Foo { + int i; +}; + +// non array +static_assert(!HasAllocateSharedForOverwrite<int>); +static_assert(!HasAllocateSharedForOverwrite<Foo>); +static_assert(HasAllocateSharedForOverwrite<int, bare_allocator<void>>); +static_assert(HasAllocateSharedForOverwrite<Foo, bare_allocator<void>>); +static_assert(!HasAllocateSharedForOverwrite<int, bare_allocator<void>, std::size_t>); +static_assert(!HasAllocateSharedForOverwrite<Foo, bare_allocator<void>, std::size_t>); + +// bounded array +static_assert(!HasAllocateSharedForOverwrite<int[2]>); +static_assert(!HasAllocateSharedForOverwrite<Foo[2]>); +static_assert(HasAllocateSharedForOverwrite<int[2], bare_allocator<void>>); +static_assert(HasAllocateSharedForOverwrite<Foo[2], bare_allocator<void>>); +static_assert(!HasAllocateSharedForOverwrite<int[2], bare_allocator<void>, std::size_t>); +static_assert(!HasAllocateSharedForOverwrite<Foo[2], bare_allocator<void>, std::size_t>); + +// unbounded array +static_assert(!HasAllocateSharedForOverwrite<int[]>); +static_assert(!HasAllocateSharedForOverwrite<Foo[]>); +static_assert(!HasAllocateSharedForOverwrite<int[], bare_allocator<void>>); +static_assert(!HasAllocateSharedForOverwrite<Foo[], bare_allocator<void>>); +static_assert(HasAllocateSharedForOverwrite<int[], bare_allocator<void>, std::size_t>); +static_assert(HasAllocateSharedForOverwrite<Foo[], bare_allocator<void>, std::size_t>); + +struct WithDefaultCtor { + int i; + WithDefaultCtor() : i(42) {} +}; + +template <class Alloc> +void testDefaultConstructor() { + // single + { + std::same_as<std::shared_ptr<WithDefaultCtor>> auto ptr = + std::allocate_shared_for_overwrite<WithDefaultCtor>(Alloc{}); + assert(ptr->i == 42); + } + + // bounded array + { + std::same_as<std::shared_ptr<WithDefaultCtor[2]>> auto ptr = + std::allocate_shared_for_overwrite<WithDefaultCtor[2]>(Alloc{}); + assert(ptr[0].i == 42); + assert(ptr[1].i == 42); + } + + // unbounded array + { + std::same_as<std::shared_ptr<WithDefaultCtor[]>> auto ptr = + std::allocate_shared_for_overwrite<WithDefaultCtor[]>(Alloc{}, 3); + assert(ptr[0].i == 42); + assert(ptr[1].i == 42); + assert(ptr[2].i == 42); + } +} + +void testTypeWithDefaultCtor() { + testDefaultConstructor<test_allocator<WithDefaultCtor>>(); + testDefaultConstructor<min_allocator<WithDefaultCtor>>(); + testDefaultConstructor<bare_allocator<WithDefaultCtor>>(); +} + +void testAllocatorOperationsCalled() { + // single + { + test_allocator_statistics alloc_stats; + { + [[maybe_unused]] std::same_as<std::shared_ptr<int>> auto ptr = + std::allocate_shared_for_overwrite<int>(test_allocator<void>{&alloc_stats}); + assert(alloc_stats.alloc_count == 1); + } + assert(alloc_stats.alloc_count == 0); + } + + // bounded array + { + test_allocator_statistics alloc_stats; + { + [[maybe_unused]] std::same_as<std::shared_ptr<int[2]>> auto ptr = + std::allocate_shared_for_overwrite<int[2]>(test_allocator<void>{&alloc_stats}); + assert(alloc_stats.alloc_count == 1); + } + assert(alloc_stats.alloc_count == 0); + } + + // unbounded array + { + test_allocator_statistics alloc_stats; + { + [[maybe_unused]] std::same_as<std::shared_ptr<int[]>> auto ptr = + std::allocate_shared_for_overwrite<int[]>(test_allocator<void>{&alloc_stats}, 3); + assert(alloc_stats.alloc_count == 1); + } + assert(alloc_stats.alloc_count == 0); + } +} + +template <class T> +struct AllocatorWithPattern { + constexpr static char pattern = 0xDE; + + using value_type = T; + + AllocatorWithPattern() = default; + + template <class U> + AllocatorWithPattern(AllocatorWithPattern<U>) noexcept {} + + T* allocate(std::size_t n) { + void* ptr = ::operator new(n * sizeof(T)); + for (std::size_t i = 0; i < n * sizeof(T); ++i) { + *(reinterpret_cast<char*>(ptr) + i) = pattern; + } + return static_cast<T*>(ptr); + } + + void deallocate(T* p, std::size_t) { return ::operator delete(static_cast<void*>(p)); } +}; + +void testNotValueInitialized() { + // single int + { + std::same_as<std::shared_ptr<int>> decltype(auto) ptr = + std::allocate_shared_for_overwrite<int>(AllocatorWithPattern<int>{}); + assert(*(reinterpret_cast<char*>(ptr.get())) == AllocatorWithPattern<int>::pattern); + } + + // bounded array int[N] + { + std::same_as<std::shared_ptr<int[2]>> decltype(auto) ptr = + std::allocate_shared_for_overwrite<int[2]>(AllocatorWithPattern<int>{}); + assert(*(reinterpret_cast<char*>(&ptr[0])) == AllocatorWithPattern<int>::pattern); + assert(*(reinterpret_cast<char*>(&ptr[1])) == AllocatorWithPattern<int>::pattern); + } + + // unbounded array int[] + { + std::same_as<std::shared_ptr<int[]>> decltype(auto) ptr = + std::allocate_shared_for_overwrite<int[]>(AllocatorWithPattern<int>{}, 3); + assert(*(reinterpret_cast<char*>(&ptr[0])) == AllocatorWithPattern<int>::pattern); + assert(*(reinterpret_cast<char*>(&ptr[1])) == AllocatorWithPattern<int>::pattern); + assert(*(reinterpret_cast<char*>(&ptr[2])) == AllocatorWithPattern<int>::pattern); + } +} + +void test() { + testTypeWithDefaultCtor(); + testAllocatorOperationsCalled(); + testNotValueInitialized(); +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared_for_overwrite.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared_for_overwrite.pass.cpp new file mode 100644 index 0000000..690a334 --- /dev/null +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared_for_overwrite.pass.cpp @@ -0,0 +1,133 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: sanitizer-new-delete + +// template<class T> +// shared_ptr<T> make_shared_for_overwrite(); // T is not U[] +// +// template<class T> +// shared_ptr<T> make_shared_for_overwrite(size_t N); // T is U[] + +#include <cassert> +#include <concepts> +#include <cstring> +#include <memory> +#include <stdlib.h> +#include <utility> + +#include "test_macros.h" + +template <class T, class... Args> +concept HasMakeSharedForOverwrite = + requires(Args&&... args) { std::make_shared_for_overwrite<T>(std::forward<Args>(args)...); }; + +struct Foo { + int i; +}; + +// non array +static_assert(HasMakeSharedForOverwrite<int>); +static_assert(HasMakeSharedForOverwrite<Foo>); +static_assert(!HasMakeSharedForOverwrite<int, int>); +static_assert(!HasMakeSharedForOverwrite<Foo, Foo>); + +// bounded array +static_assert(HasMakeSharedForOverwrite<int[2]>); +static_assert(!HasMakeSharedForOverwrite<int[2], size_t>); +static_assert(!HasMakeSharedForOverwrite<int[2], int>); +static_assert(!HasMakeSharedForOverwrite<int[2], int, int>); +static_assert(HasMakeSharedForOverwrite<Foo[2]>); +static_assert(!HasMakeSharedForOverwrite<Foo[2], size_t>); +static_assert(!HasMakeSharedForOverwrite<Foo[2], int>); +static_assert(!HasMakeSharedForOverwrite<Foo[2], int, int>); + +// unbounded array +static_assert(HasMakeSharedForOverwrite<int[], size_t>); +static_assert(HasMakeSharedForOverwrite<Foo[], size_t>); +static_assert(!HasMakeSharedForOverwrite<int[]>); +static_assert(!HasMakeSharedForOverwrite<Foo[]>); +static_assert(!HasMakeSharedForOverwrite<int[], size_t, int>); +static_assert(!HasMakeSharedForOverwrite<Foo[], size_t, int>); + +constexpr char pattern = 0xDE; + +void* operator new(std::size_t count) { + void* ptr = malloc(count); + for (std::size_t i = 0; i < count; ++i) { + *(reinterpret_cast<char*>(ptr) + i) = pattern; + } + return ptr; +} + +void* operator new[](std::size_t count) { return ::operator new(count); } + +void operator delete(void* ptr) noexcept { free(ptr); } + +void operator delete[](void* ptr) noexcept { ::operator delete(ptr); } + +struct WithDefaultConstructor { + int i; + constexpr WithDefaultConstructor() : i(5) {} +}; + +bool test() { + // single int + { + std::same_as<std::shared_ptr<int>> decltype(auto) ptr = std::make_shared_for_overwrite<int>(); + assert(*(reinterpret_cast<char*>(ptr.get())) == pattern); + } + + // bounded array int[N] + { + std::same_as<std::shared_ptr<int[2]>> decltype(auto) ptr = std::make_shared_for_overwrite<int[2]>(); + assert(*(reinterpret_cast<char*>(&ptr[0])) == pattern); + assert(*(reinterpret_cast<char*>(&ptr[1])) == pattern); + } + + // unbounded array int[] + { + std::same_as<std::shared_ptr<int[]>> decltype(auto) ptr = std::make_shared_for_overwrite<int[]>(3); + assert(*(reinterpret_cast<char*>(&ptr[0])) == pattern); + assert(*(reinterpret_cast<char*>(&ptr[1])) == pattern); + assert(*(reinterpret_cast<char*>(&ptr[2])) == pattern); + } + + // single with default constructor + { + std::same_as<std::shared_ptr<WithDefaultConstructor>> decltype(auto) ptr = + std::make_shared_for_overwrite<WithDefaultConstructor>(); + assert(ptr->i == 5); + } + + // bounded array with default constructor + { + std::same_as<std::shared_ptr<WithDefaultConstructor[2]>> decltype(auto) ptr = + std::make_shared_for_overwrite<WithDefaultConstructor[2]>(); + assert(ptr[0].i == 5); + assert(ptr[1].i == 5); + } + + // unbounded array with default constructor + { + std::same_as<std::shared_ptr<WithDefaultConstructor[]>> decltype(auto) ptrs = + std::make_shared_for_overwrite<WithDefaultConstructor[]>(3); + assert(ptrs[0].i == 5); + assert(ptrs[1].i == 5); + assert(ptrs[2].i == 5); + } + + return true; +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.default_init.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.default_init.pass.cpp new file mode 100644 index 0000000..5f4fbd4 --- /dev/null +++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.default_init.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: sanitizer-new-delete + +// template<class T> +// constexpr unique_ptr<T> make_unique_for_overwrite(); // T is not array +// +// template<class T> +// constexpr unique_ptr<T> make_unique_for_overwrite(size_t n); // T is U[] + +// Test the object is not value initialized + +#include <cassert> +#include <concepts> +#include <cstddef> +#include <memory> +#include <stdlib.h> + +constexpr char pattern = 0xDE; + +void* operator new(std::size_t count) { + void* ptr = malloc(count); + for (std::size_t i = 0; i < count; ++i) { + *(reinterpret_cast<char*>(ptr) + i) = pattern; + } + return ptr; +} + +void* operator new[](std::size_t count) { return ::operator new(count); } + +void operator delete(void* ptr) noexcept { free(ptr); } + +void operator delete[](void* ptr) noexcept { ::operator delete(ptr); } + +void test() { + { + std::same_as<std::unique_ptr<int>> auto ptr = std::make_unique_for_overwrite<int>(); + assert(*(reinterpret_cast<char*>(ptr.get())) == pattern); + } + { + std::same_as<std::unique_ptr<int[]>> auto ptr = std::make_unique_for_overwrite<int[]>(3); + assert(*(reinterpret_cast<char*>(&ptr[0])) == pattern); + assert(*(reinterpret_cast<char*>(&ptr[1])) == pattern); + assert(*(reinterpret_cast<char*>(&ptr[2])) == pattern); + } +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.pass.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.pass.cpp new file mode 100644 index 0000000..3f86904 --- /dev/null +++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.pass.cpp @@ -0,0 +1,149 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// template<class T> +// constexpr unique_ptr<T> make_unique_for_overwrite(); // T is not array +// +// template<class T> +// constexpr unique_ptr<T> make_unique_for_overwrite(size_t n); // T is U[] +// +// template<class T, class... Args> +// unspecified make_unique_for_overwrite(Args&&...) = delete; // T is U[N] + +#include <cassert> +#include <concepts> +#include <cstring> +#include <memory> +#include <utility> + +#include "test_macros.h" + +template <class T, class... Args> +concept HasMakeUniqueForOverwrite = + requires(Args&&... args) { std::make_unique_for_overwrite<T>(std::forward<Args>(args)...); }; + +struct Foo { + int i; +}; + +// template<class T> +// constexpr unique_ptr<T> make_unique_for_overwrite(); +static_assert(HasMakeUniqueForOverwrite<int>); +static_assert(HasMakeUniqueForOverwrite<Foo>); +static_assert(!HasMakeUniqueForOverwrite<int, int>); +static_assert(!HasMakeUniqueForOverwrite<Foo, Foo>); + +// template<class T> +// constexpr unique_ptr<T> make_unique_for_overwrite(size_t n); +static_assert(HasMakeUniqueForOverwrite<int[], size_t>); +static_assert(HasMakeUniqueForOverwrite<Foo[], size_t>); +static_assert(!HasMakeUniqueForOverwrite<int[]>); +static_assert(!HasMakeUniqueForOverwrite<Foo[]>); +static_assert(!HasMakeUniqueForOverwrite<int[], size_t, int>); +static_assert(!HasMakeUniqueForOverwrite<Foo[], size_t, int>); + +// template<class T, class... Args> +// unspecified make_unique_for_overwrite(Args&&...) = delete; +static_assert(!HasMakeUniqueForOverwrite<int[2]>); +static_assert(!HasMakeUniqueForOverwrite<int[2], size_t>); +static_assert(!HasMakeUniqueForOverwrite<int[2], int>); +static_assert(!HasMakeUniqueForOverwrite<int[2], int, int>); +static_assert(!HasMakeUniqueForOverwrite<Foo[2]>); +static_assert(!HasMakeUniqueForOverwrite<Foo[2], size_t>); +static_assert(!HasMakeUniqueForOverwrite<Foo[2], int>); +static_assert(!HasMakeUniqueForOverwrite<Foo[2], int, int>); + +struct WithDefaultConstructor { + int i; + constexpr WithDefaultConstructor() : i(5) {} +}; + +TEST_CONSTEXPR_CXX23 bool test() { + // single int + { + std::same_as<std::unique_ptr<int>> decltype(auto) ptr = std::make_unique_for_overwrite<int>(); + // memory is available for write, otherwise constexpr test would fail + *ptr = 5; + } + + // unbounded array int[] + { + std::same_as<std::unique_ptr<int[]>> decltype(auto) ptrs = std::make_unique_for_overwrite<int[]>(3); + + // memory is available for write, otherwise constexpr test would fail + ptrs[0] = 3; + ptrs[1] = 4; + ptrs[2] = 5; + } + + // single with default constructor + { + std::same_as<std::unique_ptr<WithDefaultConstructor>> decltype(auto) ptr = + std::make_unique_for_overwrite<WithDefaultConstructor>(); + assert(ptr->i == 5); + } + + // unbounded array with default constructor + { + std::same_as<std::unique_ptr<WithDefaultConstructor[]>> decltype(auto) ptrs = + std::make_unique_for_overwrite<WithDefaultConstructor[]>(3); + assert(ptrs[0].i == 5); + assert(ptrs[1].i == 5); + assert(ptrs[2].i == 5); + } + + return true; +} + +// The standard specifically says to use `new (p) T`, which means that we should pick up any +// custom in-class operator new if there is one. +struct WithCustomNew { + inline static bool customNewCalled = false; + inline static bool customNewArrCalled = false; + + static void* operator new(size_t n) { + customNewCalled = true; + return ::operator new(n); + ; + } + + static void* operator new[](size_t n) { + customNewArrCalled = true; + return ::operator new[](n); + } +}; + +void testCustomNew() { + // single with custom operator new + { + [[maybe_unused]] std::same_as<std::unique_ptr<WithCustomNew>> decltype(auto) ptr = + std::make_unique_for_overwrite<WithCustomNew>(); + + assert(WithCustomNew::customNewCalled); + } + + // unbounded array with custom operator new + { + [[maybe_unused]] std::same_as<std::unique_ptr<WithCustomNew[]>> decltype(auto) ptr = + std::make_unique_for_overwrite<WithCustomNew[]>(3); + + assert(WithCustomNew::customNewArrCalled); + } +} + +int main(int, char**) { + test(); + testCustomNew(); +#if TEST_STD_VER >= 23 + static_assert(test()); +#endif + + return 0; +} |