[libc++] Fix LWG 2874: Constructor shared_ptr::shared_ptr(Y*) should be constrained.
This patch fixes LWG2874. It is based on the original patch by Zoe Carver
originally uploaded at D81417.
Differential Revision: https://reviews.llvm.org/D81417
GitOrigin-RevId: 1f8a6dcf128064e2ac75e4ffd227750779d83ade
diff --git a/docs/Cxx1zStatusIssuesStatus.csv b/docs/Cxx1zStatusIssuesStatus.csv
index 383c3af..090e12f 100644
--- a/docs/Cxx1zStatusIssuesStatus.csv
+++ b/docs/Cxx1zStatusIssuesStatus.csv
@@ -299,7 +299,7 @@
"`2868 <https://wg21.link/LWG2868>`__","Missing specification of bad_any_cast::what()","Kona","|Complete|",""
"`2872 <https://wg21.link/LWG2872>`__","Add definition for direct-non-list-initialization","Kona","|Complete|",""
"`2873 <https://wg21.link/LWG2873>`__","Add noexcept to several shared_ptr related functions","Kona","|Complete|",""
-"`2874 <https://wg21.link/LWG2874>`__","Constructor ``shared_ptr::shared_ptr(Y*)``\ should be constrained","Kona","",""
+"`2874 <https://wg21.link/LWG2874>`__","Constructor ``shared_ptr::shared_ptr(Y*)``\ should be constrained","Kona","|Complete|","13.0"
"`2875 <https://wg21.link/LWG2875>`__","shared_ptr::shared_ptr(Y\*, D, [|hellip|\ ]) constructors should be constrained","Kona","|Complete|",""
"`2876 <https://wg21.link/LWG2876>`__","``shared_ptr::shared_ptr(const weak_ptr<Y>&)``\ constructor should be constrained","Kona","",""
"`2878 <https://wg21.link/LWG2878>`__","Missing DefaultConstructible requirement for istream_iterator default constructor","Kona","|Complete|",""
diff --git a/include/__memory/shared_ptr.h b/include/__memory/shared_ptr.h
index 8ded2ab..e4c267b 100644
--- a/include/__memory/shared_ptr.h
+++ b/include/__memory/shared_ptr.h
@@ -379,6 +379,16 @@
: is_convertible<_Tp*, _Up*> {};
#endif // _LIBCPP_STD_VER > 14
+template <class _Ptr, class = void>
+struct __is_deletable : false_type { };
+template <class _Ptr>
+struct __is_deletable<_Ptr, decltype(delete _VSTD::declval<_Ptr>())> : true_type { };
+
+template <class _Ptr, class = void>
+struct __is_array_deletable : false_type { };
+template <class _Ptr>
+struct __is_array_deletable<_Ptr, decltype(delete[] _VSTD::declval<_Ptr>())> : true_type { };
+
template <class _Dp, class _Pt,
class = decltype(_VSTD::declval<_Dp>()(_VSTD::declval<_Pt>()))>
static true_type __well_formed_deleter_test(int);
@@ -424,9 +434,27 @@
_LIBCPP_CONSTEXPR shared_ptr() _NOEXCEPT;
_LIBCPP_INLINE_VISIBILITY
_LIBCPP_CONSTEXPR shared_ptr(nullptr_t) _NOEXCEPT;
- template<class _Yp>
- explicit shared_ptr(_Yp* __p,
- typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type = __nat());
+
+ template<class _Yp, class = _EnableIf<
+ _And<
+ __compatible_with<_Yp, _Tp>
+ // In C++03 we get errors when trying to do SFINAE with the
+ // delete operator, so we always pretend that it's deletable.
+ // The same happens on GCC.
+#if !defined(_LIBCPP_CXX03_LANG) && !defined(_LIBCPP_COMPILER_GCC)
+ , _If<is_array<_Tp>::value, __is_array_deletable<_Yp*>, __is_deletable<_Yp*> >
+#endif
+ >::value
+ > >
+ explicit shared_ptr(_Yp* __p) : __ptr_(__p) {
+ unique_ptr<_Yp> __hold(__p);
+ typedef typename __shared_ptr_default_allocator<_Yp>::type _AllocT;
+ typedef __shared_ptr_pointer<_Yp*, __shared_ptr_default_delete<_Tp, _Yp>, _AllocT > _CntrlBlk;
+ __cntrl_ = new _CntrlBlk(__p, __shared_ptr_default_delete<_Tp, _Yp>(), _AllocT());
+ __hold.release();
+ __enable_weak_this(__p, __p);
+ }
+
template<class _Yp, class _Dp>
shared_ptr(_Yp* __p, _Dp __d,
typename enable_if<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, element_type>::value, __nat>::type = __nat());
@@ -679,20 +707,6 @@
}
template<class _Tp>
-template<class _Yp>
-shared_ptr<_Tp>::shared_ptr(_Yp* __p,
- typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type)
- : __ptr_(__p)
-{
- unique_ptr<_Yp> __hold(__p);
- typedef typename __shared_ptr_default_allocator<_Yp>::type _AllocT;
- typedef __shared_ptr_pointer<_Yp*, __shared_ptr_default_delete<_Tp, _Yp>, _AllocT > _CntrlBlk;
- __cntrl_ = new _CntrlBlk(__p, __shared_ptr_default_delete<_Tp, _Yp>(), _AllocT());
- __hold.release();
- __enable_weak_this(__p, __p);
-}
-
-template<class _Tp>
template<class _Yp, class _Dp>
shared_ptr<_Tp>::shared_ptr(_Yp* __p, _Dp __d,
typename enable_if<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, element_type>::value, __nat>::type)
diff --git a/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp b/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp
index 62eb5eb..9af199d 100644
--- a/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp
+++ b/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/shared_ptr_Y.pass.cpp
@@ -52,6 +52,28 @@
int C::count = 0;
+class private_delete_op
+{
+ static void operator delete (void *p) {
+ return delete static_cast<char*>(p);
+ }
+public:
+ static void operator delete[] (void *p) {
+ return delete[] static_cast<char*>(p);
+ }
+};
+
+class private_delete_arr_op
+{
+ static void operator delete[] (void *p) {
+ return delete[] static_cast<char*>(p);
+ }
+public:
+ static void operator delete (void *p) {
+ return delete static_cast<char*>(p);
+ }
+};
+
int main(int, char**)
{
static_assert(( std::is_convertible<std::shared_ptr<A>, std::shared_ptr<B> >::value), "");
@@ -96,5 +118,19 @@
assert(B::count == 0);
assert(A::count == 0);
- return 0;
+ // This should work in C++03 but we get errors when trying to do SFINAE with the delete operator.
+ // GCC also complains about this.
+#if TEST_STD_VER >= 11 && !defined(TEST_COMPILER_GCC)
+ {
+ // LWG2874: Make sure that when T (for std::shared_ptr<T>) is an array type,
+ // this constructor only participates in overload resolution when
+ // `delete[] p` is well formed. And when T is not an array type,
+ // this constructor only participates in overload resolution when
+ // `delete p` is well formed.
+ static_assert(!std::is_constructible<std::shared_ptr<private_delete_op>,
+ private_delete_op*>::value, "");
+ static_assert(!std::is_constructible<std::shared_ptr<private_delete_arr_op[4]>,
+ private_delete_arr_op*>::value, "");
+ }
+#endif
}