[libc++] Rewrite the tuple constructors to be strictly Standards conforming

This nasty patch rewrites the tuple constructors to match those defined
by the Standard. We were previously providing several extensions in those
constructors - those extensions are removed by this patch.

The issue with those extensions is that we've had numerous bugs filed
against us over the years for problems essentially caused by them. As a
result, people are unable to use tuple in ways that are blessed by the
Standard, all that for the perceived benefit of providing them extensions
that they never asked for.

Since this is an API break, I communicated it in the release notes.
I do not foresee major issues with this break because I don't think the
extensions are too widely relied upon, but we can ship it and see if we
get complaints before the next LLVM release - that will give us some
amount of information regarding how much use these extensions have.

Differential Revision: https://reviews.llvm.org/D96523

GitOrigin-RevId: a3ab5120fd572215afeac190757834a041dda73a
diff --git a/include/tuple b/include/tuple
index 2b11a3f..9a58d6a 100644
--- a/include/tuple
+++ b/include/tuple
@@ -449,165 +449,6 @@
 
     _BaseT __base_;
 
-#if defined(_LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION)
-    static constexpr bool _EnableImplicitReducedArityExtension = true;
-#else
-    static constexpr bool _EnableImplicitReducedArityExtension = false;
-#endif
-
-    template <class ..._Args>
-    struct _PackExpandsToThisTuple : false_type {};
-
-    template <class _Arg>
-    struct _PackExpandsToThisTuple<_Arg>
-        : is_same<typename __uncvref<_Arg>::type, tuple> {};
-
-    template <bool _MaybeEnable, class _Dummy = void>
-    struct _CheckArgsConstructor : __check_tuple_constructor_fail {};
-
-    template <class _Dummy>
-    struct _CheckArgsConstructor<true, _Dummy>
-    {
-        template <int&...>
-        static constexpr bool __enable_implicit_default() {
-           return __all<__is_implicitly_default_constructible<_Tp>::value... >::value;
-        }
-
-        template <int&...>
-        static constexpr bool __enable_explicit_default() {
-            return
-                __all<is_default_constructible<_Tp>::value...>::value &&
-                !__enable_implicit_default< >();
-        }
-
-
-        template <class ..._Args>
-        static constexpr bool __enable_explicit() {
-            return
-                __tuple_constructible<
-                    tuple<_Args...>,
-                    typename __make_tuple_types<tuple,
-                             sizeof...(_Args) < sizeof...(_Tp) ?
-                                 sizeof...(_Args) :
-                                 sizeof...(_Tp)>::type
-                >::value &&
-                !__tuple_convertible<
-                    tuple<_Args...>,
-                    typename __make_tuple_types<tuple,
-                             sizeof...(_Args) < sizeof...(_Tp) ?
-                                 sizeof...(_Args) :
-                                 sizeof...(_Tp)>::type
-                >::value &&
-                __all_default_constructible<
-                    typename __make_tuple_types<tuple, sizeof...(_Tp),
-                             sizeof...(_Args) < sizeof...(_Tp) ?
-                                 sizeof...(_Args) :
-                                 sizeof...(_Tp)>::type
-                >::value;
-        }
-
-        template <class ..._Args>
-        static constexpr bool __enable_implicit() {
-            return
-               __tuple_constructible<
-                    tuple<_Args...>,
-                    typename __make_tuple_types<tuple,
-                             sizeof...(_Args) < sizeof...(_Tp) ?
-                                 sizeof...(_Args) :
-                                 sizeof...(_Tp)>::type
-                >::value &&
-                __tuple_convertible<
-                    tuple<_Args...>,
-                    typename __make_tuple_types<tuple,
-                             sizeof...(_Args) < sizeof...(_Tp) ?
-                                 sizeof...(_Args) :
-                                 sizeof...(_Tp)>::type
-                >::value &&
-                __all_default_constructible<
-                    typename __make_tuple_types<tuple, sizeof...(_Tp),
-                             sizeof...(_Args) < sizeof...(_Tp) ?
-                                 sizeof...(_Args) :
-                                 sizeof...(_Tp)>::type
-                >::value;
-        }
-    };
-
-    template <bool _MaybeEnable,
-              bool = sizeof...(_Tp) == 1,
-              class _Dummy = void>
-    struct _CheckTupleLikeConstructor : __check_tuple_constructor_fail {};
-
-    template <class _Dummy>
-    struct _CheckTupleLikeConstructor<true, false, _Dummy>
-    {
-        template <class _Tuple>
-        static constexpr bool __enable_implicit() {
-            return __tuple_constructible<_Tuple, tuple>::value
-                && __tuple_convertible<_Tuple, tuple>::value;
-        }
-
-        template <class _Tuple>
-        static constexpr bool __enable_explicit() {
-            return __tuple_constructible<_Tuple, tuple>::value
-               && !__tuple_convertible<_Tuple, tuple>::value;
-        }
-    };
-
-    template <class _Dummy>
-    struct _CheckTupleLikeConstructor<true, true, _Dummy>
-    {
-        // This trait is used to disable the tuple-like constructor when
-        // the UTypes... constructor should be selected instead.
-        // See LWG issue #2549.
-        template <class _Tuple>
-        using _PreferTupleLikeConstructor = _Or<
-            // Don't attempt the two checks below if the tuple we are given
-            // has the same type as this tuple.
-            _IsSame<__uncvref_t<_Tuple>, tuple>,
-            _Lazy<_And,
-                _Not<is_constructible<_Tp..., _Tuple>>,
-                _Not<is_convertible<_Tuple, _Tp...>>
-            >
-        >;
-
-        template <class _Tuple>
-        static constexpr bool __enable_implicit() {
-            return _And<
-                __tuple_constructible<_Tuple, tuple>,
-                __tuple_convertible<_Tuple, tuple>,
-                _PreferTupleLikeConstructor<_Tuple>
-            >::value;
-        }
-
-        template <class _Tuple>
-        static constexpr bool __enable_explicit() {
-            return _And<
-                __tuple_constructible<_Tuple, tuple>,
-                _PreferTupleLikeConstructor<_Tuple>,
-                _Not<__tuple_convertible<_Tuple, tuple>>
-            >::value;
-        }
-    };
-
-    template <class _Tuple, bool _DisableIfLValue>
-    using _EnableImplicitTupleLikeConstructor = _EnableIf<
-                         _CheckTupleLikeConstructor<
-                             __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
-                             && !_PackExpandsToThisTuple<_Tuple>::value
-                             && (!is_lvalue_reference<_Tuple>::value || !_DisableIfLValue)
-                         >::template __enable_implicit<_Tuple>(),
-                         bool
-                      >;
-
-    template <class _Tuple, bool _DisableIfLValue>
-    using _EnableExplicitTupleLikeConstructor = _EnableIf<
-                         _CheckTupleLikeConstructor<
-                             __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
-                             && !_PackExpandsToThisTuple<_Tuple>::value
-                             && (!is_lvalue_reference<_Tuple>::value || !_DisableIfLValue)
-                         >::template __enable_explicit<_Tuple>(),
-                         bool
-                      >;
     template <size_t _Jp, class ..._Up> friend _LIBCPP_CONSTEXPR_AFTER_CXX11
         typename tuple_element<_Jp, tuple<_Up...> >::type& get(tuple<_Up...>&) _NOEXCEPT;
     template <size_t _Jp, class ..._Up> friend _LIBCPP_CONSTEXPR_AFTER_CXX11
@@ -617,273 +458,489 @@
     template <size_t _Jp, class ..._Up> friend _LIBCPP_CONSTEXPR_AFTER_CXX11
         const typename tuple_element<_Jp, tuple<_Up...> >::type&& get(const tuple<_Up...>&&) _NOEXCEPT;
 public:
+    // [tuple.cnstr]
 
-    template <bool _Dummy = true, _EnableIf<
-        _CheckArgsConstructor<_Dummy>::__enable_implicit_default()
-    , void*> = nullptr>
+    // tuple() constructors (including allocator_arg_t variants)
+    template <template<class...> class _IsImpDefault = __is_implicitly_default_constructible, _EnableIf<
+        _And<
+            _IsImpDefault<_Tp>... // explicit check
+        >::value
+    , int> = 0>
     _LIBCPP_INLINE_VISIBILITY constexpr
     tuple()
-        _NOEXCEPT_(__all<is_nothrow_default_constructible<_Tp>::value...>::value) {}
+        _NOEXCEPT_(_And<is_nothrow_default_constructible<_Tp>...>::value)
+    { }
 
-    template <bool _Dummy = true, _EnableIf<
-        _CheckArgsConstructor<_Dummy>::__enable_explicit_default()
-    , void*> = nullptr>
-    explicit _LIBCPP_INLINE_VISIBILITY constexpr
-    tuple()
-        _NOEXCEPT_(__all<is_nothrow_default_constructible<_Tp>::value...>::value) {}
+    template <template<class...> class _IsImpDefault = __is_implicitly_default_constructible,
+              template<class...> class _IsDefault = is_default_constructible, _EnableIf<
+        _And<
+            _IsDefault<_Tp>...,
+            _Not<_Lazy<_And, _IsImpDefault<_Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+    explicit tuple()
+        _NOEXCEPT_(_And<is_nothrow_default_constructible<_Tp>...>::value)
+    { }
 
-    tuple(tuple const&) = default;
+    template <class _Alloc, template<class...> class _IsImpDefault = __is_implicitly_default_constructible, _EnableIf<
+        _And<
+            _IsImpDefault<_Tp>... // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    tuple(allocator_arg_t, _Alloc const& __a)
+      : __base_(allocator_arg_t(), __a,
+                    __tuple_indices<>(), __tuple_types<>(),
+                    typename __make_tuple_indices<sizeof...(_Tp), 0>::type(),
+                    __tuple_types<_Tp...>()) {}
+
+    template <class _Alloc,
+              template<class...> class _IsImpDefault = __is_implicitly_default_constructible,
+              template<class...> class _IsDefault = is_default_constructible, _EnableIf<
+        _And<
+            _IsDefault<_Tp>...,
+            _Not<_Lazy<_And, _IsImpDefault<_Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    explicit tuple(allocator_arg_t, _Alloc const& __a)
+      : __base_(allocator_arg_t(), __a,
+                    __tuple_indices<>(), __tuple_types<>(),
+                    typename __make_tuple_indices<sizeof...(_Tp), 0>::type(),
+                    __tuple_types<_Tp...>()) {}
+
+    // tuple(const T&...) constructors (including allocator_arg_t variants)
+    template <template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) >= 1>,
+            is_copy_constructible<_Tp>...,
+            is_convertible<const _Tp&, _Tp>... // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    tuple(const _Tp& ... __t)
+        _NOEXCEPT_(_And<is_nothrow_copy_constructible<_Tp>...>::value)
+        : __base_(typename __make_tuple_indices<sizeof...(_Tp)>::type(),
+                typename __make_tuple_types<tuple, sizeof...(_Tp)>::type(),
+                typename __make_tuple_indices<0>::type(),
+                typename __make_tuple_types<tuple, 0>::type(),
+                __t...
+               ) {}
+
+    template <template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) >= 1>,
+            is_copy_constructible<_Tp>...,
+            _Not<_Lazy<_And, is_convertible<const _Tp&, _Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    explicit tuple(const _Tp& ... __t)
+        _NOEXCEPT_(_And<is_nothrow_copy_constructible<_Tp>...>::value)
+        : __base_(typename __make_tuple_indices<sizeof...(_Tp)>::type(),
+                typename __make_tuple_types<tuple, sizeof...(_Tp)>::type(),
+                typename __make_tuple_indices<0>::type(),
+                typename __make_tuple_types<tuple, 0>::type(),
+                __t...
+               ) {}
+
+    template <class _Alloc, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) >= 1>,
+            is_copy_constructible<_Tp>...,
+            is_convertible<const _Tp&, _Tp>... // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
+        : __base_(allocator_arg_t(), __a,
+                typename __make_tuple_indices<sizeof...(_Tp)>::type(),
+                typename __make_tuple_types<tuple, sizeof...(_Tp)>::type(),
+                typename __make_tuple_indices<0>::type(),
+                typename __make_tuple_types<tuple, 0>::type(),
+                __t...
+               ) {}
+
+    template <class _Alloc, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) >= 1>,
+            is_copy_constructible<_Tp>...,
+            _Not<_Lazy<_And, is_convertible<const _Tp&, _Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    explicit tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
+        : __base_(allocator_arg_t(), __a,
+                typename __make_tuple_indices<sizeof...(_Tp)>::type(),
+                typename __make_tuple_types<tuple, sizeof...(_Tp)>::type(),
+                typename __make_tuple_indices<0>::type(),
+                typename __make_tuple_types<tuple, 0>::type(),
+                __t...
+               ) {}
+
+    // tuple(U&& ...) constructors (including allocator_arg_t variants)
+    template <class ..._Up> struct _IsThisTuple : false_type { };
+    template <class _Up> struct _IsThisTuple<_Up> : is_same<__uncvref_t<_Up>, tuple> { };
+
+    template <class ..._Up>
+    struct _EnableUTypesCtor : _And<
+        _BoolConstant<sizeof...(_Tp) >= 1>,
+        _Not<_IsThisTuple<_Up...> >, // extension to allow mis-behaved user constructors
+        is_constructible<_Tp, _Up>...
+    > { };
+
+    template <class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableUTypesCtor<_Up...>,
+            is_convertible<_Up, _Tp>... // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    tuple(_Up&&... __u)
+        _NOEXCEPT_((_And<is_nothrow_constructible<_Tp, _Up>...>::value))
+        : __base_(typename __make_tuple_indices<sizeof...(_Up)>::type(),
+                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type(),
+                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type(),
+                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
+                    _VSTD::forward<_Up>(__u)...) {}
+
+    template <class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableUTypesCtor<_Up...>,
+            _Not<_Lazy<_And, is_convertible<_Up, _Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    explicit tuple(_Up&&... __u)
+        _NOEXCEPT_((_And<is_nothrow_constructible<_Tp, _Up>...>::value))
+        : __base_(typename __make_tuple_indices<sizeof...(_Up)>::type(),
+                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type(),
+                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type(),
+                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
+                    _VSTD::forward<_Up>(__u)...) {}
+
+    template <class _Alloc, class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableUTypesCtor<_Up...>,
+            is_convertible<_Up, _Tp>... // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
+        : __base_(allocator_arg_t(), __a,
+                    typename __make_tuple_indices<sizeof...(_Up)>::type(),
+                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type(),
+                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type(),
+                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
+                    _VSTD::forward<_Up>(__u)...) {}
+
+    template <class _Alloc, class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableUTypesCtor<_Up...>,
+            _Not<_Lazy<_And, is_convertible<_Up, _Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    explicit tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
+        : __base_(allocator_arg_t(), __a,
+                    typename __make_tuple_indices<sizeof...(_Up)>::type(),
+                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type(),
+                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type(),
+                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
+                    _VSTD::forward<_Up>(__u)...) {}
+
+    // Copy and move constructors (including the allocator_arg_t variants)
+    tuple(const tuple&) = default;
     tuple(tuple&&) = default;
 
-    template <class _AllocArgT, class _Alloc, _EnableIf<
-             _CheckArgsConstructor<_IsSame<allocator_arg_t, _AllocArgT>::value >::__enable_implicit_default()
-      , void*> = nullptr
-    >
+    template <class _Alloc, template<class...> class _And = _And, _EnableIf<
+        _And<is_copy_constructible<_Tp>...>::value
+    , int> = 0>
+    tuple(allocator_arg_t, const _Alloc& __alloc, const tuple& __t)
+        : __base_(allocator_arg_t(), __alloc, __t)
+    { }
+
+    template <class _Alloc, template<class...> class _And = _And, _EnableIf<
+        _And<is_move_constructible<_Tp>...>::value
+    , int> = 0>
+    tuple(allocator_arg_t, const _Alloc& __alloc, tuple&& __t)
+        : __base_(allocator_arg_t(), __alloc, _VSTD::move(__t))
+    { }
+
+    // tuple(const tuple<U...>&) constructors (including allocator_arg_t variants)
+    template <class ..._Up>
+    struct _EnableCopyFromOtherTuple : _And<
+        _Or<
+            _BoolConstant<sizeof...(_Tp) != 1>,
+            // _Tp and _Up are 1-element packs - the pack expansions look
+            // weird to avoid tripping up the type traits in degenerate cases
+            _Lazy<_And,
+                _Not<is_same<_Tp, _Up> >...,
+                _Not<is_convertible<const tuple<_Up>&, _Tp> >...,
+                _Not<is_constructible<_Tp, const tuple<_Up>&> >...
+            >
+        >,
+        is_constructible<_Tp, const _Up&>...
+    > { };
+
+    template <class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableCopyFromOtherTuple<_Up...>,
+            is_convertible<const _Up&, _Tp>... // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    tuple(const tuple<_Up...>& __t)
+        _NOEXCEPT_((_And<is_nothrow_constructible<_Tp, const _Up&>...>::value))
+        : __base_(__t)
+    { }
+
+    template <class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableCopyFromOtherTuple<_Up...>,
+            _Not<_Lazy<_And, is_convertible<const _Up&, _Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    explicit tuple(const tuple<_Up...>& __t)
+        _NOEXCEPT_((_And<is_nothrow_constructible<_Tp, const _Up&>...>::value))
+        : __base_(__t)
+    { }
+
+    template <class ..._Up, class _Alloc, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableCopyFromOtherTuple<_Up...>,
+            is_convertible<const _Up&, _Tp>... // explicit check
+        >::value
+    , int> = 0>
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-    tuple(_AllocArgT, _Alloc const& __a)
-      : __base_(allocator_arg_t(), __a,
-                    __tuple_indices<>(), __tuple_types<>(),
-                    typename __make_tuple_indices<sizeof...(_Tp), 0>::type(),
-                    __tuple_types<_Tp...>()) {}
+    tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
+        : __base_(allocator_arg_t(), __a, __t)
+    { }
 
-    template <class _AllocArgT, class _Alloc, _EnableIf<
-             _CheckArgsConstructor<_IsSame<allocator_arg_t, _AllocArgT>::value>::__enable_explicit_default()
-      , void*> = nullptr
-    >
-    explicit _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-    tuple(_AllocArgT, _Alloc const& __a)
-      : __base_(allocator_arg_t(), __a,
-                    __tuple_indices<>(), __tuple_types<>(),
-                    typename __make_tuple_indices<sizeof...(_Tp), 0>::type(),
-                    __tuple_types<_Tp...>()) {}
+    template <class ..._Up, class _Alloc, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableCopyFromOtherTuple<_Up...>,
+            _Not<_Lazy<_And, is_convertible<const _Up&, _Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    explicit tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
+        : __base_(allocator_arg_t(), __a, __t)
+    { }
 
-    template <bool _Dummy = true,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                            _Dummy
-                         >::template __enable_implicit<_Tp const&...>(),
-                         bool
-                      >::type = false
-        >
+    // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
+    template <class ..._Up>
+    struct _EnableMoveFromOtherTuple : _And<
+        _Or<
+            _BoolConstant<sizeof...(_Tp) != 1>,
+            // _Tp and _Up are 1-element packs - the pack expansions look
+            // weird to avoid tripping up the type traits in degenerate cases
+            _Lazy<_And,
+                _Not<is_same<_Tp, _Up> >...,
+                _Not<is_convertible<tuple<_Up>, _Tp> >...,
+                _Not<is_constructible<_Tp, tuple<_Up> > >...
+            >
+        >,
+        is_constructible<_Tp, _Up>...
+    > { };
+
+    template <class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableMoveFromOtherTuple<_Up...>,
+            is_convertible<_Up, _Tp>... // explicit check
+        >::value
+    , int> = 0>
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
-    tuple(const _Tp& ... __t) _NOEXCEPT_((__all<is_nothrow_copy_constructible<_Tp>::value...>::value))
-        : __base_(typename __make_tuple_indices<sizeof...(_Tp)>::type(),
-                typename __make_tuple_types<tuple, sizeof...(_Tp)>::type(),
-                typename __make_tuple_indices<0>::type(),
-                typename __make_tuple_types<tuple, 0>::type(),
-                __t...
-               ) {}
+    tuple(tuple<_Up...>&& __t)
+        _NOEXCEPT_((_And<is_nothrow_constructible<_Tp, _Up>...>::value))
+        : __base_(_VSTD::move(__t))
+    { }
 
-    template <bool _Dummy = true,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                            _Dummy
-                         >::template __enable_explicit<_Tp const&...>(),
-                         bool
-                      >::type = false
-        >
+    template <class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableMoveFromOtherTuple<_Up...>,
+            _Not<_Lazy<_And, is_convertible<_Up, _Tp>...> > // explicit check
+        >::value
+    , int> = 0>
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
-    explicit tuple(const _Tp& ... __t) _NOEXCEPT_((__all<is_nothrow_copy_constructible<_Tp>::value...>::value))
-        : __base_(typename __make_tuple_indices<sizeof...(_Tp)>::type(),
-                typename __make_tuple_types<tuple, sizeof...(_Tp)>::type(),
-                typename __make_tuple_indices<0>::type(),
-                typename __make_tuple_types<tuple, 0>::type(),
-                __t...
-               ) {}
+    explicit tuple(tuple<_Up...>&& __t)
+        _NOEXCEPT_((_And<is_nothrow_constructible<_Tp, _Up>...>::value))
+        : __base_(_VSTD::move(__t))
+    { }
 
-    template <class _Alloc, bool _Dummy = true,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                            _Dummy
-                         >::template __enable_implicit<_Tp const&...>(),
-                         bool
-                      >::type = false
-        >
-      _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-      tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
-        : __base_(allocator_arg_t(), __a,
-                typename __make_tuple_indices<sizeof...(_Tp)>::type(),
-                typename __make_tuple_types<tuple, sizeof...(_Tp)>::type(),
-                typename __make_tuple_indices<0>::type(),
-                typename __make_tuple_types<tuple, 0>::type(),
-                __t...
-               ) {}
+    template <class _Alloc, class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableMoveFromOtherTuple<_Up...>,
+            is_convertible<_Up, _Tp>... // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
+        : __base_(allocator_arg_t(), __a, _VSTD::move(__t))
+    { }
 
-    template <class _Alloc, bool _Dummy = true,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                            _Dummy
-                         >::template __enable_explicit<_Tp const&...>(),
-                         bool
-                      >::type = false
-        >
-      _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-      explicit
-      tuple(allocator_arg_t, const _Alloc& __a, const _Tp& ... __t)
-        : __base_(allocator_arg_t(), __a,
-                typename __make_tuple_indices<sizeof...(_Tp)>::type(),
-                typename __make_tuple_types<tuple, sizeof...(_Tp)>::type(),
-                typename __make_tuple_indices<0>::type(),
-                typename __make_tuple_types<tuple, 0>::type(),
-                __t...
-               ) {}
+    template <class _Alloc, class ..._Up, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
+            _EnableMoveFromOtherTuple<_Up...>,
+            _Not<_Lazy<_And, is_convertible<_Up, _Tp>...> > // explicit check
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    explicit tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
+        : __base_(allocator_arg_t(), __a, _VSTD::move(__t))
+    { }
 
-    template <class ..._Up,
-              bool _PackIsTuple = _PackExpandsToThisTuple<_Up...>::value,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                             sizeof...(_Up) == sizeof...(_Tp)
-                             && !_PackIsTuple
-                         >::template __enable_implicit<_Up...>() ||
-                        _CheckArgsConstructor<
-                            _EnableImplicitReducedArityExtension
-                            && sizeof...(_Up) < sizeof...(_Tp)
-                            && !_PackIsTuple
-                         >::template __enable_implicit<_Up...>(),
-                         bool
-                      >::type = false
-             >
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
-        tuple(_Up&&... __u)
-            _NOEXCEPT_((
-                is_nothrow_constructible<_BaseT,
-                    typename __make_tuple_indices<sizeof...(_Up)>::type,
-                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type,
-                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type,
-                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type,
-                    _Up...
-                >::value
-            ))
-            : __base_(typename __make_tuple_indices<sizeof...(_Up)>::type(),
-                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type(),
-                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type(),
-                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
-                    _VSTD::forward<_Up>(__u)...) {}
+    // tuple(const pair<U1, U2>&) constructors (including allocator_arg_t variants)
+    template <class _Up1, class _Up2, class ..._DependentTp>
+    struct _EnableImplicitCopyFromPair : _And<
+        is_constructible<_FirstType<_DependentTp...>, const _Up1&>,
+        is_constructible<_SecondType<_DependentTp...>, const _Up2&>,
+        is_convertible<const _Up1&, _FirstType<_DependentTp...> >, // explicit check
+        is_convertible<const _Up2&, _SecondType<_DependentTp...> >
+    > { };
 
-    template <class ..._Up,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                             sizeof...(_Up) <= sizeof...(_Tp)
-                             && !_PackExpandsToThisTuple<_Up...>::value
-                         >::template __enable_explicit<_Up...>() ||
-                         _CheckArgsConstructor<
-                            !_EnableImplicitReducedArityExtension
-                            && sizeof...(_Up) < sizeof...(_Tp)
-                            && !_PackExpandsToThisTuple<_Up...>::value
-                         >::template __enable_implicit<_Up...>(),
-                         bool
-                      >::type = false
-             >
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
-        explicit
-        tuple(_Up&&... __u)
-            _NOEXCEPT_((
-                is_nothrow_constructible<_BaseT,
-                    typename __make_tuple_indices<sizeof...(_Up)>::type,
-                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type,
-                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type,
-                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type,
-                    _Up...
-                >::value
-            ))
-            : __base_(typename __make_tuple_indices<sizeof...(_Up)>::type(),
-                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type(),
-                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type(),
-                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
-                    _VSTD::forward<_Up>(__u)...) {}
+    template <class _Up1, class _Up2, class ..._DependentTp>
+    struct _EnableExplicitCopyFromPair : _And<
+        is_constructible<_FirstType<_DependentTp...>, const _Up1&>,
+        is_constructible<_SecondType<_DependentTp...>, const _Up2&>,
+        _Not<is_convertible<const _Up1&, _FirstType<_DependentTp...> > >, // explicit check
+        _Not<is_convertible<const _Up2&, _SecondType<_DependentTp...> > >
+    > { };
 
-    template <class _Alloc, class ..._Up,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                             sizeof...(_Up) == sizeof...(_Tp) &&
-                             !_PackExpandsToThisTuple<_Up...>::value
-                         >::template __enable_implicit<_Up...>(),
-                         bool
-                      >::type = false
-             >
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-        tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
-            : __base_(allocator_arg_t(), __a,
-                    typename __make_tuple_indices<sizeof...(_Up)>::type(),
-                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type(),
-                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type(),
-                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
-                    _VSTD::forward<_Up>(__u)...) {}
+    template <class _Up1, class _Up2, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            _EnableImplicitCopyFromPair<_Up1, _Up2, _Tp...>
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    tuple(const pair<_Up1, _Up2>& __p)
+        _NOEXCEPT_((_And<
+            is_nothrow_constructible<_FirstType<_Tp...>, const _Up1&>,
+            is_nothrow_constructible<_SecondType<_Tp...>, const _Up2&>
+        >::value))
+        : __base_(__p)
+    { }
 
-    template <class _Alloc, class ..._Up,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                             sizeof...(_Up) == sizeof...(_Tp) &&
-                             !_PackExpandsToThisTuple<_Up...>::value
-                         >::template __enable_explicit<_Up...>(),
-                         bool
-                      >::type = false
-             >
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-        explicit
-        tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
-            : __base_(allocator_arg_t(), __a,
-                    typename __make_tuple_indices<sizeof...(_Up)>::type(),
-                    typename __make_tuple_types<tuple, sizeof...(_Up)>::type(),
-                    typename __make_tuple_indices<sizeof...(_Tp), sizeof...(_Up)>::type(),
-                    typename __make_tuple_types<tuple, sizeof...(_Tp), sizeof...(_Up)>::type(),
-                    _VSTD::forward<_Up>(__u)...) {}
+    template <class _Up1, class _Up2, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            _EnableExplicitCopyFromPair<_Up1, _Up2, _Tp...>
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+    explicit tuple(const pair<_Up1, _Up2>& __p)
+        _NOEXCEPT_((_And<
+            is_nothrow_constructible<_FirstType<_Tp...>, const _Up1&>,
+            is_nothrow_constructible<_SecondType<_Tp...>, const _Up2&>
+        >::value))
+        : __base_(__p)
+    { }
 
-    template <class _Tuple, _EnableImplicitTupleLikeConstructor<_Tuple, true> = false>
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
-        tuple(_Tuple&& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, _Tuple>::value))
-            : __base_(_VSTD::forward<_Tuple>(__t)) {}
+    template <class _Alloc, class _Up1, class _Up2, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            _EnableImplicitCopyFromPair<_Up1, _Up2, _Tp...>
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
+        : __base_(allocator_arg_t(), __a, __p)
+    { }
 
-    template <class _Tuple, _EnableImplicitTupleLikeConstructor<const _Tuple&, false> = false>
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
-        tuple(const _Tuple& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, const _Tuple&>::value))
-            : __base_(__t) {}
-    template <class _Tuple, _EnableExplicitTupleLikeConstructor<_Tuple, true> = false>
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
-        explicit
-        tuple(_Tuple&& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, _Tuple>::value))
-            : __base_(_VSTD::forward<_Tuple>(__t)) {}
+    template <class _Alloc, class _Up1, class _Up2, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            _EnableExplicitCopyFromPair<_Up1, _Up2, _Tp...>
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    explicit tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
+        : __base_(allocator_arg_t(), __a, __p)
+    { }
 
-    template <class _Tuple, _EnableExplicitTupleLikeConstructor<const _Tuple&, false> = false>
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
-        explicit
-        tuple(const _Tuple& __t) _NOEXCEPT_((is_nothrow_constructible<_BaseT, const _Tuple&>::value))
-            : __base_(__t) {}
+    // tuple(pair<U1, U2>&&) constructors (including allocator_arg_t variants)
+    template <class _Up1, class _Up2, class ..._DependentTp>
+    struct _EnableImplicitMoveFromPair : _And<
+        is_constructible<_FirstType<_DependentTp...>, _Up1>,
+        is_constructible<_SecondType<_DependentTp...>, _Up2>,
+        is_convertible<_Up1, _FirstType<_DependentTp...> >, // explicit check
+        is_convertible<_Up2, _SecondType<_DependentTp...> >
+    > { };
 
-    template <class _Alloc, class _Tuple,
-              typename enable_if
-                      <
-                         _CheckTupleLikeConstructor<
-                             __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
-                         >::template __enable_implicit<_Tuple>(),
-                         bool
-                      >::type = false
-             >
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-        tuple(allocator_arg_t, const _Alloc& __a, _Tuple&& __t)
-            : __base_(allocator_arg_t(), __a, _VSTD::forward<_Tuple>(__t)) {}
+    template <class _Up1, class _Up2, class ..._DependentTp>
+    struct _EnableExplicitMoveFromPair : _And<
+        is_constructible<_FirstType<_DependentTp...>, _Up1>,
+        is_constructible<_SecondType<_DependentTp...>, _Up2>,
+        _Not<is_convertible<_Up1, _FirstType<_DependentTp...> > >, // explicit check
+        _Not<is_convertible<_Up2, _SecondType<_DependentTp...> > >
+    > { };
 
-    template <class _Alloc, class _Tuple,
-              typename enable_if
-                      <
-                         _CheckTupleLikeConstructor<
-                             __tuple_like_with_size<_Tuple, sizeof...(_Tp)>::value
-                         >::template __enable_explicit<_Tuple>(),
-                         bool
-                      >::type = false
-             >
-        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-        explicit
-        tuple(allocator_arg_t, const _Alloc& __a, _Tuple&& __t)
-            : __base_(allocator_arg_t(), __a, _VSTD::forward<_Tuple>(__t)) {}
+    template <class _Up1, class _Up2, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            _EnableImplicitMoveFromPair<_Up1, _Up2, _Tp...>
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY
+    tuple(pair<_Up1, _Up2>&& __p)
+        _NOEXCEPT_((_And<
+            is_nothrow_constructible<_FirstType<_Tp...>, _Up1>,
+            is_nothrow_constructible<_SecondType<_Tp...>, _Up2>
+        >::value))
+        : __base_(_VSTD::move(__p))
+    { }
+
+    template <class _Up1, class _Up2, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            _EnableExplicitMoveFromPair<_Up1, _Up2, _Tp...>
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY
+    explicit tuple(pair<_Up1, _Up2>&& __p)
+        _NOEXCEPT_((_And<
+            is_nothrow_constructible<_FirstType<_Tp...>, _Up1>,
+            is_nothrow_constructible<_SecondType<_Tp...>, _Up2>
+        >::value))
+        : __base_(_VSTD::move(__p))
+    { }
+
+    template <class _Alloc, class _Up1, class _Up2, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            _EnableImplicitMoveFromPair<_Up1, _Up2, _Tp...>
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
+        : __base_(allocator_arg_t(), __a, _VSTD::move(__p))
+    { }
+
+    template <class _Alloc, class _Up1, class _Up2, template<class...> class _And = _And, _EnableIf<
+        _And<
+            _BoolConstant<sizeof...(_Tp) == 2>,
+            _EnableExplicitMoveFromPair<_Up1, _Up2, _Tp...>
+        >::value
+    , int> = 0>
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    explicit tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
+        : __base_(allocator_arg_t(), __a, _VSTD::move(__p))
+    { }
 
     // [tuple.assign]
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17