EricWF created this revision.
EricWF added reviewers: mclow.lists, eugenis, ldionne.
EricWF added a subscriber: cfe-commits.

This patch fixes the following bugs:
  * PR23256  tuple<A> is_constructible attempts tuple<A> to A conversion:
       * Fix this by disabling the UTypes... overloads using the 
`__disable_tuple_args_ctor<...>` meta-function.
  *  PR22806 Move from destroyed object in tuple_cat of nested tuples
       * Fix this by disabling the tuple-like overloads using the 
`__disable_tuple_like_ctor<...>` meta-function.

The goal of the `__disable_*_ctor` metafunctions is to disable obviously 
incorrect constructor overloads before they evaluate more SFINAE and cause a 
hard compile error. The implementation of these traits uses NO SFINAE that 
could result in a hard compile error. These must be safe to evaluate always.



http://reviews.llvm.org/D12502

Files:
  include/__tuple
  include/tuple
  
test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp
  
test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp

Index: test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
===================================================================
--- /dev/null
+++ test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR23256_constrain_UTypes_ctor.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: c++98, c++03
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class ...UTypes>
+//    EXPLICIT(...) tuple(UTypes&&...)
+
+// Check that the UTypes... ctor is properly disabled before evaluating any
+// SFINAE when the tuple-like copy/move ctor should *clearly* be selected
+// instead. This happens 'sizeof...(UTypes) == 1' and the first element of
+// 'UTypes...' is an instance of the tuple itself. See PR23256.
+
+#include <tuple>
+#include <memory>
+#include <type_traits>
+
+
+class UnconstrainedCtor {
+public:
+  UnconstrainedCtor() : value_(0) {}
+
+  template <typename T>
+  constexpr UnconstrainedCtor(T value) : value_(static_cast<int>(value)) {
+      static_assert(std::is_same<int, T>::value, "");
+  }
+
+private:
+  int value_;
+};
+
+int main() {
+    typedef UnconstrainedCtor A;
+    {
+        static_assert(std::is_trivially_copy_constructible<std::tuple<A>>::value, "");
+        static_assert(std::is_trivially_move_constructible<std::tuple<A>>::value, "");
+    }
+    {
+        static_assert(std::is_constructible<
+            std::tuple<A>,
+            std::allocator_arg_t, std::allocator<void>,
+            std::tuple<A> const&
+        >::value, "");
+        static_assert(std::is_constructible<
+            std::tuple<A>,
+            std::allocator_arg_t, std::allocator<void>,
+            std::tuple<A> &&
+        >::value, "");
+    }
+}
Index: test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp
===================================================================
--- /dev/null
+++ test/std/utilities/tuple/tuple.tuple/tuple.cnstr/PR22806_constrain_tuple_like_ctor.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: c++98, c++03
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class TupleLike>
+//   tuple(TupleLike&&);
+// template <class Alloc, class TupleLike>
+//   tuple(std::allocator_arg_t, Alloc const&, TupleLike&&);
+
+// Check that the tuple-like copy/move ctors are properly disabled when the
+// UTypes... constructor should be selected. See PR22806.
+
+#include <tuple>
+#include <memory>
+#include <cassert>
+
+#include "tracked_value.h"
+
+int main()
+{
+    { // rvalue reference
+        TrackedValue v;
+        std::tuple<TrackedValue> t1(v);
+        // This selects the wrong constructor and constructs std::tuple<Tracked>&&
+        // from a temporary.
+        std::tuple<std::tuple<TrackedValue>&&> t2(std::move(t1));
+        // This moves from the reference constructed from the temporary.
+        std::tuple<std::tuple<TrackedValue>>  t3(std::move(t2));
+    }
+    { // const lvalue reference
+        TrackedValue v;
+        std::tuple<TrackedValue> t1(v);
+        std::tuple<std::tuple<TrackedValue> const &> t2(t1);
+        std::tuple<std::tuple<TrackedValue>>  t3(t2);
+    }
+    { // lvalue reference
+        TrackedValue v;
+        std::tuple<TrackedValue> t1(v);
+        std::tuple<std::tuple<TrackedValue> &> t2(t1);
+        std::tuple<std::tuple<TrackedValue>>  t3(t2);
+    }
+    { // rvalue reference via allocator ctor
+        TrackedValue v;
+        std::tuple<TrackedValue> t1(v);
+        std::allocator<void> a;
+        std::tuple<std::tuple<TrackedValue>&&> t2(std::allocator_arg, a, std::move(t1));
+        std::tuple<std::tuple<TrackedValue>>  t3(std::move(t2));
+    }
+    { // const lvalue reference via allocator ctor
+        TrackedValue v;
+        std::tuple<TrackedValue> t1(v);
+        std::allocator<void> a;
+        std::tuple<std::tuple<TrackedValue> const &> t2(std::allocator_arg, a, t1);
+        std::tuple<std::tuple<TrackedValue>>  t3(t2);
+    }
+    { // lvalue reference via allocator ctor
+        TrackedValue v;
+        std::tuple<TrackedValue> t1(v);
+        std::allocator<void> a;
+        std::tuple<std::tuple<TrackedValue> &> t2(std::allocator_arg, a, t1);
+        std::tuple<std::tuple<TrackedValue>>  t3(t2);
+    }
+}
Index: include/tuple
===================================================================
--- include/tuple
+++ include/tuple
@@ -540,6 +540,8 @@
               typename enable_if
                       <
                          sizeof...(_Up) <= sizeof...(_Tp) &&
+                        __lazy_and<
+                          __lazy_not<__disable_tuple_args_ctor<tuple, _Up...>>,
                          __tuple_convertible
                          <
                             tuple<_Up...>,
@@ -547,13 +549,14 @@
                                      sizeof...(_Up) < sizeof...(_Tp) ?
                                         sizeof...(_Up) :
                                         sizeof...(_Tp)>::type
-                         >::value &&
+                         >,
                          __all_default_constructible<
                             typename __make_tuple_types<tuple, sizeof...(_Tp),
                                 sizeof...(_Up) < sizeof...(_Tp) ?
                                     sizeof...(_Up) :
                                     sizeof...(_Tp)>::type
-                         >::value,
+                         >
+                    >::type::value,
                          bool
                       >::type = false
              >
@@ -578,6 +581,8 @@
               typename enable_if
                       <
                          sizeof...(_Up) <= sizeof...(_Tp) &&
+                        __lazy_and<
+                         __lazy_not<__disable_tuple_args_ctor<tuple, _Up...>>,
                          __tuple_constructible
                          <
                             tuple<_Up...>,
@@ -585,21 +590,21 @@
                                      sizeof...(_Up) < sizeof...(_Tp) ?
                                         sizeof...(_Up) :
                                         sizeof...(_Tp)>::type
-                         >::value &&
-                         !__tuple_convertible
+                         >,
+                         __lazy_not<__tuple_convertible
                          <
                             tuple<_Up...>,
                             typename __make_tuple_types<tuple,
                                      sizeof...(_Up) < sizeof...(_Tp) ?
                                         sizeof...(_Up) :
                                         sizeof...(_Tp)>::type
-                         >::value &&
+                         >>,
                          __all_default_constructible<
                             typename __make_tuple_types<tuple, sizeof...(_Tp),
                                 sizeof...(_Up) < sizeof...(_Tp) ?
                                     sizeof...(_Up) :
                                     sizeof...(_Tp)>::type
-                         >::value,
+                         >>::type::value,
                          bool
                       >::type =false
              >
@@ -625,6 +630,8 @@
               class = typename enable_if
                       <
                          sizeof...(_Up) <= sizeof...(_Tp) &&
+                         __lazy_and<
+                         __lazy_not<__disable_tuple_args_ctor<tuple, _Up...>>,
                          __tuple_convertible
                          <
                             tuple<_Up...>,
@@ -632,13 +639,13 @@
                                      sizeof...(_Up) < sizeof...(_Tp) ?
                                         sizeof...(_Up) :
                                         sizeof...(_Tp)>::type
-                         >::value &&
+                         >,
                          __all_default_constructible<
                             typename __make_tuple_types<tuple, sizeof...(_Tp),
                                 sizeof...(_Up) < sizeof...(_Tp) ?
                                     sizeof...(_Up) :
                                     sizeof...(_Tp)>::type
-                         >::value
+                         >>::value
                       >::type
              >
         _LIBCPP_INLINE_VISIBILITY
@@ -653,6 +660,7 @@
     template <class _Tuple,
               typename enable_if
                       <
+                        !__disable_tuple_like_ctor<_Tuple, tuple>::value &&
                          __tuple_convertible<_Tuple, tuple>::value,
                          bool
                       >::type = false
@@ -664,6 +672,7 @@
     template <class _Tuple,
               typename enable_if
                       <
+                         !__disable_tuple_like_ctor<_Tuple, tuple>::value &&
                          __tuple_constructible<_Tuple, tuple>::value &&
                          !__tuple_convertible<_Tuple, tuple>::value,
                          bool
@@ -677,6 +686,7 @@
     template <class _Alloc, class _Tuple,
               class = typename enable_if
                       <
+                         !__disable_tuple_like_ctor<_Tuple, tuple>::value &&
                          __tuple_convertible<_Tuple, tuple>::value
                       >::type
              >
Index: include/__tuple
===================================================================
--- include/__tuple
+++ include/__tuple
@@ -341,6 +341,91 @@
                                     tuple_size<_Up>::value, _Tp, _Up>
 {};
 
+
+/**
+ * A utility metafunction that gets the first element type of a tuple
+ * and returns that type with no ref qualifiers.
+ */
+template <class _Tuple>
+struct __first_tuple_elem : remove_reference<
+    typename tuple_element<0,
+        typename remove_reference<_Tuple>::type
+    >::type
+> {};
+
+/**
+ * A metafunction that determines if the tuple-like copy/move ctor should be
+ * disabled because it would construct a dangling reference to a nested tuple.
+ * See PR22806 - https://llvm.org/bugs/show_bug.cgi?id=22806.
+ *
+ * The goal of this meta-function is to disable the tuple-like ctor for
+ * instantiations like:
+ *
+ * std::tuple<std::tuple<ValueType>&&>::tuple(std::tuple<ValueType>&&);
+ *
+ * In this case the constructor would attempt to bind 'ValueType&&' to the first
+ * element of type 'std::tuple<ValueType>&&' which would create a temporary
+ * 'std::tuple<ValueType>' from 'ValueType&&' and assign it to a reference. The
+ * temporary would then go out of scope and the tuple would contain a dangling
+ * reference.
+ *
+ * This metafunction returns true if all of the following are true.
+ *   1. _From is "tuple-like". This is always true but we need it to guard other
+ *      meta-programming.
+ *
+ *   2. _Tuple has size 1. This is the only case where the copy/move ctors can
+ *      be a better match that the UTypes... ctors because a single argument
+ *      ctor is a better match than a variadic one that deduces one argument.
+ *
+ *   3. The first element of _Tuple, 'TupleElem0', is a reference.
+ *
+ *   4. The first element of _From, 'FromElem0', is not an instance of
+ *      or derived from 'TupleElem0'. If it were then the construction of
+ *     'TupleElem0' wouldn't result in a dangling reference.
+ */
+template <class _From, class _Tuple,
+          bool = __tuple_like<typename remove_reference<_From>::type>::value &&
+                tuple_size<_Tuple>::value == 1 &&
+                is_reference<typename tuple_element<0, _Tuple>::type>::value
+          >
+struct __disable_tuple_like_ctor : false_type {};
+
+template <class _From, class _Tuple>
+struct __disable_tuple_like_ctor<_From, _Tuple, true> : integral_constant<bool,
+    tuple_size<typename remove_reference<_From>::type>::value == 1 &&
+    !is_base_of<
+        typename __first_tuple_elem<_Tuple>::type,
+        typename __first_tuple_elem<_From>::type
+    >::value
+> {};
+
+
+/**
+ * A metafunction that returns 'true' when the tuple(UTypes&&...) constructor
+ * should not participate in overload resolution. This metafunction is meant to
+ * guard the other SFINAE on the tuple(UTypes&&...) from being evaluated when
+ * its clear that a tuple copy/move constructor should be chosen instead.
+ * See PR23256 - https://llvm.org/bugs/show_bug.cgi?id=23256.
+ *
+ * The metafunction returns true if and only if:
+ *   1. sizeof...(UTypes) == 1. The copy constructor only takes 1 argument
+ *     so we obviously didn't intend to call it if we provide more that one
+ *     argument.
+ *
+ *   2. The first element (and only) element type, 'UT0', is the same type as the
+ *      tuple. Ex std::tuple<A>(std::tuple<A> const&).
+ *
+ */
+template <class _Tuple, class ..._Args>
+struct __disable_tuple_args_ctor : false_type {};
+
+template <class _Tuple, class _Arg0>
+struct __disable_tuple_args_ctor<_Tuple, _Arg0>
+    : is_same<
+        _Tuple,
+        typename remove_cv<typename remove_reference<_Arg0>::type>::type
+      > {};
+
 #endif  // _LIBCPP_HAS_NO_VARIADICS
 
 _LIBCPP_END_NAMESPACE_STD
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to