diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 2032e90abeb..b21c44fc0b8 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,20 @@ +2020-04-30 Jonathan Wakely + + PR libstdc++/89510 + * include/bits/alloc_traits.h (allocator_traits::_S_construct) + (allocator_traits::_S_destroy) + (allocator_traits>::construct): Use traits in + noexcept-specifiers. + * include/bits/allocator.h (allocator::construct) + (allocator::destroy): Likewise. + * include/ext/malloc_allocator.h (malloc_allocator::construct) + (malloc_allocator::destroy): Likewise. + * include/ext/new_allocator.h (new_allocator::construct) + (new_allocator::destroy): Likewise. + * testsuite/20_util/allocator/89510.cc: New test. + * testsuite/ext/malloc_allocator/89510.cc: New test. + * testsuite/ext/new_allocator/89510.cc: New test. + 2020-04-29 Jonathan Wakely PR libstdc++/94854 diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h index 6066f48d24c..86d8ed221ff 100644 --- a/libstdc++-v3/include/bits/alloc_traits.h +++ b/libstdc++-v3/include/bits/alloc_traits.h @@ -251,8 +251,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Require<__and_<__not_<__has_construct<_Tp, _Args...>>, is_constructible<_Tp, _Args...>>> _S_construct(_Alloc&, _Tp* __p, _Args&&... __args) - noexcept(noexcept(::new((void*)__p) - _Tp(std::forward<_Args>(__args)...))) + noexcept(std::is_nothrow_constructible<_Tp, _Args...>::value) { #if __cplusplus <= 201703L ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); @@ -271,7 +270,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template static _GLIBCXX14_CONSTEXPR void _S_destroy(_Alloc2&, _Tp* __p, ...) - noexcept(noexcept(__p->~_Tp())) + noexcept(std::is_nothrow_destructible<_Tp>::value) { std::_Destroy(__p); } template @@ -507,7 +506,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static _GLIBCXX20_CONSTEXPR void construct(allocator_type& __a __attribute__((__unused__)), _Up* __p, _Args&&... __args) - noexcept(noexcept(::new((void*)__p) _Up(std::declval<_Args>()...))) + noexcept(std::is_nothrow_constructible<_Up, _Args...>::value) { #if __cplusplus <= 201703L __a.construct(__p, std::forward<_Args>(__args)...); diff --git a/libstdc++-v3/include/bits/allocator.h b/libstdc++-v3/include/bits/allocator.h index dcca769938c..d224aa3ec5e 100644 --- a/libstdc++-v3/include/bits/allocator.h +++ b/libstdc++-v3/include/bits/allocator.h @@ -93,14 +93,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template void construct(_Up* __p, _Args&&... __args) - noexcept(noexcept(::new((void *)__p) - _Up(std::forward<_Args>(__args)...))) + noexcept(std::is_nothrow_constructible<_Up, _Args...>::value) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template void destroy(_Up* __p) - noexcept(noexcept(__p->~_Up())) + noexcept(std::is_nothrow_destructible<_Up>::value) { __p->~_Up(); } #endif // C++11 to C++17 }; diff --git a/libstdc++-v3/include/ext/malloc_allocator.h b/libstdc++-v3/include/ext/malloc_allocator.h index 1f41660fac7..366c766f25b 100644 --- a/libstdc++-v3/include/ext/malloc_allocator.h +++ b/libstdc++-v3/include/ext/malloc_allocator.h @@ -147,14 +147,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template void construct(_Up* __p, _Args&&... __args) - noexcept(noexcept(::new((void *)__p) - _Up(std::forward<_Args>(__args)...))) + noexcept(std::is_nothrow_constructible<_Up, _Args...>::value) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template void destroy(_Up* __p) - noexcept(noexcept(__p->~_Up())) + noexcept(std::is_nothrow_destructible<_Up>::value) { __p->~_Up(); } #else // _GLIBCXX_RESOLVE_LIB_DEFECTS diff --git a/libstdc++-v3/include/ext/new_allocator.h b/libstdc++-v3/include/ext/new_allocator.h index 959d6880276..131718b8b2f 100644 --- a/libstdc++-v3/include/ext/new_allocator.h +++ b/libstdc++-v3/include/ext/new_allocator.h @@ -146,14 +146,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template void construct(_Up* __p, _Args&&... __args) - noexcept(noexcept(::new((void *)__p) - _Up(std::forward<_Args>(__args)...))) + noexcept(std::is_nothrow_constructible<_Up, _Args...>::value) { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } template void destroy(_Up* __p) - noexcept(noexcept( __p->~_Up())) + noexcept(std::is_nothrow_destructible<_Up>::value) { __p->~_Up(); } #else // _GLIBCXX_RESOLVE_LIB_DEFECTS diff --git a/libstdc++-v3/testsuite/20_util/allocator/89510.cc b/libstdc++-v3/testsuite/20_util/allocator/89510.cc new file mode 100644 index 00000000000..a3100a2b062 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/allocator/89510.cc @@ -0,0 +1,147 @@ +// Copyright (C) 2020 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 +// . + +// { dg-do compile { target c++11 } } + +#include + +using AT = std::allocator_traits>; + +template using void_t = void; + +template +struct has_construct +: std::false_type +{ }; + +template +struct has_construct().construct(std::declval()))>> +: std::true_type +{ }; + +template +struct has_destroy +: std::false_type +{ }; + +template +struct has_destroy().destroy(std::declval()))>> +: std::true_type +{ }; + +template +struct has_traits_construct +: std::false_type +{ }; + +template +struct has_traits_construct(), std::declval()))>> +: std::true_type +{ }; + +template +struct has_traits_destroy +: std::false_type +{ }; + +template +struct has_traits_destroy(), std::declval()))>> +: std::true_type +{ }; + +struct NoDefault { NoDefault(int); }; +struct NoDest { private: ~NoDest(); }; + +// Whether true or false, this should not give an error: +constexpr bool c = has_construct, NoDefault>::value; +constexpr bool cv = has_construct, NoDefault>::value; +constexpr bool c2 = has_traits_construct, NoDefault>::value; +constexpr bool d = has_destroy, NoDest>::value; +constexpr bool d2 = has_traits_destroy, NoDest>::value; + +std::allocator a; + +long* lp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(lp)), "" ); +static_assert( noexcept(a.construct(lp, 1L)), "" ); +static_assert( noexcept(a.construct(lp, 2)), "" ); +static_assert( noexcept(a.construct(lp, 2U)), "" ); +static_assert( noexcept(a.destroy(lp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, lp)), "" ); +static_assert( noexcept(AT::construct(a, lp, 1L)), "" ); +static_assert( noexcept(AT::construct(a, lp, 2)), "" ); +static_assert( noexcept(AT::construct(a, lp, 2U)), "" ); +static_assert( noexcept(AT::destroy(a, lp)), "" ); + +struct X +{ + X() noexcept; + X(int) noexcept; + ~X() noexcept; +}; + +X* xp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(xp)), "" ); +static_assert( noexcept(a.construct(xp, 1)), "" ); +static_assert( noexcept(a.destroy(xp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, xp)), "" ); +static_assert( noexcept(AT::construct(a, xp, 1)), "" ); +static_assert( noexcept(AT::destroy(a, xp)), "" ); + +struct Y +{ + Y() noexcept; + Y(int) noexcept(false); + ~Y() noexcept; +}; + +Y* yp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(yp)), "" ); +static_assert( ! noexcept(a.construct(yp, 1)), "" ); +static_assert( noexcept(a.destroy(yp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, yp)), "" ); +static_assert( ! noexcept(AT::construct(a, yp, 1)), "" ); +static_assert( noexcept(AT::destroy(a, yp)), "" ); + +struct Z +{ + Z() noexcept; + Z(int) noexcept; + ~Z() noexcept(false); +}; + +Z* zp; +// These construct calls should be noexcept, but they are false because +// they use is_nothrow_constructible which depends on is_nothrow_destructible. +#if __cplusplus <= 201703L +static_assert( ! noexcept(a.construct(zp)), "wrong" ); +static_assert( ! noexcept(a.construct(zp, 1)), "wrong" ); +static_assert( ! noexcept(a.destroy(zp)), "" ); +#endif +static_assert( ! noexcept(AT::construct(a, zp)), "" ); +static_assert( ! noexcept(AT::construct(a, zp, 1)), "" ); +static_assert( ! noexcept(AT::destroy(a, zp)), "" ); diff --git a/libstdc++-v3/testsuite/ext/malloc_allocator/89510.cc b/libstdc++-v3/testsuite/ext/malloc_allocator/89510.cc new file mode 100644 index 00000000000..f2ab25adb74 --- /dev/null +++ b/libstdc++-v3/testsuite/ext/malloc_allocator/89510.cc @@ -0,0 +1,149 @@ +// Copyright (C) 2020 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 +// . + +// { dg-do compile { target c++11 } } + +#include +#include +#include + +using __gnu_cxx::malloc_allocator; +using AT = std::allocator_traits>; + +template using void_t = void; + +template +struct has_construct +: std::false_type +{ }; + +template +struct has_construct().construct(std::declval()))>> +: std::true_type +{ }; + +template +struct has_destroy +: std::false_type +{ }; + +template +struct has_destroy().destroy(std::declval()))>> +: std::true_type +{ }; + +template +struct has_traits_construct +: std::false_type +{ }; + +template +struct has_traits_construct(), std::declval()))>> +: std::true_type +{ }; + +template +struct has_traits_destroy +: std::false_type +{ }; + +template +struct has_traits_destroy(), std::declval()))>> +: std::true_type +{ }; + +struct NoDefault { NoDefault(int); }; +struct NoDest { private: ~NoDest(); }; + +// Whether true or false, these should not give errors: +constexpr bool c = has_construct, NoDefault>::value; +constexpr bool c2 = has_traits_construct, NoDefault>::value; +constexpr bool d = has_destroy, NoDest>::value; +constexpr bool b2 = has_traits_destroy, NoDest>::value; + +malloc_allocator a; + +long* lp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(lp)), "" ); +static_assert( noexcept(a.construct(lp, 1L)), "" ); +static_assert( noexcept(a.construct(lp, 2)), "" ); +static_assert( noexcept(a.construct(lp, 2U)), "" ); +static_assert( noexcept(a.destroy(lp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, lp)), "" ); +static_assert( noexcept(AT::construct(a, lp, 1L)), "" ); +static_assert( noexcept(AT::construct(a, lp, 2)), "" ); +static_assert( noexcept(AT::construct(a, lp, 2U)), "" ); +static_assert( noexcept(AT::destroy(a, lp)), "" ); + +struct X +{ + X() noexcept; + X(int) noexcept; + ~X() noexcept; +}; + +X* xp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(xp)), "" ); +static_assert( noexcept(a.construct(xp, 1)), "" ); +static_assert( noexcept(a.destroy(xp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, xp)), "" ); +static_assert( noexcept(AT::construct(a, xp, 1)), "" ); +static_assert( noexcept(AT::destroy(a, xp)), "" ); + +struct Y +{ + Y() noexcept; + Y(int) noexcept(false); + ~Y() noexcept; +}; + +Y* yp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(yp)), "" ); +static_assert( ! noexcept(a.construct(yp, 1)), "" ); +static_assert( noexcept(a.destroy(yp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, yp)), "" ); +static_assert( ! noexcept(AT::construct(a, yp, 1)), "" ); +static_assert( noexcept(AT::destroy(a, yp)), "" ); + +struct Z +{ + Z() noexcept; + Z(int) noexcept; + ~Z() noexcept(false); +}; + +Z* zp; +// These construct calls should be noexcept, but they are false because +// they use is_nothrow_constructible which depends on is_nothrow_destructible. +#if __cplusplus <= 201703L +static_assert( ! noexcept(a.construct(zp)), "wrong" ); +static_assert( ! noexcept(a.construct(zp, 1)), "wrong" ); +static_assert( ! noexcept(a.destroy(zp)), "" ); +#endif +static_assert( ! noexcept(AT::construct(a, zp)), "" ); +static_assert( ! noexcept(AT::construct(a, zp, 1)), "" ); +static_assert( ! noexcept(AT::destroy(a, zp)), "" ); diff --git a/libstdc++-v3/testsuite/ext/new_allocator/89510.cc b/libstdc++-v3/testsuite/ext/new_allocator/89510.cc new file mode 100644 index 00000000000..f684a9c157d --- /dev/null +++ b/libstdc++-v3/testsuite/ext/new_allocator/89510.cc @@ -0,0 +1,149 @@ +// Copyright (C) 2020 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 +// . + +// { dg-do compile { target c++11 } } + +#include +#include +#include + +using __gnu_cxx::new_allocator; +using AT = std::allocator_traits>; + +template using void_t = void; + +template +struct has_construct +: std::false_type +{ }; + +template +struct has_construct().construct(std::declval()))>> +: std::true_type +{ }; + +template +struct has_destroy +: std::false_type +{ }; + +template +struct has_destroy().destroy(std::declval()))>> +: std::true_type +{ }; + +template +struct has_traits_construct +: std::false_type +{ }; + +template +struct has_traits_construct(), std::declval()))>> +: std::true_type +{ }; + +template +struct has_traits_destroy +: std::false_type +{ }; + +template +struct has_traits_destroy(), std::declval()))>> +: std::true_type +{ }; + +struct NoDefault { NoDefault(int); }; +struct NoDest { private: ~NoDest(); }; + +// Whether true or false, these should not give errors: +constexpr bool c = has_construct, NoDefault>::value; +constexpr bool c2 = has_traits_construct, NoDefault>::value; +constexpr bool d = has_destroy, NoDest>::value; +constexpr bool d2 = has_traits_destroy, NoDest>::value; + +new_allocator a; + +long* lp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(lp)), "" ); +static_assert( noexcept(a.construct(lp, 1L)), "" ); +static_assert( noexcept(a.construct(lp, 2)), "" ); +static_assert( noexcept(a.construct(lp, 2U)), "" ); +static_assert( noexcept(a.destroy(lp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, lp)), "" ); +static_assert( noexcept(AT::construct(a, lp, 1L)), "" ); +static_assert( noexcept(AT::construct(a, lp, 2)), "" ); +static_assert( noexcept(AT::construct(a, lp, 2U)), "" ); +static_assert( noexcept(AT::destroy(a, lp)), "" ); + +struct X +{ + X() noexcept; + X(int) noexcept; + ~X() noexcept; +}; + +X* xp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(xp)), "" ); +static_assert( noexcept(a.construct(xp, 1)), "" ); +static_assert( noexcept(a.destroy(xp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, xp)), "" ); +static_assert( noexcept(AT::construct(a, xp, 1)), "" ); +static_assert( noexcept(AT::destroy(a, xp)), "" ); + +struct Y +{ + Y() noexcept; + Y(int) noexcept(false); + ~Y() noexcept; +}; + +Y* yp; +#if __cplusplus <= 201703L +static_assert( noexcept(a.construct(yp)), "" ); +static_assert( ! noexcept(a.construct(yp, 1)), "" ); +static_assert( noexcept(a.destroy(yp)), "" ); +#endif +static_assert( noexcept(AT::construct(a, yp)), "" ); +static_assert( ! noexcept(AT::construct(a, yp, 1)), "" ); +static_assert( noexcept(AT::destroy(a, yp)), "" ); + +struct Z +{ + Z() noexcept; + Z(int) noexcept; + ~Z() noexcept(false); +}; + +Z* zp; +// These construct calls should be noexcept, but they are false because +// they use is_nothrow_constructible which depends on is_nothrow_destructible. +#if __cplusplus <= 201703L +static_assert( ! noexcept(a.construct(zp)), "wrong" ); +static_assert( ! noexcept(a.construct(zp, 1)), "wrong" ); +static_assert( ! noexcept(a.destroy(zp)), "" ); +#endif +static_assert( ! noexcept(AT::construct(a, zp)), "" ); +static_assert( ! noexcept(AT::construct(a, zp, 1)), "" ); +static_assert( ! noexcept(AT::destroy(a, zp)), "" );