EricWF created this revision.

The STL types `std::pair` and `std::tuple` can both store reference types. 
However their constructors cannot adequately check if the initialization of 
reference types is safe.  For example:

  std::tuple<std::tuple<int> const&> t = 42;
  // The stored reference is already dangling.

This patch attempts to implement a traits that acts the same as 
`__is_constructible(type, argtypes...)` except when `type` is a reference. In 
which case the trait disallows initialization from a materialized temporary.  
For example:

  static_assert(__is_constructible(int const&, long));
  static_assert(!__is_direct_constructible(int const&, long));

I don't think `__is_direct_constructible` is a good name, but I don't have any 
better ideas. All bikeshedding welcome.


https://reviews.llvm.org/D29930

Files:
  docs/LanguageExtensions.rst
  include/clang/Basic/TokenKinds.def
  include/clang/Basic/TypeTraits.h
  lib/Parse/ParseDeclCXX.cpp
  lib/Parse/ParseExpr.cpp
  lib/Sema/SemaExprCXX.cpp
  test/SemaCXX/type-traits.cpp

Index: test/SemaCXX/type-traits.cpp
===================================================================
--- test/SemaCXX/type-traits.cpp
+++ test/SemaCXX/type-traits.cpp
@@ -2048,6 +2048,8 @@
 
   // PR25513
   { int arr[F(__is_constructible(int(int)))]; }
+
+  { int arr[T(__is_constructible(int const&, long))]; }
 }
 
 // Instantiation of __is_trivially_constructible
@@ -2078,6 +2080,29 @@
   { int arr[F((is_trivially_constructible<Abstract>::value))]; } // PR19178
 }
 
+struct MultiInit {
+  template <class ...Args>
+  MultiInit(Args&&...) {}
+};
+
+void direct_constructible_checks() {
+  { int arr[T((__is_direct_constructible(int&, int&)))]; }
+  { int arr[F((__is_direct_constructible(int&, int&&)))]; }
+
+  { int arr[T((__is_direct_constructible(int const&, int&)))]; }
+  { int arr[T((__is_direct_constructible(int const&, int const&)))]; }
+  { int arr[T((__is_direct_constructible(int const&, int&&)))]; }
+
+  { int arr[F((__is_direct_constructible(int&, long &)))]; }
+  { int arr[F((__is_direct_constructible(int &&, long &)))]; }
+  { int arr[F((__is_direct_constructible(MultiInit&&, long, int, void*)))]; }
+  { int arr[F((__is_direct_constructible(MultiInit&&, MultiInit const&)))]; }
+
+  // Test that it works like __is_constructible on object types.
+  { int arr[T((__is_direct_constructible(int, long)))]; }
+  { int arr[T((__is_direct_constructible(MultiInit, long, int, void*)))]; }
+}
+
 void array_rank() {
   int t01[T(__array_rank(IntAr) == 1)];
   int t02[T(__array_rank(ConstIntArAr) == 2)];
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -4570,6 +4570,7 @@
 
   switch (Kind) {
   case clang::TT_IsConstructible:
+  case clang::TT_IsDirectConstructible:
   case clang::TT_IsNothrowConstructible:
   case clang::TT_IsTriviallyConstructible: {
     // C++11 [meta.unary.prop]:
@@ -4644,6 +4645,13 @@
     if (Kind == clang::TT_IsConstructible)
       return true;
 
+    if (Kind == clang::TT_IsDirectConstructible) {
+      if (!T->isReferenceType())
+        return true;
+
+      return Init.isDirectReferenceBinding();
+    }
+
     if (Kind == clang::TT_IsNothrowConstructible)
       return S.canThrow(Result.get()) == CT_Cannot;
 
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -809,6 +809,7 @@
           REVERTIBLE_TYPE_TRAIT(__is_constructible);
           REVERTIBLE_TYPE_TRAIT(__is_convertible);
           REVERTIBLE_TYPE_TRAIT(__is_convertible_to);
+          REVERTIBLE_TYPE_TRAIT(__is_direct_constructible);
           REVERTIBLE_TYPE_TRAIT(__is_destructible);
           REVERTIBLE_TYPE_TRAIT(__is_empty);
           REVERTIBLE_TYPE_TRAIT(__is_enum);
Index: lib/Parse/ParseDeclCXX.cpp
===================================================================
--- lib/Parse/ParseDeclCXX.cpp
+++ lib/Parse/ParseDeclCXX.cpp
@@ -1394,6 +1394,7 @@
                   tok::kw___is_constructible,
                   tok::kw___is_convertible,
                   tok::kw___is_convertible_to,
+                  tok::kw___is_direct_constructible,
                   tok::kw___is_destructible,
                   tok::kw___is_empty,
                   tok::kw___is_enum,
Index: include/clang/Basic/TypeTraits.h
===================================================================
--- include/clang/Basic/TypeTraits.h
+++ include/clang/Basic/TypeTraits.h
@@ -79,6 +79,7 @@
     BTT_IsTriviallyAssignable,
     BTT_Last = BTT_IsTriviallyAssignable,
     TT_IsConstructible,
+    TT_IsDirectConstructible,
     TT_IsNothrowConstructible,
     TT_IsTriviallyConstructible
   };
Index: include/clang/Basic/TokenKinds.def
===================================================================
--- include/clang/Basic/TokenKinds.def
+++ include/clang/Basic/TokenKinds.def
@@ -415,6 +415,7 @@
 TYPE_TRAIT_2(__is_nothrow_assignable, IsNothrowAssignable, KEYCXX)
 TYPE_TRAIT_N(__is_constructible, IsConstructible, KEYCXX)
 TYPE_TRAIT_N(__is_nothrow_constructible, IsNothrowConstructible, KEYCXX)
+TYPE_TRAIT_N(__is_direct_constructible, IsDirectConstructible, KEYCXX)
 
 // MSVC14.0 / VS2015 Type Traits
 TYPE_TRAIT_2(__is_assignable, IsAssignable, KEYCXX)
Index: docs/LanguageExtensions.rst
===================================================================
--- docs/LanguageExtensions.rst
+++ docs/LanguageExtensions.rst
@@ -996,6 +996,12 @@
 * ``__is_base_of`` (GNU, Microsoft)
 * ``__is_class`` (GNU, Microsoft)
 * ``__is_convertible_to`` (Microsoft)
+* ``__is_direct_constructible(type, argtypes...)`` (Clang): Determines whether a
+  value of type ``type`` can be direct-initialized with arguments of types
+  ``argtypes...`` with the added condition that if ``type`` is a reference type
+  it cannot be initialized from a materialized temporary. This trait is used
+  to check if construction of a reference is safe in contexts where lifetime
+  extensions doesn't apply.
 * ``__is_empty`` (GNU, Microsoft)
 * ``__is_enum`` (GNU, Microsoft)
 * ``__is_interface_class`` (Microsoft)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to