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,10 @@ __a.deallocate(_PTraits::pointer_to(*this), 1); } +#if _LIBCPP_STD_VER >= 20 +struct __shared_ptr_overwrite_tag{}; +#endif + template struct __shared_ptr_emplace : __shared_weak_count @@ -278,6 +282,16 @@ #endif } + +#if _LIBCPP_STD_VER >= 20 + _LIBCPP_HIDE_FROM_ABI + explicit __shared_ptr_emplace(__shared_ptr_overwrite_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 +959,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(__shared_ptr_overwrite_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 +1012,15 @@ 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, __shared_ptr_overwrite_tag) + : __alloc_(__alloc), __count_(__count) + { + 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,13 @@ 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, __shared_ptr_overwrite_tag) : __alloc_(__alloc) { + std::uninitialized_default_construct_n(std::addressof(__data_[0]), _Count); + } +#endif + _LIBCPP_HIDE_FROM_ABI ~__bounded_array_control_block() override { } // can't be `= default` because of the sometimes-non-trivial union member __data_ @@ -1129,6 +1182,20 @@ 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) +{ + return std::__allocate_shared_bounded_array<_Tp>(__a, __shared_ptr_overwrite_tag{}); +} + +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, __shared_ptr_overwrite_tag{}); +} + template::value>> _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared() @@ -1157,6 +1224,20 @@ 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() +{ + return std::__allocate_shared_bounded_array<_Tp>(allocator<_Tp>(), __shared_ptr_overwrite_tag{}); +} + +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, __shared_ptr_overwrite_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/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,109 @@ +//===----------------------------------------------------------------------===// +// +// 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(); // T is not U[] +// +// template +// shared_ptr make_shared_for_overwrite(size_t N); // T is U[] + +#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); + +struct WithDefaultConstructor { + int i; + constexpr WithDefaultConstructor() : i(5) {} +}; + +bool test() { + // single int + { [[maybe_unused]] std::same_as> decltype(auto) ptr = std::make_shared_for_overwrite(); } + + // bounded array int[N] + { + [[maybe_unused]] std::same_as> decltype(auto) ptr = + std::make_shared_for_overwrite(); + } + + // unbounded array int[] + { + [[maybe_unused]] std::same_as> decltype(auto) ptrs = + std::make_shared_for_overwrite(3); + } + + // 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.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,152 @@ +//===----------------------------------------------------------------------===// +// +// 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; +} + +void testCustomNew() { + struct AggregateWithCustomNew { + char c; + + static void* operator new(size_t n) { + auto ptr = ::operator new(n); + auto charPtr = reinterpret_cast(ptr); + *charPtr = 'x'; + return ptr; + } + + static void* operator new[](size_t n) { + auto ptr = ::operator new(n); + for (size_t i = 0; i != n; ++i) { + *(reinterpret_cast(ptr) + i) = 'x'; + } + return ptr; + } + }; + + // single with custom operator new + { + std::same_as> decltype(auto) ptr = + std::make_unique_for_overwrite(); + + assert(ptr->c == 'x'); + } + + // unbounded array with custom operator new + { + std::same_as> decltype(auto) ptr = + std::make_unique_for_overwrite(3); + + assert(ptr[0].c == 'x'); + assert(ptr[1].c == 'x'); + assert(ptr[2].c == 'x'); + } +} + +int main(int, char**) { + test(); + testCustomNew(); +#if TEST_STD_VER >= 23 + static_assert(test()); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.verify.cpp b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/smartptr/unique.ptr/unique.ptr.create/make_unique_for_overwrite.verify.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 + +// test that make_unique_for_overwrite does not initialize the memory + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +constexpr bool testMemoryUninitializedSingle() { + { + auto ptr = std::make_unique_for_overwrite(); + [[maybe_unused]] int i = + *ptr; // expected-note{{read of uninitialized object is not allowed in a constant expression}} + } + + return true; +} + +static_assert(testMemoryUninitializedSingle()); // expected-error-re{{{{(static_assert|static assertion)}}}} + +constexpr bool testMemoryUninitializedArray() { + { + auto ptr = std::make_unique_for_overwrite(2); + [[maybe_unused]] int i = + ptr[0]; // expected-note{{read of uninitialized object is not allowed in a constant expression}} + } + + return true; +} + +static_assert(testMemoryUninitializedArray()); // expected-error-re{{{{(static_assert|static assertion)}}}}