Constrain std::shared_ptr assignment and resetting
authorJonathan Wakely <jwakely@redhat.com>
Wed, 31 Aug 2016 16:57:20 +0000 (17:57 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 31 Aug 2016 16:57:20 +0000 (17:57 +0100)
* include/bits/shared_ptr.h (_Assignable): New alias template.
(shared_ptr::operator=(const shared_ptr<_Tp1>&))
(shared_ptr::operator=(shared_ptr<_Tp1>&&))
(shared_ptr::operator=(unique_ptr<_Tp1>&&)): Constrain with
_Assignable.
* include/bits/shared_ptr_base.h (_Assignable): New alias template.
(__shared_ptr::operator=(const __shared_ptr<_Tp1>&))
(__shared_ptr::operator=(__shared_ptr<_Tp1>&&))
(__shared_ptr::operator=(unique_ptr<_Tp1>&&)): Constrain with
_Assignable.
(__shared_ptr::reset(_Tp1*), __shared_ptr::reset(_Tp1*, _Deleter))
(__shared_ptr::reset(_Tp1*, _Deleter, _Alloc)): Constrain with
_Convertible.
* testsuite/20_util/shared_ptr/cons/43820_neg.cc: Change dg-error to
match on any line.
* testsuite/20_util/shared_ptr/cons/void_neg.cc: Likewise.
* testsuite/20_util/shared_ptr/assign/sfinae.cc: New test.
* testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc: Update
expected errors. Remove unnecessary code.
* testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc: New test.

From-SVN: r239898

libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/shared_ptr.h
libstdc++-v3/include/bits/shared_ptr_base.h
libstdc++-v3/testsuite/20_util/shared_ptr/assign/sfinae.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc
libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc
libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc
libstdc++-v3/testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc [new file with mode: 0644]

index d608f6bdc3fe9edce677a94ba3e99359bc0e1d3c..10761e8c38a1a30942f6cd21391d3c1d0ec38469 100644 (file)
@@ -1,5 +1,26 @@
 2016-08-31  Jonathan Wakely  <jwakely@redhat.com>
 
+       * include/bits/shared_ptr.h (_Assignable): New alias template.
+       (shared_ptr::operator=(const shared_ptr<_Tp1>&))
+       (shared_ptr::operator=(shared_ptr<_Tp1>&&))
+       (shared_ptr::operator=(unique_ptr<_Tp1>&&)): Constrain with
+       _Assignable.
+       * include/bits/shared_ptr_base.h (_Assignable): New alias template.
+       (__shared_ptr::operator=(const __shared_ptr<_Tp1>&))
+       (__shared_ptr::operator=(__shared_ptr<_Tp1>&&))
+       (__shared_ptr::operator=(unique_ptr<_Tp1>&&)): Constrain with
+       _Assignable.
+       (__shared_ptr::reset(_Tp1*), __shared_ptr::reset(_Tp1*, _Deleter))
+       (__shared_ptr::reset(_Tp1*, _Deleter, _Alloc)): Constrain with
+       _Convertible.
+       * testsuite/20_util/shared_ptr/cons/43820_neg.cc: Change dg-error to
+       match on any line.
+       * testsuite/20_util/shared_ptr/cons/void_neg.cc: Likewise.
+       * testsuite/20_util/shared_ptr/assign/sfinae.cc: New test.
+       * testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc: Update
+       expected errors. Remove unnecessary code.
+       * testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc: New test.
+
        * include/bits/stl_tree.h (_Rb_tree::operator=(_Rb_tree&&)): Move
        comparison object.
        * testsuite/23_containers/set/move_comparison.cc: New test.
index 747b09a2155e20dcb67b2c52242d2057751e09a9..b2523b832286bd393d571b12f8bf904be57a61ad 100644 (file)
@@ -93,8 +93,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class shared_ptr : public __shared_ptr<_Tp>
     {
       template<typename _Ptr>
-       using _Convertible
-         = typename enable_if<is_convertible<_Ptr, _Tp*>::value>::type;
+       using _Convertible = typename
+         enable_if<is_convertible<_Ptr, _Tp*>::value>::type;
+
+      template<typename _Ptr>
+       using _Assignable = typename
+         enable_if<is_convertible<_Ptr, _Tp*>::value, shared_ptr&>::type;
 
     public:
 
@@ -276,7 +280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       shared_ptr& operator=(const shared_ptr&) noexcept = default;
 
       template<typename _Tp1>
-       shared_ptr&
+       _Assignable<_Tp1*>
        operator=(const shared_ptr<_Tp1>& __r) noexcept
        {
          this->__shared_ptr<_Tp>::operator=(__r);
@@ -301,7 +305,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       template<class _Tp1>
-       shared_ptr&
+       _Assignable<_Tp1*>
        operator=(shared_ptr<_Tp1>&& __r) noexcept
        {
          this->__shared_ptr<_Tp>::operator=(std::move(__r));
@@ -309,7 +313,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       template<typename _Tp1, typename _Del>
-       shared_ptr&
+       _Assignable<typename unique_ptr<_Tp1, _Del>::pointer>
        operator=(std::unique_ptr<_Tp1, _Del>&& __r)
        {
          this->__shared_ptr<_Tp>::operator=(std::move(__r));
index 60b825c541e7ef2e2e358c9691ff693fbc8cac0e..4ae26684dbe03b2b524373f6b16d5dd640cbe90a 100644 (file)
@@ -873,6 +873,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        using _Convertible
          = typename enable_if<is_convertible<_Ptr, _Tp*>::value>::type;
 
+      template<typename _Ptr>
+       using _Assignable = typename
+         enable_if<is_convertible<_Ptr, _Tp*>::value, __shared_ptr&>::type;
+
     public:
       typedef _Tp   element_type;
 
@@ -983,7 +987,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       constexpr __shared_ptr(nullptr_t) noexcept : __shared_ptr() { }
 
       template<typename _Tp1>
-       __shared_ptr&
+       _Assignable<_Tp1*>
        operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
        {
          _M_ptr = __r._M_ptr;
@@ -1009,7 +1013,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       template<class _Tp1>
-       __shared_ptr&
+       _Assignable<_Tp1*>
        operator=(__shared_ptr<_Tp1, _Lp>&& __r) noexcept
        {
          __shared_ptr(std::move(__r)).swap(*this);
@@ -1017,7 +1021,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       template<typename _Tp1, typename _Del>
-       __shared_ptr&
+       _Assignable<typename unique_ptr<_Tp1, _Del>::pointer>
        operator=(std::unique_ptr<_Tp1, _Del>&& __r)
        {
          __shared_ptr(std::move(__r)).swap(*this);
@@ -1029,7 +1033,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { __shared_ptr().swap(*this); }
 
       template<typename _Tp1>
-       void
+       _Convertible<_Tp1*>
        reset(_Tp1* __p) // _Tp1 must be complete.
        {
          // Catch self-reset errors.
@@ -1038,12 +1042,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       template<typename _Tp1, typename _Deleter>
-       void
+       _Convertible<_Tp1*>
        reset(_Tp1* __p, _Deleter __d)
        { __shared_ptr(__p, __d).swap(*this); }
 
       template<typename _Tp1, typename _Deleter, typename _Alloc>
-       void
+       _Convertible<_Tp1*>
         reset(_Tp1* __p, _Deleter __d, _Alloc __a)
         { __shared_ptr(__p, __d, std::move(__a)).swap(*this); }
 
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/sfinae.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/sfinae.cc
new file mode 100644 (file)
index 0000000..d79af04
--- /dev/null
@@ -0,0 +1,75 @@
+// Copyright (C) 2016 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 From>
+constexpr bool can_assign()
+{ return std::is_assignable<std::shared_ptr<T>, From>::value; }
+
+struct Base { };
+struct Derived : Base { };
+
+// Positive cases:
+
+static_assert( can_assign<const void, const std::shared_ptr<void>&>(),
+    "void* convertible to const void*");
+static_assert( can_assign<const void, std::shared_ptr<void>&&>(),
+    "void* convertible to const void*");
+static_assert( can_assign<const int, std::shared_ptr<int>>(),
+    "int* convertible to const int*");
+static_assert( can_assign<Base, std::shared_ptr<Derived>>(),
+    "Derived* convertible to Base*");
+static_assert( can_assign<const Base, std::shared_ptr<Derived>>(),
+    "Derived* convertible to const Base*");
+
+// Negative cases:
+
+static_assert( !can_assign<int, const std::shared_ptr<void>&>(),
+    "void* not convertible to int*");
+static_assert( !can_assign<int, std::shared_ptr<void>&&>(),
+    "void* not convertible to int*");
+
+static_assert( !can_assign<int, const std::shared_ptr<const int>&>(),
+    "const int* not convertible to int*");
+static_assert( !can_assign<int, std::shared_ptr<const int>&&>(),
+    "const int* not convertible to int*");
+
+static_assert( !can_assign<int, const std::shared_ptr<long>&>(),
+    "long* not convertible to int*");
+static_assert( !can_assign<int, std::shared_ptr<long>&&>(),
+    "long* not convertible to int*");
+
+static_assert( !can_assign<int, std::unique_ptr<long>&&>(),
+    "unique_ptr<long>::pointer not convertible to int*");
+
+static_assert( !can_assign<Derived, const std::shared_ptr<Base>&>(),
+    "Base* not convertible to Derived*");
+static_assert( !can_assign<int, std::shared_ptr<long>&&>(),
+    "Base* not convertible to Derived*");
+static_assert( !can_assign<Derived, std::unique_ptr<Base>&&>(),
+    "unique_ptr<Base>::pointer not convertible to Derived*");
+
+struct Deleter {
+  using pointer = void*;
+  void operator()(pointer) const { }
+};
+
+static_assert( !can_assign<Derived, std::unique_ptr<Derived, Deleter>&&>(),
+    "unique_ptr<Derived, Deleter>::pointer not convertible to Derived*");
index 751495a63db7a4de41d596865ea3ddb22b77a5b6..96f07b5549c0b7f68943059fdd06863c1acee86c 100644 (file)
@@ -28,24 +28,10 @@ struct B { };
 // 20.6.6.2.3 shared_ptr assignment [util.smartptr.shared.assign]
 
 // Assignment from incompatible shared_ptr<Y>
-int
+void
 test01()
 {
-  bool test __attribute__((unused)) = true;
-
   std::shared_ptr<A> a;
   std::shared_ptr<B> b;
-  a = b;                      // { dg-error "here" }
-
-  return 0;
-}
-
-int 
-main()
-{
-  test01();
-  return 0;
+  a = b;                      // { dg-error "no match" }
 }
-// { dg-error "In instantiation" "" { target *-*-* } 0 }
-// { dg-error "cannot convert" "" { target *-*-* } 0 }
-// { dg-error "required from" "" { target *-*-* } 0 }
index 530256a94bcfaa8f4900baa8caad95193b7620a4..c58c8421a3f5f4ed43a551e71d287fecd78a8a35 100644 (file)
@@ -32,8 +32,6 @@ void test01()
 {
   X* px = 0;
   std::shared_ptr<X> p1(px);   // { dg-error "here" }
-  // { dg-error "incomplete" "" { target *-*-* } 893 }
-
   std::shared_ptr<X> p9(ap());  // { dg-error "here" }
-  // { dg-error "incomplete" "" { target *-*-* } 307 }
+  // { dg-error "incomplete" "" { target *-*-* } 0 }
 }
index f77ddec9c7bac4d517f7848791a0b23d174c4075..0cadf25bffbe51bff46a9d87751e7498ac3b1b0b 100644 (file)
@@ -24,5 +24,5 @@
 void test01()
 {
   std::shared_ptr<void> p((void*)nullptr);   // { dg-error "here" }
-  // { dg-error "incomplete" "" { target *-*-* } 892 }
+  // { dg-error "incomplete" "" { target *-*-* } 0 }
 }
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc
new file mode 100644 (file)
index 0000000..f75530f
--- /dev/null
@@ -0,0 +1,92 @@
+// Copyright (C) 2016 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 Args, typename = void>
+  struct resettable
+  : std::false_type
+  { };
+
+template<typename... T> struct type_list { };
+
+template<typename T, typename... Args>
+  using reset_result
+    = decltype(std::shared_ptr<T>{}.reset(std::declval<Args>()...));
+
+template<typename T, typename... Args>
+  struct resettable<T, type_list<Args...>, reset_result<T, Args...>>
+  : std::true_type
+  { };
+
+template<typename T, typename... Args>
+constexpr bool can_reset()
+{ return resettable<T, type_list<Args...>>::value; }
+
+template<typename T>
+struct Deleter {
+  void operator()(T*) const;
+};
+
+template<typename T>
+using Alloc = std::allocator<T>;
+
+struct Base { };
+struct Derived : Base { };
+
+// Positive cases:
+
+static_assert( can_reset<const void, void*>(),
+    "void* convertible to const void*");
+static_assert( can_reset<const int, int*>(),
+    "int* convertible to const int*");
+static_assert( can_reset<Base, Derived*>(),
+    "Derived* convertible to Base*");
+static_assert( can_reset<const Base, Derived*>(),
+    "Derived* convertible to const Base*");
+
+// Negative cases:
+
+static_assert( !can_reset<int, void*>(),
+    "void* not convertible to int*");
+static_assert( !can_reset<int, void*, Deleter<int>>(),
+    "void* not convertible to int*");
+static_assert( !can_reset<int, void*, Deleter<int>, Alloc<int>>(),
+    "void* not convertible to int*");
+
+static_assert( !can_reset<int, const int*>(),
+    "const int* not convertible to int*");
+static_assert( !can_reset<int, const int*, Deleter<int>>(),
+    "const int* not convertible to int*");
+static_assert( !can_reset<int, const int*, Deleter<int>, Alloc<int>>(),
+    "const int* not convertible to int*");
+
+static_assert( !can_reset<int, long*>(),
+    "long* not convertible to int*");
+static_assert( !can_reset<int, long*, Deleter<int>>(),
+    "long* not convertible to int*");
+static_assert( !can_reset<int, long*, Deleter<int>, Alloc<int>>(),
+    "long* not convertible to int*");
+
+static_assert( !can_reset<Derived, Base*>(),
+    "Base* not convertible to Derived*");
+static_assert( !can_reset<Derived, Base*, Deleter<int>>(),
+    "Base* not convertible to Derived*");
+static_assert( !can_reset<Derived, Base*, Deleter<int>, Alloc<int>>(),
+    "Base* not convertible to Derived*");