forked from OSchip/llvm-project
Add `__reference_binds_to_temporary` trait for checking safe reference initialization.
Summary: The STL types `std::pair` and `std::tuple` can both store reference types. However their constructors cannot adequately check if the initialization of reference types is safe. For example: ``` std::tuple<std::tuple<int> const&> t = 42; // The stored reference is already dangling. ``` Libc++ has a best effort attempts in tuple to diagnose this, but they're not able to handle all valid cases (If I'm not mistaken). For example initialization of a reference from the result of a class's conversion operator. Libc++ would benefit from having a builtin traits which can provide a much better implementation. This patch introduce the `__reference_binds_to_temporary(T, U)` trait that determines whether a reference of type `T` bound to an expression of type `U` would bind to a materialized temporary object. Note that the trait simply returns false if `T` is not a reference type instead of reporting it as an error. ``` static_assert(__is_constructible(int const&, long)); static_assert(__reference_binds_to_temporary(int const&, long)); ``` Reviewers: majnemer, rsmith Reviewed By: rsmith Subscribers: compnerd, cfe-commits Differential Revision: https://reviews.llvm.org/D29930 llvm-svn: 322334
This commit is contained in:
parent
cd65f69808
commit
1af6c114cc
|
@ -1096,6 +1096,11 @@ The following type trait primitives are supported by Clang:
|
|||
* ``__is_constructible`` (MSVC 2013, clang)
|
||||
* ``__is_nothrow_constructible`` (MSVC 2013, clang)
|
||||
* ``__is_assignable`` (MSVC 2015, clang)
|
||||
* ``__reference_binds_to_temporary(T, U)`` (Clang): Determines whether a
|
||||
reference of type ``T`` bound to an expression of type ``U`` would bind to a
|
||||
materialized temporary object. If ``T`` is not a reference type the result
|
||||
is false. Note this trait will also return false when the initialization of
|
||||
``T`` from ``U`` is ill-formed.
|
||||
|
||||
Blocks
|
||||
======
|
||||
|
|
|
@ -464,6 +464,7 @@ TYPE_TRAIT_1(__has_unique_object_representations,
|
|||
TYPE_TRAIT_N(__is_trivially_constructible, IsTriviallyConstructible, KEYCXX)
|
||||
TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX)
|
||||
TYPE_TRAIT_2(__is_trivially_assignable, IsTriviallyAssignable, KEYCXX)
|
||||
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)
|
||||
KEYWORD(__underlying_type , KEYCXX)
|
||||
|
||||
// Embarcadero Expression Traits
|
||||
|
|
|
@ -80,7 +80,8 @@ namespace clang {
|
|||
BTT_IsAssignable,
|
||||
BTT_IsNothrowAssignable,
|
||||
BTT_IsTriviallyAssignable,
|
||||
BTT_Last = BTT_IsTriviallyAssignable,
|
||||
BTT_ReferenceBindsToTemporary,
|
||||
BTT_Last = BTT_ReferenceBindsToTemporary,
|
||||
TT_IsConstructible,
|
||||
TT_IsNothrowConstructible,
|
||||
TT_IsTriviallyConstructible
|
||||
|
|
|
@ -4645,11 +4645,14 @@ static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc,
|
|||
if (Kind <= UTT_Last)
|
||||
return EvaluateUnaryTypeTrait(S, Kind, KWLoc, Args[0]->getType());
|
||||
|
||||
if (Kind <= BTT_Last)
|
||||
// Evaluate BTT_ReferenceBindsToTemporary alongside the IsConstructible
|
||||
// traits to avoid duplication.
|
||||
if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary)
|
||||
return EvaluateBinaryTypeTrait(S, Kind, Args[0]->getType(),
|
||||
Args[1]->getType(), RParenLoc);
|
||||
|
||||
switch (Kind) {
|
||||
case clang::BTT_ReferenceBindsToTemporary:
|
||||
case clang::TT_IsConstructible:
|
||||
case clang::TT_IsNothrowConstructible:
|
||||
case clang::TT_IsTriviallyConstructible: {
|
||||
|
@ -4726,6 +4729,13 @@ static bool evaluateTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc,
|
|||
if (Kind == clang::TT_IsConstructible)
|
||||
return true;
|
||||
|
||||
if (Kind == clang::BTT_ReferenceBindsToTemporary) {
|
||||
if (!T->isReferenceType())
|
||||
return false;
|
||||
|
||||
return !Init.isDirectReferenceBinding();
|
||||
}
|
||||
|
||||
if (Kind == clang::TT_IsNothrowConstructible)
|
||||
return S.canThrow(Result.get()) == CT_Cannot;
|
||||
|
||||
|
|
|
@ -2225,6 +2225,7 @@ void constructible_checks() {
|
|||
|
||||
// PR25513
|
||||
{ int arr[F(__is_constructible(int(int)))]; }
|
||||
{ int arr[T(__is_constructible(int const &, long))]; }
|
||||
|
||||
{ int arr[T(__is_constructible(ACompleteType))]; }
|
||||
{ int arr[T(__is_nothrow_constructible(ACompleteType))]; }
|
||||
|
@ -2275,6 +2276,47 @@ void is_trivially_constructible_test() {
|
|||
{ int arr[F(__is_trivially_constructible(const volatile void))]; }
|
||||
}
|
||||
|
||||
template <class T, class RefType = T &>
|
||||
struct ConvertsToRef {
|
||||
operator RefType() const { return static_cast<RefType>(obj); }
|
||||
mutable T obj = 42;
|
||||
};
|
||||
|
||||
void reference_binds_to_temporary_checks() {
|
||||
{ int arr[F((__reference_binds_to_temporary(int &, int &)))]; }
|
||||
{ int arr[F((__reference_binds_to_temporary(int &, int &&)))]; }
|
||||
|
||||
{ int arr[F((__reference_binds_to_temporary(int const &, int &)))]; }
|
||||
{ int arr[F((__reference_binds_to_temporary(int const &, int const &)))]; }
|
||||
{ int arr[F((__reference_binds_to_temporary(int const &, int &&)))]; }
|
||||
|
||||
{ int arr[F((__reference_binds_to_temporary(int &, long &)))]; } // doesn't construct
|
||||
{ int arr[T((__reference_binds_to_temporary(int const &, long &)))]; }
|
||||
{ int arr[T((__reference_binds_to_temporary(int const &, long &&)))]; }
|
||||
{ int arr[T((__reference_binds_to_temporary(int &&, long &)))]; }
|
||||
|
||||
using LRef = ConvertsToRef<int, int &>;
|
||||
using RRef = ConvertsToRef<int, int &&>;
|
||||
using CLRef = ConvertsToRef<int, const int &>;
|
||||
using LongRef = ConvertsToRef<long, long &>;
|
||||
{ int arr[T((__is_constructible(int &, LRef)))]; }
|
||||
{ int arr[F((__reference_binds_to_temporary(int &, LRef)))]; }
|
||||
|
||||
{ int arr[T((__is_constructible(int &&, RRef)))]; }
|
||||
{ int arr[F((__reference_binds_to_temporary(int &&, RRef)))]; }
|
||||
|
||||
{ int arr[T((__is_constructible(int const &, CLRef)))]; }
|
||||
{ int arr[F((__reference_binds_to_temporary(int &&, CLRef)))]; }
|
||||
|
||||
{ int arr[T((__is_constructible(int const &, LongRef)))]; }
|
||||
{ int arr[T((__reference_binds_to_temporary(int const &, LongRef)))]; }
|
||||
|
||||
// Test that it doesn't accept non-reference types as input.
|
||||
{ int arr[F((__reference_binds_to_temporary(int, long)))]; }
|
||||
|
||||
{ int arr[T((__reference_binds_to_temporary(const int &, long)))]; }
|
||||
}
|
||||
|
||||
void array_rank() {
|
||||
int t01[T(__array_rank(IntAr) == 1)];
|
||||
int t02[T(__array_rank(ConstIntArAr) == 2)];
|
||||
|
|
Loading…
Reference in New Issue