diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -69,6 +69,7 @@ - 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 --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -67,7 +67,7 @@ "`P0972R0 `__","LWG"," ``zero()``\ , ``min()``\ , and ``max()``\ should be noexcept","San Diego","|Complete|","8.0" "`P1006R1 `__","LWG","Constexpr in std::pointer_traits","San Diego","|Complete|","8.0" "`P1007R3 `__","LWG","``std::assume_aligned``\ ","San Diego","|Complete|","15.0" -"`P1020R1 `__","LWG","Smart pointer creation with default initialization","San Diego","* *","" +"`P1020R1 `__","LWG","Smart pointer creation with default initialization","San Diego","|Complete|","16.0" "`P1032R1 `__","LWG","Misc constexpr bits","San Diego","|Complete|","13.0" "`P1085R2 `__","LWG","Should Span be Regular?","San Diego","|Complete|","8.0" "`P1123R0 `__","LWG","Editorial Guidance for merging P0019r8 and P0528r3","San Diego","* *","" @@ -177,7 +177,7 @@ "`P1963R0 `__","LWG","Fixing US 313","Prague","* *","","" "`P1964R2 `__","LWG","Wording for boolean-testable","Prague","|Complete|","13.0" "`P1970R2 `__","LWG","Consistency for size() functions: Add ranges::ssize","Prague","|Complete|","15.0","|ranges|" -"`P1973R1 `__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","* *","" +"`P1973R1 `__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","|Complete|","16.0" "`P1976R2 `__","LWG","Fixed-size span construction from dynamic range","Prague","|Complete|","11.0","|ranges|" "`P1981R0 `__","LWG","Rename leap to leap_second","Prague","* *","" "`P1982R0 `__","LWG","Rename link to time_zone_link","Prague","* *","" diff --git a/libcxx/docs/Status/Cxx2b.rst b/libcxx/docs/Status/Cxx2b.rst --- a/libcxx/docs/Status/Cxx2b.rst +++ b/libcxx/docs/Status/Cxx2b.rst @@ -39,7 +39,6 @@ .. 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 --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -44,7 +44,7 @@ "`P1206R7 `__","LWG","``ranges::to``: A function to convert any range to a container","February 2022","","","|ranges|" "`P1413R3 `__","LWG","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","February 2022","|Complete| [#note-P1413R3]_","" "`P2255R2 `__","LWG","A type trait to detect reference binding to temporary","February 2022","","" -"`P2273R3 `__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Partial| [#note-P2273]_","16.0" +"`P2273R3 `__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0" "`P2387R3 `__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|" "`P2440R1 `__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|" "`P2441R2 `__","LWG","``views::join_with``","February 2022","","","|ranges|" diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h --- a/libcxx/include/__memory/shared_ptr.h +++ b/libcxx/include/__memory/shared_ptr.h @@ -260,6 +260,8 @@ __a.deallocate(_PTraits::pointer_to(*this), 1); } +struct __default_initialize_tag {}; + template struct __shared_ptr_emplace : __shared_weak_count @@ -278,6 +280,16 @@ #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 @@ return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...); } +#if _LIBCPP_STD_VER >= 20 + +template::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::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 @@ -975,6 +1010,17 @@ 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 @@ 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 @@ #if _LIBCPP_STD_VER > 17 +// bounded array variants template::value>> _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a) @@ -1115,18 +1171,11 @@ return std::__allocate_shared_bounded_array<_Tp>(__a, __u); } -template::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::value>> +template::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::value>> @@ -1143,6 +1192,35 @@ return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>(), __u); } +template::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::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::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::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::value>> _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(size_t __n) @@ -1157,6 +1235,13 @@ return std::__allocate_shared_unbounded_array<_Tp>(allocator<_Tp>(), __n, __u); } +template::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 diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h --- a/libcxx/include/__memory/unique_ptr.h +++ b/libcxx/include/__memory/unique_ptr.h @@ -699,6 +699,25 @@ #endif // _LIBCPP_STD_VER > 11 +#if _LIBCPP_STD_VER >= 20 + +template +_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 +_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 +typename __unique_if<_Tp>::__unique_array_known_bound make_unique_for_overwrite(_Args&&...) = delete; + +#endif // _LIBCPP_STD_VER >= 20 + template struct _LIBCPP_TEMPLATE_VIS hash; template diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -569,6 +569,13 @@ constexpr unique_ptr make_unique(size_t n); // C++14, constexpr since C++23 template unspecified make_unique(Args&&...) = delete; // C++14, T == U[N] +template + constexpr unique_ptr make_unique_for_overwrite(); // T is not array, C++20, constexpr since C++23 +template + constexpr unique_ptr make_unique_for_overwrite(size_t n); // T is U[], C++20, constexpr since C++23 +template + unspecified make_unique_for_overwrite(Args&&...) = delete; // T is U[N], C++20 + template basic_ostream& operator<< (basic_ostream& os, unique_ptr const& p); @@ -717,6 +724,16 @@ template shared_ptr allocate_shared(const A& a, const remove_extent_t& u); // T is U[N] (since C++20) +template + shared_ptr make_shared_for_overwrite(); // T is not U[], C++20 +template + shared_ptr allocate_shared_for_overwrite(const A& a); // T is not U[], C++20 + +template + shared_ptr make_shared_for_overwrite(size_t N); // T is U[], C++20 +template + shared_ptr allocate_shared_for_overwrite(const A& a, size_t N); // T is U[], C++20 + template class weak_ptr { 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 --- /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 +// shared_ptr make_shared_for_overwrite(const A& a); // T is not U[] +// +// template +// shared_ptr make_shared_for_overwrite(const A& a, size_t N); // T is U[] + +#include +#include +#include +#include +#include + +#include "min_allocator.h" +#include "test_allocator.h" +#include "test_macros.h" + +template +concept HasAllocateSharedForOverwrite = + requires(Args&&... args) { std::allocate_shared_for_overwrite(std::forward(args)...); }; + +struct Foo { + int i; +}; + +// non array +static_assert(!HasAllocateSharedForOverwrite); +static_assert(!HasAllocateSharedForOverwrite); +static_assert(HasAllocateSharedForOverwrite>); +static_assert(HasAllocateSharedForOverwrite>); +static_assert(!HasAllocateSharedForOverwrite, std::size_t>); +static_assert(!HasAllocateSharedForOverwrite, std::size_t>); + +// bounded array +static_assert(!HasAllocateSharedForOverwrite); +static_assert(!HasAllocateSharedForOverwrite); +static_assert(HasAllocateSharedForOverwrite>); +static_assert(HasAllocateSharedForOverwrite>); +static_assert(!HasAllocateSharedForOverwrite, std::size_t>); +static_assert(!HasAllocateSharedForOverwrite, std::size_t>); + +// unbounded array +static_assert(!HasAllocateSharedForOverwrite); +static_assert(!HasAllocateSharedForOverwrite); +static_assert(!HasAllocateSharedForOverwrite>); +static_assert(!HasAllocateSharedForOverwrite>); +static_assert(HasAllocateSharedForOverwrite, std::size_t>); +static_assert(HasAllocateSharedForOverwrite, std::size_t>); + +struct WithDefaultCtor { + int i; + WithDefaultCtor() : i(42) {} +}; + +template +void testDefaultConstructor() { + // single + { + std::same_as> auto ptr = + std::allocate_shared_for_overwrite(Alloc{}); + assert(ptr->i == 42); + } + + // bounded array + { + std::same_as> auto ptr = + std::allocate_shared_for_overwrite(Alloc{}); + assert(ptr[0].i == 42); + assert(ptr[1].i == 42); + } + + // unbounded array + { + std::same_as> auto ptr = + std::allocate_shared_for_overwrite(Alloc{}, 3); + assert(ptr[0].i == 42); + assert(ptr[1].i == 42); + assert(ptr[2].i == 42); + } +} + +void testTypeWithDefaultCtor() { + testDefaultConstructor>(); + testDefaultConstructor>(); + testDefaultConstructor>(); +} + +void testAllocatorOperationsCalled() { + // single + { + test_allocator_statistics alloc_stats; + { + [[maybe_unused]] std::same_as> auto ptr = + std::allocate_shared_for_overwrite(test_allocator{&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> auto ptr = + std::allocate_shared_for_overwrite(test_allocator{&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> auto ptr = + std::allocate_shared_for_overwrite(test_allocator{&alloc_stats}, 3); + assert(alloc_stats.alloc_count == 1); + } + assert(alloc_stats.alloc_count == 0); + } +} + +template +struct AllocatorWithPattern { + constexpr static char pattern = 0xDE; + + using value_type = T; + + AllocatorWithPattern() = default; + + template + AllocatorWithPattern(AllocatorWithPattern) 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(ptr) + i) = pattern; + } + return static_cast(ptr); + } + + void deallocate(T* p, std::size_t) { return ::operator delete(static_cast(p)); } +}; + +void testNotValueInitialized() { + // single int + { + std::same_as> decltype(auto) ptr = + std::allocate_shared_for_overwrite(AllocatorWithPattern{}); + assert(*(reinterpret_cast(ptr.get())) == AllocatorWithPattern::pattern); + } + + // bounded array int[N] + { + std::same_as> decltype(auto) ptr = + std::allocate_shared_for_overwrite(AllocatorWithPattern{}); + assert(*(reinterpret_cast(&ptr[0])) == AllocatorWithPattern::pattern); + assert(*(reinterpret_cast(&ptr[1])) == AllocatorWithPattern::pattern); + } + + // unbounded array int[] + { + std::same_as> decltype(auto) ptr = + std::allocate_shared_for_overwrite(AllocatorWithPattern{}, 3); + assert(*(reinterpret_cast(&ptr[0])) == AllocatorWithPattern::pattern); + assert(*(reinterpret_cast(&ptr[1])) == AllocatorWithPattern::pattern); + assert(*(reinterpret_cast(&ptr[2])) == AllocatorWithPattern::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 --- /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 +// shared_ptr make_shared_for_overwrite(); // T is not U[] +// +// template +// shared_ptr make_shared_for_overwrite(size_t N); // T is U[] + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +concept HasMakeSharedForOverwrite = + requires(Args&&... args) { std::make_shared_for_overwrite(std::forward(args)...); }; + +struct Foo { + int i; +}; + +// non array +static_assert(HasMakeSharedForOverwrite); +static_assert(HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); + +// bounded array +static_assert(HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); + +// unbounded array +static_assert(HasMakeSharedForOverwrite); +static_assert(HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); +static_assert(!HasMakeSharedForOverwrite); + +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(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> decltype(auto) ptr = std::make_shared_for_overwrite(); + assert(*(reinterpret_cast(ptr.get())) == pattern); + } + + // bounded array int[N] + { + std::same_as> decltype(auto) ptr = std::make_shared_for_overwrite(); + assert(*(reinterpret_cast(&ptr[0])) == pattern); + assert(*(reinterpret_cast(&ptr[1])) == pattern); + } + + // unbounded array int[] + { + std::same_as> decltype(auto) ptr = std::make_shared_for_overwrite(3); + assert(*(reinterpret_cast(&ptr[0])) == pattern); + assert(*(reinterpret_cast(&ptr[1])) == pattern); + assert(*(reinterpret_cast(&ptr[2])) == pattern); + } + + // single with default constructor + { + std::same_as> decltype(auto) ptr = + std::make_shared_for_overwrite(); + assert(ptr->i == 5); + } + + // bounded array with default constructor + { + std::same_as> decltype(auto) ptr = + std::make_shared_for_overwrite(); + assert(ptr[0].i == 5); + assert(ptr[1].i == 5); + } + + // unbounded array with default constructor + { + std::same_as> decltype(auto) ptrs = + std::make_shared_for_overwrite(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 --- /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 +// constexpr unique_ptr make_unique_for_overwrite(); // T is not array +// +// template +// constexpr unique_ptr make_unique_for_overwrite(size_t n); // T is U[] + +// Test the object is not value initialized + +#include +#include +#include +#include +#include + +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(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> auto ptr = std::make_unique_for_overwrite(); + assert(*(reinterpret_cast(ptr.get())) == pattern); + } + { + std::same_as> auto ptr = std::make_unique_for_overwrite(3); + assert(*(reinterpret_cast(&ptr[0])) == pattern); + assert(*(reinterpret_cast(&ptr[1])) == pattern); + assert(*(reinterpret_cast(&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 --- /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 +// constexpr unique_ptr make_unique_for_overwrite(); // T is not array +// +// template +// constexpr unique_ptr make_unique_for_overwrite(size_t n); // T is U[] +// +// template +// unspecified make_unique_for_overwrite(Args&&...) = delete; // T is U[N] + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +concept HasMakeUniqueForOverwrite = + requires(Args&&... args) { std::make_unique_for_overwrite(std::forward(args)...); }; + +struct Foo { + int i; +}; + +// template +// constexpr unique_ptr make_unique_for_overwrite(); +static_assert(HasMakeUniqueForOverwrite); +static_assert(HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); + +// template +// constexpr unique_ptr make_unique_for_overwrite(size_t n); +static_assert(HasMakeUniqueForOverwrite); +static_assert(HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); + +// template +// unspecified make_unique_for_overwrite(Args&&...) = delete; +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); +static_assert(!HasMakeUniqueForOverwrite); + +struct WithDefaultConstructor { + int i; + constexpr WithDefaultConstructor() : i(5) {} +}; + +TEST_CONSTEXPR_CXX23 bool test() { + // single int + { + std::same_as> decltype(auto) ptr = std::make_unique_for_overwrite(); + // memory is available for write, otherwise constexpr test would fail + *ptr = 5; + } + + // unbounded array int[] + { + std::same_as> decltype(auto) ptrs = std::make_unique_for_overwrite(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> decltype(auto) ptr = + std::make_unique_for_overwrite(); + assert(ptr->i == 5); + } + + // unbounded array with default constructor + { + std::same_as> decltype(auto) ptrs = + std::make_unique_for_overwrite(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> decltype(auto) ptr = + std::make_unique_for_overwrite(); + + assert(WithCustomNew::customNewCalled); + } + + // unbounded array with custom operator new + { + [[maybe_unused]] std::same_as> decltype(auto) ptr = + std::make_unique_for_overwrite(3); + + assert(WithCustomNew::customNewArrCalled); + } +} + +int main(int, char**) { + test(); + testCustomNew(); +#if TEST_STD_VER >= 23 + static_assert(test()); +#endif + + return 0; +}