[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
Thu Jul 2 00:14:57 PDT 2026


https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/206679

>From 3a7a5558f04b56dff65ed4c5447f434d82df9b12 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 01:22:27 -0700
Subject: [PATCH 1/3] [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;
 }
 

>From aaac050224cb2820eb5984a880a649f427e40cdd Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 30 Jun 2026 01:39:55 -0700
Subject: [PATCH 2/3] Format

Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.pass.cpp            | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

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 f20632b840703..e83f507f5bda3 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
@@ -23,7 +23,11 @@
 #include "test_macros.h"
 
 struct NoConv {};
-struct Bad { template<class T> Bad(T v) noexcept(noexcept(member_ = v)) {} int member_; };
+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() {

>From d75213697182d2b8a261e66285ec1039442a32f1 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 2 Jul 2026 00:14:23 -0700
Subject: [PATCH 3/3] Check T and U are complete types

Co-authored-by: Kim-J-Smith
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
 .../reference_constructs_from_temporary.h     | 16 +++++--
 ...rence_constructs_from_temporary.verify.cpp | 42 +++++++++++++++++++
 2 files changed, 54 insertions(+), 4 deletions(-)
 create mode 100644 libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.verify.cpp

diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
index 1050952bccf80..bb1ec19e4aaef 100644
--- a/libcxx/include/__type_traits/reference_constructs_from_temporary.h
+++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h
@@ -19,8 +19,6 @@
 
 _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
@@ -29,11 +27,21 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 // 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>
+#if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER < 2300
+
+template <class _Tp>
+concept __complete =
+    is_reference_v<_Tp> || is_function_v<_Tp> || requires { sizeof(_Tp); } && is_object_v<_Tp>;
+
+template <class _Tp>
+concept __complete_or_unbounded = __complete<_Tp> || is_void_v<_Tp> || is_unbounded_array_v<_Tp>;
+
+template <class _Tp, class _Up,
+    bool = !is_reference_v<_Tp> && __complete_or_unbounded<_Tp> && __complete_or_unbounded<_Up>>
 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> =
+inline const bool __reference_constructs_from_temporary_v<_Tp, _Up, false> =
     __reference_constructs_from_temporary(_Tp, _Up);
 
 #else
diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.verify.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.verify.cpp
new file mode 100644
index 0000000000000..0b17f9b52d2d7
--- /dev/null
+++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.verify.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <type_traits>
+
+// template<class T, class U> struct reference_constructs_from_temporary;
+
+// template<class T, class U>
+// constexpr bool reference_constructs_from_temporary_v
+//   = reference_constructs_from_temporary<T, U>::value;
+
+// expected-error@*:* 2 {{incomplete type 'IncompleteType' used in type trait expression}}
+
+#include <type_traits>
+
+struct NoConv {};
+struct Bad {
+  template <class T>
+  Bad(T v) noexcept(noexcept(member_ = v)) {}
+  int member_;
+};
+struct IncompleteType;
+
+constexpr bool test() {
+  static_assert(!std::reference_constructs_from_temporary_v<IncompleteType, NoConv&&>);
+  static_assert(!std::reference_constructs_from_temporary_v<Bad, IncompleteType>);
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}



More information about the libcxx-commits mailing list