//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++98, c++03 // <memory> // unique_ptr // Test unique_ptr converting move ctor #include <memory> #include <cassert> #include "test_macros.h" #include "unique_ptr_test_helper.h" #include "type_id.h" template <int ID = 0> struct GenericDeleter { void operator()(void*) const {} }; template <int ID = 0> struct GenericConvertingDeleter { template <int OID> GenericConvertingDeleter(GenericConvertingDeleter<OID>) {} template <int OID> GenericConvertingDeleter& operator=(GenericConvertingDeleter<OID> const&) { return *this; } void operator()(void*) const {} }; template <class T, class U> using EnableIfNotSame = typename std::enable_if< !std::is_same<typename std::decay<T>::type, typename std::decay<U>::type>::value >::type; template <class Templ, class Other> struct is_specialization; template <template <int> class Templ, int ID1, class Other> struct is_specialization<Templ<ID1>, Other> : std::false_type {}; template <template <int> class Templ, int ID1, int ID2> struct is_specialization<Templ<ID1>, Templ<ID2> > : std::true_type {}; template <class Templ, class Other> using EnableIfSpecialization = typename std::enable_if< is_specialization<Templ, typename std::decay<Other>::type >::value >::type; template <int ID> struct TrackingDeleter; template <int ID> struct ConstTrackingDeleter; template <int ID> struct TrackingDeleter { TrackingDeleter() : arg_type(&makeArgumentID<>()) {} TrackingDeleter(TrackingDeleter const&) : arg_type(&makeArgumentID<TrackingDeleter const&>()) {} TrackingDeleter(TrackingDeleter&&) : arg_type(&makeArgumentID<TrackingDeleter &&>()) {} template <class T, class = EnableIfSpecialization<TrackingDeleter, T> > TrackingDeleter(T&&) : arg_type(&makeArgumentID<T&&>()) {} TrackingDeleter& operator=(TrackingDeleter const&) { arg_type = &makeArgumentID<TrackingDeleter const&>(); return *this; } TrackingDeleter& operator=(TrackingDeleter &&) { arg_type = &makeArgumentID<TrackingDeleter &&>(); return *this; } template <class T, class = EnableIfSpecialization<TrackingDeleter, T> > TrackingDeleter& operator=(T&&) { arg_type = &makeArgumentID<T&&>(); return *this; } void operator()(void*) const {} public: TypeID const* reset() const { TypeID const* tmp = arg_type; arg_type = nullptr; return tmp; } mutable TypeID const* arg_type; }; template <int ID> struct ConstTrackingDeleter { ConstTrackingDeleter() : arg_type(&makeArgumentID<>()) {} ConstTrackingDeleter(ConstTrackingDeleter const&) : arg_type(&makeArgumentID<ConstTrackingDeleter const&>()) {} ConstTrackingDeleter(ConstTrackingDeleter&&) : arg_type(&makeArgumentID<ConstTrackingDeleter &&>()) {} template <class T, class = EnableIfSpecialization<ConstTrackingDeleter, T> > ConstTrackingDeleter(T&&) : arg_type(&makeArgumentID<T&&>()) {} const ConstTrackingDeleter& operator=(ConstTrackingDeleter const&) const { arg_type = &makeArgumentID<ConstTrackingDeleter const&>(); return *this; } const ConstTrackingDeleter& operator=(ConstTrackingDeleter &&) const { arg_type = &makeArgumentID<ConstTrackingDeleter &&>(); return *this; } template <class T, class = EnableIfSpecialization<ConstTrackingDeleter, T> > const ConstTrackingDeleter& operator=(T&&) const { arg_type = &makeArgumentID<T&&>(); return *this; } void operator()(void*) const {} public: TypeID const* reset() const { TypeID const* tmp = arg_type; arg_type = nullptr; return tmp; } mutable TypeID const* arg_type; }; template <class ExpectT, int ID> bool checkArg(TrackingDeleter<ID> const& d) { return d.arg_type && *d.arg_type == makeArgumentID<ExpectT>(); } template <class ExpectT, int ID> bool checkArg(ConstTrackingDeleter<ID> const& d) { return d.arg_type && *d.arg_type == makeArgumentID<ExpectT>(); } template <class From, bool AssignIsConst = false> struct AssignDeleter { AssignDeleter() = default; AssignDeleter(AssignDeleter const&) = default; AssignDeleter(AssignDeleter&&) = default; AssignDeleter& operator=(AssignDeleter const&) = delete; AssignDeleter& operator=(AssignDeleter &&) = delete; template <class T> AssignDeleter& operator=(T&&) && = delete; template <class T> AssignDeleter& operator=(T&&) const && = delete; template <class T, class = typename std::enable_if< std::is_same<T&&, From>::value && !AssignIsConst >::type> AssignDeleter& operator=(T&&) & { return *this; } template <class T, class = typename std::enable_if< std::is_same<T&&, From>::value && AssignIsConst >::type> const AssignDeleter& operator=(T&&) const & { return *this; } template <class T> void operator()(T) const {} }; template <class VT, class DDest, class DSource> void doDeleterTest() { using U1 = std::unique_ptr<VT, DDest>; using U2 = std::unique_ptr<VT, DSource>; static_assert(std::is_nothrow_assignable<U1, U2&&>::value, ""); typename std::decay<DDest>::type ddest; typename std::decay<DSource>::type dsource; U1 u1(nullptr, ddest); U2 u2(nullptr, dsource); u1 = std::move(u2); } template <bool IsArray> void test_sfinae() { typedef typename std::conditional<IsArray, A[], A>::type VT; { // Test that different non-reference deleter types are allowed so long // as they convert to each other. using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> >; using U2 = std::unique_ptr<VT, GenericConvertingDeleter<1> >; static_assert(std::is_assignable<U1, U2&&>::value, ""); } { // Test that different non-reference deleter types are disallowed when // they cannot convert. using U1 = std::unique_ptr<VT, GenericDeleter<0> >; using U2 = std::unique_ptr<VT, GenericDeleter<1> >; static_assert(!std::is_assignable<U1, U2&&>::value, ""); } { // Test that if the deleter assignment is not valid the assignment operator // SFINAEs. using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> const& >; using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> >; using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>; using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >; using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>; static_assert(!std::is_assignable<U1, U2&&>::value, ""); static_assert(!std::is_assignable<U1, U3&&>::value, ""); static_assert(!std::is_assignable<U1, U4&&>::value, ""); static_assert(!std::is_assignable<U1, U5&&>::value, ""); using U1C = std::unique_ptr<const VT, GenericConvertingDeleter<0> const&>; static_assert(std::is_nothrow_assignable<U1C, U1&&>::value, ""); } { // Test that if the deleter assignment is not valid the assignment operator // SFINAEs. using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> & >; using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> >; using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>; using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >; using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>; static_assert(std::is_nothrow_assignable<U1, U2&&>::value, ""); static_assert(std::is_nothrow_assignable<U1, U3&&>::value, ""); static_assert(std::is_nothrow_assignable<U1, U4&&>::value, ""); static_assert(std::is_nothrow_assignable<U1, U5&&>::value, ""); using U1C = std::unique_ptr<const VT, GenericConvertingDeleter<0> &>; static_assert(std::is_nothrow_assignable<U1C, U1&&>::value, ""); } { // Test that non-reference destination deleters can be assigned // from any source deleter type with a suitable conversion. Including // reference types. using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> >; using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>; using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> const &>; using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >; using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> &>; using U6 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>; static_assert(std::is_assignable<U1, U2&&>::value, ""); static_assert(std::is_assignable<U1, U3&&>::value, ""); static_assert(std::is_assignable<U1, U4&&>::value, ""); static_assert(std::is_assignable<U1, U5&&>::value, ""); static_assert(std::is_assignable<U1, U6&&>::value, ""); } ///////////////////////////////////////////////////////////////////////////// { using Del = GenericDeleter<0>; using AD = AssignDeleter<Del&&>; using ADC = AssignDeleter<Del&&, /*AllowConstAssign*/true>; doDeleterTest<VT, AD, Del>(); doDeleterTest<VT, AD&, Del>(); doDeleterTest<VT, ADC const&, Del>(); } { using Del = GenericDeleter<0>; using AD = AssignDeleter<Del&>; using ADC = AssignDeleter<Del&, /*AllowConstAssign*/true>; doDeleterTest<VT, AD, Del&>(); doDeleterTest<VT, AD&, Del&>(); doDeleterTest<VT, ADC const&, Del&>(); } { using Del = GenericDeleter<0>; using AD = AssignDeleter<Del const&>; using ADC = AssignDeleter<Del const&, /*AllowConstAssign*/true>; doDeleterTest<VT, AD, Del const&>(); doDeleterTest<VT, AD&, Del const&>(); doDeleterTest<VT, ADC const&, Del const&>(); } } template <bool IsArray> void test_noexcept() { typedef typename std::conditional<IsArray, A[], A>::type VT; { typedef std::unique_ptr<const VT> APtr; typedef std::unique_ptr<VT> BPtr; static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, ""); } { typedef std::unique_ptr<const VT, CDeleter<const VT> > APtr; typedef std::unique_ptr<VT, CDeleter<VT> > BPtr; static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, ""); } { typedef std::unique_ptr<const VT, NCDeleter<const VT>&> APtr; typedef std::unique_ptr<VT, NCDeleter<const VT>&> BPtr; static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, ""); } { typedef std::unique_ptr<const VT, const NCConstDeleter<const VT>&> APtr; typedef std::unique_ptr<VT, const NCConstDeleter<const VT>&> BPtr; static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, ""); } } template <bool IsArray> void test_deleter_value_category() { typedef typename std::conditional<IsArray, A[], A>::type VT; using TD1 = TrackingDeleter<1>; using TD2 = TrackingDeleter<2>; TD1 d1; TD2 d2; using CD1 = ConstTrackingDeleter<1>; using CD2 = ConstTrackingDeleter<2>; CD1 cd1; CD2 cd2; { // Test non-reference deleter conversions using U1 = std::unique_ptr<VT, TD1 >; using U2 = std::unique_ptr<VT, TD2 >; U1 u1; U2 u2; u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<TD2&&>(u1.get_deleter())); } { // Test assignment to non-const ref using U1 = std::unique_ptr<VT, TD1& >; using U2 = std::unique_ptr<VT, TD2 >; U1 u1(nullptr, d1); U2 u2; u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<TD2&&>(u1.get_deleter())); } { // Test assignment to const&. using U1 = std::unique_ptr<VT, CD1 const& >; using U2 = std::unique_ptr<VT, CD2 >; U1 u1(nullptr, cd1); U2 u2; u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<CD2&&>(u1.get_deleter())); } { // Test assignment from non-const ref using U1 = std::unique_ptr<VT, TD1 >; using U2 = std::unique_ptr<VT, TD2& >; U1 u1; U2 u2(nullptr, d2); u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<TD2&>(u1.get_deleter())); } { // Test assignment from const ref using U1 = std::unique_ptr<VT, TD1 >; using U2 = std::unique_ptr<VT, TD2 const& >; U1 u1; U2 u2(nullptr, d2); u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<TD2 const&>(u1.get_deleter())); } { // Test assignment from non-const ref using U1 = std::unique_ptr<VT, TD1& >; using U2 = std::unique_ptr<VT, TD2& >; U1 u1(nullptr, d1); U2 u2(nullptr, d2); u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<TD2&>(u1.get_deleter())); } { // Test assignment from const ref using U1 = std::unique_ptr<VT, TD1& >; using U2 = std::unique_ptr<VT, TD2 const& >; U1 u1(nullptr, d1); U2 u2(nullptr, d2); u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<TD2 const&>(u1.get_deleter())); } { // Test assignment from non-const ref using U1 = std::unique_ptr<VT, CD1 const& >; using U2 = std::unique_ptr<VT, CD2 & >; U1 u1(nullptr, cd1); U2 u2(nullptr, cd2); u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<CD2 &>(u1.get_deleter())); } { // Test assignment from const ref using U1 = std::unique_ptr<VT, CD1 const& >; using U2 = std::unique_ptr<VT, CD2 const& >; U1 u1(nullptr, cd1); U2 u2(nullptr, cd2); u1.get_deleter().reset(); u1 = std::move(u2); assert(checkArg<CD2 const&>(u1.get_deleter())); } } int main(int, char**) { { test_sfinae</*IsArray*/false>(); test_noexcept<false>(); test_deleter_value_category<false>(); } { test_sfinae</*IsArray*/true>(); test_noexcept<true>(); test_deleter_value_category<true>(); } return 0; }