Skip to content

Commit 2dbe89d

Browse files
authored
[Clang] Implement __reference_converts_from_temporary (#91199)
This completes the required language support for P2255R2.
1 parent 5bde2aa commit 2dbe89d

File tree

9 files changed

+182
-82
lines changed

9 files changed

+182
-82
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1662,8 +1662,11 @@ The following type trait primitives are supported by Clang. Those traits marked
16621662
``T`` from ``U`` is ill-formed.
16631663
Deprecated, use ``__reference_constructs_from_temporary``.
16641664
* ``__reference_constructs_from_temporary(T, U)`` (C++)
1665-
Returns true if a reference ``T`` can be constructed from a temporary of type
1665+
Returns true if a reference ``T`` can be direct-initialized from a temporary of type
16661666
a non-cv-qualified ``U``.
1667+
* ``__reference_converts_from_temporary(T, U)`` (C++)
1668+
Returns true if a reference ``T`` can be copy-initialized from a temporary of type
1669+
a non-cv-qualified ``U``.
16671670
* ``__underlying_type`` (C++, GNU, Microsoft)
16681671

16691672
In addition, the following expression traits are supported:

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ C++23 Feature Support
182182

183183
- Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_.
184184

185+
- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for
186+
`P2255R2: Type trait to determine if a reference binds to a temporary <https://wg21.link/P2255R2>`_.
187+
185188
C++2c Feature Support
186189
^^^^^^^^^^^^^^^^^^^^^
187190

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ TYPE_TRAIT_1(__is_referenceable, IsReferenceable, KEYCXX)
537537
TYPE_TRAIT_1(__can_pass_in_regs, CanPassInRegs, KEYCXX)
538538
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)
539539
TYPE_TRAIT_2(__reference_constructs_from_temporary, ReferenceConstructsFromTemporary, KEYCXX)
540+
TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary, KEYCXX)
540541

541542
// Embarcadero Expression Traits
542543
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)

clang/lib/Lex/PPMacroExpansion.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,8 +1714,6 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
17141714
return llvm::StringSwitch<bool>(II->getName())
17151715
.Case("__array_rank", true)
17161716
.Case("__array_extent", true)
1717-
.Case("__reference_binds_to_temporary", true)
1718-
.Case("__reference_constructs_from_temporary", true)
17191717
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) .Case("__" #Trait, true)
17201718
#include "clang/Basic/TransformTypeTraits.def"
17211719
.Default(false);

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,9 +1779,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
17791779
tok::kw___is_union,
17801780
tok::kw___is_unsigned,
17811781
tok::kw___is_void,
1782-
tok::kw___is_volatile,
1783-
tok::kw___reference_binds_to_temporary,
1784-
tok::kw___reference_constructs_from_temporary))
1782+
tok::kw___is_volatile
1783+
))
17851784
// GNU libstdc++ 4.2 and libc++ use certain intrinsic names as the
17861785
// name of struct templates, but some are keywords in GCC >= 4.3
17871786
// and Clang. Therefore, when we see the token sequence "struct

clang/lib/Parse/ParseExpr.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,6 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
11661166
REVERTIBLE_TYPE_TRAIT(__is_void);
11671167
REVERTIBLE_TYPE_TRAIT(__is_volatile);
11681168
REVERTIBLE_TYPE_TRAIT(__reference_binds_to_temporary);
1169-
REVERTIBLE_TYPE_TRAIT(__reference_constructs_from_temporary);
11701169
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \
11711170
REVERTIBLE_TYPE_TRAIT(RTT_JOIN(__, Trait));
11721171
#include "clang/Basic/TransformTypeTraits.def"

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 91 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5627,6 +5627,77 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
56275627
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
56285628
const TypeSourceInfo *Rhs, SourceLocation KeyLoc);
56295629

5630+
static ExprResult CheckConvertibilityForTypeTraits(Sema &Self,
5631+
const TypeSourceInfo *Lhs,
5632+
const TypeSourceInfo *Rhs,
5633+
SourceLocation KeyLoc) {
5634+
5635+
QualType LhsT = Lhs->getType();
5636+
QualType RhsT = Rhs->getType();
5637+
5638+
// C++0x [meta.rel]p4:
5639+
// Given the following function prototype:
5640+
//
5641+
// template <class T>
5642+
// typename add_rvalue_reference<T>::type create();
5643+
//
5644+
// the predicate condition for a template specialization
5645+
// is_convertible<From, To> shall be satisfied if and only if
5646+
// the return expression in the following code would be
5647+
// well-formed, including any implicit conversions to the return
5648+
// type of the function:
5649+
//
5650+
// To test() {
5651+
// return create<From>();
5652+
// }
5653+
//
5654+
// Access checking is performed as if in a context unrelated to To and
5655+
// From. Only the validity of the immediate context of the expression
5656+
// of the return-statement (including conversions to the return type)
5657+
// is considered.
5658+
//
5659+
// We model the initialization as a copy-initialization of a temporary
5660+
// of the appropriate type, which for this expression is identical to the
5661+
// return statement (since NRVO doesn't apply).
5662+
5663+
// Functions aren't allowed to return function or array types.
5664+
if (RhsT->isFunctionType() || RhsT->isArrayType())
5665+
return ExprError();
5666+
5667+
// A function definition requires a complete, non-abstract return type.
5668+
if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) ||
5669+
Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT))
5670+
return ExprError();
5671+
5672+
// Compute the result of add_rvalue_reference.
5673+
if (LhsT->isObjectType() || LhsT->isFunctionType())
5674+
LhsT = Self.Context.getRValueReferenceType(LhsT);
5675+
5676+
// Build a fake source and destination for initialization.
5677+
InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
5678+
OpaqueValueExpr From(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
5679+
Expr::getValueKindForType(LhsT));
5680+
Expr *FromPtr = &From;
5681+
InitializationKind Kind =
5682+
InitializationKind::CreateCopy(KeyLoc, SourceLocation());
5683+
5684+
// Perform the initialization in an unevaluated context within a SFINAE
5685+
// trap at translation unit scope.
5686+
EnterExpressionEvaluationContext Unevaluated(
5687+
Self, Sema::ExpressionEvaluationContext::Unevaluated);
5688+
Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
5689+
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
5690+
InitializationSequence Init(Self, To, Kind, FromPtr);
5691+
if (Init.Failed())
5692+
return ExprError();
5693+
5694+
ExprResult Result = Init.Perform(Self, To, Kind, FromPtr);
5695+
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
5696+
return ExprError();
5697+
5698+
return Result;
5699+
}
5700+
56305701
static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
56315702
SourceLocation KWLoc,
56325703
ArrayRef<TypeSourceInfo *> Args,
@@ -5640,13 +5711,16 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
56405711

56415712
// Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary
56425713
// alongside the IsConstructible traits to avoid duplication.
5643-
if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary && Kind != BTT_ReferenceConstructsFromTemporary)
5714+
if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary &&
5715+
Kind != BTT_ReferenceConstructsFromTemporary &&
5716+
Kind != BTT_ReferenceConvertsFromTemporary)
56445717
return EvaluateBinaryTypeTrait(S, Kind, Args[0],
56455718
Args[1], RParenLoc);
56465719

56475720
switch (Kind) {
56485721
case clang::BTT_ReferenceBindsToTemporary:
56495722
case clang::BTT_ReferenceConstructsFromTemporary:
5723+
case clang::BTT_ReferenceConvertsFromTemporary:
56505724
case clang::TT_IsConstructible:
56515725
case clang::TT_IsNothrowConstructible:
56525726
case clang::TT_IsTriviallyConstructible: {
@@ -5710,8 +5784,10 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
57105784
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
57115785
InitializedEntity To(
57125786
InitializedEntity::InitializeTemporary(S.Context, Args[0]));
5713-
InitializationKind InitKind(InitializationKind::CreateDirect(KWLoc, KWLoc,
5714-
RParenLoc));
5787+
InitializationKind InitKind(
5788+
Kind == clang::BTT_ReferenceConvertsFromTemporary
5789+
? InitializationKind::CreateCopy(KWLoc, KWLoc)
5790+
: InitializationKind::CreateDirect(KWLoc, KWLoc, RParenLoc));
57155791
InitializationSequence Init(S, To, InitKind, ArgExprs);
57165792
if (Init.Failed())
57175793
return false;
@@ -5723,7 +5799,9 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
57235799
if (Kind == clang::TT_IsConstructible)
57245800
return true;
57255801

5726-
if (Kind == clang::BTT_ReferenceBindsToTemporary || Kind == clang::BTT_ReferenceConstructsFromTemporary) {
5802+
if (Kind == clang::BTT_ReferenceBindsToTemporary ||
5803+
Kind == clang::BTT_ReferenceConstructsFromTemporary ||
5804+
Kind == clang::BTT_ReferenceConvertsFromTemporary) {
57275805
if (!T->isReferenceType())
57285806
return false;
57295807

@@ -5737,9 +5815,12 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
57375815
if (U->isReferenceType())
57385816
return false;
57395817

5740-
TypeSourceInfo *TPtr = S.Context.CreateTypeSourceInfo(S.Context.getPointerType(S.BuiltinRemoveReference(T, UnaryTransformType::RemoveCVRef, {})));
5741-
TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(S.Context.getPointerType(S.BuiltinRemoveReference(U, UnaryTransformType::RemoveCVRef, {})));
5742-
return EvaluateBinaryTypeTrait(S, TypeTrait::BTT_IsConvertibleTo, UPtr, TPtr, RParenLoc);
5818+
TypeSourceInfo *TPtr = S.Context.CreateTypeSourceInfo(
5819+
S.Context.getPointerType(T.getNonReferenceType()));
5820+
TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(
5821+
S.Context.getPointerType(U.getNonReferenceType()));
5822+
return !CheckConvertibilityForTypeTraits(S, UPtr, TPtr, RParenLoc)
5823+
.isInvalid();
57435824
}
57445825

57455826
if (Kind == clang::TT_IsNothrowConstructible)
@@ -5945,68 +6026,12 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
59456026
case BTT_IsConvertible:
59466027
case BTT_IsConvertibleTo:
59476028
case BTT_IsNothrowConvertible: {
5948-
// C++0x [meta.rel]p4:
5949-
// Given the following function prototype:
5950-
//
5951-
// template <class T>
5952-
// typename add_rvalue_reference<T>::type create();
5953-
//
5954-
// the predicate condition for a template specialization
5955-
// is_convertible<From, To> shall be satisfied if and only if
5956-
// the return expression in the following code would be
5957-
// well-formed, including any implicit conversions to the return
5958-
// type of the function:
5959-
//
5960-
// To test() {
5961-
// return create<From>();
5962-
// }
5963-
//
5964-
// Access checking is performed as if in a context unrelated to To and
5965-
// From. Only the validity of the immediate context of the expression
5966-
// of the return-statement (including conversions to the return type)
5967-
// is considered.
5968-
//
5969-
// We model the initialization as a copy-initialization of a temporary
5970-
// of the appropriate type, which for this expression is identical to the
5971-
// return statement (since NRVO doesn't apply).
5972-
5973-
// Functions aren't allowed to return function or array types.
5974-
if (RhsT->isFunctionType() || RhsT->isArrayType())
5975-
return false;
5976-
5977-
// A return statement in a void function must have void type.
59786029
if (RhsT->isVoidType())
59796030
return LhsT->isVoidType();
59806031

5981-
// A function definition requires a complete, non-abstract return type.
5982-
if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) ||
5983-
Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT))
5984-
return false;
5985-
5986-
// Compute the result of add_rvalue_reference.
5987-
if (LhsT->isObjectType() || LhsT->isFunctionType())
5988-
LhsT = Self.Context.getRValueReferenceType(LhsT);
5989-
5990-
// Build a fake source and destination for initialization.
5991-
InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
5992-
OpaqueValueExpr From(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
5993-
Expr::getValueKindForType(LhsT));
5994-
Expr *FromPtr = &From;
5995-
InitializationKind Kind(InitializationKind::CreateCopy(KeyLoc,
5996-
SourceLocation()));
5997-
5998-
// Perform the initialization in an unevaluated context within a SFINAE
5999-
// trap at translation unit scope.
6000-
EnterExpressionEvaluationContext Unevaluated(
6001-
Self, Sema::ExpressionEvaluationContext::Unevaluated);
6002-
Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
6003-
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
6004-
InitializationSequence Init(Self, To, Kind, FromPtr);
6005-
if (Init.Failed())
6006-
return false;
6007-
6008-
ExprResult Result = Init.Perform(Self, To, Kind, FromPtr);
6009-
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
6032+
ExprResult Result =
6033+
CheckConvertibilityForTypeTraits(Self, Lhs, Rhs, KeyLoc);
6034+
if (Result.isInvalid())
60106035
return false;
60116036

60126037
if (BTT != BTT_IsNothrowConvertible)

clang/test/SemaCXX/type-traits.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2908,6 +2908,12 @@ struct ConvertsToRef {
29082908
operator RefType() const { return static_cast<RefType>(obj); }
29092909
mutable T obj = 42;
29102910
};
2911+
template <class T, class RefType = T &>
2912+
class ConvertsToRefPrivate {
2913+
operator RefType() const { return static_cast<RefType>(obj); }
2914+
mutable T obj = 42;
2915+
};
2916+
29112917

29122918
void reference_binds_to_temporary_checks() {
29132919
static_assert(!(__reference_binds_to_temporary(int &, int &)));
@@ -2937,13 +2943,26 @@ void reference_binds_to_temporary_checks() {
29372943

29382944
static_assert((__is_constructible(int const &, LongRef)));
29392945
static_assert((__reference_binds_to_temporary(int const &, LongRef)));
2946+
static_assert(!__reference_binds_to_temporary(int const &, ConvertsToRefPrivate<long, long &>));
2947+
29402948

29412949
// Test that it doesn't accept non-reference types as input.
29422950
static_assert(!(__reference_binds_to_temporary(int, long)));
29432951

29442952
static_assert((__reference_binds_to_temporary(const int &, long)));
29452953
}
29462954

2955+
2956+
struct ExplicitConversionRvalueRef {
2957+
operator int();
2958+
explicit operator int&&();
2959+
};
2960+
2961+
struct ExplicitConversionRef {
2962+
operator int();
2963+
explicit operator int&();
2964+
};
2965+
29472966
void reference_constructs_from_temporary_checks() {
29482967
static_assert(!__reference_constructs_from_temporary(int &, int &));
29492968
static_assert(!__reference_constructs_from_temporary(int &, int &&));
@@ -2973,6 +2992,8 @@ void reference_constructs_from_temporary_checks() {
29732992

29742993
static_assert(__is_constructible(int const &, LongRef));
29752994
static_assert(__reference_constructs_from_temporary(int const &, LongRef));
2995+
static_assert(!__reference_constructs_from_temporary(int const &, ConvertsToRefPrivate<long, long &>));
2996+
29762997

29772998
// Test that it doesn't accept non-reference types as input.
29782999
static_assert(!__reference_constructs_from_temporary(int, long));
@@ -2987,6 +3008,65 @@ void reference_constructs_from_temporary_checks() {
29873008
static_assert(!__reference_constructs_from_temporary(const int&, int&&));
29883009
static_assert(__reference_constructs_from_temporary(int&&, long&&));
29893010
static_assert(__reference_constructs_from_temporary(int&&, long));
3011+
3012+
3013+
static_assert(!__reference_constructs_from_temporary(int&, ExplicitConversionRef));
3014+
static_assert(!__reference_constructs_from_temporary(const int&, ExplicitConversionRef));
3015+
static_assert(!__reference_constructs_from_temporary(int&&, ExplicitConversionRvalueRef));
3016+
3017+
3018+
}
3019+
3020+
void reference_converts_from_temporary_checks() {
3021+
static_assert(!__reference_converts_from_temporary(int &, int &));
3022+
static_assert(!__reference_converts_from_temporary(int &, int &&));
3023+
3024+
static_assert(!__reference_converts_from_temporary(int const &, int &));
3025+
static_assert(!__reference_converts_from_temporary(int const &, int const &));
3026+
static_assert(!__reference_converts_from_temporary(int const &, int &&));
3027+
3028+
static_assert(!__reference_converts_from_temporary(int &, long &)); // doesn't construct
3029+
3030+
static_assert(__reference_converts_from_temporary(int const &, long &));
3031+
static_assert(__reference_converts_from_temporary(int const &, long &&));
3032+
static_assert(__reference_converts_from_temporary(int &&, long &));
3033+
3034+
using LRef = ConvertsToRef<int, int &>;
3035+
using RRef = ConvertsToRef<int, int &&>;
3036+
using CLRef = ConvertsToRef<int, const int &>;
3037+
using LongRef = ConvertsToRef<long, long &>;
3038+
static_assert(__is_constructible(int &, LRef));
3039+
static_assert(!__reference_converts_from_temporary(int &, LRef));
3040+
3041+
static_assert(__is_constructible(int &&, RRef));
3042+
static_assert(!__reference_converts_from_temporary(int &&, RRef));
3043+
3044+
static_assert(__is_constructible(int const &, CLRef));
3045+
static_assert(!__reference_converts_from_temporary(int &&, CLRef));
3046+
3047+
static_assert(__is_constructible(int const &, LongRef));
3048+
static_assert(__reference_converts_from_temporary(int const &, LongRef));
3049+
static_assert(!__reference_converts_from_temporary(int const &, ConvertsToRefPrivate<long, long &>));
3050+
3051+
3052+
// Test that it doesn't accept non-reference types as input.
3053+
static_assert(!__reference_converts_from_temporary(int, long));
3054+
3055+
static_assert(__reference_converts_from_temporary(const int &, long));
3056+
3057+
// Additional checks
3058+
static_assert(__reference_converts_from_temporary(POD const&, Derives));
3059+
static_assert(__reference_converts_from_temporary(int&&, int));
3060+
static_assert(__reference_converts_from_temporary(const int&, int));
3061+
static_assert(!__reference_converts_from_temporary(int&&, int&&));
3062+
static_assert(!__reference_converts_from_temporary(const int&, int&&));
3063+
static_assert(__reference_converts_from_temporary(int&&, long&&));
3064+
static_assert(__reference_converts_from_temporary(int&&, long));
3065+
3066+
static_assert(!__reference_converts_from_temporary(int&, ExplicitConversionRef));
3067+
static_assert(__reference_converts_from_temporary(const int&, ExplicitConversionRef));
3068+
static_assert(__reference_converts_from_temporary(int&&, ExplicitConversionRvalueRef));
3069+
29903070
}
29913071

29923072
void array_rank() {

clang/www/cxx_status.html

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -347,15 +347,7 @@ <h2 id="cxx23">C++23 implementation status</h2>
347347
<tr>
348348
<td>Type trait to determine if a reference binds to a temporary</td>
349349
<td><a href="https://wg21.link/P2255R2">P2255R2</a></td>
350-
<td class="partial" align="center">
351-
<details><summary>Partial</summary>
352-
Clang provides <tt>__reference_constructs_from_temporary</tt> type
353-
trait builtin, with which <tt>std::reference_constructs_from_temporary</tt>
354-
is implemented. <tt>__reference_converts_from_temporary</tt> needs to be
355-
provided, following the normal cross-vendor convention to implement
356-
traits requiring compiler support directly.
357-
</details></td>
358-
</td>
350+
<td class="unreleased" align="center">Clang 19</td>
359351
</tr>
360352
<!-- July 2022 papers -->
361353
<tr>

0 commit comments

Comments
 (0)