diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1463,6 +1463,8 @@ 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. +* ``__reference_constructs_from_temporary(T, U)`` (C++) +* ``__reference_converts_from_temporary(T, U)`` (C++) * ``__underlying_type`` (C++, GNU, Microsoft) In addition, the following expression traits are supported: diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -529,13 +529,15 @@ TYPE_TRAIT_1(__is_copy_assignable, IsCopyAssignable, KEYCXX) TYPE_TRAIT_1(__is_nothrow_copy_assignable, IsNothrowCopyAssignable, KEYCXX) TYPE_TRAIT_1(__is_trivially_copy_assignable, IsTriviallyCopyAssignable, KEYCXX) -TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) TYPE_TRAIT_1(__is_move_constructible, IsMoveConstructible, KEYCXX) TYPE_TRAIT_1(__is_nothrow_move_constructible, IsNothrowMoveConstructible, KEYCXX) TYPE_TRAIT_1(__is_trivially_move_constructible, IsTriviallyMoveConstructible, KEYCXX) TYPE_TRAIT_1(__is_move_assignable, IsMoveAssignable, KEYCXX) TYPE_TRAIT_1(__is_nothrow_move_assignable, IsNothrowMoveAssignable, KEYCXX) TYPE_TRAIT_1(__is_trivially_move_assignable, IsTriviallyMoveAssignable, KEYCXX) +TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX) +TYPE_TRAIT_2(__reference_constructs_from_temporary, ReferenceConstructsFromTemporary, KEYCXX) +TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary, KEYCXX) // Embarcadero Expression Traits EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX) diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1700,6 +1700,8 @@ .Case("__array_rank", true) .Case("__array_extent", true) .Case("__reference_binds_to_temporary", true) + .Case("__reference_constructs_from_temporary", true) + .Case("__reference_converts_from_temporary", true) #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) .Case("__" #Trait, true) #include "clang/Basic/TransformTypeTraits.def" .Default(false); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1640,7 +1640,10 @@ tok::kw___is_union, tok::kw___is_unsigned, tok::kw___is_void, - tok::kw___is_volatile)) + tok::kw___is_volatile, + tok::kw___reference_binds_to_temporary, + tok::kw___reference_constructs_from_temporary, + tok::kw___reference_converts_from_temporary)) // GNU libstdc++ 4.2 and libc++ use certain intrinsic names as the // name of struct templates, but some are keywords in GCC >= 4.3 // and Clang. Therefore, when we see the token sequence "struct diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1130,6 +1130,9 @@ REVERTIBLE_TYPE_TRAIT(__is_unsigned); REVERTIBLE_TYPE_TRAIT(__is_void); REVERTIBLE_TYPE_TRAIT(__is_volatile); + REVERTIBLE_TYPE_TRAIT(__reference_binds_to_temporary); + REVERTIBLE_TYPE_TRAIT(__reference_constructs_from_temporary); + REVERTIBLE_TYPE_TRAIT(__reference_converts_from_temporary); #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \ REVERTIBLE_TYPE_TRAIT(RTT_JOIN(__, Trait)); #include "clang/Basic/TransformTypeTraits.def" diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -29,6 +29,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TokenKinds.h" #include "clang/Basic/TypeTraits.h" +#include "clang/Basic/TokenKinds.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Initialization.h" @@ -5402,14 +5403,15 @@ if (Kind <= UTT_Last) return EvaluateUnaryTypeTrait(S, Kind, KWLoc, Args[0]->getType()); - // Evaluate BTT_ReferenceBindsToTemporary alongside the IsConstructible - // traits to avoid duplication. - if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary) + // Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary + // alongside the IsConstructible traits to avoid duplication. + if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary && Kind != BTT_ReferenceConstructsFromTemporary) return EvaluateBinaryTypeTrait(S, Kind, Args[0]->getType(), Args[1]->getType(), RParenLoc); switch (Kind) { case clang::BTT_ReferenceBindsToTemporary: + case clang::BTT_ReferenceConstructsFromTemporary: case clang::TT_IsConstructible: case clang::TT_IsNothrowConstructible: case clang::TT_IsTriviallyConstructible: { @@ -5493,6 +5495,16 @@ return !Init.isDirectReferenceBinding(); } + if (Kind == clang::BTT_ReferenceConstructsFromTemporary) { + QualType U = Args[1]->getType(); + if (!T->isReferenceType() || U->isReferenceType()) + return false; + + QualType TPtr = S.BuiltinAddPointer(S.BuiltinRemoveReference(T, UnaryTransformType::RemoveCVRef, {}), {}); + QualType UPtr = S.BuiltinAddPointer(S.BuiltinRemoveReference(U, UnaryTransformType::RemoveCVRef, {}), {}); + return EvaluateBinaryTypeTrait(S, TypeTrait::BTT_IsConvertibleTo, UPtr, TPtr, RParenLoc) && Init.isDirectReferenceBinding(); + } + if (Kind == clang::TT_IsNothrowConstructible) return S.canThrow(Result.get()) == CT_Cannot; @@ -5687,7 +5699,8 @@ return Self.Context.typesAreCompatible(Lhs, Rhs); } case BTT_IsConvertible: - case BTT_IsConvertibleTo: { + case BTT_IsConvertibleTo: + case BTT_ReferenceConvertsFromTemporary: { // C++0x [meta.rel]p4: // Given the following function prototype: // @@ -5726,8 +5739,14 @@ return false; // Compute the result of add_rvalue_reference. - if (LhsT->isObjectType() || LhsT->isFunctionType()) + bool RvalueReferenceAdded = false; + if (LhsT->isObjectType() || LhsT->isFunctionType()) { + RvalueReferenceAdded = true; LhsT = Self.Context.getRValueReferenceType(LhsT); + } + + if (BTT == BTT_ReferenceConvertsFromTemporary) + std::swap(LhsT, RhsT); // Build a fake source and destination for initialization. InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT)); @@ -5748,7 +5767,18 @@ return false; ExprResult Result = Init.Perform(Self, To, Kind, FromPtr); - return !Result.isInvalid() && !SFINAE.hasErrorOccurred(); + if (Result.isInvalid() || SFINAE.hasErrorOccurred()) + return false; + + if (BTT != clang::BTT_ReferenceConvertsFromTemporary) return true; + + // They're swapped, so we need to consider LhsT and RhsT back-to-front. + if (RvalueReferenceAdded || LhsT->isReferenceType()) + return false; + + QualType UPtr = Self.BuiltinAddPointer(Self.BuiltinRemoveReference(LhsT, UnaryTransformType::RemoveCVRef, {}), {}); + QualType TPtr = Self.BuiltinAddPointer(Self.BuiltinRemoveReference(RhsT, UnaryTransformType::RemoveCVRef, {}), {}); + return EvaluateBinaryTypeTrait(Self, TypeTrait::BTT_IsConvertibleTo, UPtr, TPtr, KeyLoc) and Kind.isCopyInit(); } case BTT_IsAssignable: diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -4704,6 +4704,92 @@ { int arr[T((__reference_binds_to_temporary(const int &, long)))]; } } +void reference_constructs_from_temporary_checks() { + static_assert(!__reference_constructs_from_temporary(int &, int &), ""); + static_assert(!__reference_constructs_from_temporary(int &, int &&), ""); + + static_assert(!__reference_constructs_from_temporary(int const &, int &), ""); + static_assert(!__reference_constructs_from_temporary(int const &, int const &), ""); + static_assert(!__reference_constructs_from_temporary(int const &, int &&), ""); + + static_assert(!__reference_constructs_from_temporary(int &, long &), ""); // doesn't construct + + // Diverges from __reference_binds_to_temporary + static_assert(!__reference_constructs_from_temporary(int const &, long &), ""); + // Diverges from __reference_binds_to_temporary + static_assert(!__reference_constructs_from_temporary(int const &, long &&), ""); + // Diverges from __reference_binds_to_temporary + static_assert(!__reference_constructs_from_temporary(int &&, long &), ""); + + using LRef = ConvertsToRef; + using RRef = ConvertsToRef; + using CLRef = ConvertsToRef; + using LongRef = ConvertsToRef; + static_assert(__is_constructible(int &, LRef), ""); + static_assert(!__reference_constructs_from_temporary(int &, LRef), ""); + + static_assert(__is_constructible(int &&, RRef), ""); + static_assert(!__reference_constructs_from_temporary(int &&, RRef), ""); + + static_assert(__is_constructible(int const &, CLRef), ""); + static_assert(!__reference_constructs_from_temporary(int &&, CLRef), ""); + + static_assert(__is_constructible(int const &, LongRef), ""); + static_assert(!__reference_constructs_from_temporary(int const &, LongRef), ""); + + // Test that it doesn't accept non-reference types as input. + static_assert(!__reference_constructs_from_temporary(int, long), ""); + + static_assert(!__reference_constructs_from_temporary(const int &, long), ""); + + // Additional checks + static_assert(__reference_constructs_from_temporary(int&&, int), ""); + static_assert(__reference_constructs_from_temporary(POD const&, Derives), ""); +} + +void reference_converts_from_temporary_checks() { + static_assert(!__reference_converts_from_temporary(int &, int &), ""); + static_assert(!__reference_converts_from_temporary(int &, int &&), ""); + + static_assert(!__reference_converts_from_temporary(int const &, int &), ""); + static_assert(!__reference_converts_from_temporary(int const &, int const &), ""); + static_assert(!__reference_converts_from_temporary(int const &, int &&), ""); + + static_assert(!__reference_converts_from_temporary(int &, long &), ""); // doesn't construct + + // Diverges from __reference_binds_to_temporary + static_assert(!__reference_converts_from_temporary(int const &, long &), ""); + // Diverges from __reference_binds_to_temporary + static_assert(!__reference_converts_from_temporary(int const &, long &&), ""); + // Diverges from __reference_binds_to_temporary + static_assert(!__reference_converts_from_temporary(int &&, long &), ""); + + using LRef = ConvertsToRef; + using RRef = ConvertsToRef; + using CLRef = ConvertsToRef; + using LongRef = ConvertsToRef; + static_assert(__is_convertible(LRef, int &), ""); + static_assert(!__reference_converts_from_temporary(int &, LRef), ""); + + static_assert(__is_convertible(RRef, int &&), ""); + static_assert(!__reference_converts_from_temporary(int &&, RRef), ""); + + static_assert(__is_convertible(CLRef, int const &), ""); + static_assert(!__reference_converts_from_temporary(int &&, CLRef), ""); + + static_assert(__is_convertible(LongRef, int const &), ""); + static_assert(!__reference_converts_from_temporary(int const &, LongRef), ""); + + // Test that it doesn't accept non-reference types as input. + static_assert(!__reference_converts_from_temporary(int, long), ""); + + static_assert(!__reference_converts_from_temporary(const int &, long), ""); + + // Additional checks + static_assert(__reference_converts_from_temporary(int&&, int), ""); + static_assert(__reference_converts_from_temporary(POD const&, Derives), ""); +} + void array_rank() { int t01[T(__array_rank(IntAr) == 1)]; int t02[T(__array_rank(ConstIntArAr) == 2)]; diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1417,16 +1417,7 @@ Type trait to determine if a reference binds to a temporary P2255R2 - -
Partial - Clang provides a __reference_binds_to_temporary type trait - builtin, with which the library facility can be partially implemented. - Both __reference_constructs_from_temporary and - __reference_converts_from_temporary builtins should be - provided, following the normal cross-vendor convention to implement - traits requiring compiler support directly. -
- + Clang 16