431 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++0310 11// <memory>12 13// unique_ptr14 15// Test unique_ptr converting move ctor16 17#include <memory>18#include <cassert>19 20#include "test_macros.h"21#include "unique_ptr_test_helper.h"22#include "type_id.h"23 24template <int ID = 0>25struct GenericDeleter {26 TEST_CONSTEXPR_CXX23 void operator()(void*) const {}27};28 29template <int ID = 0>30struct GenericConvertingDeleter {31 template <int OID>32 TEST_CONSTEXPR_CXX23 GenericConvertingDeleter(GenericConvertingDeleter<OID>) {}33 34 template <int OID>35 TEST_CONSTEXPR_CXX23 GenericConvertingDeleter& operator=(GenericConvertingDeleter<OID> const&) {36 return *this;37 }38 39 TEST_CONSTEXPR_CXX23 void operator()(void*) const {}40};41 42template <class T, class U>43using EnableIfNotSame = typename std::enable_if<44 !std::is_same<typename std::decay<T>::type, typename std::decay<U>::type>::value45>::type;46 47template <class Templ, class Other>48struct is_specialization;49 50template <template <int> class Templ, int ID1, class Other>51struct is_specialization<Templ<ID1>, Other> : std::false_type {};52 53template <template <int> class Templ, int ID1, int ID2>54struct is_specialization<Templ<ID1>, Templ<ID2> > : std::true_type {};55 56template <class Templ, class Other>57using EnableIfSpecialization = typename std::enable_if<58 is_specialization<Templ, typename std::decay<Other>::type >::value59 >::type;60 61template <int ID> struct TrackingDeleter;62template <int ID> struct ConstTrackingDeleter;63 64template <int ID>65struct TrackingDeleter {66 TrackingDeleter() : arg_type(&makeArgumentID<>()) {}67 68 TrackingDeleter(TrackingDeleter const&)69 : arg_type(&makeArgumentID<TrackingDeleter const&>()) {}70 71 TrackingDeleter(TrackingDeleter&&)72 : arg_type(&makeArgumentID<TrackingDeleter &&>()) {}73 74 template <class T, class = EnableIfSpecialization<TrackingDeleter, T> >75 TrackingDeleter(T&&) : arg_type(&makeArgumentID<T&&>()) {}76 77 TrackingDeleter& operator=(TrackingDeleter const&) {78 arg_type = &makeArgumentID<TrackingDeleter const&>();79 return *this;80 }81 82 TrackingDeleter& operator=(TrackingDeleter &&) {83 arg_type = &makeArgumentID<TrackingDeleter &&>();84 return *this;85 }86 87 template <class T, class = EnableIfSpecialization<TrackingDeleter, T> >88 TrackingDeleter& operator=(T&&) {89 arg_type = &makeArgumentID<T&&>();90 return *this;91 }92 93 void operator()(void*) const {}94 95public:96 TypeID const* reset() const {97 TypeID const* tmp = arg_type;98 arg_type = nullptr;99 return tmp;100 }101 102 mutable TypeID const* arg_type;103};104 105template <int ID>106struct ConstTrackingDeleter {107 ConstTrackingDeleter() : arg_type(&makeArgumentID<>()) {}108 109 ConstTrackingDeleter(ConstTrackingDeleter const&)110 : arg_type(&makeArgumentID<ConstTrackingDeleter const&>()) {}111 112 ConstTrackingDeleter(ConstTrackingDeleter&&)113 : arg_type(&makeArgumentID<ConstTrackingDeleter &&>()) {}114 115 template <class T, class = EnableIfSpecialization<ConstTrackingDeleter, T> >116 ConstTrackingDeleter(T&&) : arg_type(&makeArgumentID<T&&>()) {}117 118 const ConstTrackingDeleter& operator=(ConstTrackingDeleter const&) const {119 arg_type = &makeArgumentID<ConstTrackingDeleter const&>();120 return *this;121 }122 123 const ConstTrackingDeleter& operator=(ConstTrackingDeleter &&) const {124 arg_type = &makeArgumentID<ConstTrackingDeleter &&>();125 return *this;126 }127 128 template <class T, class = EnableIfSpecialization<ConstTrackingDeleter, T> >129 const ConstTrackingDeleter& operator=(T&&) const {130 arg_type = &makeArgumentID<T&&>();131 return *this;132 }133 134 void operator()(void*) const {}135 136public:137 TypeID const* reset() const {138 TypeID const* tmp = arg_type;139 arg_type = nullptr;140 return tmp;141 }142 143 mutable TypeID const* arg_type;144};145 146template <class ExpectT, int ID>147bool checkArg(TrackingDeleter<ID> const& d) {148 return d.arg_type && *d.arg_type == makeArgumentID<ExpectT>();149}150 151template <class ExpectT, int ID>152bool checkArg(ConstTrackingDeleter<ID> const& d) {153 return d.arg_type && *d.arg_type == makeArgumentID<ExpectT>();154}155 156template <class From, bool AssignIsConst = false>157struct AssignDeleter {158 TEST_CONSTEXPR_CXX23 AssignDeleter() = default;159 TEST_CONSTEXPR_CXX23 AssignDeleter(AssignDeleter const&) = default;160 TEST_CONSTEXPR_CXX23 AssignDeleter(AssignDeleter&&) = default;161 162 AssignDeleter& operator=(AssignDeleter const&) = delete;163 AssignDeleter& operator=(AssignDeleter &&) = delete;164 165 template <class T> AssignDeleter& operator=(T&&) && = delete;166 template <class T> AssignDeleter& operator=(T&&) const && = delete;167 168 template <class T, class = typename std::enable_if< std::is_same<T&&, From>::value && !AssignIsConst >::type>169 TEST_CONSTEXPR_CXX23 AssignDeleter& operator=(T&&) & {170 return *this;171 }172 173 template <class T, class = typename std::enable_if< std::is_same<T&&, From>::value && AssignIsConst >::type>174 TEST_CONSTEXPR_CXX23 const AssignDeleter& operator=(T&&) const& {175 return *this;176 }177 178 template <class T>179 TEST_CONSTEXPR_CXX23 void operator()(T) const {}180};181 182template <class VT, class DDest, class DSource>183TEST_CONSTEXPR_CXX23 void doDeleterTest() {184 using U1 = std::unique_ptr<VT, DDest>;185 using U2 = std::unique_ptr<VT, DSource>;186 static_assert(std::is_nothrow_assignable<U1, U2&&>::value, "");187 typename std::decay<DDest>::type ddest;188 typename std::decay<DSource>::type dsource;189 U1 u1(nullptr, ddest);190 U2 u2(nullptr, dsource);191 u1 = std::move(u2);192}193 194template <bool IsArray>195TEST_CONSTEXPR_CXX23 void test_sfinae() {196 typedef typename std::conditional<IsArray, A[], A>::type VT;197 198 { // Test that different non-reference deleter types are allowed so long199 // as they convert to each other.200 using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> >;201 using U2 = std::unique_ptr<VT, GenericConvertingDeleter<1> >;202 static_assert(std::is_assignable<U1, U2&&>::value, "");203 }204 { // Test that different non-reference deleter types are disallowed when205 // they cannot convert.206 using U1 = std::unique_ptr<VT, GenericDeleter<0> >;207 using U2 = std::unique_ptr<VT, GenericDeleter<1> >;208 static_assert(!std::is_assignable<U1, U2&&>::value, "");209 }210 { // Test that if the deleter assignment is not valid the assignment operator211 // SFINAEs.212 using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> const& >;213 using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> >;214 using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>;215 using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >;216 using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>;217 static_assert(!std::is_assignable<U1, U2&&>::value, "");218 static_assert(!std::is_assignable<U1, U3&&>::value, "");219 static_assert(!std::is_assignable<U1, U4&&>::value, "");220 static_assert(!std::is_assignable<U1, U5&&>::value, "");221 222 using U1C = std::unique_ptr<const VT, GenericConvertingDeleter<0> const&>;223 static_assert(std::is_nothrow_assignable<U1C, U1&&>::value, "");224 }225 { // Test that if the deleter assignment is not valid the assignment operator226 // SFINAEs.227 using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> & >;228 using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> >;229 using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>;230 using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >;231 using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>;232 233 static_assert(std::is_nothrow_assignable<U1, U2&&>::value, "");234 static_assert(std::is_nothrow_assignable<U1, U3&&>::value, "");235 static_assert(std::is_nothrow_assignable<U1, U4&&>::value, "");236 static_assert(std::is_nothrow_assignable<U1, U5&&>::value, "");237 238 using U1C = std::unique_ptr<const VT, GenericConvertingDeleter<0> &>;239 static_assert(std::is_nothrow_assignable<U1C, U1&&>::value, "");240 }241 { // Test that non-reference destination deleters can be assigned242 // from any source deleter type with a suitable conversion. Including243 // reference types.244 using U1 = std::unique_ptr<VT, GenericConvertingDeleter<0> >;245 using U2 = std::unique_ptr<VT, GenericConvertingDeleter<0> &>;246 using U3 = std::unique_ptr<VT, GenericConvertingDeleter<0> const &>;247 using U4 = std::unique_ptr<VT, GenericConvertingDeleter<1> >;248 using U5 = std::unique_ptr<VT, GenericConvertingDeleter<1> &>;249 using U6 = std::unique_ptr<VT, GenericConvertingDeleter<1> const&>;250 static_assert(std::is_assignable<U1, U2&&>::value, "");251 static_assert(std::is_assignable<U1, U3&&>::value, "");252 static_assert(std::is_assignable<U1, U4&&>::value, "");253 static_assert(std::is_assignable<U1, U5&&>::value, "");254 static_assert(std::is_assignable<U1, U6&&>::value, "");255 }256 /////////////////////////////////////////////////////////////////////////////257 {258 using Del = GenericDeleter<0>;259 using AD = AssignDeleter<Del&&>;260 using ADC = AssignDeleter<Del&&, /*AllowConstAssign*/true>;261 doDeleterTest<VT, AD, Del>();262 doDeleterTest<VT, AD&, Del>();263 doDeleterTest<VT, ADC const&, Del>();264 }265 {266 using Del = GenericDeleter<0>;267 using AD = AssignDeleter<Del&>;268 using ADC = AssignDeleter<Del&, /*AllowConstAssign*/true>;269 doDeleterTest<VT, AD, Del&>();270 doDeleterTest<VT, AD&, Del&>();271 doDeleterTest<VT, ADC const&, Del&>();272 }273 {274 using Del = GenericDeleter<0>;275 using AD = AssignDeleter<Del const&>;276 using ADC = AssignDeleter<Del const&, /*AllowConstAssign*/true>;277 doDeleterTest<VT, AD, Del const&>();278 doDeleterTest<VT, AD&, Del const&>();279 doDeleterTest<VT, ADC const&, Del const&>();280 }281}282 283template <bool IsArray>284TEST_CONSTEXPR_CXX23 void test_noexcept() {285 typedef typename std::conditional<IsArray, A[], A>::type VT;286 {287 typedef std::unique_ptr<const VT> APtr;288 typedef std::unique_ptr<VT> BPtr;289 static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");290 }291 {292 typedef std::unique_ptr<const VT, CDeleter<const VT> > APtr;293 typedef std::unique_ptr<VT, CDeleter<VT> > BPtr;294 static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");295 }296 {297 typedef std::unique_ptr<const VT, NCDeleter<const VT>&> APtr;298 typedef std::unique_ptr<VT, NCDeleter<const VT>&> BPtr;299 static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");300 }301 {302 typedef std::unique_ptr<const VT, const NCConstDeleter<const VT>&> APtr;303 typedef std::unique_ptr<VT, const NCConstDeleter<const VT>&> BPtr;304 static_assert(std::is_nothrow_assignable<APtr, BPtr>::value, "");305 }306}307 308template <bool IsArray>309void test_deleter_value_category() {310 typedef typename std::conditional<IsArray, A[], A>::type VT;311 using TD1 = TrackingDeleter<1>;312 using TD2 = TrackingDeleter<2>;313 TD1 d1;314 TD2 d2;315 using CD1 = ConstTrackingDeleter<1>;316 using CD2 = ConstTrackingDeleter<2>;317 CD1 cd1;318 CD2 cd2;319 320 { // Test non-reference deleter conversions321 using U1 = std::unique_ptr<VT, TD1 >;322 using U2 = std::unique_ptr<VT, TD2 >;323 U1 u1;324 U2 u2;325 u1.get_deleter().reset();326 u1 = std::move(u2);327 assert(checkArg<TD2&&>(u1.get_deleter()));328 }329 { // Test assignment to non-const ref330 using U1 = std::unique_ptr<VT, TD1& >;331 using U2 = std::unique_ptr<VT, TD2 >;332 U1 u1(nullptr, d1);333 U2 u2;334 u1.get_deleter().reset();335 u1 = std::move(u2);336 assert(checkArg<TD2&&>(u1.get_deleter()));337 }338 { // Test assignment to const&.339 using U1 = std::unique_ptr<VT, CD1 const& >;340 using U2 = std::unique_ptr<VT, CD2 >;341 U1 u1(nullptr, cd1);342 U2 u2;343 u1.get_deleter().reset();344 u1 = std::move(u2);345 assert(checkArg<CD2&&>(u1.get_deleter()));346 }347 348 { // Test assignment from non-const ref349 using U1 = std::unique_ptr<VT, TD1 >;350 using U2 = std::unique_ptr<VT, TD2& >;351 U1 u1;352 U2 u2(nullptr, d2);353 u1.get_deleter().reset();354 u1 = std::move(u2);355 assert(checkArg<TD2&>(u1.get_deleter()));356 }357 { // Test assignment from const ref358 using U1 = std::unique_ptr<VT, TD1 >;359 using U2 = std::unique_ptr<VT, TD2 const& >;360 U1 u1;361 U2 u2(nullptr, d2);362 u1.get_deleter().reset();363 u1 = std::move(u2);364 assert(checkArg<TD2 const&>(u1.get_deleter()));365 }366 367 { // Test assignment from non-const ref368 using U1 = std::unique_ptr<VT, TD1& >;369 using U2 = std::unique_ptr<VT, TD2& >;370 U1 u1(nullptr, d1);371 U2 u2(nullptr, d2);372 u1.get_deleter().reset();373 u1 = std::move(u2);374 assert(checkArg<TD2&>(u1.get_deleter()));375 }376 { // Test assignment from const ref377 using U1 = std::unique_ptr<VT, TD1& >;378 using U2 = std::unique_ptr<VT, TD2 const& >;379 U1 u1(nullptr, d1);380 U2 u2(nullptr, d2);381 u1.get_deleter().reset();382 u1 = std::move(u2);383 assert(checkArg<TD2 const&>(u1.get_deleter()));384 }385 386 { // Test assignment from non-const ref387 using U1 = std::unique_ptr<VT, CD1 const& >;388 using U2 = std::unique_ptr<VT, CD2 & >;389 U1 u1(nullptr, cd1);390 U2 u2(nullptr, cd2);391 u1.get_deleter().reset();392 u1 = std::move(u2);393 assert(checkArg<CD2 &>(u1.get_deleter()));394 }395 { // Test assignment from const ref396 using U1 = std::unique_ptr<VT, CD1 const& >;397 using U2 = std::unique_ptr<VT, CD2 const& >;398 U1 u1(nullptr, cd1);399 U2 u2(nullptr, cd2);400 u1.get_deleter().reset();401 u1 = std::move(u2);402 assert(checkArg<CD2 const&>(u1.get_deleter()));403 }404}405 406TEST_CONSTEXPR_CXX23 bool test() {407 {408 test_sfinae</*IsArray*/false>();409 test_noexcept<false>();410 if (!TEST_IS_CONSTANT_EVALUATED)411 test_deleter_value_category<false>();412 }413 {414 test_sfinae</*IsArray*/true>();415 test_noexcept<true>();416 if (!TEST_IS_CONSTANT_EVALUATED)417 test_deleter_value_category<true>();418 }419 420 return true;421}422 423int main(int, char**) {424 test();425#if TEST_STD_VER >= 23426 static_assert(test());427#endif428 429 return 0;430}431