diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -3537,6 +3537,24 @@ : is_convertible<_Tp*, _Up*> {}; #endif // _LIBCPP_STD_VER > 14 +template ()(_VSTD::declval<_Pt>()))> +static true_type __well_formed_deleter_test(int); + +template +static false_type __well_formed_deleter_test(...); + +template +struct __well_formed_deleter : decltype(__well_formed_deleter_test<_Dp, _Pt>(0)) {}; + +template +struct __shared_ptr_deleter_ctor_reqs +{ + static const bool value = __compatible_with<_Tp, _Yp>::value && + is_move_constructible<_Dp>::value && + __well_formed_deleter<_Dp, _Tp*>::value; +}; + template class _LIBCPP_TEMPLATE_VIS shared_ptr { @@ -3563,10 +3581,10 @@ typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type = __nat()); template shared_ptr(_Yp* __p, _Dp __d, - typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type = __nat()); + typename enable_if<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, element_type>::value, __nat>::type = __nat()); template shared_ptr(_Yp* __p, _Dp __d, _Alloc __a, - typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type = __nat()); + typename enable_if<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, element_type>::value, __nat>::type = __nat()); template shared_ptr(nullptr_t __p, _Dp __d); template shared_ptr(nullptr_t __p, _Dp __d, _Alloc __a); template _LIBCPP_INLINE_VISIBILITY shared_ptr(const shared_ptr<_Yp>& __r, element_type* __p) _NOEXCEPT; @@ -3859,7 +3877,7 @@ template template shared_ptr<_Tp>::shared_ptr(_Yp* __p, _Dp __d, - typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type) + typename enable_if<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, element_type>::value, __nat>::type) : __ptr_(__p) { #ifndef _LIBCPP_NO_EXCEPTIONS @@ -3905,7 +3923,7 @@ template template shared_ptr<_Tp>::shared_ptr(_Yp* __p, _Dp __d, _Alloc __a, - typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type) + typename enable_if<__shared_ptr_deleter_ctor_reqs<_Dp, _Yp, element_type>::value, __nat>::type) : __ptr_(__p) { #ifndef _LIBCPP_NO_EXCEPTIONS diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp --- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter.pass.cpp @@ -28,6 +28,22 @@ int A::count = 0; +struct bad_ty { }; + +struct bad_deleter +{ + void operator()(bad_ty) { } +}; + +struct no_move_deleter +{ + no_move_deleter(no_move_deleter const&) = delete; + no_move_deleter(no_move_deleter &&) = delete; + void operator()(int*) { } +}; + +static_assert(!std::is_move_constructible::value, ""); + int main(int, char**) { { @@ -46,5 +62,14 @@ assert(test_deleter::count == 0); assert(test_deleter::dealloc_count == 1); + { + // Make sure we can't construct with: + // a) a deleter that doesn't have an operator ()(int*) + // b) a deleter that doesn't have a move constructor. + static_assert(!std::is_constructible, int*, bad_deleter>::value, ""); + static_assert(!std::is_constructible, int*, no_move_deleter>::value, + ""); + } + return 0; } diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp --- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp +++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer_deleter_allocator.pass.cpp @@ -28,6 +28,22 @@ int A::count = 0; +struct bad_ty { }; + +struct bad_deleter +{ + void operator()(bad_ty) { } +}; + +struct no_move_deleter +{ + no_move_deleter(no_move_deleter const&) = delete; + no_move_deleter(no_move_deleter &&) = delete; + void operator()(int*) { } +}; + +static_assert(!std::is_move_constructible::value, ""); + int main(int, char**) { @@ -87,5 +103,15 @@ assert(test_deleter::dealloc_count == 1); #endif + { + // Make sure we can't construct with: + // a) a deleter that doesn't have an operator ()(int*) + // b) a deleter that doesn't have a move constructor. + static_assert(!std::is_constructible, int*, bad_deleter, + test_allocator >::value, ""); + static_assert(!std::is_constructible, int*, no_move_deleter, + test_allocator >::value, ""); + } + return 0; } diff --git a/libcxx/www/cxx1z_status.html b/libcxx/www/cxx1z_status.html --- a/libcxx/www/cxx1z_status.html +++ b/libcxx/www/cxx1z_status.html @@ -480,7 +480,7 @@ 2872Add definition for direct-non-list-initializationKonaComplete 2873Add noexcept to several shared_ptr related functionsKonaComplete 2874Constructor shared_ptr::shared_ptr(Y*) should be constrainedKona - 2875shared_ptr::shared_ptr(Y*, D, […]) constructors should be constrainedKona + 2875shared_ptr::shared_ptr(Y*, D, […]) constructors should be constrainedKonaComplete 2876shared_ptr::shared_ptr(const weak_ptr<Y>&) constructor should be constrainedKona 2878Missing DefaultConstructible requirement for istream_iterator default constructorKonaComplete 2890The definition of 'object state' applies only to class typesKonaComplete