diff mbox

[libstdc++] Implement __is_nothrow_swappable and use it

Message ID 20150605144448.GN12728@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely June 5, 2015, 2:44 p.m. UTC
On 01/05/15 12:47 +0300, Ville Voutilainen wrote:
>This patch partially solves the problem described in N4426, which is
>basically LWG
>issue 2456, which in turn is caused by CWG DR 1330. Some remarks:
>- the __is_swappable and __is_nothrow_swappable are at this time not
>meant to be general traits, they make the shortcut of automatically transforming
>their template parameter into a reference, which the truly general trait
>as described in N4426 wouldn't do.
>- swap is now constrained..
>- ..which means that everything from bits/move.h moves to type_traits since
>swap now needs type_traits. type_traits becomes ok to include in pre-c++14
>modes, but only enables the parts that work in pre-c++14 modes.
>- this patch does _not_ yet implement N4426.

I've committed this slightly altered patch, which doesn't move swap
into <type_traits> but just declares it there. The definition remains
in <bits/move.h> and including <type_traits> in c++98 mode is still
an error.

Tested powerpc64le-linux, committed to trunk.
diff mbox

Patch

commit d485df2eebd2869b3a17a3d67b2a394dbbbedab9
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Jun 5 13:20:53 2015 +0100

    2015-06-04  Ville Voutilainen  <ville.voutilainen@gmail.com>
    
    	Add __is_nothrow_swappable and take it into use.
    	* include/bits/algorithmfwd.h (swap): Only declare for C++98 mode.
    	* include/bits/move.h (swap): Add constraints in C++11 and later.
    	* include/bits/stl_pair.h (swap): Use __is_nothrow_swappable
    	for the free swap function for pair.
    	* include/bits/stl_queue.h (swap): Use __is_nothrow_swappable
    	for the free swap functions for queue and priority_queue.
    	* include/bits/stl_stack.h (swap): Use __is_nothrow_swappable
    	for the free swap function for stack.
    	* include/debug/array (swap): Use __is_nothrow_swappable
    	for the free swap function for array.
    	* include/profile/array (swap): Likewise.
    	* include/std/array (swap): Likewise.
    	* include/std/tuple (_Tuple_impl::_M_swap): Use __is_nothrow_swappable.
    	* include/std/type_traits (__is_swappable_impl::__is_swappable,
    	__is_nothrow_swappable_impl, __is_nothrow_swappable): New.
    	* testsuite/20_util/is_nothrow_swappable/requirements/
    	explicit_instantiation.cc: New.
    	* testsuite/20_util/is_nothrow_swappable/requirements/typedefs.cc:
    	New.
    	* testsuite/20_util/is_nothrow_swappable/value.cc: New.

diff --git a/libstdc++-v3/include/bits/algorithmfwd.h b/libstdc++-v3/include/bits/algorithmfwd.h
index 1dfc4ad..c972f33 100644
--- a/libstdc++-v3/include/bits/algorithmfwd.h
+++ b/libstdc++-v3/include/bits/algorithmfwd.h
@@ -566,22 +566,17 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _BIter 
     stable_partition(_BIter, _BIter, _Predicate);
 
-  template<typename _Tp> 
-    void 
-    swap(_Tp&, _Tp&)
-#if __cplusplus >= 201103L
-    noexcept(__and_<is_nothrow_move_constructible<_Tp>,
-	            is_nothrow_move_assignable<_Tp>>::value)
-#endif
-    ;
+#if __cplusplus < 201103L
+  // For C++11 swap() is declared in <type_traits>.
 
   template<typename _Tp, size_t _Nm>
-    void
-    swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm])
-#if __cplusplus >= 201103L
-    noexcept(noexcept(swap(*__a, *__b)))
+    inline void
+    swap(_Tp& __a, _Tp& __b);
+
+  template<typename _Tp, size_t _Nm>
+    inline void
+    swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm]);
 #endif
-    ;
 
   template<typename _FIter1, typename _FIter2>
     _FIter2 
diff --git a/libstdc++-v3/include/bits/move.h b/libstdc++-v3/include/bits/move.h
index 1dfd667..88c4f7b 100644
--- a/libstdc++-v3/include/bits/move.h
+++ b/libstdc++-v3/include/bits/move.h
@@ -172,11 +172,16 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *  @return   Nothing.
   */
   template<typename _Tp>
-    inline void
-    swap(_Tp& __a, _Tp& __b)
+    inline
 #if __cplusplus >= 201103L
+    typename enable_if<__and_<is_move_constructible<_Tp>,
+			      is_move_assignable<_Tp>>::value>::type
+    swap(_Tp& __a, _Tp& __b)
     noexcept(__and_<is_nothrow_move_constructible<_Tp>,
 	            is_nothrow_move_assignable<_Tp>>::value)
+#else
+    void
+    swap(_Tp& __a, _Tp& __b)
 #endif
     {
       // concept requirements
@@ -191,10 +196,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // DR 809. std::swap should be overloaded for array types.
   /// Swap the contents of two arrays.
   template<typename _Tp, size_t _Nm>
-    inline void
-    swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm])
+    inline
 #if __cplusplus >= 201103L
+    typename enable_if<__is_swappable_impl::__is_swappable<_Tp>::value>::type
+    swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm])
     noexcept(noexcept(swap(*__a, *__b)))
+#else
+    void
+    swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm])
 #endif
     {
       for (size_t __n = 0; __n < _Nm; ++__n)
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 3daeb60..490b005 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -192,8 +192,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       void
       swap(pair& __p)
-      noexcept(noexcept(swap(first, __p.first))
-	       && noexcept(swap(second, __p.second)))
+      noexcept(__is_nothrow_swappable<_T1>::value
+               && __is_nothrow_swappable<_T2>::value)
       {
 	using std::swap;
 	swap(first, __p.first);
diff --git a/libstdc++-v3/include/bits/stl_queue.h b/libstdc++-v3/include/bits/stl_queue.h
index 48e1ee7..5f8e6fb 100644
--- a/libstdc++-v3/include/bits/stl_queue.h
+++ b/libstdc++-v3/include/bits/stl_queue.h
@@ -247,7 +247,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus >= 201103L
       void
       swap(queue& __q)
-      noexcept(noexcept(swap(c, __q.c)))
+      noexcept(__is_nothrow_swappable<_Tp>::value)
       {
 	using std::swap;
 	swap(c, __q.c);
@@ -541,7 +541,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus >= 201103L
       void
       swap(priority_queue& __pq)
-      noexcept(noexcept(swap(c, __pq.c)) && noexcept(swap(comp, __pq.comp)))
+      noexcept(__is_nothrow_swappable<_Tp>::value
+               && __is_nothrow_swappable<_Compare>::value)
       {
 	using std::swap;
 	swap(c, __pq.c);
diff --git a/libstdc++-v3/include/bits/stl_stack.h b/libstdc++-v3/include/bits/stl_stack.h
index 3ff307f..0ed212e 100644
--- a/libstdc++-v3/include/bits/stl_stack.h
+++ b/libstdc++-v3/include/bits/stl_stack.h
@@ -221,7 +221,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus >= 201103L
       void
       swap(stack& __s)
-      noexcept(noexcept(swap(c, __s.c)))
+      noexcept(__is_nothrow_swappable<_Tp>::value)
       {
 	using std::swap;
 	swap(c, __s.c);
diff --git a/libstdc++-v3/include/debug/array b/libstdc++-v3/include/debug/array
index 7bb74c1..34e6281 100644
--- a/libstdc++-v3/include/debug/array
+++ b/libstdc++-v3/include/debug/array
@@ -84,7 +84,7 @@  namespace __debug
 
       void
       swap(array& __other)
-      noexcept(noexcept(swap(std::declval<_Tp&>(), std::declval<_Tp&>())))
+      noexcept(__is_nothrow_swappable<_Tp>::value)
       { std::swap_ranges(begin(), end(), __other.begin()); }
 
       // Iterators.
diff --git a/libstdc++-v3/include/profile/array b/libstdc++-v3/include/profile/array
index 5198bb3..434ca96 100644
--- a/libstdc++-v3/include/profile/array
+++ b/libstdc++-v3/include/profile/array
@@ -63,7 +63,7 @@  namespace __profile
 
       void
       swap(array& __other)
-      noexcept(noexcept(swap(std::declval<_Tp&>(), std::declval<_Tp&>())))
+      noexcept(__is_nothrow_swappable<_Tp>::value)
       { std::swap_ranges(begin(), end(), __other.begin()); }
 
       // Iterators.
diff --git a/libstdc++-v3/include/std/array b/libstdc++-v3/include/std/array
index 24be44f..40fbd46 100644
--- a/libstdc++-v3/include/std/array
+++ b/libstdc++-v3/include/std/array
@@ -113,7 +113,7 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
       void
       swap(array& __other)
-      noexcept(noexcept(swap(std::declval<_Tp&>(), std::declval<_Tp&>())))
+      noexcept(__is_nothrow_swappable<_Tp>::value)
       { std::swap_ranges(begin(), end(), __other.begin()); }
 
       // Iterators.
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index ad132bd..ccea02b 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -324,9 +324,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     protected:
       void
       _M_swap(_Tuple_impl& __in)
-      noexcept(noexcept(swap(std::declval<_Head&>(),
-			     std::declval<_Head&>()))
-	       && noexcept(_M_tail(__in)._M_swap(_M_tail(__in))))
+      noexcept(__is_nothrow_swappable<_Head>::value
+               && noexcept(_M_tail(__in)._M_swap(_M_tail(__in))))
       {
 	using std::swap;
 	swap(_M_head(*this), _M_head(__in));
@@ -451,7 +450,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     protected:
       void
       _M_swap(_Tuple_impl& __in)
-      noexcept(noexcept(swap(std::declval<_Head&>(), std::declval<_Head&>())))
+      noexcept(__is_nothrow_swappable<_Head>::value)
       {
 	using std::swap;
 	swap(_M_head(*this), _M_head(__in));
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index b8ec61f..2eae61b 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2427,6 +2427,52 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     : true_type								\
     { };
 
+
+   namespace __is_swappable_impl {
+     template <typename _Tp, typename=void>
+     struct __is_swappable : public false_type
+     { };
+   }
+
+  template<typename _Tp>
+    inline
+    typename enable_if<__and_<is_move_constructible<_Tp>,
+			      is_move_assignable<_Tp>>::value>::type
+    swap(_Tp&, _Tp&)
+    noexcept(__and_<is_nothrow_move_constructible<_Tp>,
+	            is_nothrow_move_assignable<_Tp>>::value);
+
+  template<typename _Tp, size_t _Nm>
+    inline
+    typename enable_if<__is_swappable_impl::__is_swappable<_Tp>::value>::type
+    swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm])
+    noexcept(noexcept(swap(*__a, *__b)));
+
+  namespace __is_swappable_impl {
+    using std::swap;
+
+    template <typename _Tp>
+    struct __is_swappable<_Tp, __void_t<decltype(swap(declval<_Tp&>(),
+                                                      declval<_Tp&>()))>>
+    : public true_type
+    { };
+  }
+
+  template <bool, typename _Tp>
+    struct __is_nothrow_swappable_impl
+    : public __bool_constant<noexcept(swap(declval<_Tp&>(), declval<_Tp&>()))>
+    { };
+
+  template <typename _Tp>
+    struct __is_nothrow_swappable_impl<false, _Tp> : public false_type
+    { };
+
+  template <typename _Tp>
+    struct __is_nothrow_swappable
+    : public __is_nothrow_swappable_impl<
+               __is_swappable_impl::__is_swappable<_Tp>::value, _Tp>
+    { };
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/requirements/explicit_instantiation.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/requirements/explicit_instantiation.cc
new file mode 100644
index 0000000..93fa94b
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/requirements/explicit_instantiation.cc
@@ -0,0 +1,27 @@ 
+// { dg-options "-std=gnu++11" }
+// { dg-do compile }
+
+// Copyright (C) 2015 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 <type_traits>
+
+namespace std
+{
+  typedef short test_type;
+  template struct std::__is_nothrow_swappable<test_type>;
+}
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/requirements/typedefs.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/requirements/typedefs.cc
new file mode 100644
index 0000000..2372349
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/requirements/typedefs.cc
@@ -0,0 +1,32 @@ 
+// { dg-options "-std=gnu++11" }
+// { dg-do compile }
+
+// Copyright (C) 2015 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 <type_traits>
+
+
+void test01()
+{
+  // Check for required typedefs
+  typedef std::__is_nothrow_swappable<int>          test_type;
+  typedef test_type::value_type                     value_type;
+  typedef test_type::type                           type;
+  typedef test_type::type::value_type               type_value_type;
+  typedef test_type::type::type                     type_type;
+}
diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/value.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/value.cc
new file mode 100644
index 0000000..bc778b5
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/value.cc
@@ -0,0 +1,72 @@ 
+// { dg-options "-std=gnu++11" }
+// { dg-do compile }
+
+// Copyright (C) 2015 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 <type_traits>
+#include <testsuite_tr1.h>
+#include <utility>
+#include <array>
+#include <tuple>
+#include <queue>
+#include <stack>
+
+namespace funny {
+  struct F {};
+  void swap(F&, F&) = delete;
+}
+void test01()
+{
+  using std::__is_nothrow_swappable;
+  using std::__is_swappable_impl::__is_swappable;
+  using namespace __gnu_test;
+  // Positive tests.
+  static_assert(test_property<__is_swappable, int>(true), "");
+  static_assert(test_property<__is_nothrow_swappable, int>(true), "");
+  static_assert(test_property<__is_nothrow_swappable, int[1]>(true), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::pair<int, int>>(true), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::tuple<int>>(true), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::array<int, 1>>(true), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::queue<int>>(true), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::priority_queue<int>>(true), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::stack<int>>(true), "");
+  // Negative tests.
+  static_assert(test_property<__is_swappable, construct::DelCopy>(false), "");
+  static_assert(test_property<__is_swappable, funny::F>(false), "");
+  static_assert(test_property<__is_swappable, funny::F[1]>(false), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		ThrowCopyConsClass>(false), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::pair<ThrowCopyConsClass, ThrowCopyConsClass>>(false), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::tuple<ThrowCopyConsClass>>(false), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::array<ThrowCopyConsClass, 1>>(false), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::queue<ThrowCopyConsClass>>(false), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::priority_queue<ThrowCopyConsClass>>(false), "");
+  static_assert(test_property<__is_nothrow_swappable,
+		std::stack<ThrowCopyConsClass>>(false), "");
+}