From 1725d05d199fc256844040d2900d5ee206c9d289 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Tue, 11 Oct 2016 01:08:58 +0300 Subject: [PATCH] Make any's copy assignment operator exception-safe, don't copy the underlying value when any is moved, make in_place constructors explicit. * include/std/any (any(in_place_type_t<_ValueType>, _Args&&...)): Make explicit. (any(in_place_type_t<_ValueType>, initializer_list<_Up>, _Args&&...)): Likewise. (operator=(const any&)): Make strongly exception-safe. (operator=(any&&)): reset() unconditionally in the case where rhs has a value. (operator=(_ValueType&&)): Indent the return type. (_Manager_internal<_Tp>::_S_manage): Move in _Op_xfer, don't copy. * testsuite/20_util/any/assign/2.cc: Adjust. * testsuite/20_util/any/assign/exception.cc: New. * testsuite/20_util/any/cons/2.cc: Adjust. * testsuite/20_util/any/cons/explicit.cc: New. * testsuite/20_util/any/misc/any_cast_neg.cc: Ajust. From-SVN: r240951 --- libstdc++-v3/ChangeLog | 20 +++++ libstdc++-v3/include/std/any | 21 ++--- .../testsuite/20_util/any/assign/2.cc | 55 +++++++++++-- .../testsuite/20_util/any/assign/exception.cc | 77 +++++++++++++++++++ libstdc++-v3/testsuite/20_util/any/cons/2.cc | 47 +++++++++-- .../testsuite/20_util/any/cons/explicit.cc | 30 ++++++++ .../20_util/any/misc/any_cast_neg.cc | 2 +- 7 files changed, 223 insertions(+), 29 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/any/assign/exception.cc create mode 100644 libstdc++-v3/testsuite/20_util/any/cons/explicit.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 7987c5e5d59..411474c7ebb 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,23 @@ +2016-10-10 Ville Voutilainen + + Make any's copy assignment operator exception-safe, + don't copy the underlying value when any is moved, + make in_place constructors explicit. + * include/std/any (any(in_place_type_t<_ValueType>, _Args&&...)): + Make explicit. + (any(in_place_type_t<_ValueType>, initializer_list<_Up>, _Args&&...)): + Likewise. + (operator=(const any&)): Make strongly exception-safe. + (operator=(any&&)): reset() unconditionally in the case where + rhs has a value. + (operator=(_ValueType&&)): Indent the return type. + (_Manager_internal<_Tp>::_S_manage): Move in _Op_xfer, don't copy. + * testsuite/20_util/any/assign/2.cc: Adjust. + * testsuite/20_util/any/assign/exception.cc: New. + * testsuite/20_util/any/cons/2.cc: Adjust. + * testsuite/20_util/any/cons/explicit.cc: New. + * testsuite/20_util/any/misc/any_cast_neg.cc: Ajust. + 2016-10-10 Jonathan Wakely * doc/xml/manual/appendix_contributing.xml (contrib.organization): diff --git a/libstdc++-v3/include/std/any b/libstdc++-v3/include/std/any index 9160035006e..45a2145179b 100644 --- a/libstdc++-v3/include/std/any +++ b/libstdc++-v3/include/std/any @@ -179,6 +179,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename _Tp = _Decay<_ValueType>, typename _Mgr = _Manager<_Tp>, __any_constructible_t<_Tp, _Args&&...> = false> + explicit any(in_place_type_t<_ValueType>, _Args&&... __args) : _M_manager(&_Mgr::_S_manage) { @@ -192,6 +193,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typename _Mgr = _Manager<_Tp>, __any_constructible_t<_Tp, initializer_list<_Up>, _Args&&...> = false> + explicit any(in_place_type_t<_ValueType>, initializer_list<_Up> __il, _Args&&... __args) : _M_manager(&_Mgr::_S_manage) @@ -207,16 +209,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Copy the state of another object. any& operator=(const any& __rhs) { - if (!__rhs.has_value()) - reset(); - else if (this != &__rhs) - { - if (has_value()) - _M_manager(_Op_destroy, this, nullptr); - _Arg __arg; - __arg._M_any = this; - __rhs._M_manager(_Op_clone, &__rhs, &__arg); - } + *this = any(__rhs); return *this; } @@ -231,8 +224,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION reset(); else if (this != &__rhs) { - if (has_value()) - _M_manager(_Op_destroy, this, nullptr); + reset(); _Arg __arg; __arg._M_any = this; __rhs._M_manager(_Op_xfer, &__rhs, &__arg); @@ -243,7 +235,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Store a copy of @p __rhs as the contained object. template> - enable_if_t::value, any&> + enable_if_t::value, any&> operator=(_ValueType&& __rhs) { *this = any(std::forward<_ValueType>(__rhs)); @@ -556,7 +548,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __ptr->~_Tp(); break; case _Op_xfer: - ::new(&__arg->_M_any->_M_storage._M_buffer) _Tp(*__ptr); + ::new(&__arg->_M_any->_M_storage._M_buffer) _Tp + (std::move(*const_cast<_Tp*>(__ptr))); __ptr->~_Tp(); __arg->_M_any->_M_manager = __any->_M_manager; const_cast(__any)->_M_manager = nullptr; diff --git a/libstdc++-v3/testsuite/20_util/any/assign/2.cc b/libstdc++-v3/testsuite/20_util/any/assign/2.cc index b333e5df796..28f06a080ff 100644 --- a/libstdc++-v3/testsuite/20_util/any/assign/2.cc +++ b/libstdc++-v3/testsuite/20_util/any/assign/2.cc @@ -24,28 +24,69 @@ using std::any; using std::any_cast; +bool moved = false; +bool copied = false; + struct X { - bool moved = false; - bool moved_from = false; X() = default; - X(const X&) = default; - X(X&& x) : moved(true) { x.moved_from = true; } + X(const X&) { copied = true; } + X(X&& x) { moved = true; } +}; + +struct X2 +{ + X2() = default; + X2(const X2&) { copied = true; } + X2(X2&& x) noexcept { moved = true; } }; void test01() { + moved = false; X x; any a1; a1 = x; - VERIFY(x.moved_from == false); + VERIFY(moved == false); any a2; + copied = false; a2 = std::move(x); - VERIFY(x.moved_from == true); - VERIFY(any_cast(a2).moved == true ); + VERIFY(moved == true); + VERIFY(copied == false); +} + +void test02() +{ + moved = false; + X x; + any a1; + a1 = x; + VERIFY(moved == false); + any a2; + copied = false; + a2 = std::move(a1); + VERIFY(moved == false); + VERIFY(copied == false); +} + +void test03() +{ + moved = false; + X2 x; + any a1; + a1 = x; + VERIFY(copied && moved); + any a2; + moved = false; + copied = false; + a2 = std::move(a1); + VERIFY(moved == true); + VERIFY(copied == false); } int main() { test01(); + test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/20_util/any/assign/exception.cc b/libstdc++-v3/testsuite/20_util/any/assign/exception.cc new file mode 100644 index 00000000000..11a1a55ce80 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/any/assign/exception.cc @@ -0,0 +1,77 @@ +// { dg-options "-std=gnu++17" } +// { dg-do run } + +// Copyright (C) 2016 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 +// . + +#include +#include + +using std::any; + +bool should_throw = false; +struct Bad +{ + Bad() = default; + Bad(const Bad&) {if (should_throw) throw 666;} +}; + +struct Bad2 +{ + Bad2() = default; + Bad2(const Bad2&) {if (should_throw) throw 666;} + Bad2(Bad2&&) noexcept {} +}; + +int del_count = 0; +struct Good +{ + Good() = default; + Good(const Good&) = default; + Good(Good&&) = default; + ~Good() {++del_count;} +}; + +int main() +{ + std::any a1 = Good(); + del_count = 0; + try { + Bad b; + std::any a2 = b; + should_throw = true; + a1 = a2; + } catch (...) { + auto x = std::any_cast(a1); + assert(del_count == 0); + assert(a1.has_value()); + std::any_cast(a1); + } + std::any a3 = Good(); + del_count = 0; + try { + Bad2 b; + std::any a4 = b; + should_throw = true; + a3 = a4; + } catch (...) { + auto x = std::any_cast(a1); + assert(del_count == 0); + assert(a1.has_value()); + std::any_cast(a1); + } +} diff --git a/libstdc++-v3/testsuite/20_util/any/cons/2.cc b/libstdc++-v3/testsuite/20_util/any/cons/2.cc index 613fa626d06..be410cd2f54 100644 --- a/libstdc++-v3/testsuite/20_util/any/cons/2.cc +++ b/libstdc++-v3/testsuite/20_util/any/cons/2.cc @@ -24,26 +24,59 @@ using std::any; using std::any_cast; +bool moved = false; +bool copied = false; + struct X { - bool moved = false; - bool moved_from = false; X() = default; - X(const X&) = default; - X(X&& x) : moved(true) { x.moved_from = true; } + X(const X&) { copied = true; } + X(X&& x) { moved = true; } +}; + +struct X2 +{ + X2() = default; + X2(const X2&) { copied = true; } + X2(X2&& x) noexcept { moved = true; } }; void test01() { + moved = false; X x; any a1(x); - VERIFY(x.moved_from == false); + VERIFY(moved == false); any a2(std::move(x)); - VERIFY(x.moved_from == true); - VERIFY(any_cast(a2).moved == true ); + VERIFY(moved == true); +} + +void test02() +{ + moved = false; + X x; + any a1(x); + VERIFY(moved == false); + copied = false; + any a2(std::move(a1)); + VERIFY(copied == false); +} + +void test03() +{ + moved = false; + X2 x; + any a1(x); + VERIFY(moved == false); + copied = false; + any a2(std::move(a1)); + VERIFY(copied == false); + VERIFY(moved == true); } int main() { test01(); + test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/20_util/any/cons/explicit.cc b/libstdc++-v3/testsuite/20_util/any/cons/explicit.cc new file mode 100644 index 00000000000..61d50359a34 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/any/cons/explicit.cc @@ -0,0 +1,30 @@ +// { dg-options "-std=gnu++17" } +// { dg-do compile } + +// Copyright (C) 2016 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 +// . + +#include +#include +#include + +int main() +{ + std::any a = {std::in_place, 42}; // { dg-error "converting" } + std::any a2 = + {std::in_place>, {42, 666}}; // { dg-error "converting" } +} diff --git a/libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc b/libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc index 05fdeb7198d..8b306665f39 100644 --- a/libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc +++ b/libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc @@ -26,5 +26,5 @@ void test01() using std::any_cast; const any y(1); - any_cast(y); // { dg-error "qualifiers" "" { target { *-*-* } } 440 } + any_cast(y); // { dg-error "qualifiers" "" { target { *-*-* } } 432 } } -- 2.30.2