diff mbox series

libstdc++/89130 and libstdc++/89090 fixes for vector relocation

Message ID 20190205144506.GL4162@redhat.com
State New
Headers show
Series libstdc++/89130 and libstdc++/89090 fixes for vector relocation | expand

Commit Message

Jonathan Wakely Feb. 5, 2019, 2:45 p.m. UTC
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.

Comments

Jonathan Wakely Feb. 5, 2019, 2:53 p.m. UTC | #1
On 05/02/19 14:45 +0000, Jonathan Wakely wrote:
>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.

Oh, and the test change can be reverted now that types with deleted
moves work again.

Tested x86_64-linux, committed to trunk.
commit 3cd6139661801ab6bd88558e336a8ba12d1ed640
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Feb 5 14:50:58 2019 +0000

    Restore previous behaviour of test
    
    Go back to using CopyConsOnlyType as before r265485, because it works
    again now. Add test using DelAnyAssign for completeness and additional
    coverage.
    
            * testsuite/23_containers/vector/modifiers/push_back/49836.cc: Restore
            use of CopyConsOnlyType, but also test DelAnyAssign for completeness.

diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/49836.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/49836.cc
index 1a7d8729718..3e59781e7cc 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/49836.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/49836.cc
@@ -24,11 +24,12 @@
 // libstdc++/49836
 void test01()
 {
-  using __gnu_test::assign::DelAnyAssign;
+  using __gnu_test::CopyConsOnlyType;
   using __gnu_test::MoveConsOnlyType;
+  using __gnu_test::assign::DelAnyAssign;
 
-  std::vector<DelAnyAssign> v1;
-  DelAnyAssign t1;
+  std::vector<CopyConsOnlyType> v1;
+  CopyConsOnlyType t1(1);
   v1.push_back(t1);
   v1.push_back(t1);
   v1.push_back(t1);
@@ -40,6 +41,14 @@ void test01()
   v2.push_back(std::move(t2));
   v2.push_back(std::move(t2));
   VERIFY( v2.size() == 3 );
+
+  std::vector<DelAnyAssign> v3;
+  DelAnyAssign t3;
+  v3.push_back(t3);
+  v3.push_back(t3);
+  v3.push_back(t3);
+  VERIFY( v3.size() == 3 );
+
 }
 
 int main()
Jonathan Wakely Feb. 21, 2019, 8:47 p.m. UTC | #2
On 05/02/19 14:45 +0000, Jonathan Wakely wrote:
>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 <jwakely@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));

This caused another regression, fixed by the attached patch.

Tested powerpc64le-linux, committed to trunk.
commit 802fc355d929152fb2c70e5fc1422d16143eeb10
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Feb 21 02:28:16 2019 +0000

    PR libstdc++/89416 fix __is_move_insertable trait
    
    The common base class for __is_move_insertable and __is_copy_insertable
    instantiates both the copy and move tests, when only one is needed. The
    unneeded one might cause errors outside the immediate context.
    
    The solution used in this patch is to replace them with alias templates,
    which will only be instantiated as needed.
    
            PR libstdc++/89416
            * include/bits/alloc_traits.h (__is_alloc_insertable_impl): Replace
            class template with class. Replace move and copy member types with
            member alias templates, so they are only instantiated when needed.
            (__is_copy_insertable, __is_move_insertable): Adjust base class.
            * testsuite/23_containers/vector/modifiers/push_back/89130.cc: Enable
            test for C++11/14/17 as well.
            * testsuite/23_containers/vector/modifiers/push_back/89416.cc: New
            test.

diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
index 3b0c16fbf64..71892cbfaba 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -576,33 +576,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __do_alloc_on_swap(__one, __two, __pocs());
     }
 
-  template<typename _Alloc>
-    class __is_alloc_insertable_impl
-    {
-      using _Traits = allocator_traits<_Alloc>;
-      using value_type = typename _Traits::value_type;
+  class __is_alloc_insertable_impl
+  {
+    template<typename _Alloc, typename _Up,
+	     typename _Tp = __remove_cvref_t<_Up>,
+	     typename = decltype(allocator_traits<_Alloc>::construct(
+		   std::declval<_Alloc&>(), std::declval<_Tp*>(),
+		   std::declval<_Up>()))>
+      static true_type
+      _M_select(int);
 
-      template<typename _Up, typename _Tp = __remove_cvref_t<_Up>,
-	       typename
-	       = decltype(_Traits::construct(std::declval<_Alloc&>(),
-					     std::declval<_Tp*>(),
-					     std::declval<_Up>()))>
-	static true_type
-	_M_select(int);
+    template<typename, typename>
+      static false_type
+      _M_select(...);
 
-      template<typename _Up>
-	static false_type
-	_M_select(...);
+  protected:
+    template<typename _Alloc, typename _Tp = typename _Alloc::value_type>
+      using copy = decltype(_M_select<_Alloc, const _Tp&>(0));
 
-    public:
-      using copy = decltype(_M_select<const value_type&>(0));
-      using move = decltype(_M_select<value_type>(0));
-    };
+    template<typename _Alloc, typename _Tp = typename _Alloc::value_type>
+      using move = decltype(_M_select<_Alloc, _Tp>(0));
+  };
 
   // true if _Alloc::value_type is CopyInsertable into containers using _Alloc
   template<typename _Alloc>
     struct __is_copy_insertable
-    : __is_alloc_insertable_impl<_Alloc>::copy
+    : __is_alloc_insertable_impl::template copy<_Alloc>
     { };
 
   // std::allocator<_Tp> just requires CopyConstructible
@@ -614,7 +613,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // true if _Alloc::value_type is MoveInsertable into containers using _Alloc
   template<typename _Alloc>
     struct __is_move_insertable
-    : __is_alloc_insertable_impl<_Alloc>::move
+    : __is_alloc_insertable_impl::template move<_Alloc>
     { };
 
   // std::allocator<_Tp> just requires MoveConstructible
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
index 54b3f53069b..1a34f3e25d3 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89130.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89130.cc
@@ -15,8 +15,7 @@
 // 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 } }
+// { dg-do compile { target c++11 } }
 
 #include <vector>
 
diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89416.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89416.cc
new file mode 100644
index 00000000000..b1077611887
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/push_back/89416.cc
@@ -0,0 +1,44 @@
+// 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-do compile { target c++11 } }
+
+// PR libstdc++/89416
+
+#include <vector>
+
+template<typename T>
+  struct Alloc : std::allocator<T>
+  {
+    using std::allocator<T>::allocator;
+
+    template<typename U>
+      struct rebind { using other = Alloc<U>;  };
+  };
+
+struct X
+{
+  X(int);
+  X(X&&);
+};
+
+void test01()
+{
+  std::vector<X, Alloc<X>> V;
+  V.push_back(X(1));
+  V.emplace_back(1);
+}
Jonathan Wakely Feb. 24, 2019, 3:46 p.m. UTC | #3
On 21/02/19 20:47 +0000, Jonathan Wakely wrote:
>On 05/02/19 14:45 +0000, Jonathan Wakely wrote:
>>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 <jwakely@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));
>
>This caused another regression, fixed by the attached patch.

That patch doesn't work with Clang because I made the members
protected and forgot to make them public again (and GCC doesn't cre,
only Clang notices).

Tested powerpc64le-linux, committed to trunk.
commit 6f46cf624c814ea231490bf69afe970acf493543
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sun Feb 24 15:32:03 2019 +0000

    PR libstdc++/89416 fix accessibility of members
    
            PR libstdc++/89416
            * include/bits/alloc_traits.h (__is_alloc_insertable_impl): Make
            copy and move members public.

diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
index 71892cbfaba..9a3d816c42c 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -590,7 +590,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static false_type
       _M_select(...);
 
-  protected:
+  public:
     template<typename _Alloc, typename _Tp = typename _Alloc::value_type>
       using copy = decltype(_M_select<_Alloc, const _Tp&>(0));
Jonathan Wakely Feb. 26, 2019, 8:35 p.m. UTC | #4
On 24/02/19 15:46 +0000, Jonathan Wakely wrote:
>On 21/02/19 20:47 +0000, Jonathan Wakely wrote:
>>On 05/02/19 14:45 +0000, Jonathan Wakely wrote:
>>>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 <jwakely@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));
>>
>>This caused another regression, fixed by the attached patch.
>
>That patch doesn't work with Clang because I made the members
>protected and forgot to make them public again (and GCC doesn't cre,
>only Clang notices).

Aaaand another patch for Clang, because my test using Clang was flawed
and only tested std::allocator, which is handled by the partial
specialization. These problems are not found using G++ because of the
numerous bugs with access control in templates.

Tested powerpc64le-linux, committed to trunk.
commit f4f6e96aca78d6bfe90dab9b0fd7b94a9e31f241
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Feb 26 16:56:36 2019 +0000

    PR libstdc++/89416 fix alloc insertable trait for clang (again)
    
            PR libstdc++/89416
            * include/bits/alloc_traits.h (__is_alloc_insertable_impl): Change
            to class template and partial specialization using void_t.
            (__is_copy_insertable, __is_move_insertable): Adjust base class.

diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
index 9a3d816c42c..b8689daf74b 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -576,32 +576,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __do_alloc_on_swap(__one, __two, __pocs());
     }
 
-  class __is_alloc_insertable_impl
-  {
-    template<typename _Alloc, typename _Up,
-	     typename _Tp = __remove_cvref_t<_Up>,
-	     typename = decltype(allocator_traits<_Alloc>::construct(
-		   std::declval<_Alloc&>(), std::declval<_Tp*>(),
-		   std::declval<_Up>()))>
-      static true_type
-      _M_select(int);
+  template<typename _Alloc, typename _Tp,
+	   typename _ValueT = __remove_cvref_t<typename _Alloc::value_type>,
+	   typename = void>
+    struct __is_alloc_insertable_impl
+    : false_type
+    { };
 
-    template<typename, typename>
-      static false_type
-      _M_select(...);
-
-  public:
-    template<typename _Alloc, typename _Tp = typename _Alloc::value_type>
-      using copy = decltype(_M_select<_Alloc, const _Tp&>(0));
-
-    template<typename _Alloc, typename _Tp = typename _Alloc::value_type>
-      using move = decltype(_M_select<_Alloc, _Tp>(0));
-  };
+  template<typename _Alloc, typename _Tp, typename _ValueT>
+    struct __is_alloc_insertable_impl<_Alloc, _Tp, _ValueT,
+      __void_t<decltype(allocator_traits<_Alloc>::construct(
+		   std::declval<_Alloc&>(), std::declval<_ValueT*>(),
+		   std::declval<_Tp>()))>>
+    : true_type
+    { };
 
   // true if _Alloc::value_type is CopyInsertable into containers using _Alloc
+  // (might be wrong if _Alloc::construct exists but is not constrained,
+  // i.e. actually trying to use it would still be invalid. Use with caution.)
   template<typename _Alloc>
     struct __is_copy_insertable
-    : __is_alloc_insertable_impl::template copy<_Alloc>
+    : __is_alloc_insertable_impl<_Alloc,
+				 typename _Alloc::value_type const&>::type
     { };
 
   // std::allocator<_Tp> just requires CopyConstructible
@@ -611,9 +607,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
 
   // true if _Alloc::value_type is MoveInsertable into containers using _Alloc
+  // (might be wrong if _Alloc::construct exists but is not constrained,
+  // i.e. actually trying to use it would still be invalid. Use with caution.)
   template<typename _Alloc>
     struct __is_move_insertable
-    : __is_alloc_insertable_impl::template move<_Alloc>
+    : __is_alloc_insertable_impl<_Alloc, typename _Alloc::value_type>::type
     { };
 
   // std::allocator<_Tp> just requires MoveConstructible
diff mbox series

Patch

commit 51908e56bd32b5f89bc909193c3da957de01c3e0
Author: Jonathan Wakely <jwakely@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 <jwakely@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