diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -3191,6 +3191,22 @@ {__alloc_traits::deallocate(__alloc_, __p, __s_);} }; +template +class __allocator_destroyer +{ + typedef allocator_traits<_Alloc> __alloc_traits; + _Alloc __alloc_; +public: + _LIBCPP_INLINE_VISIBILITY __allocator_destroyer(_Alloc const& __a) + _NOEXCEPT + : __alloc_(__a) {} + + template + _LIBCPP_INLINE_VISIBILITY + void operator()(_Tp* __p) _NOEXCEPT + {__alloc_traits::destroy(__alloc_, __p);} +}; + template _ForwardIterator uninitialized_copy(_InputIterator __f, _InputIterator __l, _ForwardIterator __r) @@ -3481,6 +3497,20 @@ #endif } +template +struct __shared_ptr_aligned_block +{ + typename _VSTD::aligned_storage< + sizeof(_CntrlBlk), + _VSTD::alignment_of<_CntrlBlk>::value + >::type __cntrl_buff[sizeof(_CntrlBlk)]; + + typename _VSTD::aligned_storage< + sizeof(_Tp), + _VSTD::alignment_of<_CntrlBlk>::value + >::type __value_buff[sizeof(_Tp)]; +}; + template class _LIBCPP_TEMPLATE_VIS weak_ptr; class _LIBCPP_TYPE_VIS __shared_count @@ -3578,10 +3608,14 @@ : public __shared_weak_count { __compressed_pair<__compressed_pair<_Tp, _Dp>, _Alloc> __data_; + size_t __size; + public: _LIBCPP_INLINE_VISIBILITY - __shared_ptr_pointer(_Tp __p, _Dp __d, _Alloc __a) - : __data_(__compressed_pair<_Tp, _Dp>(__p, _VSTD::move(__d)), _VSTD::move(__a)) {} + __shared_ptr_pointer(_Tp __p, _Dp __d, _Alloc __a, + size_t __size = sizeof(__shared_ptr_pointer)) + : __data_(__compressed_pair<_Tp, _Dp>(__p, _VSTD::move(__d)), _VSTD::move(__a)), + __size(__size) {} #ifndef _LIBCPP_NO_RTTI virtual const void* __get_deleter(const type_info&) const _NOEXCEPT; @@ -3615,13 +3649,13 @@ void __shared_ptr_pointer<_Tp, _Dp, _Alloc>::__on_zero_shared_weak() _NOEXCEPT { - typedef typename __allocator_traits_rebind<_Alloc, __shared_ptr_pointer>::type _Al; + typedef typename __allocator_traits_rebind<_Alloc, char>::type _Al; typedef allocator_traits<_Al> _ATraits; typedef pointer_traits _PTraits; _Al __a(__data_.second()); __data_.second().~_Alloc(); - __a.deallocate(_PTraits::pointer_to(*this), 1); + __a.deallocate(_PTraits::pointer_to(*reinterpret_cast(this)), __size); } template @@ -4507,17 +4541,27 @@ { static_assert( is_constructible<_Tp, _Args...>::value, "Can't construct object in allocate_shared"); - typedef __shared_ptr_emplace<_Tp, _Alloc> _CntrlBlk; - typedef typename __allocator_traits_rebind<_Alloc, _CntrlBlk>::type _A2; + typedef typename __allocator_traits_rebind<_Alloc, _Tp>::type _TAlloc; + typedef allocator_traits<_TAlloc> _ATraits; + typedef __allocator_destroyer<_TAlloc> _D1; + + typedef __shared_ptr_pointer<_Tp*, _D1, _Alloc> _CntrlBlk; + typedef __shared_ptr_aligned_block<_CntrlBlk, _Tp> _AlignedBlk; + typedef typename __allocator_traits_rebind<_Alloc, _AlignedBlk>::type _A2; typedef __allocator_destructor<_A2> _D2; _A2 __a2(__a); - unique_ptr<_CntrlBlk, _D2> __hold2(__a2.allocate(1), _D2(__a2, 1)); - ::new(static_cast(_VSTD::addressof(*__hold2.get()))) - _CntrlBlk(__a, _VSTD::forward<_Args>(__args)...); + _TAlloc __a3(__a); + unique_ptr<_AlignedBlk, _D2> __hold2(__a2.allocate(1), _D2(__a2, 1)); + _CntrlBlk* __cntrl_buff = reinterpret_cast<_CntrlBlk*>(__hold2.get()->__cntrl_buff); + _Tp* __value_buff = reinterpret_cast<_Tp*>(__hold2.get()->__value_buff); + + ::new(static_cast(__cntrl_buff)) + _CntrlBlk(__value_buff, _D1(__a), __a2, sizeof(_AlignedBlk)); - typename shared_ptr<_Tp>::element_type *__p = __hold2.get()->get(); - return shared_ptr<_Tp>::__create_with_control_block(__p, _VSTD::addressof(*__hold2.release())); + _ATraits::construct(__a3, __value_buff, _VSTD::forward<_Args>(__args)...); + __hold2.release(); + return shared_ptr<_Tp>::__create_with_control_block(__value_buff, __cntrl_buff); } template diff --git a/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/function_type_default_deleter.fail.cpp b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/function_type_default_deleter.fail.cpp --- a/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/function_type_default_deleter.fail.cpp +++ b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/function_type_default_deleter.fail.cpp @@ -38,6 +38,8 @@ } // expected-error-re@memory:* 2 {{static_assert failed{{.*}} "default_delete cannot be instantiated for function types"}} { + // expected-error@memory:* {{no member named 'value' in 'std::__1::is_empty), std::__1::default_delete)> > >'}} + // expected-error@memory:* {{cannot initialize return object of type 'std::__1::__shared_weak_count *' with an rvalue of type 'std::__1::__shared_ptr_pointer), std::__1::default_delete)>, std::__1::allocator > *'}} SPtr<4> s4(getFn<4>()); // expected-note {{requested here}} SPtr<5> s5(getFn<5>(), std::default_delete>{}); // expected-note {{requested here}} } diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/allocate_shared_construct.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_construct.pass.cpp @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// shared_ptr + +// template +// shared_ptr allocate_shared(const A& a, Args&&... args); + +// This patch tests that allocator_traits::construct is used in allocate_shared. + +#include "test_macros.h" + +#include +#include + +static bool construct_called = false; +static bool destroy_called = false; +static unsigned allocator_id = 0; + +template +struct MyAllocator { +public: + typedef T value_type; + typedef T* pointer; + + unsigned id = 0; + + MyAllocator() = default; + MyAllocator(int id) : id(id) {} + + template + MyAllocator(MyAllocator const& other) + : id(other.id) {}; + + pointer allocate(std::ptrdiff_t n) + { + return pointer(static_cast(::operator new(n*sizeof(T)))); + } + + void deallocate(pointer p, std::ptrdiff_t) + { + return ::operator delete(p); + } + + void construct(T *p) + { + construct_called = true; + destroy_called = false; + allocator_id = id; + ::new(p) T; + } + + void construct(T *p, T x) + { + construct_called = true; + destroy_called = false; + allocator_id = id; + ::new(p) T(x); + } + + void construct(T *p, T x, T y) + { + construct_called = true; + destroy_called = false; + allocator_id = id; + ::new(p) T(x, y); + } + + void destroy(T *p) + { + destroy_called = true; + construct_called = false; + allocator_id = id; + p->~T(); + } +}; + +struct mchar { char c; }; + +struct Foo +{ + int val; + + Foo(int val) : val(val) {} + + Foo(Foo a, Foo b) + : val(a.val + b.val) {} +}; + +#if TEST_STD_VER >= 11 +struct Bar { + std::max_align_t y; +}; +#endif // TEST_STD_VER >= 11 + +void test_aligned(void *p, size_t align) { + assert(reinterpret_cast(p) % align == 0); +} + +int main(int, char**) +{ + // TODO: in C++03 mode construct only works for some variadics. +#if TEST_STD_VER >= 11 + { + std::shared_ptr p = std::allocate_shared(MyAllocator()); + assert(construct_called); + } + assert(destroy_called); + { + std::shared_ptr p = std::allocate_shared(MyAllocator(), Foo(42), Foo(100)); + assert(construct_called); + assert(p->val == 142); + } + assert(destroy_called); + { // make sure allocator is copied + std::shared_ptr p = std::allocate_shared(MyAllocator(3)); + assert(allocator_id == 3); + + allocator_id = 0; + } + assert(allocator_id == 3); +#endif // TEST_STD_VER >= 11 + + { + std::shared_ptr p = std::allocate_shared(MyAllocator(), 42); + assert(construct_called); + assert(*p == 42); + } + assert(destroy_called); + + { // make sure allocator is properly re-bound + std::shared_ptr p = std::allocate_shared(MyAllocator(), 42); + assert(construct_called); + assert(*p == 42); + } + assert(destroy_called); + +#if TEST_STD_VER >= 11 + { + std::shared_ptr p; + test_aligned(p.get(), alignof(Bar)); + } +#endif // TEST_STD_VER >= 11 + + return 0; +}