From: Eric Fiselier Date: Fri, 12 Jan 2018 00:09:37 +0000 (+0000) Subject: Add `__reference_binds_to_temporary` trait for checking safe reference initialization. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=902a1d196b60c457d01db004e2f8b8c9339d8472;p=clang 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 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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@322334 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/LanguageExtensions.rst b/docs/LanguageExtensions.rst index 9b407f31d9..41f161302f 100644 --- a/docs/LanguageExtensions.rst +++ b/docs/LanguageExtensions.rst @@ -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 ====== diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 6ae8821a83..16c08da8c6 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -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 diff --git a/include/clang/Basic/TypeTraits.h b/include/clang/Basic/TypeTraits.h index 8ecd63f9c3..509e8b4351 100644 --- a/include/clang/Basic/TypeTraits.h +++ b/include/clang/Basic/TypeTraits.h @@ -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 diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 89055aecd4..5bbb367eee 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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; diff --git a/test/SemaCXX/type-traits.cpp b/test/SemaCXX/type-traits.cpp index b334e50755..8cf688014c 100644 --- a/test/SemaCXX/type-traits.cpp +++ b/test/SemaCXX/type-traits.cpp @@ -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 +struct ConvertsToRef { + operator RefType() const { return static_cast(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; + using RRef = ConvertsToRef; + using CLRef = ConvertsToRef; + using LongRef = ConvertsToRef; + { 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)];