diff options
author | Hristo Hristov <hghristov.rmm@gmail.com> | 2025-03-08 16:20:56 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-08 15:20:56 +0100 |
commit | 6d9dfd75e4df9fdff9ea34f1c0bd741bf5b53b89 (patch) | |
tree | 17d44a0da22bc9bff29ec49f51163a5126ccb95e | |
parent | 3b8f9a228c5f12f282778b18117b9a88c07e87cb (diff) | |
download | llvm-6d9dfd75e4df9fdff9ea34f1c0bd741bf5b53b89.zip llvm-6d9dfd75e4df9fdff9ea34f1c0bd741bf5b53b89.tar.gz llvm-6d9dfd75e4df9fdff9ea34f1c0bd741bf5b53b89.tar.bz2 |
[libc++][type_traits] Implements "A type trait to detect reference binding to temporary" (#128649)
Implements partially: [P2255R2: A type trait to detect reference binding
to temporary](https://wg21.link/P2255R2)
Issue: https://github.com/llvm/llvm-project/issues/105180
https://eel.is/c++draft/meta.type.synop
https://eel.is/c++draft/meta.unary.prop
Implented type traits:
- [x] `reference_constructs_from_temporary`
- [x] `reference_converts_from_temporary`
Closes #129049
Minor drive-by tweak to `std::is_implicit_lifetime` tests.
---------
Co-authored-by: Hristo Hristov <zingam@outlook.com>
13 files changed, 337 insertions, 10 deletions
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index c45adba..a1100f0 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -41,6 +41,7 @@ Implemented Papers - N4258: Cleaning-up noexcept in the Library (`Github <https://github.com/llvm/llvm-project/issues/99937>`__) - P0767R1: Deprecate POD (`Github <https://github.com/llvm/llvm-project/issues/104013>`__) - P1361R2: Integration of chrono with text formatting (`Github <https://github.com/llvm/llvm-project/issues/100014>`__) +- P2255R2: A type trait to detect reference binding to temporary (implemented the type traits only) (`Github <https://github.com/llvm/llvm-project/issues/105180>`) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index bfaa63a7..0f3db57 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -43,7 +43,7 @@ "`P0627R6 <https://wg21.link/P0627R6>`__","Function to mark unreachable code","2022-02 (Virtual)","|Complete|","15","" "`P1206R7 <https://wg21.link/P1206R7>`__","``ranges::to``: A function to convert any range to a container","2022-02 (Virtual)","|Complete|","17","" "`P1413R3 <https://wg21.link/P1413R3>`__","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","2022-02 (Virtual)","|Complete|","","``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but clang doesn't issue a diagnostic for deprecated using template declarations." -"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","","","" +"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","Implemented the type traits only." "`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","" "`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","" "`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index ee1d7b8..d7c36d6 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -867,6 +867,8 @@ set(files __type_traits/negation.h __type_traits/promote.h __type_traits/rank.h + __type_traits/reference_constructs_from_temporary.h + __type_traits/reference_converts_from_temporary.h __type_traits/remove_all_extents.h __type_traits/remove_const.h __type_traits/remove_const_ref.h diff --git a/libcxx/include/__type_traits/reference_constructs_from_temporary.h b/libcxx/include/__type_traits/reference_constructs_from_temporary.h new file mode 100644 index 0000000..1666032 --- /dev/null +++ b/libcxx/include/__type_traits/reference_constructs_from_temporary.h @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___TYPE_TRAITS_REFERENCE_CONSTRUCTS_FROM_TEMPORARY_H +#define _LIBCPP___TYPE_TRAITS_REFERENCE_CONSTRUCTS_FROM_TEMPORARY_H + +#include <__config> +#include <__type_traits/integral_constant.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 && __has_builtin(__reference_constructs_from_temporary) + +template <class _Tp, class _Up> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_NO_SPECIALIZATIONS reference_constructs_from_temporary + : public bool_constant<__reference_constructs_from_temporary(_Tp, _Up)> {}; + +template <class _Tp, class _Up> +_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_constructs_from_temporary_v = + __reference_constructs_from_temporary(_Tp, _Up); + +#endif + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___TYPE_TRAITS_REFERENCE_CONSTRUCTS_FROM_TEMPORARY_H diff --git a/libcxx/include/__type_traits/reference_converts_from_temporary.h b/libcxx/include/__type_traits/reference_converts_from_temporary.h new file mode 100644 index 0000000..043bdbf --- /dev/null +++ b/libcxx/include/__type_traits/reference_converts_from_temporary.h @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___TYPE_TRAITS_REFERENCE_CONVERTS_FROM_TEMPORARY_H +#define _LIBCPP___TYPE_TRAITS_REFERENCE_CONVERTS_FROM_TEMPORARY_H + +#include <__config> +#include <__type_traits/integral_constant.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 && __has_builtin(__reference_converts_from_temporary) + +template <class _Tp, class _Up> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_NO_SPECIALIZATIONS reference_converts_from_temporary + : public bool_constant<__reference_converts_from_temporary(_Tp, _Up)> {}; + +template <class _Tp, class _Up> +_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool reference_converts_from_temporary_v = + __reference_converts_from_temporary(_Tp, _Up); + +#endif + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___TYPE_TRAITS_REFERENCE_CONVERTS_FROM_TEMPORARY_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 81c7378..b9964da 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -369,6 +369,14 @@ module std_core [system] { module negation { header "__type_traits/negation.h" } module promote { header "__type_traits/promote.h" } module rank { header "__type_traits/rank.h" } + module reference_constructs_from_temporary { + header "__type_traits/reference_constructs_from_temporary.h" + export std_core.type_traits.integral_constant + } + module reference_converts_from_temporary { + header "__type_traits/reference_converts_from_temporary.h" + export std_core.type_traits.integral_constant + } module remove_all_extents { header "__type_traits/remove_all_extents.h" } module remove_const_ref { header "__type_traits/remove_const_ref.h" } module remove_const { header "__type_traits/remove_const.h" } diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits index 772e4cb..e09a911 100644 --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -143,6 +143,9 @@ namespace std template<class T> struct has_unique_object_representations; // C++17 + template<class T, class U> struct reference_constructs_from_temporary; // Since C++23 + template<class T, class U> struct reference_converts_from_temporary; // Since C++23 + // Relationships between types: template <class T, class U> struct is_same; template <class Base, class Derived> struct is_base_of; @@ -382,6 +385,12 @@ namespace std = has_virtual_destructor<T>::value; // C++17 template<class T> inline constexpr bool has_unique_object_representations_v // C++17 = has_unique_object_representations<T>::value; + template<class T, class U> + constexpr bool reference_constructs_from_temporary_v + = reference_constructs_from_temporary<T, U>::value; // Since C++23 + template<class T, class U> + constexpr bool reference_converts_from_temporary_v + = reference_converts_from_temporary<T, U>::value; // Since C++23 // See C++14 20.10.5, type property queries template <class T> inline constexpr size_t alignment_of_v @@ -523,6 +532,8 @@ namespace std # if _LIBCPP_STD_VER >= 23 # include <__type_traits/is_implicit_lifetime.h> +# include <__type_traits/reference_constructs_from_temporary.h> +# include <__type_traits/reference_converts_from_temporary.h> # endif # include <version> diff --git a/libcxx/modules/std/type_traits.inc b/libcxx/modules/std/type_traits.inc index f544f95..6823c86 100644 --- a/libcxx/modules/std/type_traits.inc +++ b/libcxx/modules/std/type_traits.inc @@ -106,8 +106,14 @@ export namespace std { using std::has_unique_object_representations; - // using std::reference_constructs_from_temporary; - // using std::reference_converts_from_temporary; +#if _LIBCPP_STD_VER >= 23 +# if __has_builtin(__reference_constructs_from_temporary) + using std::reference_constructs_from_temporary; +# endif +# if __has_builtin(__reference_converts_from_temporary) + using std::reference_converts_from_temporary; +# endif +#endif // [meta.unary.prop.query], type property queries using std::alignment_of; @@ -284,8 +290,14 @@ export namespace std { using std::is_unbounded_array_v; using std::is_unsigned_v; using std::is_volatile_v; - // using std::reference_constructs_from_temporary_v; - // using std::reference_converts_from_temporary_v; +#if _LIBCPP_STD_VER >= 23 +# if __has_builtin(__reference_constructs_from_temporary) + using std::reference_constructs_from_temporary_v; +# endif +# if __has_builtin(__reference_converts_from_temporary) + using std::reference_converts_from_temporary_v; +# endif +#endif // [meta.unary.prop.query], type property queries using std::alignment_of_v; diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/common.h b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/common.h index be9149d..b514449 100644 --- a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/common.h +++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/common.h @@ -39,9 +39,52 @@ struct A { A& operator=(const A&); }; -class Abstract -{ - virtual ~Abstract() = 0; +class Abstract { + virtual ~Abstract() = 0; }; +// Types for reference_{constructs/converts}_from_temporary + +#if TEST_STD_VER >= 23 + +class NonPODClass { +public: + NonPODClass(int); +}; +enum Enum { EV }; +struct Base { + Enum e; + int i; + float f; + NonPODClass* p; +}; +// Not PODs +struct Derived : Base {}; + +template <class T, class RefType = T&> +class ConvertsToRef { +public: + operator RefType() const { return static_cast<RefType>(obj); } + mutable T obj = 42; +}; +template <class T, class RefType = T&> +class ConvertsToRefPrivate { + operator RefType() const { return static_cast<RefType>(obj); } + mutable T obj = 42; +}; + +class ExplicitConversionRvalueRef { +public: + operator int(); + explicit operator int&&(); +}; + +class ExplicitConversionRef { +public: + operator int(); + explicit operator int&(); +}; + +#endif + #endif // TEST_META_UNARY_COMP_COMMON_H diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp index 681ad13..24adec3 100644 --- a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp +++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp @@ -9,7 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 // These compilers don't support __builtin_is_implicit_lifetime yet. -// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-15, apple-clang-16, apple-clang-17 +// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-15, apple-clang-16 // <type_traits> diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.verify.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.verify.cpp index 34462f9..4bcb10d 100644 --- a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.verify.cpp +++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.verify.cpp @@ -9,7 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 // These compilers don't support __builtin_is_implicit_lifetime yet. -// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-15, apple-clang-16, apple-clang-17 +// UNSUPPORTED: clang-18, clang-19, gcc-14, apple-clang-15, apple-clang-16 // <type_traits> 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 new file mode 100644 index 0000000..5b3753c --- /dev/null +++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_constructs_from_temporary.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// These compilers don't support std::reference_converts_from_temporary yet. +// UNSUPPORTED: android, apple-clang-15, apple-clang-16, clang-19.1 + +// <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; + +#include <cassert> +#include <type_traits> + +#include "common.h" +#include "test_macros.h" + +template <typename T, typename U, bool Expected> +constexpr void test_reference_constructs_from_temporary() { + assert((std::reference_constructs_from_temporary<T, U>::value == Expected)); + assert((std::reference_constructs_from_temporary_v<T, U> == Expected)); +} + +constexpr bool test() { + test_reference_constructs_from_temporary<int&, int&, false>(); + test_reference_constructs_from_temporary<int&, int&, false>(); + test_reference_constructs_from_temporary<int&, int&&, false>(); + + test_reference_constructs_from_temporary<const int&, int&, false>(); + test_reference_constructs_from_temporary<const int&, const int&, false>(); + test_reference_constructs_from_temporary<const int&, int&&, false>(); + + test_reference_constructs_from_temporary<int&, long&, false>(); // doesn't construct + + test_reference_constructs_from_temporary<const int&, long&, true>(); + test_reference_constructs_from_temporary<const int&, long&&, true>(); + test_reference_constructs_from_temporary<int&&, long&, true>(); + + assert((std::is_constructible_v<int&, ConvertsToRef<int, int&>>)); + test_reference_constructs_from_temporary<int&, ConvertsToRef<int, int&>, false>(); + + assert((std::is_constructible_v<int&&, ConvertsToRef<int, int&&>>)); + test_reference_constructs_from_temporary<int&&, ConvertsToRef<int, int&&>, false>(); + + assert((std::is_constructible_v<const int&, ConvertsToRef<int, const int&>>)); + test_reference_constructs_from_temporary<int&&, ConvertsToRef<int, const int&>, false>(); + + assert((std::is_constructible_v<const int&, ConvertsToRef<long, long&>>)); + test_reference_constructs_from_temporary<const int&, ConvertsToRef<long, long&>, true>(); +#ifndef TEST_COMPILER_GCC + test_reference_constructs_from_temporary<const int&, ConvertsToRefPrivate<long, long&>, false>(); +#endif + + // Test that it doesn't accept non-reference types as input. + test_reference_constructs_from_temporary<int, long, false>(); + + test_reference_constructs_from_temporary<const int&, long, true>(); + + // Additional checks + test_reference_constructs_from_temporary<const Base&, Derived, true>(); + test_reference_constructs_from_temporary<int&&, int, true>(); + test_reference_constructs_from_temporary<const int&, int, true>(); + test_reference_constructs_from_temporary<int&&, int&&, false>(); + test_reference_constructs_from_temporary<const int&, int&&, false>(); + test_reference_constructs_from_temporary<int&&, long&&, true>(); + test_reference_constructs_from_temporary<int&&, long, true>(); + + test_reference_constructs_from_temporary<int&, ExplicitConversionRef, false>(); + test_reference_constructs_from_temporary<const int&, ExplicitConversionRef, false>(); + test_reference_constructs_from_temporary<int&&, ExplicitConversionRvalueRef, false>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_converts_from_temporary.pass.cpp b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_converts_from_temporary.pass.cpp new file mode 100644 index 0000000..849e286 --- /dev/null +++ b/libcxx/test/std/utilities/meta/meta.unary/meta.unary.prop/reference_converts_from_temporary.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// These compilers don't support std::reference_converts_from_temporary yet. +// UNSUPPORTED: android, apple-clang-15, apple-clang-16, clang-18, clang-19.1 + +// <type_traits> + +// template<class T, class U> struct reference_converts_from_temporary; + +// template<class T, class U> +// constexpr bool reference_converts_from_temporary_v +// = reference_converts_from_temporary<T, U>::value; + +#include <cassert> +#include <type_traits> + +#include "common.h" +#include "test_macros.h" + +template <typename T, typename U, bool Expected> +constexpr void test_reference_converts_from_temporary() { + assert((std::reference_converts_from_temporary<T, U>::value == Expected)); + assert((std::reference_converts_from_temporary_v<T, U> == Expected)); +} + +constexpr bool test() { + test_reference_converts_from_temporary<int&, int&, false>(); + test_reference_converts_from_temporary<int&, int&, false>(); + test_reference_converts_from_temporary<int&, int&&, false>(); + + test_reference_converts_from_temporary<const int&, int&, false>(); + test_reference_converts_from_temporary<const int&, const int&, false>(); + test_reference_converts_from_temporary<const int&, int&&, false>(); + + test_reference_converts_from_temporary<int&, long&, false>(); // doesn't construct + + test_reference_converts_from_temporary<const int&, long&, true>(); + test_reference_converts_from_temporary<const int&, long&&, true>(); + test_reference_converts_from_temporary<int&&, long&, true>(); + + assert((std::is_constructible_v<int&, ConvertsToRef<int, int&>>)); + test_reference_converts_from_temporary<int&, ConvertsToRef<int, int&>, false>(); + + assert((std::is_constructible_v<int&&, ConvertsToRef<int, int&&>>)); + test_reference_converts_from_temporary<int&&, ConvertsToRef<int, int&&>, false>(); + + assert((std::is_constructible_v<const int&, ConvertsToRef<int, const int&>>)); + test_reference_converts_from_temporary<int&&, ConvertsToRef<int, const int&>, false>(); + + assert((std::is_constructible_v<const int&, ConvertsToRef<long, long&>>)); + test_reference_converts_from_temporary<const int&, ConvertsToRef<long, long&>, true>(); +#ifndef TEST_COMPILER_GCC + test_reference_converts_from_temporary<const int&, ConvertsToRefPrivate<long, long&>, false>(); +#endif + + // Test that it doesn't accept non-reference types as input. + test_reference_converts_from_temporary<int, long, false>(); + + test_reference_converts_from_temporary<const int&, long, true>(); + + // Additional checks + test_reference_converts_from_temporary<const Base&, Derived, true>(); + test_reference_converts_from_temporary<int&&, int, true>(); + test_reference_converts_from_temporary<const int&, int, true>(); + test_reference_converts_from_temporary<int&&, int&&, false>(); + test_reference_converts_from_temporary<const int&, int&&, false>(); + test_reference_converts_from_temporary<int&&, long&&, true>(); + test_reference_converts_from_temporary<int&&, long, true>(); + + test_reference_converts_from_temporary<int&, ExplicitConversionRef, false>(); + test_reference_converts_from_temporary<const int&, ExplicitConversionRef, true>(); + test_reference_converts_from_temporary<int&&, ExplicitConversionRvalueRef, true>(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} |