Make any's copy assignment operator exception-safe,
authorVille Voutilainen <ville.voutilainen@gmail.com>
Mon, 10 Oct 2016 22:08:58 +0000 (01:08 +0300)
committerVille Voutilainen <ville@gcc.gnu.org>
Mon, 10 Oct 2016 22:08:58 +0000 (01:08 +0300)
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
libstdc++-v3/include/std/any
libstdc++-v3/testsuite/20_util/any/assign/2.cc
libstdc++-v3/testsuite/20_util/any/assign/exception.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/any/cons/2.cc
libstdc++-v3/testsuite/20_util/any/cons/explicit.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc

index 7987c5e5d59e5d92ac2cb532d396dde8bbb62f12..411474c7ebb093035daa4d6e6bf35643a8ccc6cd 100644 (file)
@@ -1,3 +1,23 @@
+2016-10-10  Ville Voutilainen  <ville.voutilainen@gmail.com>
+
+       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  <jwakely@redhat.com>
 
        * doc/xml/manual/appendix_contributing.xml (contrib.organization):
index 9160035006e0f8019b7ef178e3528e88ac33453a..45a2145179b21435ed300633b2ba99b4fea5eb2d 100644 (file)
@@ -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<typename _ValueType,
             typename _Tp = _Decay<_ValueType>>
-    enable_if_t<is_copy_constructible<_Tp>::value, any&>
+      enable_if_t<is_copy_constructible<_Tp>::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*>(__any)->_M_manager = nullptr;
index b333e5df7969da39bdc0d01fb47256294e6e2803..28f06a080ffdddb6fb4b188881116e1301559565 100644 (file)
 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<X&>(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 (file)
index 0000000..11a1a55
--- /dev/null
@@ -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
+// <http://www.gnu.org/licenses/>.
+
+#include <any>
+#include <testsuite_hooks.h>
+
+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<Good>(a1);
+        assert(del_count == 0);
+        assert(a1.has_value());
+        std::any_cast<Good>(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<Good>(a1);
+        assert(del_count == 0);
+        assert(a1.has_value());
+        std::any_cast<Good>(a1);
+    }
+}
index 613fa626d0671d7d0b9c982fe9b7d2f1ca668b1d..be410cd2f541421bff230ab471ac989548377f66 100644 (file)
 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<X&>(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 (file)
index 0000000..61d5035
--- /dev/null
@@ -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
+// <http://www.gnu.org/licenses/>.
+
+#include <any>
+#include <testsuite_hooks.h>
+#include <vector>
+
+int main()
+{
+  std::any a = {std::in_place<int>, 42}; // { dg-error "converting" }
+  std::any a2 =
+    {std::in_place<std::vector<int>>, {42, 666}}; // { dg-error "converting" }  
+}
index 05fdeb7198da6b99707725f010df4e6547bc7e7d..8b306665f391c5755ee7f206f09603fa3fb16e45 100644 (file)
@@ -26,5 +26,5 @@ void test01()
   using std::any_cast;
 
   const any y(1);
-  any_cast<int&>(y); // { dg-error "qualifiers" "" { target { *-*-* } } 440 }
+  any_cast<int&>(y); // { dg-error "qualifiers" "" { target { *-*-* } } 432 }
 }