[libc++] Use `__reference_constructs_from_temporary` if eligible (#141916)
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.
---------
Co-authored-by: Nikolas Klauser <nikolasklauser@berlin.de>
GitOrigin-RevId: 437ad06f762ab07d89badecdd20627db200b98d3
diff --git a/include/__type_traits/reference_constructs_from_temporary.h b/include/__type_traits/reference_constructs_from_temporary.h
index 1c62e58..2ff549b 100644
--- a/include/__type_traits/reference_constructs_from_temporary.h
+++ b/include/__type_traits/reference_constructs_from_temporary.h
@@ -30,6 +30,15 @@
#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);
+#else
+// 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
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___TYPE_TRAITS_REFERENCE_CONSTRUCTS_FROM_TEMPORARY_H
diff --git a/include/tuple b/include/tuple
index 8dd62ae..6e7a430 100644
--- a/include/tuple
+++ b/include/tuple
@@ -258,6 +258,7 @@
# 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,15 +309,6 @@
class __tuple_leaf {
_Hp __value_;
- template <class _Tp>
- static _LIBCPP_HIDE_FROM_ABI constexpr bool __can_bind_reference() {
-# if __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;
@@ -346,7 +338,7 @@
_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");
}
@@ -354,7 +346,7 @@
_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/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp b/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
index 4a6e309..a1a8048 100644
--- a/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
+++ b/test/libcxx/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.verify.cpp
@@ -38,9 +38,7 @@
template <class ...Args>
void F(typename CannotDeduce<std::tuple<Args...>>::type const&) {}
-
void f() {
-#if TEST_HAS_BUILTIN_IDENTIFIER(__reference_binds_to_temporary)
// Test that we emit our diagnostic from the library.
// expected-error@tuple:* 8 {{Attempted construction of reference element binds to a temporary whose lifetime has ended}}
@@ -73,8 +71,4 @@
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@-1 {{force failure}}
-#endif
}
diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp
index 4638169..d78de0e 100644
--- a/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp
+++ b/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR20855_tuple_ref_binding_diagnostics.pass.cpp
@@ -18,12 +18,14 @@
#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(__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, "")
+// 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__), "")
#endif
template <class Tp>