[libcxx-commits] [libcxx] a3ab512 - [libc++] Rewrite the tuple constructors to be strictly Standards conforming

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Fri Apr 23 09:46:45 PDT 2021


Author: Louis Dionne
Date: 2021-04-23T12:46:37-04:00
New Revision: a3ab5120fd572215afeac190757834a041dda73a

URL: https://github.com/llvm/llvm-project/commit/a3ab5120fd572215afeac190757834a041dda73a
DIFF: https://github.com/llvm/llvm-project/commit/a3ab5120fd572215afeac190757834a041dda73a.diff

LOG: [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

Added: 
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/default.lazy.verify.cpp

Modified: 
    libcxx/docs/ReleaseNotes.rst
    libcxx/docs/UsingLibcxx.rst
    libcxx/include/tuple
    libcxx/include/type_traits
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR27684_contains_ref_to_incomplete_type.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR31384.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_pair.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move_pair.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/deduct.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp

Removed: 
    libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/disable_reduced_arity_initialization_extension.pass.cpp
    libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/enable_reduced_arity_initialization_extension.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/tuple_array_template_depth.pass.cpp


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 00ada48ab3d68..1bcb26de840d9 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -43,4 +43,15 @@ New Features
 API Changes
 -----------
 
-- ...
+- There has been several changes in the tuple constructors provided by libc++.
+  Those changes were made as part of an effort to regularize libc++'s tuple
+  implementation, which contained several subtle bugs due to these extensions.
+  If you notice a build breakage when initializing a tuple, make sure you
+  properly initialize all the tuple elements - this is probably the culprit.
+
+  In particular, the extension allowing tuples to be constructed from fewer
+  elements than the number of elements in the tuple (in which case the remaining
+  elements would be default-constructed) has been removed. See https://godbolt.org/z/sqozjd.
+
+  Also, the extension allowing a tuple to be constructed from an array has been
+  removed. See https://godbolt.org/z/5esqbW.

diff  --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index f7de6f64c10df..f146abdf42a53 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -165,33 +165,6 @@ thread safety annotations.
   headers. The intended use case is for clients who wish to use the libc++
   headers without taking a dependency on the libc++ library itself.
 
-**_LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION**:
-  This macro is used to re-enable an extension in `std::tuple` which allowed
-  it to be implicitly constructed from fewer initializers than contained
-  elements. Elements without an initializer are default constructed. For example:
-
-  .. code-block:: cpp
-
-    std::tuple<std::string, int, std::error_code> foo() {
-      return {"hello world", 42}; // default constructs error_code
-    }
-
-
-  Since libc++ 4.0 this extension has been disabled by default. This macro
-  may be defined to re-enable it in order to support existing code that depends
-  on the extension. New use of this extension should be discouraged.
-  See `PR 27374 <https://llvm.org/PR27374>`_ for more information.
-
-  Note: The "reduced-arity-initialization" extension is still offered but only
-  for explicit conversions. Example:
-
-  .. code-block:: cpp
-
-    auto foo() {
-      using Tup = std::tuple<std::string, int, std::error_code>;
-      return Tup{"hello world", 42}; // explicit constructor called. OK.
-    }
-
 **_LIBCPP_DISABLE_ADDITIONAL_DIAGNOSTICS**:
   This macro disables the additional diagnostics generated by libc++ using the
   `diagnose_if` attribute. These additional diagnostics include checks for:

diff  --git a/libcxx/include/tuple b/libcxx/include/tuple
index 2b11a3f80e4a3..9a58d6af7dad2 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -449,165 +449,6 @@ class _LIBCPP_TEMPLATE_VIS tuple
 
     _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,57 +458,69 @@ class _LIBCPP_TEMPLATE_VIS tuple
     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) {}
-
-    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) {}
+        _NOEXCEPT_(_And<is_nothrow_default_constructible<_Tp>...>::value)
+    { }
 
-    tuple(tuple const&) = default;
-    tuple(tuple&&) = default;
+    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)
+    { }
 
-    template <class _AllocArgT, class _Alloc, _EnableIf<
-             _CheckArgsConstructor<_IsSame<allocator_arg_t, _AllocArgT>::value >::__enable_implicit_default()
-      , void*> = nullptr
-    >
+    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(_AllocArgT, _Alloc const& __a)
+    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 _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)
+    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...>()) {}
 
-    template <bool _Dummy = true,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                            _Dummy
-                         >::template __enable_implicit<_Tp const&...>(),
-                         bool
-                      >::type = false
-        >
+    // 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_((__all<is_nothrow_copy_constructible<_Tp>::value...>::value))
+    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(),
@@ -675,17 +528,16 @@ public:
                 __t...
                ) {}
 
-    template <bool _Dummy = true,
-              typename enable_if
-                      <
-                         _CheckArgsConstructor<
-                            _Dummy
-                         >::template __enable_explicit<_Tp const&...>(),
-                         bool
-                      >::type = false
-        >
+    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_((__all<is_nothrow_copy_constructible<_Tp>::value...>::value))
+    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(),
@@ -693,17 +545,15 @@ public:
                 __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)
+    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(),
@@ -712,18 +562,15 @@ public:
                 __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)
+    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(),
@@ -732,158 +579,368 @@ public:
                 __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(),
+    // 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,
-              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(),
+    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,
-              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,
+    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,
-              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,
+    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)...) {}
 
-    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)) {}
+    // Copy and move constructors (including the allocator_arg_t variants)
+    tuple(const tuple&) = default;
+    tuple(tuple&&) = default;
 
-    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, 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 _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) {}
+    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 _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 ..._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 _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 ..._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(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
+        : __base_(allocator_arg_t(), __a, __t)
+    { }
+
+    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)
+    { }
+
+    // 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(tuple<_Up...>&& __t)
+        _NOEXCEPT_((_And<is_nothrow_constructible<_Tp, _Up>...>::value))
+        : __base_(_VSTD::move(__t))
+    { }
+
+    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(tuple<_Up...>&& __t)
+        _NOEXCEPT_((_And<is_nothrow_constructible<_Tp, _Up>...>::value))
+        : __base_(_VSTD::move(__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, 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))
+    { }
+
+    // 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 _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 _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 _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 _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 _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)
+    { }
+
+    // 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 _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 _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

diff  --git a/libcxx/include/type_traits b/libcxx/include/type_traits
index 9ba5d25abc254..e68021ea9b1a5 100644
--- a/libcxx/include/type_traits
+++ b/libcxx/include/type_traits
@@ -3206,18 +3206,18 @@ _LIBCPP_INLINE_VAR _LIBCPP_CONSTEXPR bool is_default_constructible_v
 template <class _Tp>
 void __test_implicit_default_constructible(_Tp);
 
-template <class _Tp, class = void, bool = is_default_constructible<_Tp>::value>
+template <class _Tp, class = void, class = typename is_default_constructible<_Tp>::type>
 struct __is_implicitly_default_constructible
     : false_type
 { };
 
 template <class _Tp>
-struct __is_implicitly_default_constructible<_Tp, decltype(__test_implicit_default_constructible<_Tp const&>({})), true>
+struct __is_implicitly_default_constructible<_Tp, decltype(__test_implicit_default_constructible<_Tp const&>({})), true_type>
     : true_type
 { };
 
 template <class _Tp>
-struct __is_implicitly_default_constructible<_Tp, decltype(__test_implicit_default_constructible<_Tp const&>({})), false>
+struct __is_implicitly_default_constructible<_Tp, decltype(__test_implicit_default_constructible<_Tp const&>({})), false_type>
     : false_type
 { };
 #endif // !C++03

diff  --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/disable_reduced_arity_initialization_extension.pass.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/disable_reduced_arity_initialization_extension.pass.cpp
deleted file mode 100644
index 53e8f5f4098ce..0000000000000
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/disable_reduced_arity_initialization_extension.pass.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// <tuple>
-
-// template <class... Types> class tuple;
-
-// template <class... UTypes>
-//   explicit tuple(UTypes&&... u);
-
-// UNSUPPORTED: c++03
-
-#include <tuple>
-#include <cassert>
-#include <type_traits>
-#include <string>
-#include <system_error>
-
-#include "test_macros.h"
-#include "test_convertible.h"
-#include "MoveOnly.h"
-
-#if defined(_LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION)
-#error This macro should not be defined by default
-#endif
-
-struct NoDefault { NoDefault() = delete; };
-
-
-// Make sure the _Up... constructor SFINAEs out when the types that
-// are not explicitly initialized are not all default constructible.
-// Otherwise, std::is_constructible would return true but instantiating
-// the constructor would fail.
-void test_default_constructible_extension_sfinae()
-{
-    typedef MoveOnly MO;
-    typedef NoDefault ND;
-    {
-        typedef std::tuple<MO, ND> Tuple;
-        static_assert(!std::is_constructible<Tuple, MO>::value, "");
-        static_assert(std::is_constructible<Tuple, MO, ND>::value, "");
-        static_assert(test_convertible<Tuple, MO, ND>(), "");
-    }
-    {
-        typedef std::tuple<MO, MO, ND> Tuple;
-        static_assert(!std::is_constructible<Tuple, MO, MO>::value, "");
-        static_assert(std::is_constructible<Tuple, MO, MO, ND>::value, "");
-        static_assert(test_convertible<Tuple, MO, MO, ND>(), "");
-    }
-    {
-        // Same idea as above but with a nested tuple type.
-        typedef std::tuple<MO, ND> Tuple;
-        typedef std::tuple<MO, Tuple, MO, MO> NestedTuple;
-
-        static_assert(!std::is_constructible<
-            NestedTuple, MO, MO, MO, MO>::value, "");
-        static_assert(std::is_constructible<
-            NestedTuple, MO, Tuple, MO, MO>::value, "");
-    }
-}
-
-using ExplicitTup = std::tuple<std::string, int, std::error_code>;
-ExplicitTup doc_example() {
-      return ExplicitTup{"hello world", 42}; // explicit constructor called. OK.
-}
-
-// Test that the example given in UsingLibcxx.rst actually works.
-void test_example_from_docs() {
-  auto tup = doc_example();
-  assert(std::get<0>(tup) == "hello world");
-  assert(std::get<1>(tup) == 42);
-  assert(std::get<2>(tup) == std::error_code{});
-}
-
-int main(int, char**)
-{
-    {
-        using E = MoveOnly;
-        using Tup = std::tuple<E, E, E>;
-        // Test that the reduced arity initialization extension is only
-        // allowed on the explicit constructor.
-        static_assert(test_convertible<Tup, E, E, E>(), "");
-
-        Tup t(E(0), E(1));
-        static_assert(std::is_constructible<Tup, E, E>::value, "");
-        static_assert(!test_convertible<Tup, E, E>(), "");
-        assert(std::get<0>(t) == E(0));
-        assert(std::get<1>(t) == E(1));
-        assert(std::get<2>(t) == E());
-
-        Tup t2(E(0));
-        static_assert(std::is_constructible<Tup, E>::value, "");
-        static_assert(!test_convertible<Tup, E>(), "");
-        assert(std::get<0>(t2) == E(0));
-        assert(std::get<1>(t2) == E());
-        assert(std::get<2>(t2) == E());
-    }
-    // Check that SFINAE is properly applied with the default reduced arity
-    // constructor extensions.
-    test_default_constructible_extension_sfinae();
-    test_example_from_docs();
-
-  return 0;
-}

diff  --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/enable_reduced_arity_initialization_extension.pass.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/enable_reduced_arity_initialization_extension.pass.cpp
deleted file mode 100644
index 5f941ad3e2e5c..0000000000000
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/enable_reduced_arity_initialization_extension.pass.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// <tuple>
-
-// template <class... Types> class tuple;
-
-// template <class... UTypes>
-//   explicit tuple(UTypes&&... u);
-
-// UNSUPPORTED: c++03
-
-// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_TUPLE_IMPLICIT_REDUCED_ARITY_EXTENSION
-
-#include <tuple>
-#include <cassert>
-#include <type_traits>
-#include <string>
-#include <system_error>
-
-#include "test_macros.h"
-#include "test_convertible.h"
-#include "MoveOnly.h"
-
-
-struct NoDefault { NoDefault() = delete; };
-
-
-// Make sure the _Up... constructor SFINAEs out when the types that
-// are not explicitly initialized are not all default constructible.
-// Otherwise, std::is_constructible would return true but instantiating
-// the constructor would fail.
-void test_default_constructible_extension_sfinae()
-{
-    typedef MoveOnly MO;
-    typedef NoDefault ND;
-    {
-        typedef std::tuple<MO, ND> Tuple;
-        static_assert(!std::is_constructible<Tuple, MO>::value, "");
-        static_assert(std::is_constructible<Tuple, MO, ND>::value, "");
-        static_assert(test_convertible<Tuple, MO, ND>(), "");
-    }
-    {
-        typedef std::tuple<MO, MO, ND> Tuple;
-        static_assert(!std::is_constructible<Tuple, MO, MO>::value, "");
-        static_assert(std::is_constructible<Tuple, MO, MO, ND>::value, "");
-        static_assert(test_convertible<Tuple, MO, MO, ND>(), "");
-    }
-    {
-        // Same idea as above but with a nested tuple type.
-        typedef std::tuple<MO, ND> Tuple;
-        typedef std::tuple<MO, Tuple, MO, MO> NestedTuple;
-
-        static_assert(!std::is_constructible<
-            NestedTuple, MO, MO, MO, MO>::value, "");
-        static_assert(std::is_constructible<
-            NestedTuple, MO, Tuple, MO, MO>::value, "");
-    }
-    {
-        typedef std::tuple<MO, int> Tuple;
-        typedef std::tuple<MO, Tuple, MO, MO> NestedTuple;
-
-        static_assert(std::is_constructible<
-            NestedTuple, MO, MO, MO, MO>::value, "");
-        static_assert(test_convertible<
-            NestedTuple, MO, MO, MO, MO>(), "");
-
-        static_assert(std::is_constructible<
-            NestedTuple, MO, Tuple, MO, MO>::value, "");
-        static_assert(test_convertible<
-            NestedTuple, MO, Tuple, MO, MO>(), "");
-    }
-}
-
-std::tuple<std::string, int, std::error_code> doc_example() {
-      return {"hello world", 42};
-}
-
-// Test that the example given in UsingLibcxx.rst actually works.
-void test_example_from_docs() {
-  auto tup = doc_example();
-  assert(std::get<0>(tup) == "hello world");
-  assert(std::get<1>(tup) == 42);
-  assert(std::get<2>(tup) == std::error_code{});
-}
-
-int main(int, char**)
-{
-
-    {
-        using E = MoveOnly;
-        using Tup = std::tuple<E, E, E>;
-        static_assert(test_convertible<Tup, E, E, E>(), "");
-
-        Tup t = {E(0), E(1)};
-        static_assert(test_convertible<Tup, E, E>(), "");
-        assert(std::get<0>(t) == E(0));
-        assert(std::get<1>(t) == E(1));
-        assert(std::get<2>(t) == E());
-
-        Tup t2 = {E(0)};
-        static_assert(test_convertible<Tup, E>(), "");
-        assert(std::get<0>(t2) == E(0));
-        assert(std::get<1>(t2) == E());
-        assert(std::get<2>(t2) == E());
-    }
-    // Check that SFINAE is properly applied with the default reduced arity
-    // constructor extensions.
-    test_default_constructible_extension_sfinae();
-    test_example_from_docs();
-
-  return 0;
-}

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp
index 65733616f56a3..88321edeb9fab 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp
@@ -11,7 +11,7 @@
 
 // <tuple>
 
-// See llvm.org/PR20855
+// See https://llvm.org/PR20855.
 
 #include <functional>
 #include <tuple>

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp
index 09a9bdf800cc1..648f8bfaffb1d 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp
@@ -12,13 +12,10 @@
 
 // template <class... Types> class tuple;
 
-// template <class TupleLike>
-//   tuple(TupleLike&&);
-// template <class Alloc, class TupleLike>
-//   tuple(std::allocator_arg_t, Alloc const&, TupleLike&&);
-
 // Check that the tuple-like ctors are properly disabled when the UTypes...
-// constructor should be selected. See PR22806.
+// constructor should be selected.
+//
+// See https://llvm.org/PR22806.
 
 #include <tuple>
 #include <memory>

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
index f2c66f4b5cacd..ebc31ce01cfc7 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
@@ -16,9 +16,11 @@
 //    EXPLICIT(...) tuple(UTypes&&...)
 
 // Check that the UTypes... ctor is properly disabled before evaluating any
-// SFINAE when the tuple-like copy/move ctor should *clearly* be selected
+// SFINAE when the copy/move ctor from another tuple should clearly be selected
 // instead. This happens 'sizeof...(UTypes) == 1' and the first element of
-// 'UTypes...' is an instance of the tuple itself. See PR23256.
+// 'UTypes...' is an instance of the tuple itself.
+//
+// See https://llvm.org/PR23256.
 
 #include <tuple>
 #include <memory>

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR27684_contains_ref_to_incomplete_type.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR27684_contains_ref_to_incomplete_type.pass.cpp
index a3289ebfcf855..cdc0ec2bc6fc8 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR27684_contains_ref_to_incomplete_type.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR27684_contains_ref_to_incomplete_type.pass.cpp
@@ -14,11 +14,7 @@
 
 // template <class Alloc> tuple(allocator_arg_t, Alloc const&)
 
-// Libc++ has to deduce the 'allocator_arg_t' parameter for this constructor
-// as 'AllocArgT'. Previously libc++ has tried to support tags derived from
-// 'allocator_arg_t' by using 'is_base_of<AllocArgT, allocator_arg_t>'.
-// However this breaks whenever a 2-tuple contains a reference to an incomplete
-// type as its first parameter. See PR27684.
+// See https://llvm.org/PR27684.
 
 #include <tuple>
 #include <cassert>

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR31384.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR31384.pass.cpp
index 055d9cb6e3d00..20f64daab0bf8 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR31384.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR31384.pass.cpp
@@ -9,11 +9,8 @@
 
 // UNSUPPORTED: c++03
 
-// <tuple>
+// See https://llvm.org/PR31384.
 
-// template <class TupleLike> tuple(TupleLike&&); // libc++ extension
-
-// See llvm.org/PR31384
 #include <tuple>
 #include <cassert>
 
@@ -50,42 +47,51 @@ int main(int, char**) {
   {
     std::tuple<Explicit> foo = Derived<int>{42}; ((void)foo);
     assert(count == 1);
-    std::tuple<Explicit> bar(Derived<int>{42}); ((void)bar);
+    Derived<int> d{42};
+    std::tuple<Explicit> bar(std::move(d)); ((void)bar);
+#if TEST_STD_VER < 17
+    assert(count == 1);
+#else
     assert(count == 2);
+#endif
   }
   count = 0;
   {
     std::tuple<Implicit> foo = Derived<int>{42}; ((void)foo);
     assert(count == 1);
-    std::tuple<Implicit> bar(Derived<int>{42}); ((void)bar);
+    Derived<int> d{42};
+    std::tuple<Implicit> bar(std::move(d)); ((void)bar);
+#if TEST_STD_VER < 17
+    assert(count == 1);
+#else
     assert(count == 2);
+#endif
   }
   count = 0;
   {
-    static_assert(!std::is_convertible<
-        ExplicitDerived<int>, std::tuple<Explicit>>::value, "");
-    std::tuple<Explicit> bar(ExplicitDerived<int>{42}); ((void)bar);
+    static_assert(!std::is_convertible<ExplicitDerived<int>, std::tuple<Explicit>>::value, "");
+    ExplicitDerived<int> d{42};
+    std::tuple<Explicit> bar(std::move(d)); ((void)bar);
+#if TEST_STD_VER < 17
+    assert(count == 0);
+#else
     assert(count == 1);
+#endif
   }
   count = 0;
   {
-    // FIXME: Libc++ incorrectly rejects this code.
-#ifndef _LIBCPP_VERSION
     std::tuple<Implicit> foo = ExplicitDerived<int>{42}; ((void)foo);
-    static_assert(std::is_convertible<
-        ExplicitDerived<int>, std::tuple<Implicit>>::value,
-        "correct STLs accept this");
-#else
-    static_assert(!std::is_convertible<
-        ExplicitDerived<int>, std::tuple<Implicit>>::value,
-        "libc++ incorrectly rejects this");
-#endif
+    static_assert(std::is_convertible<ExplicitDerived<int>, std::tuple<Implicit>>::value, "");
     assert(count == 0);
-    std::tuple<Implicit> bar(ExplicitDerived<int>{42}); ((void)bar);
+    ExplicitDerived<int> d{42};
+    std::tuple<Implicit> bar(std::move(d)); ((void)bar);
+#if TEST_STD_VER < 17
+    assert(count == 0);
+#else
     assert(count == 1);
+#endif
   }
   count = 0;
 
-
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp
index c28e49a5ca6f0..d6e41a496a3c7 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/UTypes.pass.cpp
@@ -36,11 +36,9 @@ struct A
 
 struct NoDefault { NoDefault() = delete; };
 
-// Make sure the _Up... constructor SFINAEs out when the types that
-// are not explicitly initialized are not all default constructible.
-// Otherwise, std::is_constructible would return true but instantiating
-// the constructor would fail.
-void test_default_constructible_extension_sfinae()
+// Make sure the _Up... constructor SFINAEs out when there are fewer
+// constructor arguments than tuple elements.
+void test_sfinae_missing_elements()
 {
     {
         typedef std::tuple<MoveOnly, NoDefault> Tuple;
@@ -83,23 +81,6 @@ void test_default_constructible_extension_sfinae()
             MoveOnly, Tuple, MoveOnly, MoveOnly
         >::value, "");
     }
-    // testing extensions
-#ifdef _LIBCPP_VERSION
-    {
-        typedef std::tuple<MoveOnly, int> Tuple;
-        typedef std::tuple<MoveOnly, Tuple, MoveOnly, MoveOnly> NestedTuple;
-
-        static_assert(std::is_constructible<
-            NestedTuple,
-            MoveOnly, MoveOnly, MoveOnly, MoveOnly
-        >::value, "");
-
-        static_assert(std::is_constructible<
-            NestedTuple,
-            MoveOnly, Tuple, MoveOnly, MoveOnly
-        >::value, "");
-    }
-#endif
 }
 
 int main(int, char**)
@@ -121,28 +102,6 @@ int main(int, char**)
         assert(std::get<1>(t) == 1);
         assert(std::get<2>(t) == 2);
     }
-    // extensions
-#ifdef _LIBCPP_VERSION
-    {
-        using E = MoveOnly;
-        using Tup = std::tuple<E, E, E>;
-        // Test that the reduced arity initialization extension is only
-        // allowed on the explicit constructor.
-        static_assert(test_convertible<Tup, E, E, E>(), "");
-
-        Tup t(E(0), E(1));
-        static_assert(!test_convertible<Tup, E, E>(), "");
-        assert(std::get<0>(t) == E(0));
-        assert(std::get<1>(t) == E(1));
-        assert(std::get<2>(t) == E());
-
-        Tup t2(E(0));
-        static_assert(!test_convertible<Tup, E>(), "");
-        assert(std::get<0>(t2) == E(0));
-        assert(std::get<1>(t2) == E());
-        assert(std::get<2>(t2) == E());
-    }
-#endif
 #if TEST_STD_VER > 11
     {
         constexpr std::tuple<Empty> t0{Empty()};
@@ -153,9 +112,8 @@ int main(int, char**)
         static_assert(std::get<0>(t).id_ == 3, "");
     }
 #endif
-    // Check that SFINAE is properly applied with the default reduced arity
-    // constructor extensions.
-    test_default_constructible_extension_sfinae();
+
+    test_sfinae_missing_elements();
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp
index 5267f69e5da44..f3ddc859aff27 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc.pass.cpp
@@ -15,10 +15,6 @@
 // template <class Alloc>
 //   explicit(see-below) tuple(allocator_arg_t, const Alloc& a);
 
-// NOTE: this constructor does not currently support tags derived from
-// allocator_arg_t because libc++ has to deduce the parameter as a template
-// argument. See PR27684 (https://llvm.org/PR27684)
-
 #include <tuple>
 #include <cassert>
 
@@ -94,6 +90,14 @@ int main(int, char**)
         assert(!alloc_last::allocator_constructed);
         assert(std::get<2>(t) == alloc_last());
     }
+    {
+        // Test that we can use a tag derived from allocator_arg_t
+        struct DerivedFromAllocatorArgT : std::allocator_arg_t { };
+        DerivedFromAllocatorArgT derived;
+        std::tuple<> t1(derived, A1<int>());
+        std::tuple<int> t2(derived, A1<int>());
+        std::tuple<int, int> t3(derived, A1<int>());
+    }
     {
         // Test that the uses-allocator default constructor does not evaluate
         // its SFINAE when it otherwise shouldn't be selected. Do this by

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp
index f5055b34483bc..2ded9d5de7085 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_Types.pass.cpp
@@ -95,6 +95,14 @@ int main(int, char**)
         assert(!alloc_last::allocator_constructed);
         assert(std::get<2>(t) == alloc_last(3));
     }
+    {
+        // Test that we can use a tag derived from allocator_arg_t
+        struct DerivedFromAllocatorArgT : std::allocator_arg_t { };
+        DerivedFromAllocatorArgT derived;
+        std::tuple<> t1(derived, A1<int>());
+        std::tuple<int> t2(derived, A1<int>(), 1);
+        std::tuple<int, int> t3(derived, A1<int>(), 1, 2);
+    }
 
-  return 0;
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_pair.pass.cpp
index 0772e211b8444..6b81f716b0f02 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_pair.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_pair.pass.cpp
@@ -56,6 +56,13 @@ int main(int, char**)
         assert(std::get<0>(t1) == 2);
         assert(std::get<1>(t1) == 3);
     }
+    {
+        // Test that we can use a tag derived from allocator_arg_t
+        struct DerivedFromAllocatorArgT : std::allocator_arg_t { };
+        DerivedFromAllocatorArgT derived;
+        std::pair<int, int> p(1, 2);
+        std::tuple<int, int> t(derived, A1<int>(), p);
+    }
 
-  return 0;
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp
index 1f784bf576241..681e2e70c5f1d 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp
@@ -87,6 +87,13 @@ int main(int, char**)
         std::tuple<Implicit> t2 = {std::allocator_arg, std::allocator<int>{}, t1};
         assert(std::get<0>(t2).value == 42);
     }
+    {
+        // Test that we can use a tag derived from allocator_arg_t
+        struct DerivedFromAllocatorArgT : std::allocator_arg_t { };
+        DerivedFromAllocatorArgT derived;
+        std::tuple<long> from(3l);
+        std::tuple<long long> t0(derived, A1<int>(), from);
+    }
 
-  return 0;
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp
index 7179bf971ae6e..b823e8f2afe91 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp
@@ -101,6 +101,13 @@ int main(int, char**)
         std::tuple<Implicit> t2 = {std::allocator_arg, std::allocator<int>{}, std::move(t1)};
         assert(std::get<0>(t2).value == 42);
     }
+    {
+        // Test that we can use a tag derived from allocator_arg_t
+        struct DerivedFromAllocatorArgT : std::allocator_arg_t { };
+        DerivedFromAllocatorArgT derived;
+        std::tuple<long> from(3l);
+        std::tuple<long long> t0(derived, A1<int>(), std::move(from));
+    }
 
-  return 0;
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp
index e101808f35cbe..72a8fbfc59558 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_copy.pass.cpp
@@ -52,8 +52,6 @@ int main(int, char**)
         assert(alloc_last::allocator_constructed);
         assert(std::get<0>(t) == 2);
     }
-// testing extensions
-#ifdef _LIBCPP_VERSION
     {
         typedef std::tuple<alloc_first, alloc_last> T;
         T t0(2, 3);
@@ -77,7 +75,13 @@ int main(int, char**)
         assert(std::get<1>(t) == 2);
         assert(std::get<2>(t) == 3);
     }
-#endif
+    {
+        // Test that we can use a tag derived from allocator_arg_t
+        struct DerivedFromAllocatorArgT : std::allocator_arg_t { };
+        DerivedFromAllocatorArgT derived;
+        std::tuple<int> from(3);
+        std::tuple<int> t0(derived, A1<int>(), from);
+    }
 
-  return 0;
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp
index 58de67e4f38c1..d13f76ff4e059 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move.pass.cpp
@@ -53,8 +53,6 @@ int main(int, char**)
         assert(alloc_last::allocator_constructed);
         assert(std::get<0>(t) == 1);
     }
-// testing extensions
-#ifdef _LIBCPP_VERSION
     {
         typedef std::tuple<MoveOnly, alloc_first> T;
         T t0(0 ,1);
@@ -76,7 +74,13 @@ int main(int, char**)
         assert(std::get<1>(t) == 2);
         assert(std::get<2>(t) == 3);
     }
-#endif
+    {
+        // Test that we can use a tag derived from allocator_arg_t
+        struct DerivedFromAllocatorArgT : std::allocator_arg_t { };
+        DerivedFromAllocatorArgT derived;
+        std::tuple<int> from(3);
+        std::tuple<int> t0(derived, A1<int>(), std::move(from));
+    }
 
-  return 0;
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move_pair.pass.cpp
index ee8e00519cba1..e7e438ee73a17 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move_pair.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_move_pair.pass.cpp
@@ -52,6 +52,13 @@ int main(int, char**)
         assert(std::get<0>(t1) == 2);
         assert(std::get<1>(t1)->id_ == 3);
     }
+    {
+        // Test that we can use a tag derived from allocator_arg_t
+        struct DerivedFromAllocatorArgT : std::allocator_arg_t { };
+        DerivedFromAllocatorArgT derived;
+        std::pair<int, int> from(1, 2);
+        std::tuple<int, int> t0(derived, A1<int>(), std::move(from));
+    }
 
-  return 0;
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp
index b78cc06ae6ec9..9609abb3a4d3e 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_Types.pass.cpp
@@ -137,28 +137,6 @@ int main(int, char**)
         assert(std::get<2>(t) == 2);
         assert(std::get<3>(t) == 3);
     }
-// extensions
-#ifdef _LIBCPP_VERSION
-    {
-        std::tuple<int, char*, std::string> t(2);
-        assert(std::get<0>(t) == 2);
-        assert(std::get<1>(t) == nullptr);
-        assert(std::get<2>(t) == "");
-    }
-    {
-        std::tuple<int, char*, std::string> t(2, nullptr);
-        assert(std::get<0>(t) == 2);
-        assert(std::get<1>(t) == nullptr);
-        assert(std::get<2>(t) == "");
-    }
-    {
-        std::tuple<int, char*, std::string, double> t(2, nullptr, "text");
-        assert(std::get<0>(t) == 2);
-        assert(std::get<1>(t) == nullptr);
-        assert(std::get<2>(t) == "text");
-        assert(std::get<3>(t) == 0.0);
-    }
-#endif
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/deduct.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/deduct.pass.cpp
index db0958ca6de18..b882e660a94ff 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/deduct.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/deduct.pass.cpp
@@ -13,7 +13,7 @@
 // GCC's implementation of class template deduction is still immature and runs
 // into issues with libc++. However GCC accepts this code when compiling
 // against libstdc++.
-// XFAIL: gcc-5, gcc-6, gcc-7
+// XFAIL: gcc-5, gcc-6, gcc-7, gcc-8, gcc-9, gcc-10
 
 // <tuple>
 

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/default.lazy.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/default.lazy.verify.cpp
new file mode 100644
index 0000000000000..e3ad94da67661
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/default.lazy.verify.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// This test makes sure that we don't evaluate `is_default_constructible<T>`
+// too early in std::tuple's default constructor.
+
+// UNSUPPORTED: c++03
+
+#include <tuple>
+
+#include "test_macros.h"
+
+struct Outer {
+    template <class T>
+    struct Inner {
+        bool foo = false;
+    };
+    std::tuple<Inner<int>> tup;
+};
+
+Outer x; // expected-no-diagnostics

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp
index 7f7c76cb72864..93f8fbc7bc40c 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp
@@ -12,61 +12,18 @@
 
 // UNSUPPORTED: c++03
 
+// Test the following constructors:
+// (1) tuple(Types const&...)
+// (2) tuple(UTypes&&...)
+// Test that (1) short circuits before evaluating the copy constructor of the
+// second argument. Constructor (2) should be selected.
+
 #include <tuple>
 #include <utility>
 #include <cassert>
 
 #include "test_macros.h"
 
-template <class ConstructFrom>
-struct ConstructibleFromT {
-  ConstructibleFromT() = default;
-  ConstructibleFromT(ConstructFrom v) : value(v) {}
-  ConstructFrom value;
-};
-
-template <class AssertOn>
-struct CtorAssertsT {
-  bool defaulted;
-  CtorAssertsT() : defaulted(true) {}
-  template <class T>
-  constexpr CtorAssertsT(T) : defaulted(false) {
-      static_assert(!std::is_same<T, AssertOn>::value, "");
-  }
-};
-
-template <class AllowT, class AssertT>
-struct AllowAssertT {
-  AllowAssertT() = default;
-  AllowAssertT(AllowT) {}
-  template <class U>
-  constexpr AllowAssertT(U) {
-      static_assert(!std::is_same<U, AssertT>::value, "");
-  }
-};
-
-// Construct a tuple<T1, T2> from pair<int, int> where T1 and T2
-// are not constructible from ints but T1 is constructible from std::pair.
-// This considers the following constructors:
-// (1) tuple(TupleLike) -> checks is_constructible<Tn, int>
-// (2) tuple(UTypes...) -> checks is_constructible<T1, pair<int, int>>
-//                            and is_default_constructible<T2>
-// The point of this test is to ensure that the consideration of (1)
-// short circuits before evaluating is_constructible<T2, int>, which
-// will cause a static assertion.
-void test_tuple_like_lazy_sfinae() {
-#if defined(_LIBCPP_VERSION)
-    // This test requires libc++'s reduced arity initialization.
-    using T1 = ConstructibleFromT<std::pair<int, int>>;
-    using T2 = CtorAssertsT<int>;
-    std::pair<int, int> p(42, 100);
-    std::tuple<T1, T2> t(p);
-    assert(std::get<0>(t).value == p);
-    assert(std::get<1>(t).defaulted);
-#endif
-}
-
-
 struct NonConstCopyable {
   NonConstCopyable() = default;
   explicit NonConstCopyable(int v) : value(v) {}
@@ -84,22 +41,11 @@ struct BlowsUpOnConstCopy {
   BlowsUpOnConstCopy(BlowsUpOnConstCopy&) = default;
 };
 
-// Test the following constructors:
-// (1) tuple(Types const&...)
-// (2) tuple(UTypes&&...)
-// Test that (1) short circuits before evaluating the copy constructor of the
-// second argument. Constructor (2) should be selected.
-void test_const_Types_lazy_sfinae()
-{
-    NonConstCopyable v(42);
-    BlowsUpOnConstCopy<int> b;
-    std::tuple<NonConstCopyable, BlowsUpOnConstCopy<int>> t(v, b);
-    assert(std::get<0>(t).value == 42);
-}
-
 int main(int, char**) {
-    test_tuple_like_lazy_sfinae();
-    test_const_Types_lazy_sfinae();
+  NonConstCopyable v(42);
+  BlowsUpOnConstCopy<int> b;
+  std::tuple<NonConstCopyable, BlowsUpOnConstCopy<int>> t(v, b);
+  assert(std::get<0>(t).value == 42);
 
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/tuple_array_template_depth.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/tuple_array_template_depth.pass.cpp
deleted file mode 100644
index 11ba79a41cd35..0000000000000
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/tuple_array_template_depth.pass.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03
-
-// <tuple>
-
-// template <class... Types> class tuple;
-
-// template <class Tuple, __tuple_convertible<Tuple, tuple> >
-//   tuple(Tuple &&);
-//
-// template <class Tuple, __tuple_constructible<Tuple, tuple> >
-//   tuple(Tuple &&);
-
-// This test checks that we do not evaluate __make_tuple_types
-// on the array.
-
-#include <array>
-#include <tuple>
-
-#include "test_macros.h"
-
-// Use 1256 to try and blow the template instantiation depth for all compilers.
-typedef std::array<char, 1256> array_t;
-typedef std::tuple<array_t> tuple_t;
-
-int main(int, char**)
-{
-    array_t arr;
-    tuple_t tup(arr);
-
-  return 0;
-}


        


More information about the libcxx-commits mailing list