Patchwork [v3] fix libstdc++/52591

login
register
mail settings
Submitter Jonathan Wakely
Date April 1, 2012, 10:09 p.m.
Message ID <CAH6eHdQCqUSk1kdjwZjukV-NgcO+nYBO3knvmdbrDOvZVcY64g@mail.gmail.com>
Download mbox | patch
Permalink /patch/149994/
State New
Headers show

Comments

Jonathan Wakely - April 1, 2012, 10:09 p.m.
This allows move-assignment for std::vector<T> when T is not
MoveAssignable but the allocator is moved or equal, as a QoI
extension.  It also makes the code a bit cleaner and simpler, so I
plan to use the same pattern as I make the rest of the library meet
the allocator-aware container requirements.

        PR libstdc++/52591
        * include/bits/stl_vector.h (vector::operator=(vector&&)): Dispatch
        to _M_move_assign depending on whether allocator is moved.
        (vector::_M_move_assign): Add overloaded functions.
        * testsuite/23_containers/vector/52591.cc: New.
        * testsuite/23_containers/vector/requirements/dr438/assign_neg.cc:
        Adjust dg-error line number.
        * testsuite/23_containers/vector/requirements/dr438/constructor_1_neg.cc:
        Likewise.
        * testsuite/23_containers/vector/requirements/dr438/constructor_2_neg.cc:
        Likewise.
        * testsuite/23_containers/vector/requirements/dr438/insert_neg.cc:
        Likewise.

Tested x86_64-linux, committed to trunk.  After this has been on the
trunk for a while I plan to apply it to the 4.7 branch too.
commit e08d4fbd65667448fd93505acefc4828baff7a69
Author: Jonathan Wakely <jwakely.gcc@gmail.com>
Date:   Fri Mar 16 22:22:31 2012 +0000

    	PR libstdc++/52591
    	* include/bits/stl_vector.h (vector::operator=(vector&&)): Dispatch
    	to _M_move_assign depending on whether allocator is moved.
    	(vector::_M_move_assign): Add overloaded functions.
    	* testsuite/23_containers/vector/52591.cc: New.
    	* testsuite/23_containers/vector/requirements/dr438/assign_neg.cc:
    	Adjust dg-error line number.
    	* testsuite/23_containers/vector/requirements/dr438/constructor_1_neg.cc:
    	Likewise.
    	* testsuite/23_containers/vector/requirements/dr438/constructor_2_neg.cc:
    	Likewise.
    	* testsuite/23_containers/vector/requirements/dr438/insert_neg.cc:
    	Likewise.

Patch

diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h
index 239f8b9..31660d3 100644
--- a/libstdc++-v3/include/bits/stl_vector.h
+++ b/libstdc++-v3/include/bits/stl_vector.h
@@ -428,36 +428,18 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        *  @brief  %Vector move assignment operator.
        *  @param  __x  A %vector of identical element and allocator types.
        *
-       *  The contents of @a __x are moved into this %vector (without copying).
+       *  The contents of @a __x are moved into this %vector (without copying,
+       *  if the allocators permit it).
        *  @a __x is a valid, but unspecified %vector.
        */
       vector&
       operator=(vector&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
       {
-	if (_Alloc_traits::_S_propagate_on_move_assign())
-	  {
-	    // We're moving the rvalue's allocator so can move the data too.
-	    const vector __tmp(std::move(*this));     // discard existing data
-	    this->_M_impl._M_swap_data(__x._M_impl);
-	    std::__alloc_on_move(_M_get_Tp_allocator(),
-				 __x._M_get_Tp_allocator());
-	  }
-	else if (_Alloc_traits::_S_always_equal()
-	         || __x._M_get_Tp_allocator() == this->_M_get_Tp_allocator())
-	  {
-	    // The rvalue's allocator can free our storage and vice versa,
-	    // so can swap the data storage after destroying our contents.
-	    this->clear();
-	    this->_M_impl._M_swap_data(__x._M_impl);
-	  }
-	else
-	  {
-	    // The rvalue's allocator cannot be moved, or is not equal,
-	    // so we need to individually move each element.
-	    this->assign(std::__make_move_if_noexcept_iterator(__x.begin()),
-			 std::__make_move_if_noexcept_iterator(__x.end()));
-	    __x.clear();
-	  }
+        constexpr bool __move_storage =
+          _Alloc_traits::_S_propagate_on_move_assign()
+          || _Alloc_traits::_S_always_equal();
+        _M_move_assign(std::move(__x),
+                       integral_constant<bool, __move_storage>());
 	return *this;
       }
 
@@ -1363,6 +1345,39 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	std::_Destroy(__pos, this->_M_impl._M_finish, _M_get_Tp_allocator());
 	this->_M_impl._M_finish = __pos;
       }
+
+#ifdef __GXX_EXPERIMENTAL_CXX0X__
+    private:
+      // Constant-time move assignment when source object's memory can be
+      // moved, either because the source's allocator will move too
+      // or because the allocators are equal.
+      void
+      _M_move_assign(vector&& __x, std::true_type) noexcept
+      {
+	const vector __tmp(std::move(*this));
+	this->_M_impl._M_swap_data(__x._M_impl);
+	if (_Alloc_traits::_S_propagate_on_move_assign())
+	  std::__alloc_on_move(_M_get_Tp_allocator(),
+			       __x._M_get_Tp_allocator());
+      }
+
+      // Do move assignment when it might not be possible to move source
+      // object's memory, resulting in a linear-time operation.
+      void
+      _M_move_assign(vector&& __x, std::false_type)
+      {
+	if (__x._M_get_Tp_allocator() == this->_M_get_Tp_allocator())
+	  _M_move_assign(std::move(__x), std::true_type());
+	else
+	  {
+	    // The rvalue's allocator cannot be moved and is not equal,
+	    // so we need to individually move each element.
+	    this->assign(std::__make_move_if_noexcept_iterator(__x.begin()),
+			 std::__make_move_if_noexcept_iterator(__x.end()));
+	    __x.clear();
+	  }
+      }
+#endif
     };
 
 
diff --git a/libstdc++-v3/testsuite/23_containers/vector/52591.cc b/libstdc++-v3/testsuite/23_containers/vector/52591.cc
new file mode 100644
index 0000000..c018c72
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/52591.cc
@@ -0,0 +1,38 @@ 
+// Copyright (C) 2012 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 }
+// { dg-options "-std=gnu++0x" }
+
+// libstdc++/52591
+
+#include <vector>
+
+// As an extension we allow move-assignment of std::vector when the element
+// type is not MoveAssignable, as long as the allocator type propagates or
+// is known to always compare equal.
+
+struct C
+{
+    C& operator=(C&&) = delete;
+};
+
+void test01()
+{
+    std::vector<C> a;
+    a = std::vector<C>();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/assign_neg.cc b/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/assign_neg.cc
index 73de8ae..644750c 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/assign_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/assign_neg.cc
@@ -18,7 +18,7 @@ 
 // <http://www.gnu.org/licenses/>.
 
 // { dg-do compile }
-// { dg-error "no matching" "" { target *-*-* } 1251 }
+// { dg-error "no matching" "" { target *-*-* } 1233 }
 
 #include <vector>
 
diff --git a/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/constructor_1_neg.cc b/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/constructor_1_neg.cc
index fa479c7..bbd4cfe 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/constructor_1_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/constructor_1_neg.cc
@@ -18,7 +18,7 @@ 
 // <http://www.gnu.org/licenses/>.
 
 // { dg-do compile }
-// { dg-error "no matching" "" { target *-*-* } 1181 }
+// { dg-error "no matching" "" { target *-*-* } 1163 }
 
 #include <vector>
 
diff --git a/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/constructor_2_neg.cc b/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/constructor_2_neg.cc
index 231cace..d2282cc 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/constructor_2_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/constructor_2_neg.cc
@@ -18,7 +18,7 @@ 
 // <http://www.gnu.org/licenses/>.
 
 // { dg-do compile }
-// { dg-error "no matching" "" { target *-*-* } 1181 }
+// { dg-error "no matching" "" { target *-*-* } 1163 }
 
 #include <vector>
 #include <utility>
diff --git a/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/insert_neg.cc b/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/insert_neg.cc
index b8e18bb..d2cde66 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/insert_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/requirements/dr438/insert_neg.cc
@@ -18,7 +18,7 @@ 
 // <http://www.gnu.org/licenses/>.
 
 // { dg-do compile }
-// { dg-error "no matching" "" { target *-*-* } 1292 }
+// { dg-error "no matching" "" { target *-*-* } 1274 }
 
 #include <vector>