[clang] 2dbe89d - [Clang] Implement __reference_converts_from_temporary (#91199)
via cfe-commits
cfe-commits at lists.llvm.org
Thu May 9 23:50:48 PDT 2024
Author: cor3ntin
Date: 2024-05-10T08:50:44+02:00
New Revision: 2dbe89d15046bedcc36a5de1242e20aa91a5e598
URL: https://github.com/llvm/llvm-project/commit/2dbe89d15046bedcc36a5de1242e20aa91a5e598
DIFF: https://github.com/llvm/llvm-project/commit/2dbe89d15046bedcc36a5de1242e20aa91a5e598.diff
LOG: [Clang] Implement __reference_converts_from_temporary (#91199)
This completes the required language support for P2255R2.
Added:
Modified:
clang/docs/LanguageExtensions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/TokenKinds.def
clang/lib/Lex/PPMacroExpansion.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/test/SemaCXX/type-traits.cpp
clang/www/cxx_status.html
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 3627a780886a0..a09c409f8f91a 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1662,8 +1662,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 4547636318a74..eef627ff2e31f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -182,6 +182,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 a27fbed358a60..56c4b17f769d7 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 a5f22f01682d2..a478e0badb0c7 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 8e0e868248293..96c9708c3711b 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 5f5f9a79c8c40..0551b8314f9f6 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 c1cb03e4ec7ae..ae844bc699143 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 01991887b284a..f2fd45762abf8 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -2908,6 +2908,12 @@ struct ConvertsToRef {
operator RefType() const { return static_cast<RefType>(obj); }
mutable T obj = 42;
};
+template <class T, class RefType = T &>
+class ConvertsToRefPrivate {
+ operator RefType() const { return static_cast<RefType>(obj); }
+ mutable T obj = 42;
+};
+
void reference_binds_to_temporary_checks() {
static_assert(!(__reference_binds_to_temporary(int &, int &)));
@@ -2937,6 +2943,8 @@ void reference_binds_to_temporary_checks() {
static_assert((__is_constructible(int const &, LongRef)));
static_assert((__reference_binds_to_temporary(int const &, LongRef)));
+ static_assert(!__reference_binds_to_temporary(int const &, ConvertsToRefPrivate<long, long &>));
+
// Test that it doesn't accept non-reference types as input.
static_assert(!(__reference_binds_to_temporary(int, long)));
@@ -2944,6 +2952,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 &&));
@@ -2973,6 +2992,8 @@ void reference_constructs_from_temporary_checks() {
static_assert(__is_constructible(int const &, LongRef));
static_assert(__reference_constructs_from_temporary(int const &, LongRef));
+ static_assert(!__reference_constructs_from_temporary(int const &, ConvertsToRefPrivate<long, long &>));
+
// Test that it doesn't accept non-reference types as input.
static_assert(!__reference_constructs_from_temporary(int, long));
@@ -2987,6 +3008,65 @@ 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));
+ static_assert(!__reference_converts_from_temporary(int const &, ConvertsToRefPrivate<long, long &>));
+
+
+ // 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 6e0599cc9fe0d..1338f544ffcb5 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>
More information about the cfe-commits
mailing list