On Tue, Jun 10, 2025 at 12:37 PM Jonathan Wakely <jwak...@redhat.com> wrote:
> By using std::is_trivially_destructible instead of the old > __has_trivial_destructor built-in we no longer need the static_assert to > deal with types with deleted destructors. All non-destructible types, > including those with deleted destructors, will now give user-friendly > diagnostics that clearly explain the problem. > > Also combine the _Destroy_aux and _Destroy_n_aux class templates used > for C++98 into one, so that we perform fewer expensive class template > instantiations. > > libstdc++-v3/ChangeLog: > > PR libstdc++/120390 > * include/bits/stl_construct.h (_Destroy_aux::__destroy_n): New > static member function. > (_Destroy_aux<true>::__destroy_n): Likewise. > (_Destroy_n_aux): Remove. > (_Destroy(ForwardIterator, ForwardIterator)): Remove > static_assert. Use is_trivially_destructible instead of > __has_trivial_destructor. > (_Destroy_n): Likewise. Use _Destroy_aux::__destroy_n instead of > _Destroy_n_aux::__destroy_n. > * > testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc: > Adjust dg-error strings. Move destroy_n tests to ... > * > testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_n_neg.cc: > New test. > * testsuite/23_containers/vector/cons/destructible_debug_neg.cc: > Adjust dg-error strings. > * testsuite/23_containers/vector/cons/destructible_neg.cc: > Likewise. > --- > > Tested x86_64-linux. > LGTM. Not directly related to the patch, but I have noticed that for c++98, for trivially destructible types we make sure that we traverse range (we call advance) however in C++11+ mode we do not do that. Something changed in standard? > > libstdc++-v3/include/bits/stl_construct.h | 56 +++++++----------- > .../memory_management_tools/destroy_n_neg.cc | 59 +++++++++++++++++++ > .../memory_management_tools/destroy_neg.cc | 20 +++++-- > .../vector/cons/destructible_debug_neg.cc | 7 +-- > .../vector/cons/destructible_neg.cc | 7 +-- > 5 files changed, 98 insertions(+), 51 deletions(-) > create mode 100644 > libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_n_neg.cc > > diff --git a/libstdc++-v3/include/bits/stl_construct.h > b/libstdc++-v3/include/bits/stl_construct.h > index 23b8fb754710..3ac75715e0e0 100644 > --- a/libstdc++-v3/include/bits/stl_construct.h > +++ b/libstdc++-v3/include/bits/stl_construct.h > @@ -181,6 +181,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > for (; __first != __last; ++__first) > std::_Destroy(std::__addressof(*__first)); > } > + > + template<typename _ForwardIterator, typename _Size> > + static _GLIBCXX20_CONSTEXPR _ForwardIterator > + __destroy_n(_ForwardIterator __first, _Size __count) > + { > + for (; __count > 0; (void)++__first, --__count) > + std::_Destroy(std::__addressof(*__first)); > + return __first; > + } > }; > > template<> > @@ -189,6 +198,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > template<typename _ForwardIterator> > static void > __destroy(_ForwardIterator, _ForwardIterator) { } > + > + template<typename _ForwardIterator, typename _Size> > + static _ForwardIterator > + __destroy_n(_ForwardIterator __first, _Size __count) > + { > + std::advance(__first, __count); > + return __first; > + } > }; > #endif > > @@ -204,10 +221,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > typedef typename iterator_traits<_ForwardIterator>::value_type > _Value_type; > #if __cplusplus >= 201103L > - // A deleted destructor is trivial, this ensures we reject such > types: > - static_assert(is_destructible<_Value_type>::value, > - "value type is destructible"); > - if constexpr (!__has_trivial_destructor(_Value_type)) > + if constexpr (!is_trivially_destructible<_Value_type>::value) > for (; __first != __last; ++__first) > std::_Destroy(std::__addressof(*__first)); > #if __cpp_constexpr_dynamic_alloc // >= C++20 > @@ -221,33 +235,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif > } > > -#if __cplusplus < 201103L > - template<bool> > - struct _Destroy_n_aux > - { > - template<typename _ForwardIterator, typename _Size> > - static _GLIBCXX20_CONSTEXPR _ForwardIterator > - __destroy_n(_ForwardIterator __first, _Size __count) > - { > - for (; __count > 0; (void)++__first, --__count) > - std::_Destroy(std::__addressof(*__first)); > - return __first; > - } > - }; > - > - template<> > - struct _Destroy_n_aux<true> > - { > - template<typename _ForwardIterator, typename _Size> > - static _ForwardIterator > - __destroy_n(_ForwardIterator __first, _Size __count) > - { > - std::advance(__first, __count); > - return __first; > - } > - }; > -#endif > - > /** > * Destroy a range of objects. If the value_type of the object has > * a trivial destructor, the compiler should optimize all of this > @@ -260,10 +247,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > typedef typename iterator_traits<_ForwardIterator>::value_type > _Value_type; > #if __cplusplus >= 201103L > - // A deleted destructor is trivial, this ensures we reject such > types: > - static_assert(is_destructible<_Value_type>::value, > - "value type is destructible"); > - if constexpr (!__has_trivial_destructor(_Value_type)) > + if constexpr (!is_trivially_destructible<_Value_type>::value) > for (; __count > 0; (void)++__first, --__count) > std::_Destroy(std::__addressof(*__first)); > #if __cpp_constexpr_dynamic_alloc // >= C++20 > @@ -275,7 +259,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > std::advance(__first, __count); > return __first; > #else > - return std::_Destroy_n_aux<__has_trivial_destructor(_Value_type)>:: > + return std::_Destroy_aux<__has_trivial_destructor(_Value_type)>:: > __destroy_n(__first, __count); > #endif > } > diff --git > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_n_neg.cc > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_n_neg.cc > new file mode 100644 > index 000000000000..12c0dc575ccf > --- /dev/null > +++ > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_n_neg.cc > @@ -0,0 +1,59 @@ > +// Copyright (C) 2017-2025 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 > +// <http://www.gnu.org/licenses/>. > + > +// { dg-do compile { target c++17 } } > + > +#include <memory> > + > +// This has a trivial destructor, but should not be destructible! > +struct DeletedDtor { > + ~DeletedDtor() = delete; > +}; > + > +void > +test01() > +{ > + alignas(DeletedDtor) unsigned char buf[sizeof(DeletedDtor)]; > + auto p = ::new (buf) DeletedDtor(); > + std::destroy_n(p, 1); > +} > + > +class PrivateDtor { > + ~PrivateDtor() { } > +}; > + > +void > +test02() > +{ > + alignas(PrivateDtor) unsigned char buf[sizeof(PrivateDtor)]; > + auto p = ::new (buf) PrivateDtor(); > + std::destroy_n(p, 1); > +} > + > +#if __cpp_constexpr_dynamic_alloc // >= C++20 > +consteval bool > +test03() > +{ > + DeletedDtor* p = nullptr; > + std::destroy_n(p, 0); > + return true; > +} > +static_assert(test03()); > +#endif > + > +// { dg-error "deleted function .*DeletedDtor" "" { target *-*-* } 0 } > +// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 } > diff --git > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc > index 5946a82de3bc..096f218ee2be 100644 > --- > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc > +++ > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc > @@ -29,8 +29,7 @@ test01() > { > alignas(DeletedDtor) unsigned char buf[sizeof(DeletedDtor)]; > auto p = ::new (buf) DeletedDtor(); > - std::destroy(p, p + 1); // { dg-error "here" } > - std::destroy_n(p, 1); // { dg-error "here" } > + std::destroy(p, p + 1); > } > > class PrivateDtor { > @@ -42,8 +41,19 @@ test02() > { > alignas(PrivateDtor) unsigned char buf[sizeof(PrivateDtor)]; > auto p = ::new (buf) PrivateDtor(); > - std::destroy(p, p + 1); // { dg-error "here" } > - std::destroy_n(p, 1); // { dg-error "here" } > + std::destroy(p, p + 1); > } > > -// { dg-error "value type is destructible" "" { target *-*-* } 0 } > +#if __cpp_constexpr_dynamic_alloc // >= C++20 > +consteval bool > +test03() > +{ > + DeletedDtor* p = nullptr; > + std::destroy(p, p); > + return true; > +} > +static_assert(test03()); > +#endif > + > +// { dg-error "deleted function .*DeletedDtor" "" { target *-*-* } 0 } > +// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 } > diff --git > a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc > b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc > index 61b99d34a1ca..e6689f7ea265 100644 > --- > a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc > +++ > b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc > @@ -43,11 +43,8 @@ test02() > std::vector<PrivateDtor> v; > } > > -// { dg-error "value type is destructible" "" { target *-*-* } 0 } > +// { dg-error "deleted function .*DeletedDtor" "" { target *-*-* } 0 } > +// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 } > > // In Debug Mode the "required from here" errors come from <debug/vector> > // { dg-error "required from here" "" { target *-*-* } 182 } > - > -// Needed because of PR c++/92193 > -// { dg-prune-output "deleted function" } > -// { dg-prune-output "private within this context" } > diff --git > a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc > b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc > index 0807d158304c..2bd15bc68eec 100644 > --- a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc > +++ b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc > @@ -42,8 +42,5 @@ test02() > std::vector<PrivateDtor> v; // { dg-error "here" } > } > > -// { dg-error "value type is destructible" "" { target *-*-* } 0 } > - > -// Needed because of PR c++/92193 > -// { dg-prune-output "deleted function" } > -// { dg-prune-output "private within this context" } > +// { dg-error "deleted function .*DeletedDtor" "" { target *-*-* } 0 } > +// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 } > -- > 2.49.0 > >