This fixes two PRs, one trivial (don't use C++17 features in C++11
mode) and one more serious (don't require MoveInsertable when we
should only need CopyInsertable).

It would be nice to rely on if-constexpr in C++11 mode, but it causes
clang warnings, complicates testcase bisection/reduction, and causes
users to file bogus bug reports. So let's just avoid it.

Tested powerpc64le-linux, committed to trunk.


commit 51908e56bd32b5f89bc909193c3da957de01c3e0
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Tue Feb 5 11:50:18 2019 +0000

    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.

diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
index ed61ce845f8..3b0c16fbf64 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 43debda54f1..10bf4fac62e 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 54c09774b15..497d9f72247 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 00000000000..54b3f53069b
--- /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);
+}

commit 45094752353b4c49fb1bc8dc2f2b5254235a7948
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Mon Jan 28 21:35:33 2019 +0000

    PR libstdc++/89090 avoid C++17 features in C++11/C++14 code
    
    Although GCC and Clang both allow these features pre-C++17 in system
    headers, Clang does issue warnings with -Wsystem-headers. It can also
    complicate bisection and/or testcase reduction if # line markers are
    stripped, because the code won't be known to come from system headers.
    
            PR libstdc++/89090
            * include/bits/stl_uninitialized.h (__relocate_a_1): Make unused
            parameter unnamed. Add message to static assertion.
            * include/bits/vector.tcc (vector::reserve, vector::_M_realloc_insert)
            (vector::_M_default_append): Use _GLIBCXX17_CONSTEXPR for if constexpr
            in C++11 code.

diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h
index 03ed16b8c1a..0d42b253df1 100644
--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -904,7 +904,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template <typename _Tp, typename _Up>
     inline __enable_if_t<std::__is_bitwise_relocatable<_Tp>::value, _Tp*>
     __relocate_a_1(_Tp* __first, _Tp* __last,
-		   _Tp* __result, allocator<_Up>& __alloc) noexcept
+		   _Tp* __result, allocator<_Up>&) noexcept
     {
       ptrdiff_t __count = __last - __first;
       if (__count > 0)
@@ -925,7 +925,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_ValueType;
       typedef typename iterator_traits<_ForwardIterator>::value_type
 	_ValueType2;
-      static_assert(std::is_same<_ValueType, _ValueType2>::value);
+      static_assert(std::is_same<_ValueType, _ValueType2>::value,
+	  "relocation is only possible for values of the same type");
       _ForwardIterator __cur = __result;
       for (; __first != __last; ++__first, (void)++__cur)
 	std::__relocate_object_a(std::__addressof(*__cur),
diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc
index 4cf0e809fe9..54c09774b15 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -73,7 +73,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	  const size_type __old_size = size();
 	  pointer __tmp;
 #if __cplusplus >= 201103L
-	  if constexpr (_S_use_relocate())
+	  if _GLIBCXX17_CONSTEXPR (_S_use_relocate())
 	    {
 	      __tmp = this->_M_allocate(__n);
 	      std::__relocate_a(this->_M_impl._M_start,
@@ -457,7 +457,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	  __new_finish = pointer();
 
 #if __cplusplus >= 201103L
-	  if constexpr (_S_use_relocate())
+	  if _GLIBCXX17_CONSTEXPR (_S_use_relocate())
 	    {
 	      __new_finish
 		= std::__relocate_a
@@ -498,7 +498,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	  __throw_exception_again;
 	}
 #if __cplusplus >= 201103L
-      if constexpr (!_S_use_relocate())
+      if _GLIBCXX17_CONSTEXPR (!_S_use_relocate())
 #endif
 	std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator());
       _GLIBCXX_ASAN_ANNOTATE_REINIT;
@@ -638,8 +638,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	      const size_type __len =
 		_M_check_len(__n, "vector::_M_default_append");
 	      pointer __new_start(this->_M_allocate(__len));
-#if __cplusplus >= 201103L
-	      if constexpr (_S_use_relocate())
+	      if _GLIBCXX17_CONSTEXPR (_S_use_relocate())
 		{
 		  __try
 		    {
@@ -656,7 +655,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 				    __new_start, _M_get_Tp_allocator());
 		}
 	      else
-#endif
 		{
 		  pointer __destroy_from = pointer();
 		  __try

Reply via email to