llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: cor3ntin (cor3ntin) <details> <summary>Changes</summary> This completes the required language support for P2255R2. --- Full diff: https://github.com/llvm/llvm-project/pull/91199.diff 9 Files Affected: - (modified) clang/docs/LanguageExtensions.rst (+4-1) - (modified) clang/docs/ReleaseNotes.rst (+3) - (modified) clang/include/clang/Basic/TokenKinds.def (+1) - (modified) clang/lib/Lex/PPMacroExpansion.cpp (-2) - (modified) clang/lib/Parse/ParseDeclCXX.cpp (+2-3) - (modified) clang/lib/Parse/ParseExpr.cpp (-1) - (modified) clang/lib/Sema/SemaExprCXX.cpp (+91-66) - (modified) clang/test/SemaCXX/type-traits.cpp (+68) - (modified) clang/www/cxx_status.html (+1-9) ``````````diff diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index c2e90f4e7d587a..97c6d69cee1116 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1661,8 +1661,11 @@ The following type trait primitives are supported by Clang. Those traits marked ``T`` from ``U`` is ill-formed. Deprecated, use ``__reference_constructs_from_temporary``. * ``__reference_constructs_from_temporary(T, U)`` (C++) - Returns true if a reference ``T`` can be constructed from a temporary of type + Returns true if a reference ``T`` can be direct-initialized from a temporary of type a non-cv-qualified ``U``. +* ``__reference_converts_from_temporary(T, U)`` (C++) + Returns true if a reference ``T`` can be copy-initialized from a temporary of type + a non-cv-qualified ``U``. * ``__underlying_type`` (C++, GNU, Microsoft) In addition, the following expression traits are supported: diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 2c5308fbcb319a..4e9b23d9a6a80f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -142,6 +142,9 @@ C++23 Feature Support - Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_. +- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for + `P2255R2: Type trait to determine if a reference binds to a temporary <https://wg21.link/P2255R2>`_. + C++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index a27fbed358a60c..56c4b17f769d70 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -537,6 +537,7 @@ TYPE_TRAIT_1(__is_referenceable, IsReferenceable, KEYCXX) TYPE_TRAIT_1(__can_pass_in_regs, CanPassInRegs, 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 index a5f22f01682d25..a478e0badb0c7c 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1714,8 +1714,6 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { return llvm::StringSwitch<bool>(II->getName()) .Case("__array_rank", true) .Case("__array_extent", true) - .Case("__reference_binds_to_temporary", true) - .Case("__reference_constructs_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 index 8e0e8682482933..96c9708c3711b7 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1779,9 +1779,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, tok::kw___is_union, tok::kw___is_unsigned, tok::kw___is_void, - tok::kw___is_volatile, - tok::kw___reference_binds_to_temporary, - tok::kw___reference_constructs_from_temporary)) + tok::kw___is_volatile + )) // 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 index 7d6febb04a82c4..41e25bebde06cc 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1166,7 +1166,6 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, REVERTIBLE_TYPE_TRAIT(__is_void); REVERTIBLE_TYPE_TRAIT(__is_volatile); REVERTIBLE_TYPE_TRAIT(__reference_binds_to_temporary); - REVERTIBLE_TYPE_TRAIT(__reference_constructs_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 index c1cb03e4ec7ae2..ae844bc6991435 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5627,6 +5627,77 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs, SourceLocation KeyLoc); +static ExprResult CheckConvertibilityForTypeTraits(Sema &Self, + const TypeSourceInfo *Lhs, + const TypeSourceInfo *Rhs, + SourceLocation KeyLoc) { + + QualType LhsT = Lhs->getType(); + QualType RhsT = Rhs->getType(); + + // C++0x [meta.rel]p4: + // Given the following function prototype: + // + // template <class T> + // typename add_rvalue_reference<T>::type create(); + // + // the predicate condition for a template specialization + // is_convertible<From, To> shall be satisfied if and only if + // the return expression in the following code would be + // well-formed, including any implicit conversions to the return + // type of the function: + // + // To test() { + // return create<From>(); + // } + // + // Access checking is performed as if in a context unrelated to To and + // From. Only the validity of the immediate context of the expression + // of the return-statement (including conversions to the return type) + // is considered. + // + // We model the initialization as a copy-initialization of a temporary + // of the appropriate type, which for this expression is identical to the + // return statement (since NRVO doesn't apply). + + // Functions aren't allowed to return function or array types. + if (RhsT->isFunctionType() || RhsT->isArrayType()) + return ExprError(); + + // A function definition requires a complete, non-abstract return type. + if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) || + Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT)) + return ExprError(); + + // Compute the result of add_rvalue_reference. + if (LhsT->isObjectType() || LhsT->isFunctionType()) + LhsT = Self.Context.getRValueReferenceType(LhsT); + + // Build a fake source and destination for initialization. + InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT)); + OpaqueValueExpr From(KeyLoc, LhsT.getNonLValueExprType(Self.Context), + Expr::getValueKindForType(LhsT)); + Expr *FromPtr = &From; + InitializationKind Kind = + InitializationKind::CreateCopy(KeyLoc, SourceLocation()); + + // Perform the initialization in an unevaluated context within a SFINAE + // trap at translation unit scope. + EnterExpressionEvaluationContext Unevaluated( + Self, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true); + Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl()); + InitializationSequence Init(Self, To, Kind, FromPtr); + if (Init.Failed()) + return ExprError(); + + ExprResult Result = Init.Perform(Self, To, Kind, FromPtr); + if (Result.isInvalid() || SFINAE.hasErrorOccurred()) + return ExprError(); + + return Result; +} + static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc, ArrayRef<TypeSourceInfo *> Args, @@ -5640,13 +5711,16 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind, // Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary // alongside the IsConstructible traits to avoid duplication. - if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary && Kind != BTT_ReferenceConstructsFromTemporary) + if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary && + Kind != BTT_ReferenceConstructsFromTemporary && + Kind != BTT_ReferenceConvertsFromTemporary) return EvaluateBinaryTypeTrait(S, Kind, Args[0], Args[1], RParenLoc); switch (Kind) { case clang::BTT_ReferenceBindsToTemporary: case clang::BTT_ReferenceConstructsFromTemporary: + case clang::BTT_ReferenceConvertsFromTemporary: case clang::TT_IsConstructible: case clang::TT_IsNothrowConstructible: case clang::TT_IsTriviallyConstructible: { @@ -5710,8 +5784,10 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind, Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl()); InitializedEntity To( InitializedEntity::InitializeTemporary(S.Context, Args[0])); - InitializationKind InitKind(InitializationKind::CreateDirect(KWLoc, KWLoc, - RParenLoc)); + InitializationKind InitKind( + Kind == clang::BTT_ReferenceConvertsFromTemporary + ? InitializationKind::CreateCopy(KWLoc, KWLoc) + : InitializationKind::CreateDirect(KWLoc, KWLoc, RParenLoc)); InitializationSequence Init(S, To, InitKind, ArgExprs); if (Init.Failed()) return false; @@ -5723,7 +5799,9 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind, if (Kind == clang::TT_IsConstructible) return true; - if (Kind == clang::BTT_ReferenceBindsToTemporary || Kind == clang::BTT_ReferenceConstructsFromTemporary) { + if (Kind == clang::BTT_ReferenceBindsToTemporary || + Kind == clang::BTT_ReferenceConstructsFromTemporary || + Kind == clang::BTT_ReferenceConvertsFromTemporary) { if (!T->isReferenceType()) return false; @@ -5737,9 +5815,12 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind, if (U->isReferenceType()) return false; - TypeSourceInfo *TPtr = S.Context.CreateTypeSourceInfo(S.Context.getPointerType(S.BuiltinRemoveReference(T, UnaryTransformType::RemoveCVRef, {}))); - TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(S.Context.getPointerType(S.BuiltinRemoveReference(U, UnaryTransformType::RemoveCVRef, {}))); - return EvaluateBinaryTypeTrait(S, TypeTrait::BTT_IsConvertibleTo, UPtr, TPtr, RParenLoc); + TypeSourceInfo *TPtr = S.Context.CreateTypeSourceInfo( + S.Context.getPointerType(T.getNonReferenceType())); + TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo( + S.Context.getPointerType(U.getNonReferenceType())); + return !CheckConvertibilityForTypeTraits(S, UPtr, TPtr, RParenLoc) + .isInvalid(); } if (Kind == clang::TT_IsNothrowConstructible) @@ -5945,68 +6026,12 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI case BTT_IsConvertible: case BTT_IsConvertibleTo: case BTT_IsNothrowConvertible: { - // C++0x [meta.rel]p4: - // Given the following function prototype: - // - // template <class T> - // typename add_rvalue_reference<T>::type create(); - // - // the predicate condition for a template specialization - // is_convertible<From, To> shall be satisfied if and only if - // the return expression in the following code would be - // well-formed, including any implicit conversions to the return - // type of the function: - // - // To test() { - // return create<From>(); - // } - // - // Access checking is performed as if in a context unrelated to To and - // From. Only the validity of the immediate context of the expression - // of the return-statement (including conversions to the return type) - // is considered. - // - // We model the initialization as a copy-initialization of a temporary - // of the appropriate type, which for this expression is identical to the - // return statement (since NRVO doesn't apply). - - // Functions aren't allowed to return function or array types. - if (RhsT->isFunctionType() || RhsT->isArrayType()) - return false; - - // A return statement in a void function must have void type. if (RhsT->isVoidType()) return LhsT->isVoidType(); - // A function definition requires a complete, non-abstract return type. - if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) || - Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT)) - return false; - - // Compute the result of add_rvalue_reference. - if (LhsT->isObjectType() || LhsT->isFunctionType()) - LhsT = Self.Context.getRValueReferenceType(LhsT); - - // Build a fake source and destination for initialization. - InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT)); - OpaqueValueExpr From(KeyLoc, LhsT.getNonLValueExprType(Self.Context), - Expr::getValueKindForType(LhsT)); - Expr *FromPtr = &From; - InitializationKind Kind(InitializationKind::CreateCopy(KeyLoc, - SourceLocation())); - - // Perform the initialization in an unevaluated context within a SFINAE - // trap at translation unit scope. - EnterExpressionEvaluationContext Unevaluated( - Self, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true); - Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl()); - InitializationSequence Init(Self, To, Kind, FromPtr); - if (Init.Failed()) - return false; - - ExprResult Result = Init.Perform(Self, To, Kind, FromPtr); - if (Result.isInvalid() || SFINAE.hasErrorOccurred()) + ExprResult Result = + CheckConvertibilityForTypeTraits(Self, Lhs, Rhs, KeyLoc); + if (Result.isInvalid()) return false; if (BTT != BTT_IsNothrowConvertible) diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index 01991887b284a7..3b20edfd1d4f10 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -2944,6 +2944,17 @@ void reference_binds_to_temporary_checks() { static_assert((__reference_binds_to_temporary(const int &, long))); } + +struct ExplicitConversionRvalueRef { + operator int(); + explicit operator int&&(); +}; + +struct ExplicitConversionRef { + operator int(); + explicit operator int&(); +}; + void reference_constructs_from_temporary_checks() { static_assert(!__reference_constructs_from_temporary(int &, int &)); static_assert(!__reference_constructs_from_temporary(int &, int &&)); @@ -2987,6 +2998,63 @@ void reference_constructs_from_temporary_checks() { static_assert(!__reference_constructs_from_temporary(const int&, int&&)); static_assert(__reference_constructs_from_temporary(int&&, long&&)); static_assert(__reference_constructs_from_temporary(int&&, long)); + + + static_assert(!__reference_constructs_from_temporary(int&, ExplicitConversionRef)); + static_assert(!__reference_constructs_from_temporary(const int&, ExplicitConversionRef)); + static_assert(!__reference_constructs_from_temporary(int&&, ExplicitConversionRvalueRef)); + + +} + +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 + + static_assert(__reference_converts_from_temporary(int const &, long &)); + static_assert(__reference_converts_from_temporary(int const &, long &&)); + static_assert(__reference_converts_from_temporary(int &&, long &)); + + using LRef = ConvertsToRef<int, int &>; + using RRef = ConvertsToRef<int, int &&>; + using CLRef = ConvertsToRef<int, const int &>; + using LongRef = ConvertsToRef<long, long &>; + static_assert(__is_constructible(int &, LRef)); + static_assert(!__reference_converts_from_temporary(int &, LRef)); + + static_assert(__is_constructible(int &&, RRef)); + static_assert(!__reference_converts_from_temporary(int &&, RRef)); + + static_assert(__is_constructible(int const &, CLRef)); + static_assert(!__reference_converts_from_temporary(int &&, CLRef)); + + static_assert(__is_constructible(int const &, LongRef)); + 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(POD const&, Derives)); + static_assert(__reference_converts_from_temporary(int&&, int)); + static_assert(__reference_converts_from_temporary(const int&, int)); + static_assert(!__reference_converts_from_temporary(int&&, int&&)); + static_assert(!__reference_converts_from_temporary(const int&, int&&)); + static_assert(__reference_converts_from_temporary(int&&, long&&)); + static_assert(__reference_converts_from_temporary(int&&, long)); + + static_assert(!__reference_converts_from_temporary(int&, ExplicitConversionRef)); + static_assert(__reference_converts_from_temporary(const int&, ExplicitConversionRef)); + static_assert(__reference_converts_from_temporary(int&&, ExplicitConversionRvalueRef)); + } void array_rank() { diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 260f74ded93c54..fd764c847d39b4 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -347,15 +347,7 @@ <h2 id="cxx23">C++23 implementation status</h2> <tr> <td>Type trait to determine if a reference binds to a temporary</td> <td><a href="https://wg21.link/P2255R2">P2255R2</a></td> - <td class="partial" align="center"> - <details><summary>Partial</summary> - Clang provides <tt>__reference_constructs_from_temporary</tt> type - trait builtin, with which <tt>std::reference_constructs_from_temporary</tt> - is implemented. <tt>__reference_converts_from_temporary</tt> needs to be - provided, following the normal cross-vendor convention to implement - traits requiring compiler support directly. - </details></td> - </td> + <td class="unreleased" align="center">Clang 19</td> </tr> <!-- July 2022 papers --> <tr> `````````` </details> https://github.com/llvm/llvm-project/pull/91199 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits