PR libstdc++/83982 fix exception-safety guarantee of std::vector::resize
authorDaniel Trebbien <dtrebbien@gmail.com>
Thu, 14 Jun 2018 09:26:51 +0000 (09:26 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 14 Jun 2018 09:26:51 +0000 (10:26 +0100)
Construct new elements before moving existing ones, so that if a default
constructor throws, the existing elements are not left in a moved-from
state.

2018-06-14  Daniel Trebbien <dtrebbien@gmail.com>
    Jonathan Wakely  <jwakely@redhat.com>

PR libstdc++/83982
* include/bits/vector.tcc (vector::_M_default_append(size_type)):
Default-construct new elements before moving existing ones.
* testsuite/23_containers/vector/capacity/resize/strong_guarantee.cc:
New.

Co-Authored-By: Jonathan Wakely <jwakely@redhat.com>
From-SVN: r261585

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/vector.tcc
libstdc++-v3/testsuite/23_containers/vector/capacity/resize/strong_guarantee.cc [new file with mode: 0644]

index 223b32732ddaf3e65520b7f846c276d5421608e1..923841ab2c7de5d922375b1fd9cf81847dc9163d 100644 (file)
@@ -1,3 +1,12 @@
+2018-06-14  Daniel Trebbien <dtrebbien@gmail.com>
+           Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/83982
+       * include/bits/vector.tcc (vector::_M_default_append(size_type)):
+       Default-construct new elements before moving existing ones.
+       * testsuite/23_containers/vector/capacity/resize/strong_guarantee.cc:
+       New.
+
 2018-06-13  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/86127
index 1d1ef427b26d99a4d817c3dfeb10f8a4374d520e..86a711713b2707a7740fd743368c2b16c2747972 100644 (file)
@@ -582,7 +582,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     {
       if (__n != 0)
        {
-         size_type __size = size();
+         const size_type __size = size();
          size_type __navail = size_type(this->_M_impl._M_end_of_storage
                                         - this->_M_impl._M_finish);
 
@@ -601,23 +601,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
            {
              const size_type __len =
                _M_check_len(__n, "vector::_M_default_append");
-             const size_type __old_size = __size;
              pointer __new_start(this->_M_allocate(__len));
-             pointer __new_finish(__new_start);
+             pointer __destroy_from = pointer();
              __try
                {
-                 __new_finish
-                   = std::__uninitialized_move_if_noexcept_a
-                   (this->_M_impl._M_start, this->_M_impl._M_finish,
-                    __new_start, _M_get_Tp_allocator());
-                 __new_finish =
-                   std::__uninitialized_default_n_a(__new_finish, __n,
-                                                    _M_get_Tp_allocator());
+                 std::__uninitialized_default_n_a(__new_start + __size,
+                                                  __n, _M_get_Tp_allocator());
+                 __destroy_from = __new_start + __size;
+                 std::__uninitialized_move_if_noexcept_a(
+                     this->_M_impl._M_start, this->_M_impl._M_finish,
+                     __new_start, _M_get_Tp_allocator());
                }
              __catch(...)
                {
-                 std::_Destroy(__new_start, __new_finish,
-                               _M_get_Tp_allocator());
+                 if (__destroy_from)
+                   std::_Destroy(__destroy_from, __destroy_from + __n,
+                                 _M_get_Tp_allocator());
                  _M_deallocate(__new_start, __len);
                  __throw_exception_again;
                }
@@ -628,7 +627,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
                            this->_M_impl._M_end_of_storage
                            - this->_M_impl._M_start);
              this->_M_impl._M_start = __new_start;
-             this->_M_impl._M_finish = __new_finish;
+             this->_M_impl._M_finish = __new_start + __size + __n;
              this->_M_impl._M_end_of_storage = __new_start + __len;
            }
        }
diff --git a/libstdc++-v3/testsuite/23_containers/vector/capacity/resize/strong_guarantee.cc b/libstdc++-v3/testsuite/23_containers/vector/capacity/resize/strong_guarantee.cc
new file mode 100644 (file)
index 0000000..b209d76
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright (C) 2018 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/>.
+
+#include <vector>
+#include <testsuite_hooks.h>
+
+struct X
+{
+  X() : data(1)
+  {
+    if (fail)
+      throw 1;
+  }
+
+  static bool fail;
+
+  std::vector<int> data;
+};
+
+bool X::fail = false;
+
+void
+test01()
+{
+  std::vector<X> v(2);
+  X* const addr = &v[0];
+  bool caught = false;
+  try {
+    X::fail = true;
+    v.resize(v.capacity() + 1); // force reallocation
+  } catch (int) {
+    caught = true;
+  }
+  VERIFY( caught );
+  VERIFY( v.size() == 2 );
+  VERIFY( &v[0] == addr );
+  // PR libstdc++/83982
+  VERIFY( ! v[0].data.empty() );
+  VERIFY( ! v[1].data.empty() );
+}
+
+int
+main()
+{
+  test01();
+}