2015-08-08 Ville Voutilainen <ville.voutilainen@gmail.com>
authorville <ville@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 8 Aug 2015 11:07:34 +0000 (11:07 +0000)
committerville <ville@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 8 Aug 2015 11:07:34 +0000 (11:07 +0000)
Implement N4089 Safe conversions in unique_ptr<T[]> (LWG 2118)
and N4366 LWG 2228: Missing SFINAE rule in unique_ptr
templated assignment
* include/bits/unique_ptr.h
(__remove_cv, __is_derived_Tp): Remove.
(default_delete::default_delete(const default_delete<_Up[]>)):
Constrain with array convertibility.
(default_delete::operator(_Up*)): Turn into a template,
constrain with array convertibility.
(__safe_conversion_up): New, single object version.
(unique_ptr(unique_ptr<_Up, _Ep>&&)): Constrain with deleter
convertibility.
(unique_ptr::operator=(unique_ptr<_Up, _Ep>&&)): Likewise, and add
is_assignable as a constraint.
(__safe_conversion_up): Array version, renamed from __safe_conversion,
updated to implement N4089.
(__safe_conversion_raw): New.
(unique_ptr(_Up)): Turn into a template, constrain with array
convertibility.
(unique_ptr(_Up,
        typename conditional<is_reference<deleter_type>::value,
        deleter_type, const deleter_type&>::type)): Likewise.
(unique_ptr(_Up, typename
  remove_reference<deleter_type>::type&&)): Likewise.
(unique_ptr(unique_ptr<_Up, _Ep>&&)): Likewise.
(operator=(unique_ptr<_Up, _Ep>&&)): Likewise, and add
is_assignable as a constraint (array version).
(reset(_Up)): Turn into a template, constrain with array
convertibility.
(reset(nullptr_t)): New.
* testsuite/20_util/default_delete/48631_neg.cc: Adjust.
* testsuite/20_util/unique_ptr/assign/48635.cc: Likewise.
* testsuite/20_util/unique_ptr/assign/48635_neg.cc: Likewise.
* testsuite/20_util/unique_ptr/assign/cv_qual.cc: Likewise.
* testsuite/20_util/unique_ptr/cons/cv_qual.cc: Likewise.
* testsuite/20_util/unique_ptr/dr2228.cc: New.
* testsuite/20_util/unique_ptr/modifiers/cv_qual.cc: Adjust.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@226733 138bc75d-0d04-0410-961f-82ee72b054a4

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/unique_ptr.h
libstdc++-v3/testsuite/20_util/default_delete/48631_neg.cc
libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635.cc
libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc
libstdc++-v3/testsuite/20_util/unique_ptr/assign/cv_qual.cc
libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual.cc
libstdc++-v3/testsuite/20_util/unique_ptr/dr2228.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/cv_qual.cc

index 787a7a5..fc8b2b8 100644 (file)
@@ -1,3 +1,43 @@
+2015-08-08  Ville Voutilainen  <ville.voutilainen@gmail.com>
+
+       Implement N4089 Safe conversions in unique_ptr<T[]> (LWG 2118)
+       and N4366 LWG 2228: Missing SFINAE rule in unique_ptr
+       templated assignment
+       * include/bits/unique_ptr.h
+       (__remove_cv, __is_derived_Tp): Remove.
+       (default_delete::default_delete(const default_delete<_Up[]>)):
+       Constrain with array convertibility.
+       (default_delete::operator(_Up*)): Turn into a template,
+       constrain with array convertibility.
+       (__safe_conversion_up): New, single object version.
+       (unique_ptr(unique_ptr<_Up, _Ep>&&)): Constrain with deleter
+       convertibility.
+       (unique_ptr::operator=(unique_ptr<_Up, _Ep>&&)): Likewise, and add
+       is_assignable as a constraint.
+       (__safe_conversion_up): Array version, renamed from __safe_conversion,
+       updated to implement N4089.
+       (__safe_conversion_raw): New.
+       (unique_ptr(_Up)): Turn into a template, constrain with array
+       convertibility.
+       (unique_ptr(_Up,
+       typename conditional<is_reference<deleter_type>::value,
+       deleter_type, const deleter_type&>::type)): Likewise.
+       (unique_ptr(_Up, typename
+       remove_reference<deleter_type>::type&&)): Likewise.
+       (unique_ptr(unique_ptr<_Up, _Ep>&&)): Likewise.
+       (operator=(unique_ptr<_Up, _Ep>&&)): Likewise, and add
+       is_assignable as a constraint (array version).
+       (reset(_Up)): Turn into a template, constrain with array
+       convertibility.
+       (reset(nullptr_t)): New.
+       * testsuite/20_util/default_delete/48631_neg.cc: Adjust.
+       * testsuite/20_util/unique_ptr/assign/48635.cc: Likewise.
+       * testsuite/20_util/unique_ptr/assign/48635_neg.cc: Likewise.
+       * testsuite/20_util/unique_ptr/assign/cv_qual.cc: Likewise.
+       * testsuite/20_util/unique_ptr/cons/cv_qual.cc: Likewise.
+       * testsuite/20_util/unique_ptr/dr2228.cc: New.
+       * testsuite/20_util/unique_ptr/modifiers/cv_qual.cc: Adjust.
+
 2015-08-05  Nikolai Bozhenov  <n.bozhenov@samsung.com>
 
        * testsuite/20_util/enable_shared_from_this/cons/constexpr.cc: Remove
index 08ce01f..8ab55da 100644 (file)
@@ -83,16 +83,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct default_delete<_Tp[]>
     {
-    private:
-      template<typename _Up>
-       using __remove_cv = typename remove_cv<_Up>::type;
-
-      // Like is_base_of<_Tp, _Up> but false if unqualified types are the same
-      template<typename _Up>
-       using __is_derived_Tp
-         = __and_< is_base_of<_Tp, _Up>,
-                   __not_<is_same<__remove_cv<_Tp>, __remove_cv<_Up>>> >;
-
     public:
       /// Default constructor
       constexpr default_delete() noexcept = default;
@@ -107,21 +97,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        * pointer to the base type.
        */
       template<typename _Up, typename = typename
-              enable_if<!__is_derived_Tp<_Up>::value>::type>
+              enable_if<is_convertible<_Up(*)[], _Tp(*)[]>::value>::type>
         default_delete(const default_delete<_Up[]>&) noexcept { }
 
       /// Calls @c delete[] @p __ptr
-      void
-      operator()(_Tp* __ptr) const
+      template<typename _Up>
+      typename enable_if<is_convertible<_Up(*)[], _Tp(*)[]>::value>::type
+       operator()(_Up* __ptr) const
       {
        static_assert(sizeof(_Tp)>0,
                      "can't delete pointer to incomplete type");
        delete [] __ptr;
       }
-
-      template<typename _Up>
-       typename enable_if<__is_derived_Tp<_Up>::value>::type
-       operator()(_Up*) const = delete;
     };
 
   /// 20.7.1.2 unique_ptr for single objects.
@@ -151,6 +138,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef _Tp                       element_type;
       typedef _Dp                       deleter_type;
 
+
+      // helper template for detecting a safe conversion from another
+      // unique_ptr
+      template<typename _Up, typename _Ep>
+       using __safe_conversion_up = __and_<
+               is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
+                __not_<is_array<_Up>>,
+                __or_<__and_<is_reference<deleter_type>,
+                             is_same<deleter_type, _Ep>>,
+                      __and_<__not_<is_reference<deleter_type>>,
+                             is_convertible<_Ep, deleter_type>>
+                >
+              >;
+
       // Constructors.
 
       /// Default constructor, creates a unique_ptr that owns nothing.
@@ -212,8 +213,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        * and @p __u has a compatible deleter type.
        */
       template<typename _Up, typename _Ep, typename = _Require<
-              is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
-              __not_<is_array<_Up>>,
+               __safe_conversion_up<_Up, _Ep>,
               typename conditional<is_reference<_Dp>::value,
                                    is_same<_Ep, _Dp>,
                                    is_convertible<_Ep, _Dp>>::type>>
@@ -261,11 +261,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        * Invokes the deleter first if this object owns a pointer.
        */
       template<typename _Up, typename _Ep>
-       typename enable_if< __and_<
-         is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
-         __not_<is_array<_Up>>
-         >::value,
-         unique_ptr&>::type
+        typename enable_if< __and_<
+          __safe_conversion_up<_Up, _Ep>,
+          is_assignable<deleter_type&, _Ep&&>
+          >::value,
+          unique_ptr&>::type
        operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
        {
          reset(__u.release());
@@ -391,23 +391,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          = __and_< is_base_of<_Tp, _Up>,
                    __not_<is_same<__remove_cv<_Tp>, __remove_cv<_Up>>> >;
 
-      template<typename _Up, typename _Ep,
-              typename _Tp_pointer = typename _Pointer::type,
-              typename _Up_pointer = typename unique_ptr<_Up, _Ep>::pointer>
-       using __safe_conversion = __and_<
-           is_convertible<_Up_pointer, _Tp_pointer>,
-           is_array<_Up>,
-           __or_<__not_<is_pointer<_Up_pointer>>,
-                 __not_<is_pointer<_Tp_pointer>>,
-                 __not_<__is_derived_Tp<typename remove_extent<_Up>::type>>
-           >
-         >;
 
     public:
       typedef typename _Pointer::type  pointer;
       typedef _Tp                      element_type;
       typedef _Dp                       deleter_type;
 
+      // helper template for detecting a safe conversion from another
+      // unique_ptr
+      template<typename _Up, typename _Ep,
+               typename _Up_up = unique_ptr<_Up, _Ep>,
+              typename _Up_element_type = typename _Up_up::element_type>
+       using __safe_conversion_up = __and_<
+          is_array<_Up>,
+          is_same<pointer, element_type*>,
+          is_same<typename _Up_up::pointer, _Up_element_type*>,
+          is_convertible<_Up_element_type(*)[], element_type(*)[]>,
+          __or_<__and_<is_reference<deleter_type>, is_same<deleter_type, _Ep>>,
+                __and_<__not_<is_reference<deleter_type>>,
+                       is_convertible<_Ep, deleter_type>>>
+        >;
+
+      // helper template for detecting a safe conversion from a raw pointer
+      template<typename _Up>
+        using __safe_conversion_raw = __and_<
+          __or_<__or_<is_same<_Up, pointer>,
+                      is_same<_Up, nullptr_t>>,
+                __and_<is_pointer<_Up>,
+                       is_same<pointer, element_type*>,
+                       is_convertible<
+                         typename remove_pointer<_Up>::type(*)[],
+                         element_type(*)[]>
+                >
+          >
+        >;
+
       // Constructors.
 
       /// Default constructor, creates a unique_ptr that owns nothing.
@@ -418,42 +436,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       /** Takes ownership of a pointer.
        *
-       * @param __p  A pointer to an array of @c element_type
+       * @param __p  A pointer to an array of a type safely convertible
+       * to an array of @c element_type
        *
        * The deleter will be value-initialized.
        */
+      template<typename _Up,
+               typename = typename enable_if<
+                 __safe_conversion_raw<_Up>::value, bool>::type>
       explicit
-      unique_ptr(pointer __p) noexcept
+      unique_ptr(_Up __p) noexcept
       : _M_t(__p, deleter_type())
       { static_assert(!is_pointer<deleter_type>::value,
                      "constructed with null function pointer deleter"); }
 
-      // Disable construction from convertible pointer types.
-      template<typename _Up, typename = _Require<is_pointer<pointer>,
-              is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>>
-       explicit
-       unique_ptr(_Up* __p) = delete;
-
       /** Takes ownership of a pointer.
        *
-       * @param __p  A pointer to an array of @c element_type
+       * @param __p  A pointer to an array of a type safely convertible
+       * to an array of @c element_type
        * @param __d  A reference to a deleter.
        *
        * 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
+      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
       : _M_t(__p, __d) { }
 
       /** Takes ownership of a pointer.
        *
-       * @param __p  A pointer to an array of @c element_type
+       * @param __p  A pointer to an array of a type safely convertible
+       * to an array of @c element_type
        * @param __d  A reference to a deleter.
        *
        * The deleter will be initialized with @p std::move(__d)
        */
-      unique_ptr(pointer __p, typename
+      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,
@@ -467,11 +491,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
 
       template<typename _Up, typename _Ep,
-              typename = _Require<__safe_conversion<_Up, _Ep>,
-                typename conditional<is_reference<_Dp>::value,
-                                     is_same<_Ep, _Dp>,
-                                     is_convertible<_Ep, _Dp>>::type
-              >>
+              typename = _Require<__safe_conversion_up<_Up, _Ep>>>
        unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
        : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
        { }
@@ -510,7 +530,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        */
       template<typename _Up, typename _Ep>
        typename
-       enable_if<__safe_conversion<_Up, _Ep>::value, unique_ptr&>::type
+       enable_if<__and_<__safe_conversion_up<_Up, _Ep>,
+                         is_assignable<deleter_type&, _Ep&&>
+                  >::value,
+                  unique_ptr&>::type
        operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
        {
          reset(__u.release());
@@ -572,8 +595,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        * The deleter will be invoked if a pointer is already owned.
        */
+      template <typename _Up,
+                typename = _Require<
+                  __or_<is_same<_Up, pointer>,
+                        __and_<is_same<pointer, element_type*>,
+                               is_pointer<_Up>,
+                               is_convertible<
+                                 typename remove_pointer<_Up>::type(*)[],
+                                 element_type(*)[]
+                               >
+                        >
+                  >
+               >>
       void
-      reset(pointer __p = pointer()) noexcept
+      reset(_Up __p) noexcept
       {
        using std::swap;
        swap(std::get<0>(_M_t), __p);
@@ -581,10 +616,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          get_deleter()(__p);
       }
 
-      // Disable resetting from convertible pointer types.
-      template<typename _Up, typename = _Require<is_pointer<pointer>,
-              is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>>
-       void reset(_Up*) = delete;
+      void reset(nullptr_t = nullptr) noexcept
+      {
+        reset(pointer());
+      }
 
       /// Exchange the pointer and deleter with another object.
       void
@@ -597,19 +632,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Disable copy from lvalue.
       unique_ptr(const unique_ptr&) = delete;
       unique_ptr& operator=(const unique_ptr&) = delete;
-
-      // Disable construction from convertible pointer types.
-      template<typename _Up, typename = _Require<is_pointer<pointer>,
-              is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>>
-       unique_ptr(_Up*, typename
-                  conditional<is_reference<deleter_type>::value,
-                  deleter_type, const deleter_type&>::type) = delete;
-
-      // Disable construction from convertible pointer types.
-      template<typename _Up, typename = _Require<is_pointer<pointer>,
-              is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>>
-       unique_ptr(_Up*, typename
-                  remove_reference<deleter_type>::type&&) = delete;
     };
 
   template<typename _Tp, typename _Dp>
index aed2cf4..6ee1278 100644 (file)
@@ -26,6 +26,5 @@ struct D : B { };
 // libstdc++/48631
 D d;
 std::default_delete<B[]> db;
-typedef decltype(db(&d)) type; // { dg-error "use of deleted function" }
-// { dg-prune-output "declared" }
-// { dg-prune-output "invalid" }
+typedef decltype(db(&d)) type; // { dg-error "no match" }
+// { dg-error "no type" "" { target *-*-* } 106 }
index 9014a32..37d6c36 100644 (file)
@@ -59,16 +59,8 @@ void test01()
 
   DDeleter dd;
 
-  std::unique_ptr<int, DDeleter&> p1t(nullptr, dd);
-  std::unique_ptr<int, Deleter&> p2t(nullptr, d);
-  p2t = std::move(p1t);
-
   std::unique_ptr<int[], Deleter&> p1a(nullptr, d), p2a(nullptr, d);
   p2a = std::move(p1a);
-
-  std::unique_ptr<int[], DDeleter&> p1at(nullptr, dd);
-  std::unique_ptr<int[], Deleter&> p2at(nullptr, d);
-  p2at = std::move(p1at);
 }
 
 int main()
index 185f39c..68be4c4 100644 (file)
@@ -24,7 +24,7 @@ struct D;
 
 struct B
 {
- B& operator=(D&) = delete; // { dg-error "declared here" }
+ B& operator=(D&) = delete;
 
  template<class T>
    void operator()(T*) const {}
@@ -39,12 +39,14 @@ void f()
   D d;
 
   std::unique_ptr<int, B&> ub(nullptr, b);
+  std::unique_ptr<int, B> ub2(nullptr, b);
   std::unique_ptr<int, D&> ud(nullptr, d);
-  ub = std::move(ud);
-// { dg-error "use of deleted function" "" { target *-*-* } 272 }
+  ub = std::move(ud); // { dg-error "no match" }
+  ub2 = ud; // { dg-error "no match" }
+// { dg-error "no type" "" { target *-*-* } 269 }
 
   std::unique_ptr<int[], B&> uba(nullptr, b);
   std::unique_ptr<int[], D&> uda(nullptr, d);
-  uba = std::move(uda);
-// { dg-error "use of deleted function" "" { target *-*-* } 517 }
+  uba = std::move(uda); // { dg-error "no match" }
+// { dg-error "no type" "" { target *-*-* } 537 }
 }
index 0938377..da73fcc 100644 (file)
@@ -82,8 +82,10 @@ struct deleter
 void
 test04()
 {
-  // Allow conversions from user-defined pointer-like types
+  // Disallow conversions from incompatible deleter
   std::unique_ptr<B[], deleter<A_pointer>> p;
   std::unique_ptr<A[], deleter<A*>> upA;
-  upA = std::move(p);
+  upA = std::move(p);  // { dg-error "no match" }
+  // { dg-error "no type" "" { target *-*-* } 537 }
+  // { dg-error "no matching function" "" { target *-*-* } 614 }
 }
index f7d1606..f399b7c 100644 (file)
@@ -88,11 +88,25 @@ void
 test07()
 {
   // Allow conversions from user-defined pointer-like types
+  // for the single-object version
   A_pointer p;
-  std::unique_ptr<A[]> upA(p);
-  std::unique_ptr<const A[]> cA(p);
-  std::unique_ptr<volatile A[]> vA(p);
-  std::unique_ptr<const volatile A[]> cvA(p);
+  std::unique_ptr<A> upA(p);
+  std::unique_ptr<const A> cA(p);
+  std::unique_ptr<volatile A> vA(p);
+  std::unique_ptr<const volatile A> cvA(p);
+  // Allow conversions from user-defined pointer-like types
+  // for the array version when the type is converted explicitly
+  std::unique_ptr<A[]> upA2((A*)p);
+  std::unique_ptr<const A[]> cA2((A*)p);
+  std::unique_ptr<volatile A[]> vA2((A*)p);
+  std::unique_ptr<const volatile A[]> cvA2((A*)p);
+  // Disallow conversions from user-defined pointer-like types
+  // for the array version
+  std::unique_ptr<A[]> upA3(p); // { dg-error "no matching function" }
+  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 *-*-* } 445 }
 }
 
 template<typename T>
@@ -108,8 +122,8 @@ struct deleter
 void
 test08()
 {
-  // Allow conversions from user-defined pointer-like types
+  // Disallow conversions from non-assignable deleter
   std::unique_ptr<B[], deleter<A_pointer>> p;
-  std::unique_ptr<A[], deleter<A*>> upA(std::move(p));
+  std::unique_ptr<A[], deleter<A*>> upA(std::move(p)); // { dg-error "no matching function" }
 }
 
diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/dr2228.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/dr2228.cc
new file mode 100644 (file)
index 0000000..ae996da
--- /dev/null
@@ -0,0 +1,38 @@
+// { dg-options "-std=gnu++11" }
+// { dg-do compile }
+
+// Copyright (C) 2015 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/>.
+
+#include <memory>
+#include <type_traits>
+
+struct do_nothing
+{
+    template <class T>
+    void operator()(T*) {}
+};
+
+int
+main()
+{
+    int i = 0;
+    std::unique_ptr<int, do_nothing> p1(&i);
+    std::unique_ptr<int> p2;
+    static_assert(!std::is_assignable<decltype(p2), decltype(p1)>::value, "");
+
+}
index eaa7d43..4ae2f9d 100644 (file)
@@ -66,14 +66,36 @@ struct A_pointer { operator A*() const { return nullptr; } };
 void
 test07()
 {
-  // Allow conversions from user-defined pointer-like types
   A_pointer p;
-  std::unique_ptr<A[]> upA;
+  // Allow conversions from user-defined pointer-like types
+  // for the single-object version
+  std::unique_ptr<A> upA;
   upA.reset(p);
-  std::unique_ptr<const A[]> cA;
+  std::unique_ptr<const A> cA;
   cA.reset(p);
-  std::unique_ptr<volatile A[]> vA;
+  std::unique_ptr<volatile A> vA;
   vA.reset(p);
-  std::unique_ptr<const volatile A[]> cvA;
+  std::unique_ptr<const volatile A> cvA;
   cvA.reset(p);
+  // Allow conversions from user-defined pointer-like types
+  // for the array version when the type is converted explicitly
+  std::unique_ptr<A[]> upA2;
+  upA2.reset((A*)p);
+  std::unique_ptr<const A[]> cA2;
+  cA2.reset((A*)p);
+  std::unique_ptr<volatile A[]> vA2;
+  vA2.reset((A*)p);
+  std::unique_ptr<const volatile A[]> cvA2;
+  cvA2.reset((A*)p);
+  // Disallow conversions from user-defined pointer-like types
+  // for the array version
+  std::unique_ptr<A[]> upA3;
+  upA3.reset(p); // { dg-error "no matching function" }
+  std::unique_ptr<const A[]> cA3;
+  cA3.reset(p); // { dg-error "no matching function" }
+  std::unique_ptr<volatile A[]> vA3;
+  vA3.reset(p); // { dg-error "no matching function" }
+  std::unique_ptr<const volatile A[]> cvA3;
+  cvA3.reset(p); // { dg-error "no matching function" }
+  // { dg-error "no matching function" "" { target *-*-* } 614 }
 }