[libcxx-commits] [libcxx] [libc++] Use `__reference_constructs_from_temporary` if eligible (PR #141916)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jun 2 16:33:15 PDT 2025


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/141916

>From 417b08ace5e0703fb0dbea2ba703cc182131b9cd Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 29 May 2025 17:19:36 +0800
Subject: [PATCH 1/4] [libc++] Use `__reference_constructs_from_temporary` if
 eligible

Currently, libc++'s `<tuple>` is using the deprecated
`__reference_binds_to_temporary` intrinsic. This PR starts to use
`__reference_constructs_from_temporary` if possible.

It seems that `__reference_constructs_from_temporary` should be used via
an internal type traits provided in
`<__type_traits/reference_constructs_from_temporary.h>`. But given the
old intrinsic was directly used, this PR doesn't switch to the current
convention yet.

P2255R2 is related. Although the paper indicated that constructors of
`tuple` should be deleted in such a case.
---
 libcxx/include/tuple                                  |  4 +++-
 .../PR20855_tuple_ref_binding_diagnostics.verify.cpp  |  2 +-
 .../PR20855_tuple_ref_binding_diagnostics.pass.cpp    | 11 ++++++-----
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 8dd62ae624f5e..9aacde40b7be0 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -310,7 +310,9 @@ class __tuple_leaf {
 
   template <class _Tp>
   static _LIBCPP_HIDE_FROM_ABI constexpr bool __can_bind_reference() {
-#    if __has_keyword(__reference_binds_to_temporary)
+#    if __has_builtin(__reference_constructs_from_temporary)
+    return !__reference_constructs_from_temporary(_Hp, _Tp);
+#    elif __has_keyword(__reference_binds_to_temporary)
     return !__reference_binds_to_temporary(_Hp, _Tp);
 #    else
     return true;
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 4a6e3095c1019..df068bf35f2e5 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
@@ -40,7 +40,7 @@ void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
 
 void f() {
-#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_binds_to_temporary)
+#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_constructs_from_temporary)
   // 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}}
 
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 463816929353b..1da967d89718f 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
@@ -18,12 +18,13 @@
 #include <cassert>
 #include "test_macros.h"
 
-#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_binds_to_temporary)
-# define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(__reference_binds_to_temporary(__VA_ARGS__), "")
-# define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(!__reference_binds_to_temporary(__VA_ARGS__), "")
+#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_constructs_from_temporary)
+#  define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(__reference_constructs_from_temporary(__VA_ARGS__), "")
+#  define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...)                                                                    \
+    static_assert(!__reference_constructs_from_temporary(__VA_ARGS__), "")
 #else
-# define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "")
-# define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "")
+#  define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "")
+#  define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "")
 #endif
 
 template <class Tp>

>From 4bfdddae8ef19c899c9615e05c69319af77431e8 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sun, 1 Jun 2025 11:01:47 +0800
Subject: [PATCH 2/4] Switch to use internal type trait

---
 .../reference_constructs_from_temporary.h        | 13 +++++++++++++
 libcxx/include/tuple                             | 16 +++-------------
 ...0855_tuple_ref_binding_diagnostics.verify.cpp |  9 ++++++++-
 ...R20855_tuple_ref_binding_diagnostics.pass.cpp |  5 ++++-
 4 files changed, 28 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index 1c62e58cc4e12..32a7c1b8b1a4a 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -30,6 +30,19 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 
 #endif
 
+#if __has_builtin(__reference_constructs_from_temporary)
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
+#elif __has_builtin(__reference_binds_to_temporary)
+// TODO: Remove this once all support compilers have __reference_constructs_from_temporary implemented.
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v = __reference_binds_to_temporary(_Tp, _Up);
+#else
+// TODO: Remove this once https://github.com/llvm/llvm-project/issues/111477 no longer affects supported compilers.
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v = false;
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_REFERENCE_CONSTRUCTS_FROM_TEMPORARY_H
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 9aacde40b7be0..6e7a430d219ea 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -258,6 +258,7 @@ template <class... Types>
 #  include <__type_traits/maybe_const.h>
 #  include <__type_traits/nat.h>
 #  include <__type_traits/negation.h>
+#  include <__type_traits/reference_constructs_from_temporary.h>
 #  include <__type_traits/remove_cv.h>
 #  include <__type_traits/remove_cvref.h>
 #  include <__type_traits/remove_reference.h>
@@ -308,17 +309,6 @@ template <size_t _Ip, class _Hp, bool>
 class __tuple_leaf {
   _Hp __value_;
 
-  template <class _Tp>
-  static _LIBCPP_HIDE_FROM_ABI constexpr bool __can_bind_reference() {
-#    if __has_builtin(__reference_constructs_from_temporary)
-    return !__reference_constructs_from_temporary(_Hp, _Tp);
-#    elif __has_keyword(__reference_binds_to_temporary)
-    return !__reference_binds_to_temporary(_Hp, _Tp);
-#    else
-    return true;
-#    endif
-  }
-
 public:
   _LIBCPP_CONSTEXPR_SINCE_CXX14 __tuple_leaf& operator=(const __tuple_leaf&) = delete;
 
@@ -348,7 +338,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __tuple_leaf(_Tp&& __t) noexcept(is_nothrow_constructible<_Hp, _Tp>::value)
       : __value_(std::forward<_Tp>(__t)) {
-    static_assert(__can_bind_reference<_Tp&&>(),
+    static_assert(!__reference_constructs_from_temporary_v<_Hp, _Tp&&>,
                   "Attempted construction of reference element binds to a temporary whose lifetime has ended");
   }
 
@@ -356,7 +346,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI
   _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __tuple_leaf(integral_constant<int, 0>, const _Alloc&, _Tp&& __t)
       : __value_(std::forward<_Tp>(__t)) {
-    static_assert(__can_bind_reference<_Tp&&>(),
+    static_assert(!__reference_constructs_from_temporary_v<_Hp, _Tp&&>,
                   "Attempted construction of reference element binds to a temporary whose lifetime has ended");
   }
 
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 df068bf35f2e5..f8841180a41df 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
@@ -38,9 +38,16 @@ template <class T> struct CannotDeduce {
 template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
+#if TEST_HAS_BUILTIN(__reference_constructs_from_temporary)
+#  define TEST_HAS_REFERENCE_BINDING_TRAIT 1
+#elif TEST_HAS_BUILTIN(__reference_binds_to_temporary)
+#  define TEST_HAS_REFERENCE_BINDING_TRAIT 1
+#else
+#  define TEST_HAS_REFERENCE_BINDING_TRAIT 0
+#endif
 
 void f() {
-#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_constructs_from_temporary)
+#if TEST_HAS_REFERENCE_BINDING_TRAIT
   // 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}}
 
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 1da967d89718f..aec1e63f33ce7 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
@@ -18,10 +18,13 @@
 #include <cassert>
 #include "test_macros.h"
 
-#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_constructs_from_temporary)
+#if TEST_HAS_BUILTIN(__reference_constructs_from_temporary)
 #  define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(__reference_constructs_from_temporary(__VA_ARGS__), "")
 #  define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...)                                                                    \
     static_assert(!__reference_constructs_from_temporary(__VA_ARGS__), "")
+#elif TEST_HAS_BUILTIN(__reference_binds_to_temporary)
+#  define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(__reference_binds_to_temporary(__VA_ARGS__), "")
+#  define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(!__reference_binds_to_temporary(__VA_ARGS__), "")
 #else
 #  define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "")
 #  define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "")

>From 8af46236d1cee5fa60643913b908c842caf12339 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 2 Jun 2025 23:13:37 +0800
Subject: [PATCH 3/4] Remove inactive code branches

---
 .../reference_constructs_from_temporary.h           |  8 ++------
 ...PR20855_tuple_ref_binding_diagnostics.verify.cpp | 13 -------------
 .../PR20855_tuple_ref_binding_diagnostics.pass.cpp  |  6 ++----
 3 files changed, 4 insertions(+), 23 deletions(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index 32a7c1b8b1a4a..9caea4afc3bf7 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -33,14 +33,10 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 #if __has_builtin(__reference_constructs_from_temporary)
 template <class _Tp, class _Up>
 inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
-#elif __has_builtin(__reference_binds_to_temporary)
-// TODO: Remove this once all support compilers have __reference_constructs_from_temporary implemented.
-template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = __reference_binds_to_temporary(_Tp, _Up);
 #else
-// TODO: Remove this once https://github.com/llvm/llvm-project/issues/111477 no longer affects supported compilers.
+// TODO(LLVM 22): Remove this as all support compilers should have __reference_constructs_from_temporary implemented.
 template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = false;
+inline const bool __reference_constructs_from_temporary_v = __reference_binds_to_temporary(_Tp, _Up);
 #endif
 
 _LIBCPP_END_NAMESPACE_STD
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 f8841180a41df..a1a80483c4487 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
@@ -38,16 +38,7 @@ template <class T> struct CannotDeduce {
 template <class ...Args>
 void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
 
-#if TEST_HAS_BUILTIN(__reference_constructs_from_temporary)
-#  define TEST_HAS_REFERENCE_BINDING_TRAIT 1
-#elif TEST_HAS_BUILTIN(__reference_binds_to_temporary)
-#  define TEST_HAS_REFERENCE_BINDING_TRAIT 1
-#else
-#  define TEST_HAS_REFERENCE_BINDING_TRAIT 0
-#endif
-
 void f() {
-#if TEST_HAS_REFERENCE_BINDING_TRAIT
   // 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}}
 
@@ -80,8 +71,4 @@ void f() {
     std::tuple<std::string &&> t2("hello"); // expected-note {{requested here}}
     std::tuple<std::string &&> t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}}
   }
-#else
-#error force failure
-// expected-error at -1 {{force failure}}
-#endif
 }
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 aec1e63f33ce7..d78de0eec8e53 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
@@ -22,12 +22,10 @@
 #  define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(__reference_constructs_from_temporary(__VA_ARGS__), "")
 #  define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...)                                                                    \
     static_assert(!__reference_constructs_from_temporary(__VA_ARGS__), "")
-#elif TEST_HAS_BUILTIN(__reference_binds_to_temporary)
+#else
+// TODO(LLVM 22): Remove this as all support compilers should have __reference_constructs_from_temporary implemented.
 #  define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(__reference_binds_to_temporary(__VA_ARGS__), "")
 #  define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(!__reference_binds_to_temporary(__VA_ARGS__), "")
-#else
-#  define ASSERT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "")
-#  define ASSERT_NOT_REFERENCE_BINDS_TEMPORARY(...) static_assert(true, "")
 #endif
 
 template <class Tp>

>From e72fc82e238f4d4faf5a8d14117c4cc7525c4385 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 3 Jun 2025 01:36:20 +0800
Subject: [PATCH 4/4] Adopt @philnik777's review comment for comments

Co-authored-by: Nikolas Klauser <nikolasklauser at berlin.de>
---
 .../include/__type_traits/reference_constructs_from_temporary.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index 9caea4afc3bf7..2ff549b4e15ce 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -34,7 +34,7 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo
 template <class _Tp, class _Up>
 inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
 #else
-// TODO(LLVM 22): Remove this as all support compilers should have __reference_constructs_from_temporary implemented.
+// TODO(LLVM 22): Remove this as all supported compilers should have __reference_constructs_from_temporary implemented.
 template <class _Tp, class _Up>
 inline const bool __reference_constructs_from_temporary_v = __reference_binds_to_temporary(_Tp, _Up);
 #endif



More information about the libcxx-commits mailing list