[libcxx-commits] [libcxx] [libc++] P2255R2: Add deleted tuple constructor overloads (PR #205379)

Yihan Wang via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jun 30 00:07:23 PDT 2026


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/205379

>From 7f99d7a7375be8f9cfdcf9ca715ecf29ff61380a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:51:43 +0800
Subject: [PATCH 1/8] [libc++] P2255R2: Add deleted tuple constructor overloads

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/docs/Status/Cxx23Papers.csv            |   2 +-
 libcxx/include/tuple                          | 247 ++++++++++++++++--
 ...5_tuple_ref_binding_diagnostics.verify.cpp |  24 +-
 .../ref_constructs_from_temporary.verify.cpp  |  74 ++++++
 4 files changed, 309 insertions(+), 38 deletions(-)
 create mode 100644 libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp

diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb580ea891f5b..7e9f16fbe77ba 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -43,7 +43,7 @@
 "`P0627R6 <https://wg21.link/P0627R6>`__","Function to mark unreachable code","2022-02 (Virtual)","|Complete|","15","`#105175 <https://github.com/llvm/llvm-project/issues/105175>`__",""
 "`P1206R7 <https://wg21.link/P1206R7>`__","``ranges::to``: A function to convert any range to a container","2022-02 (Virtual)","|Complete|","17","`#105176 <https://github.com/llvm/llvm-project/issues/105176>`__",""
 "`P1413R3 <https://wg21.link/P1413R3>`__","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","2022-02 (Virtual)","|Complete|","","`#105177 <https://github.com/llvm/llvm-project/issues/105177>`__","``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations."
-"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits only."
+"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits and changes to tuple constructors only."
 "`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","`#105182 <https://github.com/llvm/llvm-project/issues/105182>`__",""
 "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
 "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 2b32df6bce74a..fabf0a1a43a70 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -633,21 +633,55 @@ public:
               _Not<_IsThisTuple<_Up...> >, // extension to allow mis-behaved user constructors
               is_constructible<_Tp, _Up>... > {};
 
-  template <class... _Up,
-            __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
-                           int> = 0>
+  template <class... _Up>
+  struct _EnableUTypesCtorBase
+      : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+
+  template <bool, class... _Up>
+  struct _EnableUTypesCtorNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class... _Up>
+  struct _CtorCreatesDanglingReference : false_type {};
+
+  template <class... _Up>
+  struct _CtorCreatesDanglingReference<true, _Up...>
+      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+
+  template <class... _Up>
+  struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+
+  template <class... _Up>
+  struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+#    else
+  template <class... _Up>
+  struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class... _Up>
+  struct _EnableNonDanglingUTypesCtor
+      : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+
+  template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
 
-  template <class _Alloc,
-            class... _Up,
-            __enable_if_t< _And< _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> >::value,
-                           int> = 0>
+  template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
       : __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
 
+#    if _LIBCPP_STD_VER >= 23
+  template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+  template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+#    endif // _LIBCPP_STD_VER >= 23
+
   // Copy and move constructors (including the allocator_arg_t variants)
   tuple(const tuple&) = default;
   tuple(tuple&&)      = default;
@@ -689,58 +723,134 @@ public:
                      _Not<is_convertible<_OtherTuple, _Tp> >...,
                      _Not<is_constructible<_Tp, _OtherTuple> >... > > > {};
 
-  template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+  template <bool, class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
+  struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
+
+  template <class _OtherTuple, class... _Up>
+  struct _CtorFromUTypesTupleCreatesDanglingReference<
+      true,
+      _OtherTuple,
+      tuple<_Up...>,
+      __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
+
+  template <class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
+      : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+
+  template <class _OtherTuple>
+  struct _EnableDanglingCtorFromUTypesTuple
+      : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+#    else
+  template <class _OtherTuple>
+  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class _OtherTuple>
+  struct _EnableNonDanglingCtorFromUTypesTuple
+      : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+
+  template <class... _Up,
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
 
   template <class... _Up,
             class _Alloc,
-            __enable_if_t< _And< _EnableCtorFromUTypesTuple<const tuple<_Up...>&> >::value, int> = 0>
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class... _Up,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+      tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
+
+  template <class... _Up,
+            class _Alloc,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
+
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
       : __base_(__from_tuple(), __t) {}
 
-  template <class _Alloc, class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up, __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+  template <class... _Up,
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
 
   template <class _Alloc,
             class... _Up,
-            __enable_if_t< _And< _EnableCtorFromUTypesTuple<tuple<_Up...>&&> >::value, int> = 0>
+            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
+
+  template <class _Alloc,
+            class... _Up,
+            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up,
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
 
+  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+      tuple(const tuple<_Up...>&&) = delete;
+
   template <class _Alloc,
             class... _Up,
-            enable_if_t< _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
+
+  template <class _Alloc,
+            class... _Up,
+            enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+      tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(const pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -758,6 +868,34 @@ public:
   template <class _Pair>
   struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
 
+  template <bool, class _Pair>
+  struct _EnableCtorFromPairNoDangling : false_type {};
+
+#    if _LIBCPP_STD_VER >= 23
+  template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
+  struct _CtorFromPairCreatesDanglingReference : false_type {};
+
+  template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
+  struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
+      : _Or<
+            _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+            _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+
+  template <class _Pair>
+  struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+
+  template <class _Pair>
+  struct _EnableDanglingCtorFromPair
+      : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+#    else
+  template <class _Pair>
+  struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+#    endif // _LIBCPP_STD_VER >= 23
+
+  template <class _Pair>
+  struct _EnableNonDanglingCtorFromPair
+      : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
 
@@ -771,7 +909,7 @@ public:
   template <class _Up1,
             class _Up2,
             template <class...> class _And                                                   = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -781,35 +919,64 @@ public:
             class _Up1,
             class _Up2,
             template <class...> class _And                                                   = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class _Up1,
+            class _Up2,
+            template <class...> class _And                                                    = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
+          delete;
+
+  template <class _Alloc,
+            class _Up1,
+            class _Up2,
+            template <class...> class _And                                                    = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+      tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
+
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>& __p)
       : __base_(__from_tuple(), __p) {}
 
+  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+      tuple(pair<_U1, _U2>&) = delete;
+
   template <class _Alloc,
             class _U1,
             class _U2,
-            enable_if_t< _EnableCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
-#    endif
+
+  template <class _Alloc,
+            class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+      tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
+#    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
   template <class _Up1,
             class _Up2,
             template <class...> class _And                                              = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -819,27 +986,59 @@ public:
             class _Up1,
             class _Up2,
             template <class...> class _And                                              = _And,
-            __enable_if_t< _And< _EnableCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
 
 #    if _LIBCPP_STD_VER >= 23
+  template <class _Up1,
+            class _Up2,
+            template <class...> class _And                                               = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+      tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
+
+  template <class _Alloc,
+            class _Up1,
+            class _Up2,
+            template <class...> class _And                                               = _And,
+            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+      tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
+
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1,
+            class _U2,
+            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
+  template <class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+      tuple(const pair<_U1, _U2>&&) = delete;
+
   template <class _Alloc,
             class _U1,
             class _U2,
-            enable_if_t< _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
+
+  template <class _Alloc,
+            class _U1,
+            class _U2,
+            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+      tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // [tuple.assign]
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a1a80483c4487..a44a38e3e17ed 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
+// REQUIRES: std-at-least-c++23
 
 // <tuple>
 
@@ -39,36 +40,33 @@ template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
 void f() {
-  // Test that we emit our diagnostic from the library.
-  // expected-error at tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
-
-  // Good news everybody! Clang now diagnoses this for us!
-  // expected-error at tuple:* 0+ {{reference member '__value_' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
+  // Test that the public constructors are deleted when constructing a reference
+  // element would bind to a temporary.
 
   {
-    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-note 1 {{requested here}}
+    F<int, const std::string&>(std::make_tuple(1, "abc")); // expected-error {{deleted}}
   }
   {
-    std::tuple<int, const std::string&> t(1, "a"); // expected-note 1 {{requested here}}
+    std::tuple<int, const std::string&> t(1, "a"); // expected-error {{deleted}}
   }
   {
-    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-note 1 {{requested here}}
+    F<int, const std::string&>(std::tuple<int, const std::string&>(1, "abc")); // expected-error {{deleted}}
   }
   {
     ConvertsTo<int&> ct;
-    std::tuple<const long&, int> t(ct, 42); // expected-note {{requested here}}
+    std::tuple<const long&, int> t(ct, 42); // expected-error {{deleted}}
   }
   {
     ConvertsTo<int> ct;
-    std::tuple<int const&, void*> t(ct, nullptr); // expected-note {{requested here}}
+    std::tuple<int const&, void*> t(ct, nullptr); // expected-error {{deleted}}
   }
   {
     ConvertsTo<Derived> ct;
-    std::tuple<Base const&, int> t(ct, 42); // expected-note {{requested here}}
+    std::tuple<Base const&, int> t(ct, 42); // expected-error {{deleted}}
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string &&> t2("hello"); // expected-note {{requested here}}
-    std::tuple<std::string &&> t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}}
+    std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
   }
 }
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
new file mode 100644
index 0000000000000..c7a5f4e247f3b
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/ref_constructs_from_temporary.verify.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <tuple>
+
+// tuple(UTypes&&... u);
+// tuple(tuple<UTypes...>& u);
+// tuple(const tuple<UTypes...>& u);
+// tuple(tuple<UTypes...>&& u);
+// tuple(const tuple<UTypes...>&& u);
+// tuple(pair<U1, U2>& u);
+// tuple(const pair<U1, U2>& u);
+// tuple(pair<U1, U2>&& u);
+// tuple(const pair<U1, U2>&& u);
+//
+// The constructors above are defined as deleted if reference_constructs_from_temporary_v
+// is true for one of the corresponding tuple elements.
+
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+struct X {
+  X(int);
+};
+
+static_assert(std::is_constructible_v<std::tuple<const X&>, X&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, int>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&>, const std::tuple<int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, std::pair<int, int>&&>);
+static_assert(!std::is_constructible_v<std::tuple<const X&, int>, const std::pair<int, int>&&>);
+
+void test() {
+  std::allocator<int> alloc;
+  std::tuple<int> t(1);
+  const std::tuple<int> ct(1);
+  std::pair<int, int> p(1, 2);
+  const std::pair<int, int> cp(1, 2);
+
+  // expected-error-re@*:* 18 {{call to deleted constructor of 'std::tuple<{{.*}}>'}}
+  std::tuple<const X&> utypes(1);
+  std::tuple<const X&> alloc_utypes(std::allocator_arg, alloc, 1);
+
+  std::tuple<const X&> tuple_lvalue(t);
+  std::tuple<const X&> alloc_tuple_lvalue(std::allocator_arg, alloc, t);
+  std::tuple<const X&> const_tuple_lvalue(ct);
+  std::tuple<const X&> alloc_const_tuple_lvalue(std::allocator_arg, alloc, ct);
+  std::tuple<const X&> tuple_rvalue(std::tuple<int>{1});
+  std::tuple<const X&> alloc_tuple_rvalue(std::allocator_arg, alloc, std::tuple<int>{1});
+  std::tuple<const X&> const_tuple_rvalue(std::move(ct));
+  std::tuple<const X&> alloc_const_tuple_rvalue(std::allocator_arg, alloc, std::move(ct));
+
+  std::tuple<const X&, int> pair_lvalue(p);
+  std::tuple<const X&, int> alloc_pair_lvalue(std::allocator_arg, alloc, p);
+  std::tuple<const X&, int> const_pair_lvalue(cp);
+  std::tuple<const X&, int> alloc_const_pair_lvalue(std::allocator_arg, alloc, cp);
+  std::tuple<const X&, int> pair_rvalue(std::pair<int, int>{1, 2});
+  std::tuple<const X&, int> alloc_pair_rvalue(std::allocator_arg, alloc, std::pair<int, int>{1, 2});
+  std::tuple<const X&, int> const_pair_rvalue(std::move(cp));
+  std::tuple<const X&, int> alloc_const_pair_rvalue(std::allocator_arg, alloc, std::move(cp));
+}

>From 6f3f035857999ee6e2ec274db5b3b1688bf401a2 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Wed, 24 Jun 2026 00:56:58 +0800
Subject: [PATCH 2/8] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple                          | 62 ++++++++-----------
 ...5_tuple_ref_binding_diagnostics.verify.cpp |  2 +-
 2 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index fabf0a1a43a70..8c6a81e6073ce 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -634,8 +634,7 @@ public:
               is_constructible<_Tp, _Up>... > {};
 
   template <class... _Up>
-  struct _EnableUTypesCtorBase
-      : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
+  struct _EnableUTypesCtorBase : _And<_BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>, _EnableUTypesCtor<_Up...> > {};
 
   template <bool, class... _Up>
   struct _EnableUTypesCtorNoDangling : false_type {};
@@ -659,8 +658,7 @@ public:
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
-  struct _EnableNonDanglingUTypesCtor
-      : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+  struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
 
   template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
@@ -731,11 +729,10 @@ public:
   struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
 
   template <class _OtherTuple, class... _Up>
-  struct _CtorFromUTypesTupleCreatesDanglingReference<
-      true,
-      _OtherTuple,
-      tuple<_Up...>,
-      __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
+  struct _CtorFromUTypesTupleCreatesDanglingReference< true,
+                                                       _OtherTuple,
+                                                       tuple<_Up...>,
+                                                       __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
       : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
 
   template <class _OtherTuple>
@@ -754,8 +751,7 @@ public:
   struct _EnableNonDanglingCtorFromUTypesTuple
       : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
 
-  template <class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
@@ -768,8 +764,7 @@ public:
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
@@ -803,8 +798,7 @@ public:
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
@@ -829,8 +823,7 @@ public:
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
@@ -877,24 +870,22 @@ public:
 
   template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
   struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
-      : _Or<
-            _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
-            _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
+      : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
+             _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
 
   template <class _Pair>
   struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
 
   template <class _Pair>
-  struct _EnableDanglingCtorFromPair
-      : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
+  };
 #    else
   template <class _Pair>
   struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>
-  struct _EnableNonDanglingCtorFromPair
-      : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
 
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -908,7 +899,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                   = _And,
+            template <class...> class _And                                                              = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -918,7 +909,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                   = _And,
+            template <class...> class _And                                                              = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -928,17 +919,16 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                    = _And,
+            template <class...> class _And                                                            = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
-      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) =
-          delete;
+      tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                    = _And,
+            template <class...> class _And                                                            = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
@@ -975,7 +965,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                              = _And,
+            template <class...> class _And                                                         = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -985,7 +975,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                              = _And,
+            template <class...> class _And                                                         = _And,
             __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -995,7 +985,7 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                               = _And,
+            template <class...> class _And                                                       = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -1004,7 +994,7 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                               = _And,
+            template <class...> class _And                                                       = _And,
             __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
@@ -1019,9 +1009,7 @@ public:
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
-  template <class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&&) = delete;
 
diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index a44a38e3e17ed..15d032fbb3a7a 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -66,7 +66,7 @@ void f() {
   }
   {
     std::allocator<int> alloc;
-    std::tuple<std::string&&> t2("hello"); // expected-error {{deleted}}
+    std::tuple<std::string&&> t2("hello");                            // expected-error {{deleted}}
     std::tuple<std::string&&> t3(std::allocator_arg, alloc, "hello"); // expected-error {{deleted}}
   }
 }

>From 03cf6c8b536234b8da2133c7a883232cfbbdfdda Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:41:49 -0700
Subject: [PATCH 3/8] [libc++] Refine the implementation use template variable
 and make reference_constructs_from_temporary SFINAE friendly

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h     |  15 +-
 libcxx/include/tuple                          | 240 ++++++++----------
 2 files changed, 126 insertions(+), 129 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index a8325620414ea..c18723fb77d81 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_reference.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -30,8 +31,20 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 
 #endif
 
+// A non-reference type can never bind to a temporary, so the result is always `false` for such a
+// `_Tp`. We short-circuit before reaching the builtin because Clang's `__reference_constructs_from_temporary`
+// eagerly instantiates the construction of `_Up` (including the element's constructor exception
+// specification) even when `_Tp` is not a reference, which can hard-error on misbehaved types.
+//
+// https://godbolt.org/z/WMTK5cMa4
+//
+// TODO: remove this guard once the Clang builtin short-circuits on a non-reference first operand.
+template <class _Tp, class _Up, bool = is_reference<_Tp>::value>
+inline const bool __reference_constructs_from_temporary_v = false;
+
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, true> =
+    __reference_constructs_from_temporary(_Tp, _Up);
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 8c6a81e6073ce..76ab9da34aabe 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -640,42 +640,60 @@ public:
   struct _EnableUTypesCtorNoDangling : false_type {};
 
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class... _Up>
-  struct _CtorCreatesDanglingReference : false_type {};
-
   template <class... _Up>
-  struct _CtorCreatesDanglingReference<true, _Up...>
-      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, _Up&&> >...> {};
+  static constexpr bool __has_danglings() {
+    // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
+    // equal length. The discarded branch is not instantiated when the lengths differ.
+    if constexpr (sizeof...(_Up) == sizeof...(_Tp))
+      return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
+    else
+      return false;
+  }
 
-  template <class... _Up>
-  struct _EnableUTypesCtorNoDangling<true, _Up...> : _Not<_CtorCreatesDanglingReference<true, _Up...> > {};
+  template <class _Tuple,
+            class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  static inline constexpr bool __has_danglings_from_tuple_like = false;
+
+  template <class _Tuple, size_t... _Idx>
+  static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
+    enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
   template <class... _Up>
-  struct _EnableDanglingUTypesCtor : _CtorCreatesDanglingReference<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
-#    else
+  static inline constexpr bool __has_danglings_from_utypes =
+      _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+
   template <class... _Up>
-  struct _EnableUTypesCtorNoDangling<true, _Up...> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes =
+      _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
-  struct _EnableNonDanglingUTypesCtor : _EnableUTypesCtorNoDangling<_EnableUTypesCtorBase<_Up...>::value, _Up...> {};
+  static inline constexpr bool __enable_utypes_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return __has_no_danglings_from_utypes<_Up...>;
+#    else
+    return _EnableUTypesCtorBase<_Up...>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
-  template <class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+  template <class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&... __u) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__forward_args{}, std::forward<_Up>(__u)...) {}
 
-  template <class _Alloc, class... _Up, __enable_if_t< _EnableNonDanglingUTypesCtor<_Up...>::value, int> = 0>
+  template <class _Alloc, class... _Up, __enable_if_t< __enable_utypes_constructor_no_dangling<_Up...>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, _Up&&... __u)
       : __base_(allocator_arg_t(), __a, __forward_args{}, std::forward<_Up>(__u)...) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes<_Up...>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
-  template <class _Alloc, class... _Up, __enable_if_t< _EnableDanglingUTypesCtor<_Up...>::value, long> = 0>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes<_Up...>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -725,123 +743,116 @@ public:
   struct _EnableCtorFromUTypesTupleNoDangling : false_type {};
 
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class _OtherTuple, class _DecayedOtherTuple = __remove_cvref_t<_OtherTuple>, class = void>
-  struct _CtorFromUTypesTupleCreatesDanglingReference : false_type {};
-
-  template <class _OtherTuple, class... _Up>
-  struct _CtorFromUTypesTupleCreatesDanglingReference< true,
-                                                       _OtherTuple,
-                                                       tuple<_Up...>,
-                                                       __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)> >
-      : _Or<_BoolConstant<__reference_constructs_from_temporary_v<_Tp, __copy_cvref_t<_OtherTuple, _Up> > >...> {};
-
   template <class _OtherTuple>
-  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple>
-      : _Not<_CtorFromUTypesTupleCreatesDanglingReference<true, _OtherTuple> > {};
+  static inline constexpr bool __has_danglings_from_utypes_tuple =
+      _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
 
   template <class _OtherTuple>
-  struct _EnableDanglingCtorFromUTypesTuple
-      : _CtorFromUTypesTupleCreatesDanglingReference<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
-#    else
-  template <class _OtherTuple>
-  struct _EnableCtorFromUTypesTupleNoDangling<true, _OtherTuple> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes_tuple =
+      _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _OtherTuple>
-  struct _EnableNonDanglingCtorFromUTypesTuple
-      : _EnableCtorFromUTypesTupleNoDangling<_EnableCtorFromUTypesTuple<_OtherTuple>::value, _OtherTuple> {};
+  static inline constexpr bool __enable_utypes_tuple_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return __has_no_danglings_from_utypes_tuple<_OtherTuple>;
+#    else
+    return _EnableCtorFromUTypesTuple<_OtherTuple>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
-  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+  template <class... _Up,
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>& __t) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value)
       : __base_(__from_tuple(), __t) {}
 
   template <class... _Up,
             class _Alloc,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, int> = 0>
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<const tuple<_Up...>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, const tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __t) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
-  template <class... _Up,
-            class _Alloc,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&>::value, long> = 0>
+  template <class... _Up, class _Alloc>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
 
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>& __t)
       : __base_(__from_tuple(), __t) {}
 
-  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __t) {}
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
-  template <class... _Up, __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+  template <class... _Up, __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&& __t) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value)
       : __base_(__from_tuple(), std::move(__t)) {}
 
   template <class _Alloc,
             class... _Up,
-            __enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, int> = 0>
+            __enable_if_t< __enable_utypes_tuple_constructor_no_dangling<tuple<_Up...>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __a, tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__t)) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class... _Up, __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            __enable_if_t< _EnableDanglingCtorFromUTypesTuple<tuple<_Up...>&&>::value, long> = 0>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
-  template <class... _Up, enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&& __t)
       : __base_(__from_tuple(), std::move(__t)) {}
 
-  template <class... _Up, enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(const tuple<_Up...>&&) = delete;
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableNonDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__t)) {}
 
-  template <class _Alloc,
-            class... _Up,
-            enable_if_t< _EnableDanglingCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+  template <class _Alloc, class... _Up>
+    requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -861,31 +872,24 @@ public:
   template <class _Pair>
   struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair> {};
 
-  template <bool, class _Pair>
-  struct _EnableCtorFromPairNoDangling : false_type {};
-
 #    if _LIBCPP_STD_VER >= 23
-  template <bool, class _Pair, class _DecayedPair = __remove_cvref_t<_Pair>, class _Tuple = tuple>
-  struct _CtorFromPairCreatesDanglingReference : false_type {};
-
-  template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
-  struct _CtorFromPairCreatesDanglingReference<true, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> >
-      : _Or< _BoolConstant<__reference_constructs_from_temporary_v<_Tp1, __copy_cvref_t<_Pair, _Up1> > >,
-             _BoolConstant<__reference_constructs_from_temporary_v<_Tp2, __copy_cvref_t<_Pair, _Up2> > > > {};
-
   template <class _Pair>
-  struct _EnableCtorFromPairNoDangling<true, _Pair> : _Not<_CtorFromPairCreatesDanglingReference<true, _Pair> > {};
+  static inline constexpr bool __has_danglings_from_utypes_pair =
+      _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
 
   template <class _Pair>
-  struct _EnableDanglingCtorFromPair : _CtorFromPairCreatesDanglingReference<_EnableCtorFromPair<_Pair>::value, _Pair> {
-  };
-#    else
-  template <class _Pair>
-  struct _EnableCtorFromPairNoDangling<true, _Pair> : true_type {};
+  static inline constexpr bool __has_no_danglings_from_utypes_pair =
+      _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>
-  struct _EnableNonDanglingCtorFromPair : _EnableCtorFromPairNoDangling<_EnableCtorFromPair<_Pair>::value, _Pair> {};
+  static inline constexpr bool __enable_utypes_pair_constructor_no_dangling() {
+#    if _LIBCPP_STD_VER >= 23
+    return _EnableCtorFromPair<_Pair>::value && __has_no_danglings_from_utypes_pair<_Pair>;
+#    else
+    return _EnableCtorFromPair<_Pair>::value;
+#    endif // _LIBCPP_STD_VER >= 23
+  }
 
   template <class _Pair>
   struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair> {};
@@ -899,8 +903,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                              = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>& __p) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value)
@@ -909,54 +912,46 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                              = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, int> = 0>
+            __enable_if_t<__enable_utypes_pair_constructor_no_dangling<const pair<_Up1, _Up2>&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, const pair<_Up1, _Up2>& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), __p) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class _Up1,
-            class _Up2,
-            template <class...> class _And                                                            = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  template <class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
-  template <class _Alloc,
-            class _Up1,
-            class _Up2,
-            template <class...> class _And                                                            = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<const pair<_Up1, _Up2>&> >::value, long> = 0>
+  template <class _Alloc, class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
 
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
 
-  template <class _U1, class _U2, enable_if_t< _EnableNonDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>& __p)
       : __base_(__from_tuple(), __p) {}
 
-  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(pair<_U1, _U2>&) = delete;
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), __p) {}
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
@@ -965,8 +960,7 @@ public:
 
   template <class _Up1,
             class _Up2,
-            template <class...> class _And                                                         = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&& __p) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value)
@@ -975,56 +969,46 @@ public:
   template <class _Alloc,
             class _Up1,
             class _Up2,
-            template <class...> class _And                                                         = _And,
-            __enable_if_t< _And< _EnableNonDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, int> = 0>
+            __enable_if_t< __enable_utypes_pair_constructor_no_dangling<pair<_Up1, _Up2>&&>(), int> = 0>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc& __a, pair<_Up1, _Up2>&& __p)
       : __base_(allocator_arg_t(), __a, __from_tuple(), std::move(__p)) {}
 
 #    if _LIBCPP_STD_VER >= 23
-  template <class _Up1,
-            class _Up2,
-            template <class...> class _And                                                       = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  template <class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
 
-  template <class _Alloc,
-            class _Up1,
-            class _Up2,
-            template <class...> class _And                                                       = _And,
-            __enable_if_t< _And< _EnableDanglingCtorFromPair<pair<_Up1, _Up2>&&> >::value, long> = 0>
+  template <class _Alloc, class _Up1, class _Up2>
+    requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
 
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
 
-  template <class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&& __p)
       : __base_(__from_tuple(), std::move(__p)) {}
 
-  template <class _U1, class _U2, enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(const pair<_U1, _U2>&&) = delete;
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableNonDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
       : __base_(allocator_arg_t(), __alloc, __from_tuple(), std::move(__p)) {}
 
-  template <class _Alloc,
-            class _U1,
-            class _U2,
-            enable_if_t< _EnableDanglingCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+  template <class _Alloc, class _U1, class _U2>
+    requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23

>From 28a00ab051c72c567a6c5082efb9dcb6963f3a68 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 09:46:03 -0700
Subject: [PATCH 4/8] [libc++] Enable test for all language modes

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 15d032fbb3a7a..0e9992194f03c 100644
--- a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
-// REQUIRES: std-at-least-c++23
 
 // <tuple>
 

>From c5d0b0e748a328c6041d0aaa8009663772e1e89a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:06:24 -0700
Subject: [PATCH 5/8] Remove unnecessary for deleted ctors

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 46 +++++++++++++++++---------------------------
 1 file changed, 18 insertions(+), 28 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 76ab9da34aabe..c368fe3ab6aea 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -689,13 +689,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes<_Up...>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(_Up&&...) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes<_Up...>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, _Up&&...) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // Copy and move constructors (including the allocator_arg_t variants)
@@ -777,12 +776,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(const tuple<_Up...>&) noexcept(_And<is_nothrow_constructible<_Tp, const _Up&>...>::value) = delete;
 
   template <class... _Up, class _Alloc>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&) = delete;
 
   // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
@@ -794,7 +793,7 @@ public:
 
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
+  explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(tuple<_Up...>&) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_no_danglings_from_utypes_tuple<tuple<_Up...>&>
@@ -804,8 +803,7 @@ public:
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<_Up&, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
+  explicit(!_And<is_convertible<_Up&, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
   // tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
@@ -824,13 +822,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value)
       tuple(tuple<_Up...>&&) noexcept(_And<is_nothrow_constructible<_Tp, _Up>...>::value) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(!_And<is_convertible<_Up, _Tp>...>::value)
-      tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
+  explicit(!_And<is_convertible<_Up, _Tp>...>::value) tuple(allocator_arg_t, const _Alloc&, tuple<_Up...>&&) = delete;
 
   // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
 
@@ -842,8 +839,7 @@ public:
 
   template <class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
-      tuple(const tuple<_Up...>&&) = delete;
+  explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value) tuple(const tuple<_Up...>&&) = delete;
 
   template <class _Alloc, class... _Up>
     requires __has_no_danglings_from_utypes_tuple<const tuple<_Up...>&&>
@@ -853,7 +849,7 @@ public:
 
   template <class _Alloc, class... _Up>
     requires __has_danglings_from_utypes_tuple<const tuple<_Up...>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
+  explicit(!_And<is_convertible<const _Up&&, _Tp>...>::value)
       tuple(allocator_arg_t, const _Alloc&, const tuple<_Up...>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
@@ -921,14 +917,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(const pair<_Up1, _Up2>&) noexcept(_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value) = delete;
 
   template <class _Alloc, class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<const pair<_Up1, _Up2>&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> >::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_Up1, _Up2>&) = delete;
 
   // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
@@ -941,8 +935,7 @@ public:
 
   template <class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<pair<_U1, _U2>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
-      tuple(pair<_U1, _U2>&) = delete;
+  explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value) tuple(pair<_U1, _U2>&) = delete;
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_no_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
@@ -952,7 +945,7 @@ public:
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<std::pair<_U1, _U2>&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+  explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 
@@ -978,14 +971,12 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(pair<_Up1, _Up2>&&) noexcept(_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value) = delete;
 
   template <class _Alloc, class _Up1, class _Up2>
     requires __has_danglings_from_utypes_pair<pair<_Up1, _Up2>&&>
-  _LIBCPP_HIDE_FROM_ABI
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
+  explicit(_Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> >::value)
       tuple(allocator_arg_t, const _Alloc&, pair<_Up1, _Up2>&&) = delete;
 
   // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
@@ -998,8 +989,7 @@ public:
 
   template <class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
-      tuple(const pair<_U1, _U2>&&) = delete;
+  explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value) tuple(const pair<_U1, _U2>&&) = delete;
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_no_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
@@ -1009,7 +999,7 @@ public:
 
   template <class _Alloc, class _U1, class _U2>
     requires __has_danglings_from_utypes_pair<const pair<_U1, _U2>&&>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+  explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
       tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete;
 #    endif // _LIBCPP_STD_VER >= 23
 

>From 12b049f103620f05a538a9c395d2dbc95bbf2020 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:13:12 -0700
Subject: [PATCH 6/8] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index c368fe3ab6aea..dd462132e4f31 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -650,13 +650,14 @@ public:
       return false;
   }
 
-  template <class _Tuple,
-            class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
+  template <class _Tuple, class _Seq = make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>, class = void>
   static inline constexpr bool __has_danglings_from_tuple_like = false;
 
   template <class _Tuple, size_t... _Idx>
-  static inline constexpr bool __has_danglings_from_tuple_like<_Tuple, index_sequence<_Idx...>,
-    enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
+  static inline constexpr bool __has_danglings_from_tuple_like<
+      _Tuple,
+      index_sequence<_Idx...>,
+      enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
   template <class... _Up>
   static inline constexpr bool __has_danglings_from_utypes =

>From df278e7b6f50fc7b81e516084a398ee3d44918d8 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Mon, 29 Jun 2026 10:20:32 -0700
Subject: [PATCH 7/8] Remove unnecessary comments

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 2 --
 1 file changed, 2 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index dd462132e4f31..1b88307ec5aa2 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -642,8 +642,6 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class... _Up>
   static constexpr bool __has_danglings() {
-    // The fold expands `_Tp` and `_Up` in lockstep, so it is only well-formed when the packs have
-    // equal length. The discarded branch is not instantiated when the lengths differ.
     if constexpr (sizeof...(_Up) == sizeof...(_Tp))
       return (__reference_constructs_from_temporary_v<_Tp, _Up&&> || ...);
     else

>From 887e5af402386ef92a56ae7c880fd7d7fec2ea49 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 00:06:59 -0700
Subject: [PATCH 8/8] [libc++] Fix hard error in deleted dangling ctor
 constraints

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 libcxx/include/tuple | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 22641af8f494c..0a95aa635663d 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -640,13 +640,25 @@ public:
       index_sequence<_Idx...>,
       enable_if_t<__has_danglings<decltype(std::get<_Idx>(std::declval<_Tuple>()))...>()>> = true;
 
+  template <bool __dangles, class _CtorEnabled>
+  static inline constexpr bool __check_danglings_from_constructor = false;
+
+  template <class _CtorEnabled>
+  static inline constexpr bool __check_danglings_from_constructor<true, _CtorEnabled> = _CtorEnabled::value;
+
+  template <bool __dangles, class _CtorEnabled>
+  static inline constexpr bool __check_no_danglings_from_constructor = _CtorEnabled::value;
+
+  template <class _CtorEnabled>
+  static inline constexpr bool __check_no_danglings_from_constructor<true, _CtorEnabled> = false;
+
   template <class... _Up>
   static inline constexpr bool __has_danglings_from_utypes =
-      _EnableUTypesCtorBase<_Up...>::value && __has_danglings<_Up...>();
+      __check_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
 
   template <class... _Up>
   static inline constexpr bool __has_no_danglings_from_utypes =
-      _EnableUTypesCtorBase<_Up...>::value && !__has_danglings<_Up...>();
+      __check_no_danglings_from_constructor<__has_danglings<_Up...>(), _EnableUTypesCtorBase<_Up...> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class... _Up>
@@ -726,11 +738,13 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _OtherTuple>
   static inline constexpr bool __has_danglings_from_utypes_tuple =
-      _EnableCtorFromUTypesTuple<_OtherTuple>::value && __has_danglings_from_tuple_like<_OtherTuple>;
+      __check_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+                                         _EnableCtorFromUTypesTuple<_OtherTuple> >;
 
   template <class _OtherTuple>
   static inline constexpr bool __has_no_danglings_from_utypes_tuple =
-      _EnableCtorFromUTypesTuple<_OtherTuple>::value && !__has_danglings_from_tuple_like<_OtherTuple>;
+      __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_OtherTuple>,
+                                            _EnableCtorFromUTypesTuple<_OtherTuple> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _OtherTuple>
@@ -853,11 +867,11 @@ public:
 #    if _LIBCPP_STD_VER >= 23
   template <class _Pair>
   static inline constexpr bool __has_danglings_from_utypes_pair =
-      _EnableCtorFromPair<_Pair>::value && __has_danglings_from_tuple_like<_Pair>;
+      __check_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
 
   template <class _Pair>
   static inline constexpr bool __has_no_danglings_from_utypes_pair =
-      _EnableCtorFromPair<_Pair>::value && !__has_danglings_from_tuple_like<_Pair>;
+      __check_no_danglings_from_constructor<__has_danglings_from_tuple_like<_Pair>, _EnableCtorFromPair<_Pair> >;
 #    endif // _LIBCPP_STD_VER >= 23
 
   template <class _Pair>



More information about the libcxx-commits mailing list