[libcxx-commits] [libcxx] [libc++] Make `std::reference_constructs_from_temporary` SFINAE friendly when the 1st template argument is not a reference type (PR #206679)
Yihan Wang via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jun 30 01:26:11 PDT 2026
https://github.com/yronglin created https://github.com/llvm/llvm-project/pull/206679
Suppose that `__reference_constructs_from_temporary` is defined as:
```cpp
__reference_constructs_from_temporary(_Tp, _Up);
```
A non-reference type can never bind to a temporary, so the result is always `false` for such a `_Tp`. We should short-circuit before reaching the instantiations by check the type of `_Tp`. But 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.
The following code should be accepted, but clang & libc++ raise a hard error:
https://godbolt.org/z/YGo8dG1n5
```cpp
#include <type_traits>
struct NoConv {};
struct Bad { template<class T> Bad(T v) noexcept(noexcept(member_ = v)) {} int member_; };
static_assert(!std::reference_constructs_from_temporary<Bad, NoConv&&>::value);
```
This builtin was fixed in clang 23, we need to add workaround for libc++ to support the old version clang.
>From 15646c33e3f7183f269ec0c5532daa64a00b0b71 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 01:22:27 -0700
Subject: [PATCH] [libc++] Make std::reference_constructs_from_temporary SFINAE
friendly when the 1st template argument is not a reference type
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
libcxx/docs/ReleaseNotes/23.rst | 2 ++
.../reference_constructs_from_temporary.h | 32 ++++++++++++++++---
...ference_constructs_from_temporary.pass.cpp | 7 ++++
3 files changed, 36 insertions(+), 5 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..5c3280746b9d1 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -69,6 +69,8 @@ Improvements and New Features
``"std::visit: variant is valueless"``, ``"std::get: variant is valueless"``, or
``"std::get: wrong alternative for variant"``. The standard only requires ``what()`` to return an
unspecified non-null string, so user code that does not match on the exact message remains correct.
+- The ``std::reference_constructs_from_temporary<_Tp, _Up>`` don't cause hard error when the ``_Tp`` template
+ argument is not a reference type.
Deprecations and Removals
-------------------------
diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index a8325620414ea..1050952bccf80 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
@@ -18,21 +19,42 @@
_LIBCPP_BEGIN_NAMESPACE_STD
+#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
+
+// 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/4xz1ozKev
+//
+// TODO: Clang 23 fix this builtin, remove this guard once all supported clang versions include this fix.
+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<_Tp, _Up, true> =
+ __reference_constructs_from_temporary(_Tp, _Up);
+
+#else
+
+template <class _Tp, class _Up>
+inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
+
+#endif // defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2300
+
#if _LIBCPP_STD_VER >= 23
template <class _Tp, class _Up>
struct _LIBCPP_NO_SPECIALIZATIONS reference_constructs_from_temporary
- : public bool_constant<__reference_constructs_from_temporary(_Tp, _Up)> {};
+ : public bool_constant<__reference_constructs_from_temporary_v<_Tp, _Up>> {};
template <class _Tp, class _Up>
_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_temporary_v =
- __reference_constructs_from_temporary(_Tp, _Up);
+ __reference_constructs_from_temporary_v<_Tp, _Up>;
#endif
-template <class _Tp, class _Up>
-inline const bool __reference_constructs_from_temporary_v = __reference_constructs_from_temporary(_Tp, _Up);
-
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___TYPE_TRAITS_REFERENCE_CONSTRUCTS_FROM_TEMPORARY_H
diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
index 84fe7cfb02208..f20632b840703 100644
--- a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
+++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp
@@ -22,6 +22,9 @@
#include "common.h"
#include "test_macros.h"
+struct NoConv {};
+struct Bad { template<class T> Bad(T v) noexcept(noexcept(member_ = v)) {} int member_; };
+
template <typename T, typename U, bool Expected>
constexpr void test_reference_constructs_from_temporary() {
assert((std::reference_constructs_from_temporary<T, U>::value == Expected));
@@ -76,6 +79,10 @@ constexpr bool test() {
test_reference_constructs_from_temporary<const int&, ExplicitConversionRef, false>();
test_reference_constructs_from_temporary<int&&, ExplicitConversionRvalueRef, false>();
+ // Make sure we don't emit "assigning to 'int' from incompatible type 'NoConv'" in SFINAE context.
+ // https://godbolt.org/z/er6e4Ejs1
+ test_reference_constructs_from_temporary<Bad, NoConv&&, false>();
+
return true;
}
More information about the libcxx-commits
mailing list