From 437ad06f762ab07d89badecdd20627db200b98d3 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Tue, 3 Jun 2025 10:34:45 +0800 Subject: [PATCH] [libc++] Use `__reference_constructs_from_temporary` if eligible (#141916) Currently, libc++'s `` 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 --- .../reference_constructs_from_temporary.h | 9 +++++++++ libcxx/include/tuple | 14 +++----------- ...R20855_tuple_ref_binding_diagnostics.verify.cpp | 6 ------ .../PR20855_tuple_ref_binding_diagnostics.pass.cpp | 12 +++++++----- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h index 1c62e58cc4e1..2ff549b4e15c 100644 --- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h +++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h @@ -30,6 +30,15 @@ _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_tempo #endif +#if __has_builtin(__reference_constructs_from_temporary) +template +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 +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/libcxx/include/tuple b/libcxx/include/tuple index 8dd62ae624f5..6e7a430d219e 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -258,6 +258,7 @@ template # 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 @@ template class __tuple_leaf { _Hp __value_; - template - 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 @@ 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"); } @@ -354,7 +346,7 @@ public: _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __tuple_leaf(integral_constant, 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 4a6e3095c101..a1a80483c448 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,7 @@ template struct CannotDeduce { template void F(typename CannotDeduce>::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 @@ void f() { std::tuple t2("hello"); // expected-note {{requested here}} std::tuple t3(std::allocator_arg, alloc, "hello"); // expected-note {{requested here}} } -#else -#error force failure -// expected-error@-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 463816929353..d78de0eec8e5 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,14 @@ #include #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