From f3344569038591116cc3b5d775443a576d1b7f96 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Sat, 8 Aug 2015 14:07:34 +0300 Subject: [PATCH] Implement N4089 Safe conversions in unique_ptr (LWG 2118) and N4366 LWG 2228... 2015-08-08 Ville Voutilainen Implement N4089 Safe conversions in unique_ptr (LWG 2118) and N4366 LWG 2228: Missing SFINAE rule in unique_ptr templated assignment * include/bits/unique_ptr.h (__remove_cv, __is_derived_Tp): Remove. (default_delete::default_delete(const default_delete<_Up[]>)): Constrain with array convertibility. (default_delete::operator(_Up*)): Turn into a template, constrain with array convertibility. (__safe_conversion_up): New, single object version. (unique_ptr(unique_ptr<_Up, _Ep>&&)): Constrain with deleter convertibility. (unique_ptr::operator=(unique_ptr<_Up, _Ep>&&)): Likewise, and add is_assignable as a constraint. (__safe_conversion_up): Array version, renamed from __safe_conversion, updated to implement N4089. (__safe_conversion_raw): New. (unique_ptr(_Up)): Turn into a template, constrain with array convertibility. (unique_ptr(_Up, typename conditional::value, deleter_type, const deleter_type&>::type)): Likewise. (unique_ptr(_Up, typename remove_reference::type&&)): Likewise. (unique_ptr(unique_ptr<_Up, _Ep>&&)): Likewise. (operator=(unique_ptr<_Up, _Ep>&&)): Likewise, and add is_assignable as a constraint (array version). (reset(_Up)): Turn into a template, constrain with array convertibility. (reset(nullptr_t)): New. * testsuite/20_util/default_delete/48631_neg.cc: Adjust. * testsuite/20_util/unique_ptr/assign/48635.cc: Likewise. * testsuite/20_util/unique_ptr/assign/48635_neg.cc: Likewise. * testsuite/20_util/unique_ptr/assign/cv_qual.cc: Likewise. * testsuite/20_util/unique_ptr/cons/cv_qual.cc: Likewise. * testsuite/20_util/unique_ptr/dr2228.cc: New. * testsuite/20_util/unique_ptr/modifiers/cv_qual.cc: Adjust. From-SVN: r226733 --- libstdc++-v3/ChangeLog | 40 +++++ libstdc++-v3/include/bits/unique_ptr.h | 168 ++++++++++-------- .../20_util/default_delete/48631_neg.cc | 5 +- .../20_util/unique_ptr/assign/48635.cc | 8 - .../20_util/unique_ptr/assign/48635_neg.cc | 12 +- .../20_util/unique_ptr/assign/cv_qual.cc | 6 +- .../20_util/unique_ptr/cons/cv_qual.cc | 26 ++- .../testsuite/20_util/unique_ptr/dr2228.cc | 38 ++++ .../20_util/unique_ptr/modifiers/cv_qual.cc | 32 +++- 9 files changed, 233 insertions(+), 102 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/unique_ptr/dr2228.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 787a7a5768e..fc8b2b8a153 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,43 @@ +2015-08-08 Ville Voutilainen + + Implement N4089 Safe conversions in unique_ptr (LWG 2118) + and N4366 LWG 2228: Missing SFINAE rule in unique_ptr + templated assignment + * include/bits/unique_ptr.h + (__remove_cv, __is_derived_Tp): Remove. + (default_delete::default_delete(const default_delete<_Up[]>)): + Constrain with array convertibility. + (default_delete::operator(_Up*)): Turn into a template, + constrain with array convertibility. + (__safe_conversion_up): New, single object version. + (unique_ptr(unique_ptr<_Up, _Ep>&&)): Constrain with deleter + convertibility. + (unique_ptr::operator=(unique_ptr<_Up, _Ep>&&)): Likewise, and add + is_assignable as a constraint. + (__safe_conversion_up): Array version, renamed from __safe_conversion, + updated to implement N4089. + (__safe_conversion_raw): New. + (unique_ptr(_Up)): Turn into a template, constrain with array + convertibility. + (unique_ptr(_Up, + typename conditional::value, + deleter_type, const deleter_type&>::type)): Likewise. + (unique_ptr(_Up, typename + remove_reference::type&&)): Likewise. + (unique_ptr(unique_ptr<_Up, _Ep>&&)): Likewise. + (operator=(unique_ptr<_Up, _Ep>&&)): Likewise, and add + is_assignable as a constraint (array version). + (reset(_Up)): Turn into a template, constrain with array + convertibility. + (reset(nullptr_t)): New. + * testsuite/20_util/default_delete/48631_neg.cc: Adjust. + * testsuite/20_util/unique_ptr/assign/48635.cc: Likewise. + * testsuite/20_util/unique_ptr/assign/48635_neg.cc: Likewise. + * testsuite/20_util/unique_ptr/assign/cv_qual.cc: Likewise. + * testsuite/20_util/unique_ptr/cons/cv_qual.cc: Likewise. + * testsuite/20_util/unique_ptr/dr2228.cc: New. + * testsuite/20_util/unique_ptr/modifiers/cv_qual.cc: Adjust. + 2015-08-05 Nikolai Bozhenov * testsuite/20_util/enable_shared_from_this/cons/constexpr.cc: Remove diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h index 08ce01f7c7e..8ab55da75d3 100644 --- a/libstdc++-v3/include/bits/unique_ptr.h +++ b/libstdc++-v3/include/bits/unique_ptr.h @@ -83,16 +83,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template struct default_delete<_Tp[]> { - private: - template - using __remove_cv = typename remove_cv<_Up>::type; - - // Like is_base_of<_Tp, _Up> but false if unqualified types are the same - template - using __is_derived_Tp - = __and_< is_base_of<_Tp, _Up>, - __not_, __remove_cv<_Up>>> >; - public: /// Default constructor constexpr default_delete() noexcept = default; @@ -107,21 +97,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * pointer to the base type. */ template::value>::type> + enable_if::value>::type> default_delete(const default_delete<_Up[]>&) noexcept { } /// Calls @c delete[] @p __ptr - void - operator()(_Tp* __ptr) const + template + typename enable_if::value>::type + operator()(_Up* __ptr) const { static_assert(sizeof(_Tp)>0, "can't delete pointer to incomplete type"); delete [] __ptr; } - - template - typename enable_if<__is_derived_Tp<_Up>::value>::type - operator()(_Up*) const = delete; }; /// 20.7.1.2 unique_ptr for single objects. @@ -151,6 +138,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef _Tp element_type; typedef _Dp deleter_type; + + // helper template for detecting a safe conversion from another + // unique_ptr + template + using __safe_conversion_up = __and_< + is_convertible::pointer, pointer>, + __not_>, + __or_<__and_, + is_same>, + __and_<__not_>, + is_convertible<_Ep, deleter_type>> + > + >; + // Constructors. /// Default constructor, creates a unique_ptr that owns nothing. @@ -212,8 +213,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * and @p __u has a compatible deleter type. */ template::pointer, pointer>, - __not_>, + __safe_conversion_up<_Up, _Ep>, typename conditional::value, is_same<_Ep, _Dp>, is_convertible<_Ep, _Dp>>::type>> @@ -261,11 +261,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * Invokes the deleter first if this object owns a pointer. */ template - typename enable_if< __and_< - is_convertible::pointer, pointer>, - __not_> - >::value, - unique_ptr&>::type + typename enable_if< __and_< + __safe_conversion_up<_Up, _Ep>, + is_assignable + >::value, + unique_ptr&>::type operator=(unique_ptr<_Up, _Ep>&& __u) noexcept { reset(__u.release()); @@ -391,23 +391,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION = __and_< is_base_of<_Tp, _Up>, __not_, __remove_cv<_Up>>> >; - template::pointer> - using __safe_conversion = __and_< - is_convertible<_Up_pointer, _Tp_pointer>, - is_array<_Up>, - __or_<__not_>, - __not_>, - __not_<__is_derived_Tp::type>> - > - >; public: typedef typename _Pointer::type pointer; typedef _Tp element_type; typedef _Dp deleter_type; + // helper template for detecting a safe conversion from another + // unique_ptr + template, + typename _Up_element_type = typename _Up_up::element_type> + using __safe_conversion_up = __and_< + is_array<_Up>, + is_same, + is_same, + is_convertible<_Up_element_type(*)[], element_type(*)[]>, + __or_<__and_, is_same>, + __and_<__not_>, + is_convertible<_Ep, deleter_type>>> + >; + + // helper template for detecting a safe conversion from a raw pointer + template + using __safe_conversion_raw = __and_< + __or_<__or_, + is_same<_Up, nullptr_t>>, + __and_, + is_same, + is_convertible< + typename remove_pointer<_Up>::type(*)[], + element_type(*)[]> + > + > + >; + // Constructors. /// Default constructor, creates a unique_ptr that owns nothing. @@ -418,42 +436,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /** Takes ownership of a pointer. * - * @param __p A pointer to an array of @c element_type + * @param __p A pointer to an array of a type safely convertible + * to an array of @c element_type * * The deleter will be value-initialized. */ + template::value, bool>::type> explicit - unique_ptr(pointer __p) noexcept + unique_ptr(_Up __p) noexcept : _M_t(__p, deleter_type()) { static_assert(!is_pointer::value, "constructed with null function pointer deleter"); } - // Disable construction from convertible pointer types. - template, - is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>> - explicit - unique_ptr(_Up* __p) = delete; - /** Takes ownership of a pointer. * - * @param __p A pointer to an array of @c element_type + * @param __p A pointer to an array of a type safely convertible + * to an array of @c element_type * @param __d A reference to a deleter. * * The deleter will be initialized with @p __d */ - unique_ptr(pointer __p, - typename conditional::value, - deleter_type, const deleter_type&>::type __d) noexcept + template::value, bool>::type> + unique_ptr(_Up __p, + typename conditional::value, + deleter_type, const deleter_type&>::type __d) noexcept : _M_t(__p, __d) { } /** Takes ownership of a pointer. * - * @param __p A pointer to an array of @c element_type + * @param __p A pointer to an array of a type safely convertible + * to an array of @c element_type * @param __d A reference to a deleter. * * The deleter will be initialized with @p std::move(__d) */ - unique_ptr(pointer __p, typename + template::value, bool>::type> + unique_ptr(_Up __p, typename remove_reference::type&& __d) noexcept : _M_t(std::move(__p), std::move(__d)) { static_assert(!is_reference::value, @@ -467,11 +491,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { } template, - typename conditional::value, - is_same<_Ep, _Dp>, - is_convertible<_Ep, _Dp>>::type - >> + typename = _Require<__safe_conversion_up<_Up, _Ep>>> unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) { } @@ -510,7 +530,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION */ template typename - enable_if<__safe_conversion<_Up, _Ep>::value, unique_ptr&>::type + enable_if<__and_<__safe_conversion_up<_Up, _Ep>, + is_assignable + >::value, + unique_ptr&>::type operator=(unique_ptr<_Up, _Ep>&& __u) noexcept { reset(__u.release()); @@ -572,8 +595,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * * The deleter will be invoked if a pointer is already owned. */ + template , + __and_, + is_pointer<_Up>, + is_convertible< + typename remove_pointer<_Up>::type(*)[], + element_type(*)[] + > + > + > + >> void - reset(pointer __p = pointer()) noexcept + reset(_Up __p) noexcept { using std::swap; swap(std::get<0>(_M_t), __p); @@ -581,10 +616,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION get_deleter()(__p); } - // Disable resetting from convertible pointer types. - template, - is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>> - void reset(_Up*) = delete; + void reset(nullptr_t = nullptr) noexcept + { + reset(pointer()); + } /// Exchange the pointer and deleter with another object. void @@ -597,19 +632,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Disable copy from lvalue. unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete; - - // Disable construction from convertible pointer types. - template, - is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>> - unique_ptr(_Up*, typename - conditional::value, - deleter_type, const deleter_type&>::type) = delete; - - // Disable construction from convertible pointer types. - template, - is_convertible<_Up*, pointer>, __is_derived_Tp<_Up>>> - unique_ptr(_Up*, typename - remove_reference::type&&) = delete; }; template diff --git a/libstdc++-v3/testsuite/20_util/default_delete/48631_neg.cc b/libstdc++-v3/testsuite/20_util/default_delete/48631_neg.cc index aed2cf47da5..6ee12787271 100644 --- a/libstdc++-v3/testsuite/20_util/default_delete/48631_neg.cc +++ b/libstdc++-v3/testsuite/20_util/default_delete/48631_neg.cc @@ -26,6 +26,5 @@ struct D : B { }; // libstdc++/48631 D d; std::default_delete db; -typedef decltype(db(&d)) type; // { dg-error "use of deleted function" } -// { dg-prune-output "declared" } -// { dg-prune-output "invalid" } +typedef decltype(db(&d)) type; // { dg-error "no match" } +// { dg-error "no type" "" { target *-*-* } 106 } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635.cc index 9014a32e606..37d6c36ecb1 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635.cc @@ -59,16 +59,8 @@ void test01() DDeleter dd; - std::unique_ptr p1t(nullptr, dd); - std::unique_ptr p2t(nullptr, d); - p2t = std::move(p1t); - std::unique_ptr p1a(nullptr, d), p2a(nullptr, d); p2a = std::move(p1a); - - std::unique_ptr p1at(nullptr, dd); - std::unique_ptr p2at(nullptr, d); - p2at = std::move(p1at); } int main() diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc index 185f39c093a..68be4c41e70 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/assign/48635_neg.cc @@ -24,7 +24,7 @@ struct D; struct B { - B& operator=(D&) = delete; // { dg-error "declared here" } + B& operator=(D&) = delete; template void operator()(T*) const {} @@ -39,12 +39,14 @@ void f() D d; std::unique_ptr ub(nullptr, b); + std::unique_ptr ub2(nullptr, b); std::unique_ptr ud(nullptr, d); - ub = std::move(ud); -// { dg-error "use of deleted function" "" { target *-*-* } 272 } + ub = std::move(ud); // { dg-error "no match" } + ub2 = ud; // { dg-error "no match" } +// { dg-error "no type" "" { target *-*-* } 269 } std::unique_ptr uba(nullptr, b); std::unique_ptr uda(nullptr, d); - uba = std::move(uda); -// { dg-error "use of deleted function" "" { target *-*-* } 517 } + uba = std::move(uda); // { dg-error "no match" } +// { dg-error "no type" "" { target *-*-* } 537 } } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/assign/cv_qual.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/assign/cv_qual.cc index 0938377806d..da73fccacec 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/assign/cv_qual.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/assign/cv_qual.cc @@ -82,8 +82,10 @@ struct deleter void test04() { - // Allow conversions from user-defined pointer-like types + // Disallow conversions from incompatible deleter std::unique_ptr> p; std::unique_ptr> upA; - upA = std::move(p); + upA = std::move(p); // { dg-error "no match" } + // { dg-error "no type" "" { target *-*-* } 537 } + // { dg-error "no matching function" "" { target *-*-* } 614 } } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual.cc index f7d160635ed..f399b7cb2a0 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/cv_qual.cc @@ -88,11 +88,25 @@ void test07() { // Allow conversions from user-defined pointer-like types + // for the single-object version A_pointer p; - std::unique_ptr upA(p); - std::unique_ptr cA(p); - std::unique_ptr vA(p); - std::unique_ptr cvA(p); + std::unique_ptr upA(p); + std::unique_ptr cA(p); + std::unique_ptr vA(p); + std::unique_ptr cvA(p); + // Allow conversions from user-defined pointer-like types + // for the array version when the type is converted explicitly + std::unique_ptr upA2((A*)p); + std::unique_ptr cA2((A*)p); + std::unique_ptr vA2((A*)p); + std::unique_ptr cvA2((A*)p); + // Disallow conversions from user-defined pointer-like types + // for the array version + std::unique_ptr upA3(p); // { dg-error "no matching function" } + std::unique_ptr cA3(p); // { dg-error "no matching function" } + std::unique_ptr vA3(p); // { dg-error "no matching function" } + std::unique_ptr cvA3(p); // { dg-error "no matching function" } + // { dg-error "no type" "" { target *-*-* } 445 } } template @@ -108,8 +122,8 @@ struct deleter void test08() { - // Allow conversions from user-defined pointer-like types + // Disallow conversions from non-assignable deleter std::unique_ptr> p; - std::unique_ptr> upA(std::move(p)); + std::unique_ptr> upA(std::move(p)); // { dg-error "no matching function" } } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/dr2228.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/dr2228.cc new file mode 100644 index 00000000000..ae996daad2d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/dr2228.cc @@ -0,0 +1,38 @@ +// { dg-options "-std=gnu++11" } +// { dg-do compile } + +// Copyright (C) 2015 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +#include +#include + +struct do_nothing +{ + template + void operator()(T*) {} +}; + +int +main() +{ + int i = 0; + std::unique_ptr p1(&i); + std::unique_ptr p2; + static_assert(!std::is_assignable::value, ""); + +} diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/cv_qual.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/cv_qual.cc index eaa7d43da39..4ae2f9de48b 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/cv_qual.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/cv_qual.cc @@ -66,14 +66,36 @@ struct A_pointer { operator A*() const { return nullptr; } }; void test07() { - // Allow conversions from user-defined pointer-like types A_pointer p; - std::unique_ptr upA; + // Allow conversions from user-defined pointer-like types + // for the single-object version + std::unique_ptr upA; upA.reset(p); - std::unique_ptr cA; + std::unique_ptr cA; cA.reset(p); - std::unique_ptr vA; + std::unique_ptr vA; vA.reset(p); - std::unique_ptr cvA; + std::unique_ptr cvA; cvA.reset(p); + // Allow conversions from user-defined pointer-like types + // for the array version when the type is converted explicitly + std::unique_ptr upA2; + upA2.reset((A*)p); + std::unique_ptr cA2; + cA2.reset((A*)p); + std::unique_ptr vA2; + vA2.reset((A*)p); + std::unique_ptr cvA2; + cvA2.reset((A*)p); + // Disallow conversions from user-defined pointer-like types + // for the array version + std::unique_ptr upA3; + upA3.reset(p); // { dg-error "no matching function" } + std::unique_ptr cA3; + cA3.reset(p); // { dg-error "no matching function" } + std::unique_ptr vA3; + vA3.reset(p); // { dg-error "no matching function" } + std::unique_ptr cvA3; + cvA3.reset(p); // { dg-error "no matching function" } + // { dg-error "no matching function" "" { target *-*-* } 614 } } -- 2.30.2