[libc++][LWG 2996] Implement c++20 shared_ptr rvalue overloads.
authorpateldeev <pateldeev@nevada.unr.edu>
Tue, 18 Jul 2023 15:48:53 +0000 (17:48 +0200)
committerMark de Wever <koraq@xs4all.nl>
Tue, 18 Jul 2023 18:04:21 +0000 (20:04 +0200)
Implement c++20 `shared_ptr` rvalue overloads for aliasing constructor and pointer casts. See https://cplusplus.github.io/LWG/issue2996

Commit from
"patedeev" <dkp10000@gmail.com>

Reviewed By: philnik, #libc, ldionne, pateldeev

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

libcxx/docs/Status/Cxx20Issues.csv
libcxx/include/__memory/shared_ptr.h
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.cast/const_pointer_cast.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.cast/dynamic_pointer_cast.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.cast/reinterpret_pointer_cast.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.cast/static_pointer_cast.pass.cpp
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_pointer.pass.cpp

index 9bdeeb0..16161c3 100644 (file)
 "`2943 <https://wg21.link/LWG2943>`__","Problematic specification of the wide version of ``basic_filebuf::open``\ ","San Diego","|Nothing To Do|",""
 "`2960 <https://wg21.link/LWG2960>`__","[fund.ts.v3] ``nonesuch``\  is insufficiently useless","San Diego","|Complete|",""
 "`2995 <https://wg21.link/LWG2995>`__","``basic_stringbuf``\  default constructor forbids it from using SSO capacity","San Diego","",""
-"`2996 <https://wg21.link/LWG2996>`__","Missing rvalue overloads for ``shared_ptr``\  operations","San Diego","",""
+"`2996 <https://wg21.link/LWG2996>`__","Missing rvalue overloads for ``shared_ptr``\  operations","San Diego","|Complete|","17.0"
 "`3008 <https://wg21.link/LWG3008>`__","``make_shared``\  (sub)object destruction semantics are not specified","San Diego","|Complete|","16.0"
 "`3022 <https://wg21.link/LWG3022>`__","``is_convertible<derived*, base*>``\  may lead to ODR","San Diego","Resolved by 1285R0",""
 "`3025 <https://wg21.link/LWG3025>`__","Map-like container deduction guides should use ``pair<Key, T>``\ , not ``pair<const Key, T>``\ ","San Diego","|Complete|",""
index 28f0260..dce44a7 100644 (file)
@@ -644,6 +644,18 @@ public:
             __cntrl_->__add_shared();
     }
 
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+    template <class _Yp>
+    _LIBCPP_HIDE_FROM_ABI shared_ptr(shared_ptr<_Yp>&& __r, element_type* __p) noexcept
+        : __ptr_(__p),
+          __cntrl_(__r.__cntrl_) {
+      __r.__ptr_   = nullptr;
+      __r.__cntrl_ = nullptr;
+    }
+#endif
+
     _LIBCPP_HIDE_FROM_ABI
     shared_ptr(const shared_ptr& __r) _NOEXCEPT
         : __ptr_(__r.__ptr_),
@@ -1519,6 +1531,15 @@ static_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT
                                typename shared_ptr<_Tp>::element_type*>(__r.get()));
 }
 
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp, class _Up>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> static_pointer_cast(shared_ptr<_Up>&& __r) noexcept {
+  return shared_ptr<_Tp>(std::move(__r), static_cast<typename shared_ptr<_Tp>::element_type*>(__r.get()));
+}
+#endif
+
 template<class _Tp, class _Up>
 inline _LIBCPP_INLINE_VISIBILITY
 shared_ptr<_Tp>
@@ -1529,6 +1550,16 @@ dynamic_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT
     return __p ? shared_ptr<_Tp>(__r, __p) : shared_ptr<_Tp>();
 }
 
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp, class _Up>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> dynamic_pointer_cast(shared_ptr<_Up>&& __r) noexcept {
+  auto* __p = dynamic_cast<typename shared_ptr<_Tp>::element_type*>(__r.get());
+  return __p ? shared_ptr<_Tp>(std::move(__r), __p) : shared_ptr<_Tp>();
+}
+#endif
+
 template<class _Tp, class _Up>
 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
 const_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT
@@ -1537,6 +1568,15 @@ const_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT
     return shared_ptr<_Tp>(__r, const_cast<_RTp*>(__r.get()));
 }
 
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp, class _Up>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> const_pointer_cast(shared_ptr<_Up>&& __r) noexcept {
+  return shared_ptr<_Tp>(std::move(__r), const_cast<typename shared_ptr<_Tp>::element_type*>(__r.get()));
+}
+#endif
+
 template<class _Tp, class _Up>
 _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
 reinterpret_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT
@@ -1546,6 +1586,15 @@ reinterpret_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT
                                typename shared_ptr<_Tp>::element_type*>(__r.get()));
 }
 
+// LWG-2996
+// We don't backport because it is an evolutionary change.
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp, class _Up>
+_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> reinterpret_pointer_cast(shared_ptr<_Up>&& __r) noexcept {
+  return shared_ptr<_Tp>(std::move(__r), reinterpret_cast<typename shared_ptr<_Tp>::element_type*>(__r.get()));
+}
+#endif
+
 #ifndef _LIBCPP_HAS_NO_RTTI
 
 template<class _Dp, class _Tp>
index d0da8c1..9046b33 100644 (file)
 
 // shared_ptr
 
-// template<class T, class U> shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r);
+// template<class T, class U> shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r) noexcept;
+// template<class T, class U> shared_ptr<T> const_pointer_cast(shared_ptr<U>&& r) noexcept;
 
+#include <cassert>
 #include <memory>
 #include <type_traits>
-#include <cassert>
+#include <utility>
 
 #include "test_macros.h"
 
@@ -45,6 +47,7 @@ int main(int, char**)
 {
     {
         const std::shared_ptr<const A> pA(new A);
+        ASSERT_NOEXCEPT(std::const_pointer_cast<A>(pA));
         std::shared_ptr<A> pB = std::const_pointer_cast<A>(pA);
         assert(pB.get() == pA.get());
         assert(!pB.owner_before(pA) && !pA.owner_before(pB));
@@ -63,6 +66,17 @@ int main(int, char**)
       assert(!pB.owner_before(pA) && !pA.owner_before(pB));
     }
 #endif // TEST_STD_VER > 14
+#if TEST_STD_VER > 20
+    {
+      A* pA_raw = new A;
+      std::shared_ptr<const A> pA(pA_raw);
+      ASSERT_NOEXCEPT(std::const_pointer_cast<A>(std::move(pA)));
+      std::shared_ptr<A> pB = std::const_pointer_cast<A>(std::move(pA));
+      assert(pA.get() == nullptr);
+      assert(pB.get() == pA_raw);
+      assert(pB.use_count() == 1);
+    }
+#endif // TEST_STD_VER > 20
 
     return 0;
 }
index 7a8041e..ae65859 100644 (file)
 
 // shared_ptr
 
-// template<class T, class U> shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r);
+// template<class T, class U> shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r) noexcept;
+// template<class T, class U> shared_ptr<T> dynamic_pointer_cast(shared_ptr<U>&& r) noexcept;
 
 // UNSUPPORTED: no-rtti
 
+#include <cassert>
 #include <memory>
 #include <type_traits>
-#include <cassert>
+#include <utility>
 
 #include "test_macros.h"
 
@@ -47,6 +49,7 @@ int main(int, char**)
 {
     {
         const std::shared_ptr<B> pB(new A);
+        ASSERT_NOEXCEPT(std::dynamic_pointer_cast<A>(pB));
         std::shared_ptr<A> pA = std::dynamic_pointer_cast<A>(pB);
         assert(pA.get() == pB.get());
         assert(!pB.owner_before(pA) && !pA.owner_before(pB));
@@ -65,6 +68,17 @@ int main(int, char**)
       assert(pA.use_count() == 0);
     }
 #endif // TEST_STD_VER > 14
+#if TEST_STD_VER > 20
+    {
+      A* pA_raw = new A;
+      std::shared_ptr<B> pB(pA_raw);
+      ASSERT_NOEXCEPT(std::dynamic_pointer_cast<A>(std::move(pB)));
+      std::shared_ptr<A> pA = std::dynamic_pointer_cast<A>(std::move(pB));
+      assert(pB.get() == nullptr);
+      assert(pA.get() == pA_raw);
+      assert(pA.use_count() == 1);
+    }
+#endif // TEST_STD_VER > 20
 
     return 0;
 }
index 743d60c..dccfd40 100644 (file)
 
 // template<class T, class U>
 //     shared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U>& r) noexcept;
+// template<class T, class U>
+//     shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U>&& r) noexcept;
 
 #include "test_macros.h"
 
+#include <cassert>
 #include <memory>
 #include <type_traits>
-#include <cassert>
+#include <utility>
 
 struct A {
   int x;
@@ -29,6 +32,7 @@ struct Derived : public Base { };
 int main(int, char**) {
   {
     const std::shared_ptr<A> pA(new A);
+    ASSERT_NOEXCEPT(std::reinterpret_pointer_cast<int>(pA));
     std::shared_ptr<int> pi = std::reinterpret_pointer_cast<int>(pA);
     std::shared_ptr<A> pA2 = std::reinterpret_pointer_cast<A>(pi);
     assert(pA2.get() == pA.get());
@@ -70,6 +74,20 @@ int main(int, char**) {
     assert(!pi.owner_before(pA) && !pA.owner_before(pi));
   }
 #endif // TEST_STD_VER > 14
+#if TEST_STD_VER > 20
+  {
+    A* pA_raw = new A;
+    std::shared_ptr<A> pA(pA_raw);
+    ASSERT_NOEXCEPT(std::reinterpret_pointer_cast<int>(std::move(pA)));
+    std::shared_ptr<int> pi = std::reinterpret_pointer_cast<int>(std::move(pA));
+    assert(pA.get() == nullptr);
+    assert(pi.use_count() == 1);
+    std::shared_ptr<A> pA2 = std::reinterpret_pointer_cast<A>(std::move(pi));
+    assert(pi.get() == nullptr);
+    assert(pA2.get() == pA_raw);
+    assert(pA2.use_count() == 1);
+  }
+#endif // TEST_STD_VER > 20
 
   return 0;
 }
index 6449f72..ebf9d7c 100644 (file)
 
 // shared_ptr
 
-// template<class T, class U> shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r);
+// template<class T, class U> shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r) noexcept;
+// template<class T, class U> shared_ptr<T> static_pointer_cast(shared_ptr<U>&& r) noexcept;
 
+#include <cassert>
 #include <memory>
 #include <type_traits>
-#include <cassert>
+#include <utility>
 
 #include "test_macros.h"
 
@@ -45,6 +47,7 @@ int main(int, char**)
 {
     {
         const std::shared_ptr<A> pA(new A);
+        ASSERT_NOEXCEPT(std::static_pointer_cast<B>(pA));
         std::shared_ptr<B> pB = std::static_pointer_cast<B>(pA);
         assert(pB.get() == pA.get());
         assert(!pB.owner_before(pA) && !pA.owner_before(pB));
@@ -81,6 +84,17 @@ int main(int, char**)
       assert(!pB.owner_before(pA) && !pA.owner_before(pB));
     }
 #endif // TEST_STD_VER > 14
+#if TEST_STD_VER > 20
+    {
+      A* pA_raw = new A;
+      std::shared_ptr<A> pA(pA_raw);
+      ASSERT_NOEXCEPT(std::static_pointer_cast<B>(std::move(pA)));
+      std::shared_ptr<B> pB = std::static_pointer_cast<B>(std::move(pA));
+      assert(pA.get() == nullptr);
+      assert(pB.get() == pA_raw);
+      assert(pB.use_count() == 1);
+    }
+#endif // TEST_STD_VER > 20
 
     return 0;
 }
index 4c66312..e0b1e51 100644 (file)
@@ -96,23 +96,23 @@ int main(int, char**)
     }
 
 #if TEST_STD_VER > 17 && defined(_LIBCPP_VERSION)
-    // This won't pass when LWG-2996 is implemented.
     {
       std::shared_ptr<A> pA(new A);
       assert(pA.use_count() == 1);
 
+#  if TEST_STD_VER >= 20
+      // LWG-2996 is only implemented in c++20 and beyond.
+      // We don't backport because it is an evolutionary change.
       {
         B b;
         std::shared_ptr<B> pB(std::move(pA), &b);
         assert(A::count == 1);
         assert(B::count == 1);
-        assert(pA.use_count() == 2);
-        assert(pB.use_count() == 2);
+        assert(pA.use_count() == 0);
+        assert(pB.use_count() == 1);
         assert(pB.get() == &b);
       }
-      assert(pA.use_count() == 1);
-      assert(A::count == 1);
-      assert(B::count == 0);
+#  endif // TEST_STD_VER > 20
     }
     assert(A::count == 0);
     assert(B::count == 0);