From dc3eb83d08d63d41824d87e64c10021faee44c6e Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Mon, 11 Jul 2016 21:38:08 +0000 Subject: Always use the allocator to construct/destruct elements of a deque/vector. Fixes PR#28412. Thanks to Jonathan Wakely for the report. llvm-svn: 275105 --- libcxx/include/deque | 8 +-- libcxx/include/memory | 20 ++++++ libcxx/include/vector | 4 +- .../deque/deque.modifiers/emplace_back.pass.cpp | 12 ++++ .../vector/vector.modifiers/emplace_back.pass.cpp | 9 +++ libcxx/test/support/test_allocator.h | 78 ++++++++++++++++++++++ 6 files changed, 125 insertions(+), 6 deletions(-) (limited to 'libcxx') diff --git a/libcxx/include/deque b/libcxx/include/deque index c6fbd51..5765042 100644 --- a/libcxx/include/deque +++ b/libcxx/include/deque @@ -2026,7 +2026,7 @@ deque<_Tp, _Allocator>::emplace(const_iterator __p, _Args&&... __args) } else { - value_type __tmp(_VSTD::forward<_Args>(__args)...); + __temp_value __tmp(this->__alloc(), _VSTD::forward<_Args>(__args)...); iterator __b = __base::begin(); iterator __bm1 = _VSTD::prev(__b); __alloc_traits::construct(__a, _VSTD::addressof(*__bm1), _VSTD::move(*__b)); @@ -2034,7 +2034,7 @@ deque<_Tp, _Allocator>::emplace(const_iterator __p, _Args&&... __args) ++__base::size(); if (__pos > 1) __b = _VSTD::move(_VSTD::next(__b), __b + __pos, __b); - *__b = _VSTD::move(__tmp); + *__b = _VSTD::move(__tmp.get()); } } else @@ -2050,14 +2050,14 @@ deque<_Tp, _Allocator>::emplace(const_iterator __p, _Args&&... __args) } else { - value_type __tmp(_VSTD::forward<_Args>(__args)...); + __temp_value __tmp(this->__alloc(), _VSTD::forward<_Args>(__args)...); iterator __e = __base::end(); iterator __em1 = _VSTD::prev(__e); __alloc_traits::construct(__a, _VSTD::addressof(*__e), _VSTD::move(*__em1)); ++__base::size(); if (__de > 1) __e = _VSTD::move_backward(__e - __de, __em1, __e); - *--__e = _VSTD::move(__tmp); + *--__e = _VSTD::move(__tmp.get()); } } return __base::begin() + __pos; diff --git a/libcxx/include/memory b/libcxx/include/memory index 50a1f00..7a3281e 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -5674,6 +5674,26 @@ struct __noexcept_move_assign_container : public integral_constant {}; + +#ifndef _LIBCPP_HAS_NO_VARIADICS +template +struct __temp_value { + typedef allocator_traits<_Alloc> _Traits; + + typename aligned_storage::type __v; + _Alloc &__a; + + _Tp *__addr() { return reinterpret_cast<_Tp *>(addressof(__v)); } + _Tp & get() { return *__addr(); } + + template + __temp_value(_Alloc &__alloc, _Args&& ... __args) : __a(__alloc) + { _Traits::construct(__a, __addr(), _VSTD::forward<_Args>(__args)...); } + + ~__temp_value() { _Traits::destroy(__a, __addr()); } + }; +#endif + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_MEMORY diff --git a/libcxx/include/vector b/libcxx/include/vector index 81c514e..021bbfb 100644 --- a/libcxx/include/vector +++ b/libcxx/include/vector @@ -1812,9 +1812,9 @@ vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) } else { - value_type __tmp(_VSTD::forward<_Args>(__args)...); + __temp_value __tmp(this->__alloc(), _VSTD::forward<_Args>(__args)...); __move_range(__p, this->__end_, __p + 1); - *__p = _VSTD::move(__tmp); + *__p = _VSTD::move(__tmp.get()); } __annotator.__done(); } diff --git a/libcxx/test/std/containers/sequences/deque/deque.modifiers/emplace_back.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.modifiers/emplace_back.pass.cpp index 4859a37..784b3a3 100644 --- a/libcxx/test/std/containers/sequences/deque/deque.modifiers/emplace_back.pass.cpp +++ b/libcxx/test/std/containers/sequences/deque/deque.modifiers/emplace_back.pass.cpp @@ -16,6 +16,7 @@ #include "../../../Emplaceable.h" #include "min_allocator.h" +#include "test_allocator.h" #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES @@ -82,6 +83,17 @@ int main() for (int j = 0; j < N; ++j) testN> >(rng[i], rng[j]); } + { + std::deque> c; + c.emplace_back(); + assert(c.size() == 1); + c.emplace_back(1, 2, 3); + assert(c.size() == 2); + c.emplace_front(); + assert(c.size() == 3); + c.emplace_front(1, 2, 3); + assert(c.size() == 4); + } #endif #endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES } diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp index a58f8f0..61ccade 100644 --- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp +++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back.pass.cpp @@ -15,6 +15,7 @@ #include #include "../../../stack_allocator.h" #include "min_allocator.h" +#include "test_allocator.h" #include "asan_testing.h" #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES @@ -102,6 +103,14 @@ int main() assert(c.back().getd() == 4.5); assert(is_contiguous_container_asan_correct(c)); } + { + std::vector> c; + c.emplace_back(); + assert(c.size() == 1); + c.emplace_back(1, 2, 3); + assert(c.size() == 2); + assert(is_contiguous_container_asan_correct(c)); + } #endif #endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES } diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h index 5514771..466c7fa 100644 --- a/libcxx/test/support/test_allocator.h +++ b/libcxx/test/support/test_allocator.h @@ -228,4 +228,82 @@ public: }; +#if TEST_STD_VER >= 11 + +struct Ctor_Tag {}; + +template class TaggingAllocator; + +struct Tag_X { + // All constructors must be passed the Tag type. + + // DefaultInsertable into vector>, + Tag_X(Ctor_Tag) {} + // CopyInsertable into vector>, + Tag_X(Ctor_Tag, const Tag_X&) {} + // MoveInsertable into vector>, and + Tag_X(Ctor_Tag, Tag_X&&) {} + + // EmplaceConstructible into vector> from args. + template + Tag_X(Ctor_Tag, Args&&...) { } + + // not DefaultConstructible, CopyConstructible or MoveConstructible. + Tag_X() = delete; + Tag_X(const Tag_X&) = delete; + Tag_X(Tag_X&&) = delete; + + // CopyAssignable. + Tag_X& operator=(const Tag_X&) { return *this; } + + // MoveAssignable. + Tag_X& operator=(Tag_X&&) { return *this; } + +private: + // Not Destructible. + ~Tag_X() { } + + // Erasable from vector>. + friend class TaggingAllocator; +}; + + +template +class TaggingAllocator { +public: + using value_type = T; + TaggingAllocator() = default; + + template + TaggingAllocator(const TaggingAllocator&) { } + + T* allocate(std::size_t n) { return std::allocator{}.allocate(n); } + + void deallocate(T* p, std::size_t n) { std::allocator{}.deallocate(p, n); } + + template + void construct(Tag_X* p, Args&&... args) + { ::new((void*)p) Tag_X(Ctor_Tag{}, std::forward(args)...); } + + template + void construct(U* p, Args&&... args) + { ::new((void*)p) U(std::forward(args)...); } + + template + void destroy(U* p) + { p->~U(); } +}; + +template +bool +operator==(const TaggingAllocator&, const TaggingAllocator&) +{ return true; } + +template +bool +operator!=(const TaggingAllocator&, const TaggingAllocator&) +{ return false; } +#endif + + #endif // TEST_ALLOCATOR_H -- cgit v1.1