Add is_swappable/is_nothrow_swappable traits

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@267079 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/algorithm b/include/algorithm
index 2ea1dae..7a6db7a 100644
--- a/include/algorithm
+++ b/include/algorithm
@@ -630,7 +630,7 @@
 #include <initializer_list>
 #include <type_traits>
 #include <cstring>
-#include <utility>
+#include <utility> // needed to provide swap_ranges.
 #include <memory>
 #include <iterator>
 #include <cstddef>
diff --git a/include/array b/include/array
index f0350ea..719286d 100644
--- a/include/array
+++ b/include/array
@@ -34,7 +34,7 @@
 
     // No explicit construct/copy/destroy for aggregate type
     void fill(const T& u);
-    void swap(array& a) noexcept(noexcept(swap(declval<T&>(), declval<T&>())));
+    void swap(array& a) noexcept(is_nothrow_swappable_v<T>);
 
     // iterators:
     iterator begin() noexcept;
@@ -141,8 +141,15 @@
     _LIBCPP_INLINE_VISIBILITY void fill(const value_type& __u)
         {_VSTD::fill_n(__elems_, _Size, __u);}
     _LIBCPP_INLINE_VISIBILITY
-    void swap(array& __a) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value)
-        {_VSTD::swap_ranges(__elems_, __elems_ + _Size, __a.__elems_);}
+    void swap(array& __a) _NOEXCEPT_(_Size == 0 || __is_nothrow_swappable<_Tp>::value)
+        { __swap_dispatch((std::integral_constant<bool, _Size == 0>()), __a); }
+
+    _LIBCPP_INLINE_VISIBILITY
+    void __swap_dispatch(std::true_type, array&) {}
+
+    _LIBCPP_INLINE_VISIBILITY
+    void __swap_dispatch(std::false_type, array& __a)
+        { _VSTD::swap_ranges(__elems_, __elems_ + _Size, __a.__elems_);}
 
     // iterators:
     _LIBCPP_INLINE_VISIBILITY
@@ -276,11 +283,12 @@
 inline _LIBCPP_INLINE_VISIBILITY
 typename enable_if
 <
+    _Size == 0 ||
     __is_swappable<_Tp>::value,
     void
 >::type
 swap(array<_Tp, _Size>& __x, array<_Tp, _Size>& __y)
-                                  _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value)
+                                  _NOEXCEPT_(noexcept(__x.swap(__y)))
 {
     __x.swap(__y);
 }
diff --git a/include/map b/include/map
index d04b803..8dc84e7 100644
--- a/include/map
+++ b/include/map
@@ -162,7 +162,7 @@
 
     void swap(map& m)
         noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
-            __is_nothrow_swappable<key_compare>::value); // C++17
+            is_nothrow_swappable<key_compare>::value); // C++17
 
     // observers:
     allocator_type get_allocator() const noexcept;
@@ -357,7 +357,7 @@
 
     void swap(multimap& m)
         noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
-            __is_nothrow_swappable<key_compare>::value); // C++17
+            is_nothrow_swappable<key_compare>::value); // C++17
 
     // observers:
     allocator_type get_allocator() const noexcept;
diff --git a/include/memory b/include/memory
index e59fd5d..7888303 100644
--- a/include/memory
+++ b/include/memory
@@ -2974,7 +2974,10 @@
 
 template <class _Tp, class _Dp>
 inline _LIBCPP_INLINE_VISIBILITY
-void
+typename enable_if<
+    __is_swappable<_Dp>::value,
+    void
+>::type
 swap(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) _NOEXCEPT {__x.swap(__y);}
 
 template <class _T1, class _D1, class _T2, class _D2>
diff --git a/include/queue b/include/queue
index 81b83a7..2509b93 100644
--- a/include/queue
+++ b/include/queue
@@ -66,7 +66,7 @@
     template <class... Args> void emplace(Args&&... args);
     void pop();
 
-    void swap(queue& q) noexcept(noexcept(swap(c, q.c)));
+    void swap(queue& q) noexcept(is_nothrow_swappable_v<Container>)
 };
 
 template <class T, class Container>
@@ -153,7 +153,8 @@
     void pop();
 
     void swap(priority_queue& q)
-        noexcept(noexcept(swap(c, q.c)) && noexcept(swap(comp.q.comp)));
+        noexcept(is_nothrow_swappable_v<Container> &&
+                 is_nothrow_swappable_v<Comp>)
 };
 
 template <class T, class Container, class Compare>
@@ -369,7 +370,10 @@
 
 template <class _Tp, class _Container>
 inline _LIBCPP_INLINE_VISIBILITY
-void
+typename enable_if<
+    __is_swappable<_Container>::value,
+    void
+>::type
 swap(queue<_Tp, _Container>& __x, queue<_Tp, _Container>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y)))
 {
@@ -700,7 +704,11 @@
 
 template <class _Tp, class _Container, class _Compare>
 inline _LIBCPP_INLINE_VISIBILITY
-void
+typename enable_if<
+    __is_swappable<_Container>::value
+    && __is_swappable<_Compare>::value,
+    void
+>::type
 swap(priority_queue<_Tp, _Container, _Compare>& __x,
      priority_queue<_Tp, _Container, _Compare>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y)))
diff --git a/include/stack b/include/stack
index 64fd652..48b3b0d 100644
--- a/include/stack
+++ b/include/stack
@@ -58,7 +58,7 @@
     template <class... Args> void emplace(Args&&... args);
     void pop();
 
-    void swap(stack& c) noexcept(noexcept(swap(c, q.c)));
+    void swap(stack& c) noexcept(is_nothrow_swappable_v<Container>)
 };
 
 template <class T, class Container>
@@ -275,7 +275,10 @@
 
 template <class _Tp, class _Container>
 inline _LIBCPP_INLINE_VISIBILITY
-void
+typename enable_if<
+    __is_swappable<_Container>::value,
+    void
+>::type
 swap(stack<_Tp, _Container>& __x, stack<_Tp, _Container>& __y)
     _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y)))
 {
diff --git a/include/type_traits b/include/type_traits
index 1ebdafb..99dd679 100644
--- a/include/type_traits
+++ b/include/type_traits
@@ -105,6 +105,8 @@
     template <class T, class U>       struct is_assignable;
     template <class T>                struct is_copy_assignable;
     template <class T>                struct is_move_assignable;
+    template <class T, class U>       struct is_swappable_with;       // C++17
+    template <class T>                struct is_swappable;            // C++17
     template <class T>                struct is_destructible;
 
     template <class T, class... Args> struct is_trivially_constructible;
@@ -123,6 +125,8 @@
     template <class T, class U>       struct is_nothrow_assignable;
     template <class T>                struct is_nothrow_copy_assignable;
     template <class T>                struct is_nothrow_move_assignable;
+    template <class T, class U>       struct is_nothrow_swappable_with; // C++17
+    template <class T>                struct is_nothrow_swappable;      // C++17
     template <class T>                struct is_nothrow_destructible;
 
     template <class T> struct has_virtual_destructor;
@@ -300,6 +304,10 @@
         = is_copy_assignable<T>::value;                                  // C++17
       template <class T> constexpr bool is_move_assignable_v
         = is_move_assignable<T>::value;                                  // C++17
+      template <class T, class U> constexpr bool is_swappable_with_v
+        = is_swappable_with<T, U>::value;                                // C++17
+      template <class T> constexpr bool is_swappable_v
+        = is_swappable<T>::value;                                        // C++17
       template <class T> constexpr bool is_destructible_v
         = is_destructible<T>::value;                                     // C++17
       template <class T, class... Args> constexpr bool is_trivially_constructible_v
@@ -332,6 +340,10 @@
         = is_nothrow_copy_assignable<T>::value;                          // C++17
       template <class T> constexpr bool is_nothrow_move_assignable_v
         = is_nothrow_move_assignable<T>::value;                          // C++17
+      template <class T, class U> constexpr bool is_nothrow_swappable_with_v
+        = is_nothrow_swappable_with<T, U>::value;                       // C++17
+      template <class T> constexpr bool is_nothrow_swappable_v
+        = is_nothrow_swappable<T>::value;                               // C++17
       template <class T> constexpr bool is_nothrow_destructible_v
         = is_nothrow_destructible<T>::value;                             // C++17
       template <class T> constexpr bool has_virtual_destructor_v
@@ -4421,6 +4433,9 @@
 
 #endif  // !defined(_LIBCPP_CXX03_LANG)
 
+template <class _Tp> struct __is_swappable;
+template <class _Tp> struct __is_nothrow_swappable;
+
 template <class _Tp>
 inline _LIBCPP_INLINE_VISIBILITY
 #ifndef _LIBCPP_HAS_NO_ADVANCED_SFINAE
@@ -4440,6 +4455,13 @@
     __y = _VSTD::move(__t);
 }
 
+template<class _Tp, size_t _Np>
+inline _LIBCPP_INLINE_VISIBILITY
+typename enable_if<
+    __is_swappable<_Tp>::value
+>::type
+swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value);
+
 template <class _ForwardIterator1, class _ForwardIterator2>
 inline _LIBCPP_INLINE_VISIBILITY
 void
@@ -4455,55 +4477,103 @@
 
 namespace __detail
 {
-
+// ALL generic swap overloads MUST already have a declaration available at this point.
 using _VSTD::swap;
 __nat swap(__any, __any);
 
-template <class _Tp>
-struct __swappable
+template <class _Tp, class _Up = _Tp,
+          bool _NotVoid = !is_void<_Tp>::value && !is_void<_Up>::value>
+struct __swappable_with
 {
-    typedef decltype(swap(_VSTD::declval<_Tp&>(), _VSTD::declval<_Tp&>())) type;
-    static const bool value = !is_same<type, __nat>::value;
+    typedef decltype(swap(_VSTD::declval<_Tp>(), _VSTD::declval<_Up>())) __swap1;
+    typedef decltype(swap(_VSTD::declval<_Up>(), _VSTD::declval<_Tp>())) __swap2;
+
+    static const bool value = !is_same<__swap1, __nat>::value
+                           && !is_same<__swap2, __nat>::value;
 };
 
+template <class _Tp, class _Up>
+struct __swappable_with<_Tp, _Up,  false> : false_type {};
+
+template <class _Tp, class _Up = _Tp, bool _Swappable = __swappable_with<_Tp, _Up>::value>
+struct __nothrow_swappable_with {
+  static const bool value =
+#ifndef _LIBCPP_HAS_NO_NOEXCEPT
+      noexcept(swap(_VSTD::declval<_Tp>(), _VSTD::declval<_Up>()))
+  &&  noexcept(swap(_VSTD::declval<_Up>(), _VSTD::declval<_Tp>()));
+#else
+      false;
+#endif
+};
+
+template <class _Tp, class _Up>
+struct __nothrow_swappable_with<_Tp, _Up, false> : false_type {};
+
 }  // __detail
 
 template <class _Tp>
 struct __is_swappable
-    : public integral_constant<bool, __detail::__swappable<_Tp>::value>
-{
-};
-
-#if __has_feature(cxx_noexcept) || (_GNUC_VER >= 407 && __cplusplus >= 201103L)
-
-template <bool, class _Tp>
-struct __is_nothrow_swappable_imp
-    : public integral_constant<bool, noexcept(swap(_VSTD::declval<_Tp&>(),
-                                                   _VSTD::declval<_Tp&>()))>
-{
-};
-
-template <class _Tp>
-struct __is_nothrow_swappable_imp<false, _Tp>
-    : public false_type
+    : public integral_constant<bool, __detail::__swappable_with<_Tp&>::value>
 {
 };
 
 template <class _Tp>
 struct __is_nothrow_swappable
-    : public __is_nothrow_swappable_imp<__is_swappable<_Tp>::value, _Tp>
+    : public integral_constant<bool, __detail::__nothrow_swappable_with<_Tp&>::value>
 {
 };
 
-#else  // __has_feature(cxx_noexcept)
+#if _LIBCPP_STD_VER > 14
+
+template <class _Tp, class _Up>
+struct _LIBCPP_TYPE_VIS_ONLY is_swappable_with
+    : public integral_constant<bool, __detail::__swappable_with<_Tp, _Up>::value>
+{
+};
 
 template <class _Tp>
-struct __is_nothrow_swappable
-    : public false_type
+struct _LIBCPP_TYPE_VIS_ONLY is_swappable
+    : public conditional<
+        __is_referenceable<_Tp>::value,
+        is_swappable_with<
+            typename add_lvalue_reference<_Tp>::type,
+            typename add_lvalue_reference<_Tp>::type>,
+        false_type
+    >::type
 {
 };
 
-#endif  // __has_feature(cxx_noexcept)
+template <class _Tp, class _Up>
+struct _LIBCPP_TYPE_VIS_ONLY is_nothrow_swappable_with
+    : public integral_constant<bool, __detail::__nothrow_swappable_with<_Tp, _Up>::value>
+{
+};
+
+template <class _Tp>
+struct _LIBCPP_TYPE_VIS_ONLY is_nothrow_swappable
+    : public conditional<
+        __is_referenceable<_Tp>::value,
+        is_nothrow_swappable_with<
+            typename add_lvalue_reference<_Tp>::type,
+            typename add_lvalue_reference<_Tp>::type>,
+        false_type
+    >::type
+{
+};
+
+template <class _Tp, class _Up>
+constexpr bool is_swappable_with_v = is_swappable_with<_Tp, _Up>::value;
+
+template <class _Tp>
+constexpr bool is_swappable_v = is_swappable<_Tp>::value;
+
+template <class _Tp, class _Up>
+constexpr bool is_nothrow_swappable_with_v = is_nothrow_swappable_with<_Tp, _Up>::value;
+
+template <class _Tp>
+constexpr bool is_nothrow_swappable_v = is_nothrow_swappable<_Tp>::value;
+
+#endif // _LIBCPP_STD_VER > 14
 
 #ifdef _LIBCPP_UNDERLYING_TYPE
 
diff --git a/include/utility b/include/utility
index e9db238..27b81a0 100644
--- a/include/utility
+++ b/include/utility
@@ -82,8 +82,8 @@
                                        is_nothrow_move_assignable<T2>::value);
     template <class U, class V> pair& operator=(pair<U, V>&& p);
 
-    void swap(pair& p) noexcept(noexcept(swap(first, p.first)) &&
-                                noexcept(swap(second, p.second)));
+    void swap(pair& p) noexcept(is_nothrow_swappable_v<T1> &&
+                                is_nothrow_swappable_v<T2>);
 };
 
 template <class T1, class T2> bool operator==(const pair<T1,T2>&, const pair<T1,T2>&); // constexpr in C++14
@@ -225,10 +225,6 @@
 
 // swap_ranges
 
-// forward
-template<class _Tp, size_t _Np>
-inline _LIBCPP_INLINE_VISIBILITY
-void swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value);
 
 template <class _ForwardIterator1, class _ForwardIterator2>
 inline _LIBCPP_INLINE_VISIBILITY
@@ -240,9 +236,12 @@
     return __first2;
 }
 
+// forward declared in <type_traits>
 template<class _Tp, size_t _Np>
 inline _LIBCPP_INLINE_VISIBILITY
-void
+typename enable_if<
+    __is_swappable<_Tp>::value
+>::type
 swap(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) _NOEXCEPT_(__is_nothrow_swappable<_Tp>::value)
 {
     _VSTD::swap_ranges(__a, __a + _Np, __b);