diff --git a/libcxx/include/any b/libcxx/include/any --- a/libcxx/include/any +++ b/libcxx/include/any @@ -82,7 +82,6 @@ #include #include -#include #include #include #include @@ -368,7 +367,11 @@ template _LIBCPP_INLINE_VISIBILITY static _Tp& __create(any & __dest, _Args&&... __args) { - _Tp* __ret = ::new (static_cast(&__dest.__s.__buf)) _Tp(_VSTD::forward<_Args>(__args)...); + typedef allocator<_Tp> _Alloc; + typedef allocator_traits<_Alloc> _ATraits; + _Alloc __a; + _Tp * __ret = static_cast<_Tp*>(static_cast(&__dest.__s.__buf)); + _ATraits::construct(__a, __ret, _VSTD::forward<_Args>(__args)...); __dest.__h = &_SmallHandler::__handle; return *__ret; } @@ -376,8 +379,11 @@ private: _LIBCPP_INLINE_VISIBILITY static void __destroy(any & __this) { - _Tp & __value = *static_cast<_Tp *>(static_cast(&__this.__s.__buf)); - __value.~_Tp(); + typedef allocator<_Tp> _Alloc; + typedef allocator_traits<_Alloc> _ATraits; + _Alloc __a; + _Tp * __p = static_cast<_Tp *>(static_cast(&__this.__s.__buf)); + _ATraits::destroy(__a, __p); __this.__h = nullptr; } @@ -445,10 +451,12 @@ _LIBCPP_INLINE_VISIBILITY static _Tp& __create(any & __dest, _Args&&... __args) { typedef allocator<_Tp> _Alloc; + typedef allocator_traits<_Alloc> _ATraits; typedef __allocator_destructor<_Alloc> _Dp; _Alloc __a; - unique_ptr<_Tp, _Dp> __hold(__a.allocate(1), _Dp(__a, 1)); - _Tp* __ret = ::new ((void*)__hold.get()) _Tp(_VSTD::forward<_Args>(__args)...); + unique_ptr<_Tp, _Dp> __hold(_ATraits::allocate(__a, 1), _Dp(__a, 1)); + _Tp * __ret = __hold.get(); + _ATraits::construct(__a, __ret, _VSTD::forward<_Args>(__args)...); __dest.__s.__ptr = __hold.release(); __dest.__h = &_LargeHandler::__handle; return *__ret; @@ -458,7 +466,12 @@ _LIBCPP_INLINE_VISIBILITY static void __destroy(any & __this){ - delete static_cast<_Tp*>(__this.__s.__ptr); + typedef allocator<_Tp> _Alloc; + typedef allocator_traits<_Alloc> _ATraits; + _Alloc __a; + _Tp * __p = static_cast<_Tp *>(__this.__s.__ptr); + _ATraits::destroy(__a, __p); + _ATraits::deallocate(__a, __p, 1); __this.__h = nullptr; } diff --git a/libcxx/test/libcxx/utilities/any/allocator.pass.cpp b/libcxx/test/libcxx/utilities/any/allocator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/any/allocator.pass.cpp @@ -0,0 +1,136 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// Check that we're consistently using std::allocator_traits to +// allocate/deallocate/construct/destroy objects in std::any. +// See https://llvm.org/PR45099 for details. + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + + +// Make sure we don't fit in std::any's SBO +struct Large { char big[sizeof(std::any) + 1]; }; + +// Make sure we fit in std::any's SBO +struct Small { }; + +bool Large_was_allocated = false; +bool Large_was_constructed = false; +bool Large_was_destroyed = false; +bool Large_was_deallocated = false; + +bool Small_was_allocated = false; +bool Small_was_constructed = false; +bool Small_was_destroyed = false; +bool Small_was_deallocated = false; + +namespace std { + template <> + struct allocator { + using value_type = Large; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + Large* allocate(std::size_t n) { + Large_was_allocated = true; + return static_cast(::operator new(n)); + } + + template + void construct(Large* p, Args&& ...args) { + new (p) Large(std::forward(args)...); + Large_was_constructed = true; + } + + void destroy(Large* p) { + p->~Large(); + Large_was_destroyed = true; + } + + void deallocate(Large* p, std::size_t) { + Large_was_deallocated = true; + return ::operator delete(p); + } + }; + + template <> + struct allocator { + using value_type = Small; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + Small* allocate(std::size_t n) { + Small_was_allocated = true; + return static_cast(::operator new(n)); + } + + template + void construct(Small* p, Args&& ...args) { + new (p) Small(std::forward(args)...); + Small_was_constructed = true; + } + + void destroy(Small* p) { + p->~Small(); + Small_was_destroyed = true; + } + + void deallocate(Small* p, std::size_t) { + Small_was_deallocated = true; + return ::operator delete(p); + } + }; +} // end namespace std + + +int main(int, char**) { + // Test large types + { + { + std::any a = Large(); + (void)a; + + assert(Large_was_allocated); + assert(Large_was_constructed); + } + + assert(Large_was_destroyed); + assert(Large_was_deallocated); + } + + // Test small types + { + { + std::any a = Small(); + (void)a; + + assert(!Small_was_allocated); + assert(Small_was_constructed); + } + + assert(Small_was_destroyed); + assert(!Small_was_deallocated); + } + + return 0; +}