Skip to content

[Clang][libc++] Implement __is_nothrow_convertible and use it in libc++ #80436

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to move __is_nothrow_convertible / __is_convertible / __is_same above the Embarcadero comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a bunch more type traits that aren't listed correctly anymore, so IMO an NFC patch that updates them all would be better.

ARRAY_TYPE_TRAIT(__array_rank, ArrayRank, KEYCXX)
ARRAY_TYPE_TRAIT(__array_extent, ArrayExtent, KEYCXX)
// Name for GCC 6 compatibility.
Expand Down
11 changes: 9 additions & 2 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
//
Expand Down Expand Up @@ -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:
Expand Down
16 changes: 14 additions & 2 deletions clang/test/SemaCXX/type-traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2118,15 +2118,15 @@ struct IntWrapper
{
int value;
IntWrapper(int _value) : value(_value) {}
operator int() const {
operator int() const noexcept {
return value;
}
};

struct FloatWrapper
{
float value;
FloatWrapper(float _value) : value(_value) {}
FloatWrapper(float _value) noexcept : value(_value) {}
FloatWrapper(const IntWrapper& obj)
: value(static_cast<float>(obj.value)) {}
operator float() const {
Expand All @@ -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();
Expand Down
12 changes: 12 additions & 0 deletions libcxx/include/__type_traits/is_nothrow_convertible.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 20

# if __has_builtin(__is_nothrow_convertible)

template <class _Tp, class _Up>
struct is_nothrow_convertible : bool_constant<__is_nothrow_convertible(_Tp, _Up)> {};

template <class _Tp, class _Up>
inline constexpr bool is_nothrow_convertible_v = __is_nothrow_convertible(_Tp, _Up);

# else // __has_builtin(__is_nothrow_convertible)

template <typename _Tp>
void __test_noexcept(_Tp) noexcept;

Expand All @@ -43,6 +53,8 @@ struct is_nothrow_convertible
template <typename _Fm, typename _To>
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
Expand Down