Tested on x86_64-pc-linux-gnu, does this look OK for trunk/16?

-- >8 --

This implements the [util.smartptr.shared.const] constraints on the
constructors that take a deleter (which before C++20 were requirements
rather than constraints).

        PR libstdc++/126120

libstdc++-v3/ChangeLog:

        * include/bits/shared_ptr.h
        (shared_ptr::shared_ptr(nullptr_t, ...)): Add _Constructible
        constraint to verify __shared_ptr base initialization is
        well-formed.
        * include/bits/shared_ptr_base.h (_ValidDeleter): New __enable_if_t
        constraint.
        (__shared_ptr::__shared_ptr(..., _Deleter, ...)): Remove
        static_assert checking deleter invocability.  Add _ValidDeleter
        constraint.
        * testsuite/20_util/shared_ptr/cons/126120.cc: New test.
---
 libstdc++-v3/include/bits/shared_ptr.h        |  5 +--
 libstdc++-v3/include/bits/shared_ptr_base.h   | 29 ++++++++---------
 .../20_util/shared_ptr/cons/126120.cc         | 31 +++++++++++++++++++
 3 files changed, 49 insertions(+), 16 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc

diff --git a/libstdc++-v3/include/bits/shared_ptr.h 
b/libstdc++-v3/include/bits/shared_ptr.h
index 8be9aace1711..28b8a3ca8f29 100644
--- a/libstdc++-v3/include/bits/shared_ptr.h
+++ b/libstdc++-v3/include/bits/shared_ptr.h
@@ -225,7 +225,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        *  The last owner will call __d(__p)
        */
-      template<typename _Deleter>
+      template<typename _Deleter, typename = _Constructible<nullptr_t, 
_Deleter>>
        shared_ptr(nullptr_t __p, _Deleter __d)
         : __shared_ptr<_Tp>(__p, std::move(__d)) { }
 
@@ -264,7 +264,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        *  The last owner will call __d(__p)
        */
-      template<typename _Deleter, typename _Alloc>
+      template<typename _Deleter, typename _Alloc,
+              typename = _Constructible<nullptr_t, _Deleter, _Alloc>>
        shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
        : __shared_ptr<_Tp>(__p, std::move(__d), std::move(__a)) { }
 
diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h 
b/libstdc++-v3/include/bits/shared_ptr_base.h
index c8342b7904a5..2eb3436183ee 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -1469,6 +1469,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return static_cast<const __shared_ptr<_Tp, _Lp>*>(this)->get(); }
     };
 
+  // Constraint for well-formedness of deleter expression d(p):
+  template<typename _Deleter, typename _Up>
+    using _ValidDeleter = __enable_if_t<__and_<
+      is_move_constructible<_Deleter>,
+      __is_invocable<_Deleter&, _Up&>
+      >::value>;
+
   template<typename _Tp, _Lock_policy _Lp>
     class __shared_ptr
     : public __shared_ptr_access<_Tp, _Lp>
@@ -1524,31 +1531,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          _M_enable_shared_from_this_with(__p);
        }
 
-      template<typename _Yp, typename _Deleter, typename = _SafeConv<_Yp>>
+      template<typename _Yp, typename _Deleter, typename = _SafeConv<_Yp>,
+              typename = _ValidDeleter<_Deleter, _Yp*>>
        __shared_ptr(_Yp* __p, _Deleter __d)
        : _M_ptr(__p), _M_refcount(__p, std::move(__d))
-       {
-         static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
-             "deleter expression d(p) is well-formed");
-         _M_enable_shared_from_this_with(__p);
-       }
+       { _M_enable_shared_from_this_with(__p); }
 
       template<typename _Yp, typename _Deleter, typename _Alloc,
-              typename = _SafeConv<_Yp>>
+              typename = _SafeConv<_Yp>, typename = _ValidDeleter<_Deleter, 
_Yp*>>
        __shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a)
        : _M_ptr(__p), _M_refcount(__p, std::move(__d), std::move(__a))
-       {
-         static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
-             "deleter expression d(p) is well-formed");
-         _M_enable_shared_from_this_with(__p);
-       }
+       { _M_enable_shared_from_this_with(__p); }
 
-      template<typename _Deleter>
+      template<typename _Deleter, typename = _ValidDeleter<_Deleter, 
nullptr_t>>
        __shared_ptr(nullptr_t __p, _Deleter __d)
        : _M_ptr(0), _M_refcount(__p, std::move(__d))
        { }
 
-      template<typename _Deleter, typename _Alloc>
+      template<typename _Deleter, typename _Alloc,
+              typename = _ValidDeleter<_Deleter, nullptr_t>>
         __shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
        : _M_ptr(0), _M_refcount(__p, std::move(__d), std::move(__a))
        { }
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc 
b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
new file mode 100644
index 000000000000..a5a29597e078
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
@@ -0,0 +1,31 @@
+// PR libstdc++/126120 - is_constructible_v with shared_ptr from an invalid 
deleter
+// { dg-do compile { target c++11 } }
+
+#include <memory>
+#include <type_traits>
+
+struct NonDeleter { };
+
+struct NonMoveableDeleter {
+  NonMoveableDeleter() = default;
+  NonMoveableDeleter(const NonMoveableDeleter&) = delete;
+  void operator()(int* p) const { delete p; }
+};
+
+using Alloc = std::allocator<int>;
+
+// 1. Pointer + Deleter
+static_assert( ! std::is_constructible<std::shared_ptr<int>, int*, 
NonDeleter>::value, "");
+static_assert( ! std::is_constructible<std::shared_ptr<int>, int*, 
NonMoveableDeleter>::value, "");
+
+// 2. Pointer + Deleter + Allocator
+static_assert( ! std::is_constructible<std::shared_ptr<int>, int*, NonDeleter, 
Alloc>::value, "");
+static_assert( ! std::is_constructible<std::shared_ptr<int>, int*, 
NonMoveableDeleter, Alloc>::value, "");
+
+// 3. nullptr_t + Deleter
+static_assert( ! std::is_constructible<std::shared_ptr<int>, std::nullptr_t, 
NonDeleter>::value, "");
+static_assert( ! std::is_constructible<std::shared_ptr<int>, std::nullptr_t, 
NonMoveableDeleter>::value, "");
+
+// 4. nullptr_t + Deleter + Allocator
+static_assert( ! std::is_constructible<std::shared_ptr<int>, std::nullptr_t, 
NonDeleter, Alloc>::value, "");
+static_assert( ! std::is_constructible<std::shared_ptr<int>, std::nullptr_t, 
NonMoveableDeleter, Alloc>::value, "");
-- 
2.55.0.669.g593c42fe07

Reply via email to