aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2019-02-05 14:45:00 +0000
committerJonathan Wakely <redi@gcc.gnu.org>2019-02-05 14:45:00 +0000
commit258bd1d63aec54899b12269325eca9712d61adfe (patch)
tree65711f6c5705ba57805df0b0ef11d57f6b05f11b /libstdc++-v3
parent2781287255a16b6f2db18c68d02c3f40004332a7 (diff)
downloadgcc-258bd1d63aec54899b12269325eca9712d61adfe.zip
gcc-258bd1d63aec54899b12269325eca9712d61adfe.tar.gz
gcc-258bd1d63aec54899b12269325eca9712d61adfe.tar.bz2
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
Diffstat (limited to 'libstdc++-v3')
-rw-r--r--libstdc++-v3/ChangeLog18
-rw-r--r--libstdc++-v3/include/bits/alloc_traits.h29
-rw-r--r--libstdc++-v3/include/bits/stl_vector.h37
-rw-r--r--libstdc++-v3/include/bits/vector.tcc22
-rw-r--r--libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89130.cc63
5 files changed, 146 insertions, 23 deletions
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 <jwakely@redhat.com>
+ 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<typename _Alloc>
- 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 _Up, typename
+ template<typename _Up, typename _Tp = __remove_cvref_t<_Up>,
+ typename
= decltype(_Traits::construct(std::declval<_Alloc&>(),
- std::declval<_Up*>(),
- std::declval<const _Up&>()))>
+ 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<typename _Alloc::value_type>(0)) type;
+ using copy = decltype(_M_select<const value_type&>(0));
+ using move = decltype(_M_select<value_type>(0));
};
// true if _Alloc::value_type is CopyInsertable into containers using _Alloc
template<typename _Alloc>
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<typename _Alloc>
+ struct __is_move_insertable
+ : __is_alloc_insertable_impl<_Alloc>::move
+ { };
+
+ // std::allocator<_Tp> just requires MoveConstructible
+ template<typename _Tp>
+ struct __is_move_insertable<allocator<_Tp>>
+ : is_move_constructible<_Tp>
+ { };
+
// Trait to detect Allocator-like types.
template<typename _Alloc, typename = void>
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<pointer>(),
std::declval<pointer>(),
std::declval<pointer>(),
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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <vector>
+
+struct T
+{
+ T() { }
+ T(const T&) { }
+ T(T&&) = delete; // this means T is not MoveInsertable into std::vector<T>
+};
+
+void f()
+{
+ const T val;
+ std::vector<T> x;
+ // push_back(const T&) only requires T is CopyInsertable into std::vector<T>:
+ x.push_back(val);
+}
+
+template<typename U>
+struct Alloc
+{
+ using value_type = U;
+ Alloc() = default;
+ Alloc(const Alloc&) = default;
+ template<typename U2>
+ Alloc(const Alloc<U2>&) { }
+
+ U* allocate(unsigned n) { return std::allocator<U>().allocate(n); }
+ void deallocate(U* p, unsigned n) { std::allocator<U>().deallocate(p, n); }
+
+ void construct(Alloc*, U* p, U&& u)
+ {
+ // construct from const lvalue instead of rvalue:
+ ::new(p) U(const_cast<const U&>(u));
+ }
+};
+
+void g()
+{
+ const T val;
+ std::vector<T, Alloc<T>> x;
+ // push_back(const T&) only requires T is CopyInsertable into std::vector<T>:
+ x.push_back(val);
+}