From 258bd1d63aec54899b12269325eca9712d61adfe Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 5 Feb 2019 14:45:00 +0000 Subject: PR libstdc++/89130 restore support for non-MoveConstructible types The changes to "relocate" std::vector elements can lead to new errors outside the immediate context, because moving the elements to new storage no longer makes use of the move-if-noexcept utilities. This means that types with deleted moves no longer degenerate to copies, but are just ill-formed. The errors happen while instantiating the noexcept-specifier for __relocate_object_a, when deciding whether to try to relocate. This patch introduces indirections to avoid the ill-formed instantiations of std::__relocate_object_a. In order to avoid using if-constexpr prior to C++17 this is done by tag dispatching. After this patch all uses of std::__relocate_a are guarded by checks that will support sensible code (i.e. code not using custom allocators that fool the new checks). PR libstdc++/89130 * include/bits/alloc_traits.h (__is_copy_insertable_impl): Rename to __is_alloc_insertable_impl. Replace single type member with two members, one for each of copy and move insertable. (__is_move_insertable): New trait for internal use. * include/bits/stl_vector.h (vector::_S_nothrow_relocate(true_type)) (vector::_S_nothrow_relocate(true_type)): New functions to conditionally check if __relocate_a can throw. (vector::_S_use_relocate()): Dispatch to _S_nothrow_relocate based on __is_move_insertable. (vector::_S_do_relocate): New overloaded functions to conditionally call __relocate_a. (vector::_S_relocate): New function that dispatches to _S_do_relocate based on _S_use_relocate. * include/bits/vector.tcc (vector::reserve, vector::_M_realloc_insert) (vector::_M_default_append): Call _S_relocate instead of __relocate_a. * testsuite/23_containers/vector/modifiers/push_back/89130.cc: New. From-SVN: r268537 --- libstdc++-v3/ChangeLog | 18 +++++++ libstdc++-v3/include/bits/alloc_traits.h | 29 +++++++--- libstdc++-v3/include/bits/stl_vector.h | 37 ++++++++++++- libstdc++-v3/include/bits/vector.tcc | 22 +++----- .../vector/modifiers/push_back/89130.cc | 63 ++++++++++++++++++++++ 5 files changed, 146 insertions(+), 23 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89130.cc (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index ff847f8..5a94e80 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,23 @@ 2019-02-05 Jonathan Wakely + PR libstdc++/89130 + * include/bits/alloc_traits.h (__is_copy_insertable_impl): Rename to + __is_alloc_insertable_impl. Replace single type member with two + members, one for each of copy and move insertable. + (__is_move_insertable): New trait for internal use. + * include/bits/stl_vector.h (vector::_S_nothrow_relocate(true_type)) + (vector::_S_nothrow_relocate(true_type)): New functions to + conditionally check if __relocate_a can throw. + (vector::_S_use_relocate()): Dispatch to _S_nothrow_relocate based + on __is_move_insertable. + (vector::_S_do_relocate): New overloaded functions to conditionally + call __relocate_a. + (vector::_S_relocate): New function that dispatches to _S_do_relocate + based on _S_use_relocate. + * include/bits/vector.tcc (vector::reserve, vector::_M_realloc_insert) + (vector::_M_default_append): Call _S_relocate instead of __relocate_a. + * testsuite/23_containers/vector/modifiers/push_back/89130.cc: New. + PR libstdc++/89090 * include/bits/stl_uninitialized.h (__relocate_a_1): Make unused parameter unnamed. Add message to static assertion. diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h index ed61ce8..3b0c16f 100644 --- a/libstdc++-v3/include/bits/alloc_traits.h +++ b/libstdc++-v3/include/bits/alloc_traits.h @@ -577,14 +577,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template - class __is_copy_insertable_impl + class __is_alloc_insertable_impl { - typedef allocator_traits<_Alloc> _Traits; + using _Traits = allocator_traits<_Alloc>; + using value_type = typename _Traits::value_type; - template, + typename = decltype(_Traits::construct(std::declval<_Alloc&>(), - std::declval<_Up*>(), - std::declval()))> + std::declval<_Tp*>(), + std::declval<_Up>()))> static true_type _M_select(int); @@ -593,13 +595,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_select(...); public: - typedef decltype(_M_select(0)) type; + using copy = decltype(_M_select(0)); + using move = decltype(_M_select(0)); }; // true if _Alloc::value_type is CopyInsertable into containers using _Alloc template struct __is_copy_insertable - : __is_copy_insertable_impl<_Alloc>::type + : __is_alloc_insertable_impl<_Alloc>::copy { }; // std::allocator<_Tp> just requires CopyConstructible @@ -608,6 +611,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : is_copy_constructible<_Tp> { }; + // true if _Alloc::value_type is MoveInsertable into containers using _Alloc + template + struct __is_move_insertable + : __is_alloc_insertable_impl<_Alloc>::move + { }; + + // std::allocator<_Tp> just requires MoveConstructible + template + struct __is_move_insertable> + : is_move_constructible<_Tp> + { }; + // Trait to detect Allocator-like types. template struct __is_allocator : false_type { }; diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h index 43debda..10bf4fa 100644 --- a/libstdc++-v3/include/bits/stl_vector.h +++ b/libstdc++-v3/include/bits/stl_vector.h @@ -425,14 +425,47 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER private: #if __cplusplus >= 201103L static constexpr bool - _S_use_relocate() + _S_nothrow_relocate(true_type) { return noexcept(std::__relocate_a(std::declval(), std::declval(), std::declval(), std::declval<_Tp_alloc_type&>())); } -#endif + + static constexpr bool + _S_nothrow_relocate(false_type) + { return false; } + + static constexpr bool + _S_use_relocate() + { + // Instantiating std::__relocate_a might cause an error outside the + // immediate context (in __relocate_object_a's noexcept-specifier), + // so only do it if we know the type can be move-inserted into *this. + return _S_nothrow_relocate(__is_move_insertable<_Tp_alloc_type>{}); + } + + static pointer + _S_do_relocate(pointer __first, pointer __last, pointer __result, + _Tp_alloc_type& __alloc, true_type) noexcept + { + return std::__relocate_a(__first, __last, __result, __alloc); + } + + static pointer + _S_do_relocate(pointer, pointer, pointer __result, + _Tp_alloc_type&, false_type) noexcept + { return __result; } + + static pointer + _S_relocate(pointer __first, pointer __last, pointer __result, + _Tp_alloc_type& __alloc) noexcept + { + using __do_it = __bool_constant<_S_use_relocate()>; + return _S_do_relocate(__first, __last, __result, __alloc, __do_it{}); + } +#endif // C++11 protected: using _Base::_M_allocate; diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc index 54c0977..497d9f7 100644 --- a/libstdc++-v3/include/bits/vector.tcc +++ b/libstdc++-v3/include/bits/vector.tcc @@ -76,9 +76,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER if _GLIBCXX17_CONSTEXPR (_S_use_relocate()) { __tmp = this->_M_allocate(__n); - std::__relocate_a(this->_M_impl._M_start, - this->_M_impl._M_finish, - __tmp, _M_get_Tp_allocator()); + _S_relocate(this->_M_impl._M_start, this->_M_impl._M_finish, + __tmp, _M_get_Tp_allocator()); } else #endif @@ -459,17 +458,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER #if __cplusplus >= 201103L if _GLIBCXX17_CONSTEXPR (_S_use_relocate()) { - __new_finish - = std::__relocate_a - (__old_start, __position.base(), - __new_start, _M_get_Tp_allocator()); + __new_finish = _S_relocate(__old_start, __position.base(), + __new_start, _M_get_Tp_allocator()); ++__new_finish; - __new_finish - = std::__relocate_a - (__position.base(), __old_finish, - __new_finish, _M_get_Tp_allocator()); + __new_finish = _S_relocate(__position.base(), __old_finish, + __new_finish, _M_get_Tp_allocator()); } else #endif @@ -650,9 +645,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER _M_deallocate(__new_start, __len); __throw_exception_again; } - std::__relocate_a(this->_M_impl._M_start, - this->_M_impl._M_finish, - __new_start, _M_get_Tp_allocator()); + _S_relocate(this->_M_impl._M_start, this->_M_impl._M_finish, + __new_start, _M_get_Tp_allocator()); } else { diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89130.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89130.cc new file mode 100644 index 0000000..54b3f53 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89130.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2019 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +struct T +{ + T() { } + T(const T&) { } + T(T&&) = delete; // this means T is not MoveInsertable into std::vector +}; + +void f() +{ + const T val; + std::vector x; + // push_back(const T&) only requires T is CopyInsertable into std::vector: + x.push_back(val); +} + +template +struct Alloc +{ + using value_type = U; + Alloc() = default; + Alloc(const Alloc&) = default; + template + Alloc(const Alloc&) { } + + U* allocate(unsigned n) { return std::allocator().allocate(n); } + void deallocate(U* p, unsigned n) { std::allocator().deallocate(p, n); } + + void construct(Alloc*, U* p, U&& u) + { + // construct from const lvalue instead of rvalue: + ::new(p) U(const_cast(u)); + } +}; + +void g() +{ + const T val; + std::vector> x; + // push_back(const T&) only requires T is CopyInsertable into std::vector: + x.push_back(val); +} -- cgit v1.1