[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