diff --git a/libcxx/docs/Cxx1zStatusIssuesStatus.csv b/libcxx/docs/Cxx1zStatusIssuesStatus.csv --- a/libcxx/docs/Cxx1zStatusIssuesStatus.csv +++ b/libcxx/docs/Cxx1zStatusIssuesStatus.csv @@ -300,7 +300,7 @@ "`2872 `__","Add definition for direct-non-list-initialization","Kona","|Complete|","" "`2873 `__","Add noexcept to several shared_ptr related functions","Kona","|Complete|","" "`2874 `__","Constructor ``shared_ptr::shared_ptr(Y*)``\ should be constrained","Kona","","" -"`2875 `__","shared_ptr::shared_ptr(Y\*, D, [|hellip|\ ]) constructors should be constrained","Kona","","" +"`2875 `__","shared_ptr::shared_ptr(Y\*, D, [|hellip|\ ]) constructors should be constrained","Kona","|Complete|","" "`2876 `__","``shared_ptr::shared_ptr(const weak_ptr&)``\ constructor should be constrained","Kona","","" "`2878 `__","Missing DefaultConstructible requirement for istream_iterator default constructor","Kona","|Complete|","" "`2890 `__","The definition of 'object state' applies only to class types","Kona","|Complete|","" diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -2620,6 +2620,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; +}; + #if defined(_LIBCPP_ABI_ENABLE_SHARED_PTR_TRIVIAL_ABI) # define _LIBCPP_SHARED_PTR_TRIVIAL_ABI __attribute__((trivial_abi)) #else @@ -2652,10 +2670,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; @@ -2921,7 +2939,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 @@ -2967,7 +2985,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/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/function_type_default_deleter.fail.cpp b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/function_type_default_deleter.fail.cpp --- a/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/function_type_default_deleter.fail.cpp +++ b/libcxx/test/libcxx/utilities/memory/util.smartptr/util.smartptr.shared/function_type_default_deleter.fail.cpp @@ -47,6 +47,7 @@ // expected-error-re@memory:* 2 {{static_assert failed{{.*}} "default_delete cannot be instantiated for function types"}} { SPtr<4> s4(getFn<4>()); // expected-note {{requested here}} + // expected-error@+1 {{no matching constructor for initialization of 'SPtr<5>'}} SPtr<5> s5(getFn<5>(), std::default_delete>{}); // expected-note {{requested here}} } 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, ""); + struct Base { }; struct Derived : Base { }; @@ -52,6 +68,12 @@ 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, ""); + // Make sure that we can construct a shared_ptr where the element type and pointer type // aren't "convertible" but are "compatible". static_assert(!std::is_constructible, Base[4], test_deleter >::value, ""); 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, ""); + struct Base { }; struct Derived : Base { }; @@ -96,6 +112,14 @@ #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, ""); + // Make sure that we can construct a shared_ptr where the element type and pointer type // aren't "convertible" but are "compatible". static_assert(!std::is_constructible,