[libcxx-commits] [libcxx] a38a465 - [libc++] fix `shared_ptr`'s incorrect constraints

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Feb 11 11:44:59 PST 2023


Author: Hui
Date: 2023-02-11T19:44:38Z
New Revision: a38a4654ce4b1d2ae8a03797d2e520e415150492

URL: https://github.com/llvm/llvm-project/commit/a38a4654ce4b1d2ae8a03797d2e520e415150492
DIFF: https://github.com/llvm/llvm-project/commit/a38a4654ce4b1d2ae8a03797d2e520e415150492.diff

LOG: [libc++] fix `shared_ptr`'s incorrect constraints

Fix several bugs:
1. https://llvm.org/PR60258
   The conversion constructors' constraint `__compatible_with` incorrectly allow array types conversion to scalar types
2. https://llvm.org/PR53368
   The constructor that takes `unique_ptr` are not suffiently constrained.
3. The constructors that take raw pointers incorretly use `__compatible_with`. They have different constraints

Differential Revision: https://reviews.llvm.org/D143346

Added: 
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_helper.h

Modified: 
    libcxx/include/__memory/shared_ptr.h
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.assign/unique_ptr_Y.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y_rv.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/unique_ptr.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/weak_ptr.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter.pass.cpp
    libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter_allocator.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 1c99686c4e5f3..b4798c6b31cff 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -367,13 +367,57 @@ class _LIBCPP_TEMPLATE_VIS allocator<__shared_ptr_dummy_rebind_allocator_type>
 
 template<class _Tp> class _LIBCPP_TEMPLATE_VIS enable_shared_from_this;
 
-template<class _Tp, class _Up>
+// http://eel.is/c++draft/util.sharedptr#util.smartptr.shared.general-6
+// A pointer type Y* is said to be compatible with a pointer type T*
+// when either Y* is convertible to T* or Y is U[N] and T is cv U[].
+#if _LIBCPP_STD_VER >= 17
+template <class _Yp, class _Tp>
+struct __bounded_convertible_to_unbounded : false_type {};
+
+template <class _Up, std::size_t _Np, class _Tp>
+struct __bounded_convertible_to_unbounded<_Up[_Np], _Tp>
+        : is_same<__remove_cv_t<_Tp>, _Up[]> {};
+
+template <class _Yp, class _Tp>
 struct __compatible_with
-#if _LIBCPP_STD_VER > 14
-    : is_convertible<remove_extent_t<_Tp>*, remove_extent_t<_Up>*> {};
+    : _Or<
+        is_convertible<_Yp*, _Tp*>,
+        __bounded_convertible_to_unbounded<_Yp, _Tp>
+    > {};
 #else
-    : is_convertible<_Tp*, _Up*> {};
-#endif // _LIBCPP_STD_VER > 14
+template <class _Yp, class _Tp>
+struct __compatible_with
+    : is_convertible<_Yp*, _Tp*> {};
+#endif // _LIBCPP_STD_VER >= 17
+
+// Constructors that take raw pointers have a 
diff erent set of "compatible" constraints
+// http://eel.is/c++draft/util.sharedptr#util.smartptr.shared.const-9.1
+// - If T is an array type, then either T is U[N] and Y(*)[N] is convertible to T*,
+//   or T is U[] and Y(*)[] is convertible to T*.
+// - If T is not an array type, then Y* is convertible to T*.
+#if _LIBCPP_STD_VER >= 17
+template <class _Yp, class _Tp, class = void>
+struct __raw_pointer_compatible_with : _And<
+        _Not<is_array<_Tp>>,
+        is_convertible<_Yp*, _Tp*>
+        > {};
+
+template <class _Yp, class _Up, std::size_t _Np>
+struct __raw_pointer_compatible_with<_Yp, _Up[_Np], __enable_if_t<
+            is_convertible<_Yp(*)[_Np], _Up(*)[_Np]>::value> >
+        : true_type {};
+
+template <class _Yp, class _Up>
+struct __raw_pointer_compatible_with<_Yp, _Up[], __enable_if_t<
+            is_convertible<_Yp(*)[], _Up(*)[]>::value> >
+        : true_type {};
+
+#else
+template <class _Yp, class _Tp>
+struct __raw_pointer_compatible_with
+    : is_convertible<_Yp*, _Tp*> {};
+#endif // _LIBCPP_STD_VER >= 17
+
 
 template <class _Ptr, class = void>
 struct __is_deletable : false_type { };
@@ -395,12 +439,12 @@ static false_type __well_formed_deleter_test(...);
 template <class _Dp, class _Pt>
 struct __well_formed_deleter : decltype(std::__well_formed_deleter_test<_Dp, _Pt>(0)) {};
 
-template<class _Dp, class _Tp, class _Yp>
+template<class _Dp, class _Yp, class _Tp>
 struct __shared_ptr_deleter_ctor_reqs
 {
-    static const bool value = __compatible_with<_Tp, _Yp>::value &&
+    static const bool value = __raw_pointer_compatible_with<_Yp, _Tp>::value &&
                               is_move_constructible<_Dp>::value &&
-                              __well_formed_deleter<_Dp, _Tp*>::value;
+                              __well_formed_deleter<_Dp, _Yp*>::value;
 };
 
 #if defined(_LIBCPP_ABI_ENABLE_SHARED_PTR_TRIVIAL_ABI)
@@ -439,7 +483,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
 
     template<class _Yp, class = __enable_if_t<
         _And<
-            __compatible_with<_Yp, _Tp>
+            __raw_pointer_compatible_with<_Yp, _Tp>
             // In C++03 we get errors when trying to do SFINAE with the
             // delete operator, so we always pretend that it's deletable.
             // The same happens on GCC.
@@ -457,7 +501,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
         __enable_weak_this(__p, __p);
     }
 
-    template<class _Yp, class _Dp, class = __enable_if_t<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, element_type>::value> >
+    template<class _Yp, class _Dp, class = __enable_if_t<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, _Tp>::value> >
     _LIBCPP_HIDE_FROM_ABI
     shared_ptr(_Yp* __p, _Dp __d)
         : __ptr_(__p)
@@ -484,7 +528,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
 #endif // _LIBCPP_NO_EXCEPTIONS
     }
 
-    template<class _Yp, class _Dp, class _Alloc, class = __enable_if_t<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, element_type>::value> >
+    template<class _Yp, class _Dp, class _Alloc, class = __enable_if_t<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, _Tp>::value> >
     _LIBCPP_HIDE_FROM_ABI
     shared_ptr(_Yp* __p, _Dp __d, _Alloc __a)
         : __ptr_(__p)
@@ -646,6 +690,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
 
     template <class _Yp, class _Dp, class = __enable_if_t<
         !is_lvalue_reference<_Dp>::value &&
+         __compatible_with<_Yp, _Tp>::value &&
          is_convertible<typename unique_ptr<_Yp, _Dp>::pointer, element_type*>::value
     > >
     _LIBCPP_HIDE_FROM_ABI
@@ -668,6 +713,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
 
     template <class _Yp, class _Dp, class = void, class = __enable_if_t<
         is_lvalue_reference<_Dp>::value &&
+         __compatible_with<_Yp, _Tp>::value &&
         is_convertible<typename unique_ptr<_Yp, _Dp>::pointer, element_type*>::value
     > >
     _LIBCPP_HIDE_FROM_ABI
@@ -740,9 +786,10 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
     }
 #endif
 
-    template <class _Yp, class _Dp, class = __enable_if_t<
-        is_convertible<typename unique_ptr<_Yp, _Dp>::pointer, element_type*>::value
-    > >
+    template <class _Yp, class _Dp, class = __enable_if_t<_And<
+        __compatible_with<_Yp, _Tp>,
+        is_convertible<typename unique_ptr<_Yp, _Dp>::pointer, element_type*>
+    >::value> >
     _LIBCPP_HIDE_FROM_ABI
     shared_ptr<_Tp>& operator=(unique_ptr<_Yp, _Dp>&& __r)
     {
@@ -764,7 +811,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
     }
 
     template<class _Yp, class = __enable_if_t<
-        __compatible_with<_Yp, _Tp>::value
+        __raw_pointer_compatible_with<_Yp, _Tp>::value
     > >
     _LIBCPP_HIDE_FROM_ABI
     void reset(_Yp* __p)
@@ -773,8 +820,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
     }
 
     template<class _Yp, class _Dp, class = __enable_if_t<
-        __compatible_with<_Yp, _Tp>::value
-    > >
+        __shared_ptr_deleter_ctor_reqs<_Dp, _Yp, _Tp>::value> >
     _LIBCPP_HIDE_FROM_ABI
     void reset(_Yp* __p, _Dp __d)
     {
@@ -782,8 +828,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
     }
 
     template<class _Yp, class _Dp, class _Alloc, class = __enable_if_t<
-        __compatible_with<_Yp, _Tp>::value
-    > >
+        __shared_ptr_deleter_ctor_reqs<_Dp, _Yp, _Tp>::value> >
     _LIBCPP_HIDE_FROM_ABI
     void reset(_Yp* __p, _Dp __d, _Alloc __a)
     {

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.assign/unique_ptr_Y.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.assign/unique_ptr_Y.pass.cpp
index 35671c41842f2..b5abcb6f04ceb 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.assign/unique_ptr_Y.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.assign/unique_ptr_Y.pass.cpp
@@ -55,6 +55,16 @@ struct StatefulArrayDeleter {
   }
 };
 
+// https://llvm.org/PR53368
+// Bogus unique_ptr-to-shared_ptr conversions should be forbidden
+#if TEST_STD_VER >= 17
+static_assert( std::is_assignable<std::shared_ptr<A>&,   std::unique_ptr<A>&&>::value, "");
+static_assert( std::is_assignable<std::shared_ptr<A[]>&, std::unique_ptr<A[]>&&>::value, "");
+static_assert(!std::is_assignable<std::shared_ptr<A>&,   std::unique_ptr<A[]>&&>::value, "");
+static_assert(!std::is_assignable<std::shared_ptr<B[]>&, std::unique_ptr<A[]>&&>::value, "");
+static_assert(!std::is_assignable<std::shared_ptr<B>&,   std::unique_ptr<A[]>&&>::value, "");
+#endif
+
 int main(int, char**)
 {
     {
@@ -126,40 +136,6 @@ int main(int, char**)
     assert(B::count == 0);
     assert(A::count == 0);
 
-#ifdef _LIBCPP_VERSION // https://llvm.org/PR53368
-    {
-      std::unique_ptr<A[]> ptr(new A[8]);
-      A* raw_ptr = ptr.get();
-      std::shared_ptr<B> p;
-      p = std::move(ptr);
-      assert(A::count == 8);
-      assert(B::count == 8);
-      assert(p.use_count() == 1);
-      assert(p.get() == raw_ptr);
-      assert(ptr.get() == 0);
-    }
-    assert(A::count == 0);
-    assert(B::count == 0);
-
-    {
-      std::unique_ptr<A[]> ptr(new A[8]);
-      A* raw_ptr = ptr.get();
-      std::shared_ptr<A> p;
-      p = std::move(ptr);
-      assert(A::count == 8);
-      assert(p.use_count() == 1);
-      assert(p.get() == raw_ptr);
-      assert(ptr.get() == 0);
-    }
-    assert(A::count == 0);
-
-    {
-      std::unique_ptr<int[]> ptr(new int[8]);
-      std::shared_ptr<int> p;
-      p = std::move(ptr);
-    }
-#endif // _LIBCPP_VERSION
-
 #if TEST_STD_VER > 14
     {
       StatefulArrayDeleter<A> d;
@@ -172,22 +148,6 @@ int main(int, char**)
     assert(A::count == 0);
     assert(B::count == 0);
 
-#ifdef _LIBCPP_VERSION // https://llvm.org/PR53368
-    {
-      std::unique_ptr<A[]> ptr(new A[8]);
-      A* raw_ptr = ptr.get();
-      std::shared_ptr<B[]> p;
-      p = std::move(ptr);
-      assert(A::count == 8);
-      assert(B::count == 8);
-      assert(p.use_count() == 1);
-      assert(p.get() == raw_ptr);
-      assert(ptr.get() == 0);
-    }
-    assert(A::count == 0);
-    assert(B::count == 0);
-#endif // _LIBCPP_VERSION
-
     {
       std::unique_ptr<A[]> ptr(new A[8]);
       A* raw_ptr = ptr.get();

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer.pass.cpp
index ef3270efc8253..87b0cbde14a35 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer.pass.cpp
@@ -10,8 +10,9 @@
 
 // template<class Y> explicit shared_ptr(Y* p);
 
-#include <memory>
 #include <cassert>
+#include <memory>
+#include <type_traits>
 
 #include "test_macros.h"
 
@@ -26,6 +27,25 @@ struct A
 
 int A::count = 0;
 
+struct Derived : A {};
+
+// https://llvm.org/PR60258
+// Invalid constructor SFINAE for std::shared_ptr's array ctors
+static_assert( std::is_constructible<std::shared_ptr<int>,  int*>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<A>,  Derived*>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<A>,  int*>::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert( std::is_constructible<std::shared_ptr<int[]>,  int*>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[]>,  int(*)[]>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<int[5]>, int*>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>, int(*)[5]>::value, "");
+#endif
+
+// Test explicit
+static_assert(std::is_constructible<std::shared_ptr<int>, int*>::value, "");
+static_assert(!std::is_convertible<int*, std::shared_ptr<int> >::value, "");
+
 int main(int, char**)
 {
     {
@@ -71,11 +91,18 @@ int main(int, char**)
     }
 
     {
-          assert(A::count == 0);
+        assert(A::count == 0);
         std::shared_ptr<const A[]> pA(new A[8]);
         assert(pA.use_count() == 1);
         assert(A::count == 8);
     }
+
+    {
+        assert(A::count == 0);
+        std::shared_ptr<A> pA(new Derived);
+        assert(pA.use_count() == 1);
+        assert(A::count == 1);
+    }
 #endif
 
     return 0;

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp
index b3580e25fe52c..463130789416e 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp
@@ -60,6 +60,22 @@ class MoveDeleter
     void operator()(T *ptr) { delete ptr; }
 };
 
+// https://llvm.org/PR60258
+// Invalid constructor SFINAE for std::shared_ptr's array ctors
+static_assert( std::is_constructible<std::shared_ptr<int>,  int*, test_deleter<int> >::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int>,  int*, bad_deleter>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<Base>,  Derived*, test_deleter<Base> >::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<A>,  int*, test_deleter<A> >::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert( std::is_constructible<std::shared_ptr<int[]>,  int*, test_deleter<int>>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[]>,  int*, bad_deleter>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[]>,  int(*)[], test_deleter<int>>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<int[5]>, int*, test_deleter<int>>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>, int*, bad_deleter>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>, int(*)[5], test_deleter<int>>::value, "");
+#endif
+
 int main(int, char**)
 {
     {

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp
index 2fc25282230c8..6d66e05e426b4 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp
@@ -60,6 +60,23 @@ class MoveDeleter
     void operator()(T *ptr) { delete ptr; }
 };
 
+// https://llvm.org/PR60258
+// Invalid constructor SFINAE for std::shared_ptr's array ctors
+static_assert( std::is_constructible<std::shared_ptr<int>,  int*, test_deleter<int>, test_allocator<int> >::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int>,  int*, bad_deleter, test_allocator<int> >::value, "");
+static_assert( std::is_constructible<std::shared_ptr<Base>,  Derived*, test_deleter<Base>, test_allocator<Base> >::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<A>,  int*, test_deleter<A>, test_allocator<A> >::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert( std::is_constructible<std::shared_ptr<int[]>,  int*, test_deleter<int>, test_allocator<int>>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[]>,  int*, bad_deleter, test_allocator<int>>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[]>,  int(*)[], test_deleter<int>, test_allocator<int>>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<int[5]>, int*, test_deleter<int>, test_allocator<int>>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>, int*, bad_deleter, test_allocator<int>>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>, int(*)[5], test_deleter<int>, test_allocator<int>>::value, "");
+#endif
+
+
 int main(int, char**)
 {
     {

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp
index 26f381c9a2892..06817302446e8 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp
@@ -74,6 +74,23 @@ class private_delete_arr_op
     }
 };
 
+// https://llvm.org/PR60258
+// Invalid constructor SFINAE for std::shared_ptr's array ctors
+static_assert(!std::is_constructible<std::shared_ptr<int>,     const std::shared_ptr<long>&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<B>,       const std::shared_ptr<A>&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<const A>, const std::shared_ptr<A>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<A>,       const std::shared_ptr<const A>&>::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert(!std::is_constructible<std::shared_ptr<int>,     const std::shared_ptr<int[]>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int>,     const std::shared_ptr<int[5]>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[]>,   const std::shared_ptr<int>&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<int[]>,   const std::shared_ptr<int[5]>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>,  const std::shared_ptr<int>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>,  const std::shared_ptr<int[]>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[7]>,  const std::shared_ptr<int[5]>&>::value, "");
+#endif
+
 int main(int, char**)
 {
     static_assert(( std::is_convertible<std::shared_ptr<A>, std::shared_ptr<B> >::value), "");

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y_rv.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y_rv.pass.cpp
index 84ec26eb140ee..10ccbc4e7adea 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y_rv.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y_rv.pass.cpp
@@ -55,6 +55,23 @@ struct C
 
 int C::count = 0;
 
+// https://llvm.org/PR60258
+// Invalid constructor SFINAE for std::shared_ptr's array ctors
+static_assert(!std::is_constructible<std::shared_ptr<int>,     std::shared_ptr<long>&&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<B>,       std::shared_ptr<A>&&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<const A>, std::shared_ptr<A>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<A>,       std::shared_ptr<const A>&&>::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert(!std::is_constructible<std::shared_ptr<int>,     std::shared_ptr<int[]>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int>,     std::shared_ptr<int[5]>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[]>,   std::shared_ptr<int>&&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<int[]>,   std::shared_ptr<int[5]>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>,  std::shared_ptr<int>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>,  std::shared_ptr<int[]>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[7]>,  std::shared_ptr<int[5]>&&>::value, "");
+#endif
+
 int main(int, char**)
 {
     static_assert(( std::is_convertible<std::shared_ptr<A>, std::shared_ptr<B> >::value), "");

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/unique_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/unique_ptr.pass.cpp
index 871aac6eae0f6..9308bb3858c65 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/unique_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/unique_ptr.pass.cpp
@@ -90,6 +90,16 @@ struct MovingDeleter {
   int *moves_;
 };
 
+// https://llvm.org/PR53368
+// Bogus unique_ptr-to-shared_ptr conversions should be forbidden
+#if TEST_STD_VER >= 17
+static_assert( std::is_constructible<std::shared_ptr<A>,   std::unique_ptr<A>&&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<A[]>, std::unique_ptr<A[]>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<A>,   std::unique_ptr<A[]>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<B[]>, std::unique_ptr<A[]>&&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<B>,   std::unique_ptr<A[]>&&>::value, "");
+#endif
+
 int main(int, char**)
 {
     {
@@ -169,36 +179,6 @@ int main(int, char**)
     }
 
     assert(A::count == 0);
-#ifdef _LIBCPP_VERSION // https://llvm.org/PR53368
-    {
-      std::unique_ptr<A[]> ptr(new A[8]);
-      A* raw_ptr = ptr.get();
-      std::shared_ptr<B> p(std::move(ptr));
-      assert(A::count == 8);
-      assert(B::count == 8);
-      assert(p.use_count() == 1);
-      assert(p.get() == raw_ptr);
-      assert(ptr.get() == 0);
-    }
-    assert(A::count == 0);
-    assert(B::count == 0);
-
-    {
-      std::unique_ptr<A[]> ptr(new A[8]);
-      A* raw_ptr = ptr.get();
-      std::shared_ptr<A> p(std::move(ptr));
-      assert(A::count == 8);
-      assert(p.use_count() == 1);
-      assert(p.get() == raw_ptr);
-      assert(ptr.get() == 0);
-    }
-    assert(A::count == 0);
-
-    {
-      std::unique_ptr<int[]> ptr(new int[8]);
-      std::shared_ptr<int> p(std::move(ptr));
-    }
-#endif // _LIBCPP_VERSION
 
 #if TEST_STD_VER > 14
     {
@@ -211,21 +191,6 @@ int main(int, char**)
     assert(A::count == 0);
     assert(B::count == 0);
 
-#ifdef _LIBCPP_VERSION // https://llvm.org/PR53368
-    {
-      std::unique_ptr<A[]> ptr(new A[8]);
-      A* raw_ptr = ptr.get();
-      std::shared_ptr<B[]> p(std::move(ptr));
-      assert(A::count == 8);
-      assert(B::count == 8);
-      assert(p.use_count() == 1);
-      assert(p.get() == raw_ptr);
-      assert(ptr.get() == 0);
-    }
-    assert(A::count == 0);
-    assert(B::count == 0);
-#endif // _LIBCPP_VERSION
-
     {
       std::unique_ptr<A[]> ptr(new A[8]);
       A* raw_ptr = ptr.get();

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/weak_ptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/weak_ptr.pass.cpp
index 57d7cc58ef68c..f59181343b910 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/weak_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/weak_ptr.pass.cpp
@@ -12,8 +12,9 @@
 
 // template<class Y> explicit shared_ptr(const weak_ptr<Y>& r);
 
-#include <memory>
 #include <cassert>
+#include <memory>
+#include <type_traits>
 
 #include "test_macros.h"
 
@@ -40,6 +41,23 @@ struct A
 
 int A::count = 0;
 
+// https://llvm.org/PR60258
+// Invalid constructor SFINAE for std::shared_ptr's array ctors
+static_assert(!std::is_constructible<std::shared_ptr<int>,     const std::weak_ptr<long>&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<B>,       const std::weak_ptr<A>&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<const A>, const std::weak_ptr<A>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<A>,       const std::weak_ptr<const A>&>::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert(!std::is_constructible<std::shared_ptr<int>,     const std::weak_ptr<int[]>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int>,     const std::weak_ptr<int[5]>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[]>,   const std::weak_ptr<int>&>::value, "");
+static_assert( std::is_constructible<std::shared_ptr<int[]>,   const std::weak_ptr<int[5]>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>,  const std::weak_ptr<int>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[5]>,  const std::weak_ptr<int[]>&>::value, "");
+static_assert(!std::is_constructible<std::shared_ptr<int[7]>,  const std::weak_ptr<int[5]>&>::value, "");
+#endif
+
 int main(int, char**)
 {
 #ifndef TEST_HAS_NO_EXCEPTIONS

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_helper.h b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_helper.h
new file mode 100644
index 0000000000000..27d7264be172e
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_helper.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef TEST_STD_SHARED_PTR_RESET_H
+#define TEST_STD_SHARED_PTR_RESET_H
+
+#include <memory>
+#include <type_traits>
+
+template <class T, class... Args>
+std::false_type test_has_reset(...);
+
+template <class T, class... Args>
+typename std::enable_if<std::is_same<decltype(std::declval<T>().reset(std::declval<Args>()...)), void>::value,
+                        std::true_type>::type
+test_has_reset(int);
+
+template <class T, class... Args>
+using HasReset = decltype(test_has_reset<T, Args...>(0));
+
+#endif // TEST_STD_SHARED_PTR_RESET_H

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer.pass.cpp
index d764e8a312d7a..d1d19e4562355 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer.pass.cpp
@@ -15,6 +15,7 @@
 #include <memory>
 #include <cassert>
 
+#include "reset_helper.h"
 #include "test_macros.h"
 
 struct B
@@ -40,6 +41,19 @@ struct A
 
 int A::count = 0;
 
+struct Derived : A {};
+
+static_assert( HasReset<std::shared_ptr<int>,  int*>::value, "");
+static_assert( HasReset<std::shared_ptr<A>,  Derived*>::value, "");
+static_assert(!HasReset<std::shared_ptr<A>,  int*>::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert( HasReset<std::shared_ptr<int[]>,  int*>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[]>,  int(*)[]>::value, "");
+static_assert( HasReset<std::shared_ptr<int[5]>, int*>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[5]>, int(*)[5]>::value, "");
+#endif
+
 int main(int, char**)
 {
     {

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter.pass.cpp
index 14c336b4f59ca..d0375d3aa2001 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter.pass.cpp
@@ -12,10 +12,11 @@
 
 // template<class Y, class D> void reset(Y* p, D d);
 
-#include <memory>
 #include <cassert>
-#include "test_macros.h"
+#include <memory>
 #include "deleter_types.h"
+#include "reset_helper.h"
+#include "test_macros.h"
 
 struct B
 {
@@ -40,6 +41,29 @@ struct A
 
 int A::count = 0;
 
+struct bad_ty { };
+struct bad_deleter
+{
+    void operator()(bad_ty) { }
+};
+
+struct Base { };
+struct Derived : Base { };
+
+static_assert( HasReset<std::shared_ptr<int>,  int*, test_deleter<int> >::value, "");
+static_assert(!HasReset<std::shared_ptr<int>,  int*, bad_deleter>::value, "");
+static_assert( HasReset<std::shared_ptr<Base>,  Derived*, test_deleter<Base> >::value, "");
+static_assert(!HasReset<std::shared_ptr<A>,  int*, test_deleter<A> >::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert( HasReset<std::shared_ptr<int[]>,  int*, test_deleter<int>>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[]>,  int*, bad_deleter>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[]>,  int(*)[], test_deleter<int>>::value, "");
+static_assert( HasReset<std::shared_ptr<int[5]>, int*, test_deleter<int>>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[5]>, int*, bad_deleter>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[5]>, int(*)[5], test_deleter<int>>::value, "");
+#endif
+
 int main(int, char**)
 {
     {

diff  --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter_allocator.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter_allocator.pass.cpp
index a6ea07ea37cee..1cd42c08a23ff 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter_allocator.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer_deleter_allocator.pass.cpp
@@ -12,10 +12,11 @@
 
 // template<class Y, class D, class A> void reset(Y* p, D d, A a);
 
-#include <memory>
 #include <cassert>
+#include <memory>
 #include "test_macros.h"
 #include "deleter_types.h"
+#include "reset_helper.h"
 #include "test_allocator.h"
 
 struct B
@@ -41,6 +42,30 @@ struct A
 
 int A::count = 0;
 
+struct bad_ty { };
+
+struct bad_deleter
+{
+    void operator()(bad_ty) { }
+};
+
+struct Base { };
+struct Derived : Base { };
+
+static_assert( HasReset<std::shared_ptr<int>,  int*, test_deleter<int>, test_allocator<int> >::value, "");
+static_assert(!HasReset<std::shared_ptr<int>,  int*, bad_deleter, test_allocator<int> >::value, "");
+static_assert( HasReset<std::shared_ptr<Base>,  Derived*, test_deleter<Base>, test_allocator<Base> >::value, "");
+static_assert(!HasReset<std::shared_ptr<A>,  int*, test_deleter<A>, test_allocator<A> >::value, "");
+
+#if TEST_STD_VER >= 17
+static_assert( HasReset<std::shared_ptr<int[]>,  int*, test_deleter<int>, test_allocator<int>>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[]>,  int*, bad_deleter, test_allocator<int>>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[]>,  int(*)[], test_deleter<int>, test_allocator<int>>::value, "");
+static_assert( HasReset<std::shared_ptr<int[5]>, int*, test_deleter<int>, test_allocator<int>>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[5]>, int*, bad_deleter, test_allocator<int>>::value, "");
+static_assert(!HasReset<std::shared_ptr<int[5]>, int(*)[5], test_deleter<int>, test_allocator<int>>::value, "");
+#endif
+
 int main(int, char**)
 {
     test_allocator_statistics alloc_stats;


        


More information about the libcxx-commits mailing list