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
Jonathan Wakely
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.
Tested powerpc64le-linux.
Daniel (CC'd) originally proposed a fix based on the code used in
vector::_M_realloc_insert. This is a slightly simpler fix, possible
because resize only inserts at the end not in the middle. I also wrote
a new testcase which is also valid in C++98, where the lack of move
constructors means the strong exception-safety guarantee is always
met. (Which means the bug is a regression since the default mode
changed to -std=gnu++14 in GCC 6.1).
I plan to commit this to trunk tomorrow, and backport soon too.
commit ef19eec2848fecf65ec51199549fdabfafed9927
Author: Jonathan Wakely
Date: Thu Jun 14 00:09:11 2018 +0100
PR libstdc++/83982 fix exception-safety guarantee of std::vector::resize
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
Jonathan Wakely
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.
diff --git a/libstdc++-v3/include/bits/vector.tcc
b/libstdc++-v3/include/bits/vector.tcc
index 1d1ef427b26..86a711713b2 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -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
index 000..b209d76867a
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/vector/capacity/resize/strong_guarantee.cc
@@ -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 Softw