From 9cc2122bf5a81f7063c2a32b2cb78c8d615578a1 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Fri, 2 Feb 2024 19:15:58 +0100 Subject: [PATCH] [Clang][libc++] Implement __is_nothrow_convertible and use it in libc++ (#80436) GCC 13 has implemented this builtin. --- clang/docs/LanguageExtensions.rst | 1 + clang/include/clang/Basic/TokenKinds.def | 1 + clang/lib/Sema/SemaExprCXX.cpp | 11 +++++++++-- clang/test/SemaCXX/type-traits.cpp | 16 ++++++++++++++-- .../__type_traits/is_nothrow_convertible.h | 12 ++++++++++++ 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index c1420079f75118..e91156837290f7 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1569,6 +1569,7 @@ The following type trait primitives are supported by Clang. Those traits marked * ``__is_const`` (C++, Embarcadero) * ``__is_constructible`` (C++, MSVC 2013) * ``__is_convertible`` (C++, Embarcadero) +* ``__is_nothrow_convertible`` (C++, GNU) * ``__is_convertible_to`` (Microsoft): Synonym for ``__is_convertible``. * ``__is_destructible`` (C++, MSVC 2013) diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 9117e4376c3715..23817cde7a9354 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -567,6 +567,7 @@ TYPE_TRAIT_1(__is_unsigned, IsUnsigned, KEYCXX) // Embarcadero Binary Type Traits TYPE_TRAIT_2(__is_same, IsSame, KEYCXX) TYPE_TRAIT_2(__is_convertible, IsConvertible, KEYCXX) +TYPE_TRAIT_2(__is_nothrow_convertible, IsNothrowConvertible, KEYCXX) ARRAY_TYPE_TRAIT(__array_rank, ArrayRank, KEYCXX) ARRAY_TYPE_TRAIT(__array_extent, ArrayExtent, KEYCXX) // Name for GCC 6 compatibility. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 3a32754e5376e3..246d2313e089f3 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5779,7 +5779,8 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT, return Self.Context.typesAreCompatible(Lhs, Rhs); } case BTT_IsConvertible: - case BTT_IsConvertibleTo: { + case BTT_IsConvertibleTo: + case BTT_IsNothrowConvertible: { // C++0x [meta.rel]p4: // Given the following function prototype: // @@ -5840,7 +5841,13 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT, return false; ExprResult Result = Init.Perform(Self, To, Kind, FromPtr); - return !Result.isInvalid() && !SFINAE.hasErrorOccurred(); + if (Result.isInvalid() || SFINAE.hasErrorOccurred()) + return false; + + if (BTT != BTT_IsNothrowConvertible) + return true; + + return Self.canThrow(Result.get()) == CT_Cannot; } case BTT_IsAssignable: diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index c5d196a2590f8d..5659594577111e 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -2118,7 +2118,7 @@ struct IntWrapper { int value; IntWrapper(int _value) : value(_value) {} - operator int() const { + operator int() const noexcept { return value; } }; @@ -2126,7 +2126,7 @@ struct IntWrapper struct FloatWrapper { float value; - FloatWrapper(float _value) : value(_value) {} + FloatWrapper(float _value) noexcept : value(_value) {} FloatWrapper(const IntWrapper& obj) : value(static_cast(obj.value)) {} operator float() const { @@ -2149,6 +2149,18 @@ void is_convertible() int t08[T(__is_convertible(float, FloatWrapper))]; } +void is_nothrow_convertible() +{ + int t01[T(__is_nothrow_convertible(IntWrapper, IntWrapper))]; + int t02[T(__is_nothrow_convertible(IntWrapper, const IntWrapper))]; + int t03[T(__is_nothrow_convertible(IntWrapper, int))]; + int t04[F(__is_nothrow_convertible(int, IntWrapper))]; + int t05[F(__is_nothrow_convertible(IntWrapper, FloatWrapper))]; + int t06[F(__is_nothrow_convertible(FloatWrapper, IntWrapper))]; + int t07[F(__is_nothrow_convertible(FloatWrapper, float))]; + int t08[T(__is_nothrow_convertible(float, FloatWrapper))]; +} + struct FromInt { FromInt(int); }; struct ToInt { operator int(); }; typedef void Function(); diff --git a/libcxx/include/__type_traits/is_nothrow_convertible.h b/libcxx/include/__type_traits/is_nothrow_convertible.h index eda7a49d7224cd..bfc5a94cbadec6 100644 --- a/libcxx/include/__type_traits/is_nothrow_convertible.h +++ b/libcxx/include/__type_traits/is_nothrow_convertible.h @@ -26,6 +26,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER >= 20 +# if __has_builtin(__is_nothrow_convertible) + +template +struct is_nothrow_convertible : bool_constant<__is_nothrow_convertible(_Tp, _Up)> {}; + +template +inline constexpr bool is_nothrow_convertible_v = __is_nothrow_convertible(_Tp, _Up); + +# else // __has_builtin(__is_nothrow_convertible) + template void __test_noexcept(_Tp) noexcept; @@ -43,6 +53,8 @@ struct is_nothrow_convertible template inline constexpr bool is_nothrow_convertible_v = is_nothrow_convertible<_Fm, _To>::value; +# endif // __has_builtin(__is_nothrow_convertible) + #endif // _LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD