[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