gcc/libstdc++-v3/include/ext/malloc_allocator.h
Jonathan Wakely b1983f4582 libstdc++: Avoid errors in allocator's noexcept-specifier (PR 89510)
This fixes a regression due to the conditional noexcept-specifier on
std::allocator::construct and std::allocator::destroy, as well as the
corresponding members of new_allocator, malloc_allocator, and
allocator_traits. Those noexcept-specifiers were using expressions which
might be ill-formed, which caused errors outside the immediate context
when checking for the presence of construct and destroy in SFINAE
contexts.

The fix is to use the is_nothrow_constructible and
is_nothrow_destructible type traits instead, because those traits are
safe to use even when the construction/destruction itself is not valid.

The is_nothrow_constructible trait will be false for a type that is not
also nothrow-destructible, even if the new-expression used in the
construct function body is actually noexcept. That's not the correct
answer, but isn't a problem because providing a noexcept-specifier on
these functions is not required by the standard anyway. If the answer is
false when it should be true, that's suboptimal but OK (unlike giving
errors for valid code, or giving a true answer when it should be false).

	PR libstdc++/89510
	* include/bits/alloc_traits.h (allocator_traits::_S_construct)
	(allocator_traits::_S_destroy)
	(allocator_traits<allocator<T>>::construct): Use traits in
	noexcept-specifiers.
	* include/bits/allocator.h (allocator<void>::construct)
	(allocator<void>::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-30 16:01:43 +01:00

200 lines
5.6 KiB
C++

// Allocator that wraps "C" malloc -*- C++ -*-
// Copyright (C) 2001-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.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file ext/malloc_allocator.h
* This file is a GNU extension to the Standard C++ Library.
*/
#ifndef _MALLOC_ALLOCATOR_H
#define _MALLOC_ALLOCATOR_H 1
#include <cstdlib>
#include <cstddef>
#include <new>
#include <bits/functexcept.h>
#include <bits/move.h>
#if __cplusplus >= 201103L
#include <type_traits>
#endif
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* @brief An allocator that uses malloc.
* @ingroup allocators
*
* This is precisely the allocator defined in the C++ Standard.
* - all allocation calls malloc
* - all deallocation calls free
*/
template<typename _Tp>
class malloc_allocator
{
public:
typedef _Tp value_type;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
#if __cplusplus <= 201703L
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
template<typename _Tp1>
struct rebind
{ typedef malloc_allocator<_Tp1> other; };
#endif
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2103. propagate_on_container_move_assignment
typedef std::true_type propagate_on_container_move_assignment;
#endif
_GLIBCXX20_CONSTEXPR
malloc_allocator() _GLIBCXX_USE_NOEXCEPT { }
_GLIBCXX20_CONSTEXPR
malloc_allocator(const malloc_allocator&) _GLIBCXX_USE_NOEXCEPT { }
template<typename _Tp1>
_GLIBCXX20_CONSTEXPR
malloc_allocator(const malloc_allocator<_Tp1>&)
_GLIBCXX_USE_NOEXCEPT { }
#if __cplusplus <= 201703L
~malloc_allocator() _GLIBCXX_USE_NOEXCEPT { }
pointer
address(reference __x) const _GLIBCXX_NOEXCEPT
{ return std::__addressof(__x); }
const_pointer
address(const_reference __x) const _GLIBCXX_NOEXCEPT
{ return std::__addressof(__x); }
#endif
// NB: __n is permitted to be 0. The C++ standard says nothing
// about what the return value is when __n == 0.
_Tp*
allocate(size_type __n, const void* = 0)
{
if (__n > this->_M_max_size())
std::__throw_bad_alloc();
_Tp* __ret = 0;
#if __cpp_aligned_new
#if __cplusplus > 201402L && _GLIBCXX_HAVE_ALIGNED_ALLOC
if (alignof(_Tp) > alignof(std::max_align_t))
{
__ret = static_cast<_Tp*>(::aligned_alloc(alignof(_Tp),
__n * sizeof(_Tp)));
}
#else
# define _GLIBCXX_CHECK_MALLOC_RESULT
#endif
#endif
if (!__ret)
__ret = static_cast<_Tp*>(std::malloc(__n * sizeof(_Tp)));
if (!__ret)
std::__throw_bad_alloc();
#ifdef _GLIBCXX_CHECK_MALLOC_RESULT
#undef _GLIBCXX_CHECK_MALLOC_RESULT
if (reinterpret_cast<std::size_t>(__ret) % alignof(_Tp))
{
// Memory returned by malloc is not suitably aligned for _Tp.
deallocate(__ret, __n);
std::__throw_bad_alloc();
}
#endif
return __ret;
}
// __p is not permitted to be a null pointer.
void
deallocate(_Tp* __p, size_type)
{ std::free(static_cast<void*>(__p)); }
#if __cplusplus <= 201703L
size_type
max_size() const _GLIBCXX_USE_NOEXCEPT
{ return _M_max_size(); }
#if __cplusplus >= 201103L
template<typename _Up, typename... _Args>
void
construct(_Up* __p, _Args&&... __args)
noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
template<typename _Up>
void
destroy(_Up* __p)
noexcept(std::is_nothrow_destructible<_Up>::value)
{ __p->~_Up(); }
#else
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_] allocator::construct
void
construct(pointer __p, const _Tp& __val)
{ ::new((void *)__p) value_type(__val); }
void
destroy(pointer __p) { __p->~_Tp(); }
#endif
#endif // ! C++20
template<typename _Up>
friend _GLIBCXX20_CONSTEXPR bool
operator==(const malloc_allocator&, const malloc_allocator<_Up>&)
_GLIBCXX_NOTHROW
{ return true; }
#if __cpp_impl_three_way_comparison < 201907L
template<typename _Up>
friend _GLIBCXX20_CONSTEXPR bool
operator!=(const malloc_allocator&, const malloc_allocator<_Up>&)
_GLIBCXX_NOTHROW
{ return false; }
#endif
private:
_GLIBCXX_CONSTEXPR size_type
_M_max_size() const _GLIBCXX_USE_NOEXCEPT
{
#if __PTRDIFF_MAX__ < __SIZE_MAX__
return std::size_t(__PTRDIFF_MAX__) / sizeof(_Tp);
#else
return std::size_t(-1) / sizeof(_Tp);
#endif
}
};
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif