cjdb created this revision.
cjdb added reviewers: aaron.ballman, shafik, erichkeane, tcanens.
Herald added a project: All.
cjdb requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

This is information that the compiler already has, and should be exposed
so that the library doesn't need to reimplement the exact same
functionality.

Depends on D135339 <https://reviews.llvm.org/D135339>.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D135341

Files:
  clang/docs/LanguageExtensions.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

Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -1417,16 +1417,7 @@
     <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 a <tt>__reference_binds_to_temporary</tt> type trait
-          builtin, with which the library facility can be partially implemented.
-          Both <tt>__reference_constructs_from_temporary</tt> and
-          <tt>__reference_converts_from_temporary</tt> builtins should be
-          provided, following the normal cross-vendor convention to implement
-          traits requiring compiler support directly.
-        </details></td>
-      </td>
+      <td class="partial" align="center">Clang 16</td>
     </tr>
     <!-- July 2022 papers -->
     <tr>
Index: clang/test/SemaCXX/type-traits.cpp
===================================================================
--- clang/test/SemaCXX/type-traits.cpp
+++ 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<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_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<int, int &>;
+  using RRef = ConvertsToRef<int, int &&>;
+  using CLRef = ConvertsToRef<int, const int &>;
+  using LongRef = ConvertsToRef<long, long &>;
+  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)];
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ 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:
Index: clang/lib/Parse/ParseExpr.cpp
===================================================================
--- clang/lib/Parse/ParseExpr.cpp
+++ 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"
Index: clang/lib/Parse/ParseDeclCXX.cpp
===================================================================
--- clang/lib/Parse/ParseDeclCXX.cpp
+++ 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
Index: clang/lib/Lex/PPMacroExpansion.cpp
===================================================================
--- clang/lib/Lex/PPMacroExpansion.cpp
+++ 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);
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ 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)
Index: clang/docs/LanguageExtensions.rst
===================================================================
--- clang/docs/LanguageExtensions.rst
+++ 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:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to