Implement LWG 2905 changes to constrain unique_ptr constructors
authorJonathan Wakely <jwakely@redhat.com>
Tue, 11 Sep 2018 10:55:49 +0000 (11:55 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Tue, 11 Sep 2018 10:55:49 +0000 (11:55 +0100)
LWG DR 2905 says that is_constructible_v<unique_ptr<P, D>, P, D const &>
should be false when D is not copy constructible. This commit implements
the changes from the DR and simplifies the signatures as per
https://github.com/cplusplus/draft/issues/1530

* include/bits/unique_ptr.h (__uniq_ptr_impl): Add assertions to
check deleter type.
(unique_ptr::unique_ptr(pointer, const deleter_type&)): Add copy
constructible constraint.
(unique_ptr::unique_ptr(pointer, deleter_type&&)): Disable for
deleters of reference type and add move constructible constraint.
(unique_ptr::unique_ptr(pointer, remove_reference_t<deleter_type>&&)):
Disable for deleters of non-reference type. Define as deleted.
(unique_ptr<T[], D>): Likewise.
* testsuite/20_util/unique_ptr/assign/48635_neg.cc: Replace dg-error
directives with unstable line numbers with dg-prune-output.
* testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc: Likewise.
* testsuite/20_util/unique_ptr/cons/lwg2905.cc: New test.
* testsuite/20_util/unique_ptr/specialized_algorithms/swap_cxx17.cc:
Make deleter types invocable.

From-SVN: r264206

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/unique_ptr.h
libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc
libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc
libstdc++-v3/testsuite/20_util/unique_ptr/cons/lwg2905.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/unique_ptr/specialized_algorithms/swap_cxx17.cc

index 3de9031fd4728ea59be584b6c9dde9bfab533f2d..1a1a535532e8581c3ba23a1db08ea5030c0f97fd 100644 (file)
@@ -1,3 +1,22 @@
+2018-09-11  Jonathan Wakely  <jwakely@redhat.com>
+
+       Implement LWG 2905 changes to constrain unique_ptr constructors
+       * include/bits/unique_ptr.h (__uniq_ptr_impl): Add assertions to
+       check deleter type.
+       (unique_ptr::unique_ptr(pointer, const deleter_type&)): Add copy
+       constructible constraint.
+       (unique_ptr::unique_ptr(pointer, deleter_type&&)): Disable for
+       deleters of reference type and add move constructible constraint.
+       (unique_ptr::unique_ptr(pointer, remove_reference_t<deleter_type>&&)):
+       Disable for deleters of non-reference type. Define as deleted.
+       (unique_ptr<T[], D>): Likewise.
+       * testsuite/20_util/unique_ptr/assign/48635_neg.cc: Replace dg-error
+       directives with unstable line numbers with dg-prune-output.
+       * testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc: Likewise.
+       * testsuite/20_util/unique_ptr/cons/lwg2905.cc: New test.
+       * testsuite/20_util/unique_ptr/specialized_algorithms/swap_cxx17.cc:
+       Make deleter types invocable.
+
 2018-09-05  Jonathan Wakely  <jwakely@redhat.com>
 
        * libsupc++/cxxabi.h (__cxa_demangle): Clarify doxygen comment.
index 4c99a9c0d9ba1cec0b68945cee9fdf8534894615..ddc6ae0e2332f83de30e05ab336fd5f299ee1822 100644 (file)
@@ -139,6 +139,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       using pointer = typename _Ptr<_Tp, _Dp>::type;
 
+      static_assert( !is_rvalue_reference<_Dp>::value,
+                    "unique_ptr's deleter type must be a function object type"
+                    " or an lvalue reference type" );
+      static_assert( __is_invocable<_Dp&, pointer&>::value,
+                    "unique_ptr's deleter must be invocable with a pointer" );
+
       __uniq_ptr_impl() = default;
       __uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }
 
@@ -159,9 +165,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template <typename _Tp, typename _Dp = default_delete<_Tp>>
     class unique_ptr
     {
-      template <class _Up>
-      using _DeleterConstraint =
-       typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
+      template <typename _Up>
+       using _DeleterConstraint =
+         typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
 
       __uniq_ptr_impl<_Tp, _Dp> _M_t;
 
@@ -170,6 +176,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       using element_type  = _Tp;
       using deleter_type  = _Dp;
 
+    private:
       // helper template for detecting a safe conversion from another
       // unique_ptr
       template<typename _Up, typename _Ep>
@@ -183,11 +190,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                 >
               >;
 
+    public:
       // Constructors.
 
       /// Default constructor, creates a unique_ptr that owns nothing.
-      template <typename _Up = _Dp,
-               typename = _DeleterConstraint<_Up>>
+      template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
        constexpr unique_ptr() noexcept
        : _M_t()
         { }
@@ -198,8 +205,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        * The deleter will be value-initialized.
        */
-      template <typename _Up = _Dp,
-               typename = _DeleterConstraint<_Up>>
+      template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
        explicit
        unique_ptr(pointer __p) noexcept
        : _M_t(__p)
@@ -212,27 +218,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        * The deleter will be initialized with @p __d
        */
-      unique_ptr(pointer __p,
-         typename conditional<is_reference<deleter_type>::value,
-           deleter_type, const deleter_type&>::type __d) noexcept
-      : _M_t(__p, __d) { }
+      template<typename _Del = deleter_type,
+              typename = _Require<is_copy_constructible<_Del>>>
+       unique_ptr(pointer __p, const deleter_type& __d) noexcept
+       : _M_t(__p, __d) { }
 
       /** Takes ownership of a pointer.
        *
        * @param __p  A pointer to an object of @c element_type
-       * @param __d  An rvalue reference to a deleter.
+       * @param __d  An rvalue reference to a (non-reference) deleter.
        *
        * The deleter will be initialized with @p std::move(__d)
        */
-      unique_ptr(pointer __p,
-         typename remove_reference<deleter_type>::type&& __d) noexcept
-      : _M_t(std::move(__p), std::move(__d))
-      { static_assert(!std::is_reference<deleter_type>::value,
-                     "rvalue deleter bound to reference"); }
+      template<typename _Del = deleter_type,
+              typename = _Require<is_move_constructible<_Del>>>
+       unique_ptr(pointer __p,
+                  __enable_if_t<!is_lvalue_reference<_Del>::value,
+                                _Del&&> __d) noexcept
+       : _M_t(__p, std::move(__d))
+       { }
+
+      template<typename _Del = deleter_type,
+              typename _DelUnref = typename remove_reference<_Del>::type>
+       unique_ptr(pointer,
+                  __enable_if_t<is_lvalue_reference<_Del>::value,
+                                _DelUnref&&>) = delete;
 
       /// Creates a unique_ptr that owns nothing.
-      template <typename _Up = _Dp,
-               typename = _DeleterConstraint<_Up>>
+      template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
        constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
 
       // Move constructors.
@@ -454,8 +467,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Constructors.
 
       /// Default constructor, creates a unique_ptr that owns nothing.
-      template <typename _Up = _Dp,
-               typename = _DeleterConstraint<_Up>>
+      template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
        constexpr unique_ptr() noexcept
        : _M_t()
         { }
@@ -485,12 +497,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        * The deleter will be initialized with @p __d
        */
-      template<typename _Up,
-               typename = typename enable_if<
-                 __safe_conversion_raw<_Up>::value, bool>::type>
-      unique_ptr(_Up __p,
-                 typename conditional<is_reference<deleter_type>::value,
-                 deleter_type, const deleter_type&>::type __d) noexcept
+      template<typename _Up, typename _Del = deleter_type,
+              typename = _Require<__safe_conversion_raw<_Up>,
+                                  is_copy_constructible<_Del>>>
+      unique_ptr(_Up __p, const deleter_type& __d) noexcept
       : _M_t(__p, __d) { }
 
       /** Takes ownership of a pointer.
@@ -501,22 +511,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        * The deleter will be initialized with @p std::move(__d)
        */
-      template<typename _Up,
-               typename = typename enable_if<
-                 __safe_conversion_raw<_Up>::value, bool>::type>
-      unique_ptr(_Up __p, typename
-                remove_reference<deleter_type>::type&& __d) noexcept
-      : _M_t(std::move(__p), std::move(__d))
-      { static_assert(!is_reference<deleter_type>::value,
-                     "rvalue deleter bound to reference"); }
+      template<typename _Up, typename _Del = deleter_type,
+              typename = _Require<__safe_conversion_raw<_Up>,
+                                  is_move_constructible<_Del>>>
+       unique_ptr(_Up __p,
+                  __enable_if_t<!is_lvalue_reference<_Del>::value,
+                                _Del&&> __d) noexcept
+       : _M_t(std::move(__p), std::move(__d))
+       { }
+
+      template<typename _Up, typename _Del = deleter_type,
+              typename _DelUnref = typename remove_reference<_Del>::type,
+              typename = _Require<__safe_conversion_raw<_Up>>>
+       unique_ptr(_Up,
+                  __enable_if_t<is_lvalue_reference<_Del>::value,
+                                _DelUnref&&>) = delete;
 
       /// Move constructor.
       unique_ptr(unique_ptr&& __u) noexcept
       : _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
 
       /// Creates a unique_ptr that owns nothing.
-      template <typename _Up = _Dp,
-               typename = _DeleterConstraint<_Up>>
+      template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
        constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
 
       template<typename _Up, typename _Ep,
index b22d0e123b487630e9d51ed6928df7e2fe4ce6cf..0c8f8b357bbc461949ce5fe42bdf8650f546e1cd 100644 (file)
@@ -42,10 +42,10 @@ void f()
   std::unique_ptr<int, D&> ud(nullptr, d);
   ub = std::move(ud); // { dg-error "no match" }
   ub2 = ud; // { dg-error "no match" }
-// { dg-error "no type" "" { target *-*-* } 307 }
 
   std::unique_ptr<int[], B&> uba(nullptr, b);
   std::unique_ptr<int[], D&> uda(nullptr, d);
   uba = std::move(uda); // { dg-error "no match" }
-// { dg-error "no type" "" { target *-*-* } 566 }
 }
+
+// { dg-prune-output "no type" }
index c1b1c9efc64945e29acf710ed0694eb9d9b453ce..7e820ba129ab98709cb0c3e7c610490334c04c14 100644 (file)
@@ -39,7 +39,7 @@ test07()
   std::unique_ptr<const A[]> cA3(p); // { dg-error "no matching function" }
   std::unique_ptr<volatile A[]> vA3(p); // { dg-error "no matching function" }
   std::unique_ptr<const volatile A[]> cvA3(p); // { dg-error "no matching function" }
-  // { dg-error "no type" "" { target *-*-* } 473 }
+  // { dg-prune-output "no type" }
 }
 
 template<typename T>
diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/lwg2905.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/lwg2905.cc
new file mode 100644 (file)
index 0000000..8700630
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright (C) 2017 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do compile { target c++11 } }
+
+#include <memory>
+
+template<typename T, typename D, typename P, typename E>
+constexpr bool check()
+{ return std::is_constructible<std::unique_ptr<T, D>, P, E>::value; }
+
+struct Del { void operator()(void*) const { } };
+
+static_assert( ! check<int, Del&, int*, Del>(), "" );
+static_assert( check<int, Del&, int*, Del&>(), "" );
+static_assert( check<int, const Del&, int*, Del&>(), "" );
+static_assert( ! check<int, Del&, int*, const Del&>(), "" );
+static_assert( check<int, Del, int*, const Del&>(), "" );
+static_assert( check<int, Del, int*, Del>(), "" );
+
+static_assert( ! check<int[], Del&, int*, Del>(), "" );
+static_assert( check<int[], Del&, int*, Del&>(), "" );
+static_assert( check<int[], const Del&, int*, Del&>(), "" );
+static_assert( ! check<int[], Del&, int*, const Del&>(), "" );
+static_assert( check<int[], Del, int*, const Del&>(), "" );
+static_assert( check<int[], Del, int*, Del>(), "" );
+
+struct DelNoCopy {
+  DelNoCopy() = default;
+  DelNoCopy(const DelNoCopy&) = delete;
+  DelNoCopy(DelNoCopy&&) = default;
+  void operator()(void*) const { }
+};
+
+static_assert( ! check<int, DelNoCopy&, int*, DelNoCopy>(), "" );
+static_assert( check<int, DelNoCopy&, int*, DelNoCopy&>(), "" );
+static_assert( check<int, const DelNoCopy&, int*, DelNoCopy&>(), "" );
+static_assert( ! check<int, DelNoCopy&, int*, const DelNoCopy&>(), "" );
+static_assert( ! check<int, DelNoCopy, int*, const DelNoCopy&>(), "" );
+static_assert( check<int, DelNoCopy, int*, DelNoCopy>(), "" );
+
+static_assert( ! check<int[], DelNoCopy&, int*, DelNoCopy>(), "" );
+static_assert( check<int[], DelNoCopy&, int*, DelNoCopy&>(), "" );
+static_assert( check<int[], const DelNoCopy&, int*, DelNoCopy&>(), "" );
+static_assert( ! check<int[], DelNoCopy&, int*, const DelNoCopy&>(), "" );
+static_assert( ! check<int[], DelNoCopy, int*, const DelNoCopy&>(), "" );
+static_assert( check<int[], DelNoCopy, int*, DelNoCopy>(), "" );
+
+struct Base { virtual ~Base() { } };
+struct Derived : Base { };
+
+static_assert( ! check<Base[], Del&, Base*, Del>(), "" );
+static_assert( check<Base[], Del&, Base*, Del&>(), "" );
+static_assert( check<Base[], const Del&, Base*, Del&>(), "" );
+static_assert( ! check<Base[], Del&, Base*, const Del&>(), "" );
+static_assert( check<Base[], Del, Base*, const Del&>(), "" );
+static_assert( check<Base[], Del, Base*, Del>(), "" );
+
+static_assert( ! check<Base[], Del&, Derived*, Del>(), "" );
+static_assert( ! check<Base[], Del&, Derived*, Del&>(), "" );
+static_assert( ! check<Base[], const Del&, Derived*, Del&>(), "" );
+static_assert( ! check<Base[], Del&, Derived*, const Del&>(), "" );
+static_assert( ! check<Base[], Del, Derived*, const Del&>(), "" );
+static_assert( ! check<Base[], Del, Derived*, Del>(), "" );
index 298d951d3197f9b1ecba0a0a140785fac01e59b7..604685c1ab10e235a6f652e9a54cc195f4d92081 100644 (file)
 #include <memory>
 
 // Not swappable, and unique_ptr not swappable via the generic std::swap.
-struct C { };
+struct C {
+  void operator()(void*) const { }
+};
 void swap(C&, C&) = delete;
 
 static_assert( !std::is_swappable_v<std::unique_ptr<int, C>> );
 
 // Not swappable, and unique_ptr not swappable via the generic std::swap.
-struct D { D(D&&) = delete; };
+struct D {
+  D(D&&) = delete;
+  void operator()(void*) const { }
+};
 
 static_assert( !std::is_swappable_v<std::unique_ptr<int, D>> );