libstdc++: Fix non-reserved name in std::allocator base class [PR64135]

The possible base classes of std::allocator are new_allocator and
malloc_allocator, which both cause a non-reserved name to be declared in
every program that includes the definition of std::allocator. This is
non-conforming.

This change replaces __gnu_cxx::new_allocator with std::__new_allocator
which is identical except for using a reserved name. The non-standard
extension __gnu_cxx::new_allocator is preserved as a thin wrapper over
std::__new_allocator. There is no problem with the extension using a
non-reserved name now that it's not included by default in other
headers.

The same change could be done to __gnu_cxx::malloc_allocator but as it's
not the default configuration it can wait.

libstdc++-v3/ChangeLog:

	PR libstdc++/64135
	* config/allocator/new_allocator_base.h: Include
	<bits/new_allocator.h> instead of <ext/new_allocator.h>.
	(__allocator_base): Use std::__new_allocator instead of
	__gnu_cxx::new_allocator.
	* doc/xml/manual/allocator.xml: Document new default base class
	for std::allocator.
	* doc/xml/manual/evolution.xml: Likewise.
	* doc/html/*: Regenerate.
	* include/Makefile.am: Add bits/new_allocator.h.
	* include/Makefile.in: Regenerate.
	* include/experimental/memory_resource (new_delete_resource):
	Use std::__new_allocator instead of __gnu_cxx::new_allocator.
	* include/ext/new_allocator.h (new_allocator): Derive from
	std::__new_allocator. Move implementation to ...
	* include/bits/new_allocator.h: New file.
	* testsuite/20_util/allocator/64135.cc: New test.
This commit is contained in:
Jonathan Wakely 2021-12-01 17:56:23 +00:00
parent f157c5362b
commit fe9571a35d
11 changed files with 293 additions and 167 deletions

View File

@ -30,7 +30,7 @@
#ifndef _GLIBCXX_CXX_ALLOCATOR_H
#define _GLIBCXX_CXX_ALLOCATOR_H 1
#include <ext/new_allocator.h>
#include <bits/new_allocator.h>
#if __cplusplus >= 201103L
namespace std
@ -38,18 +38,17 @@ namespace std
/**
* @brief An alias to the base class for std::allocator.
*
* Used to set the std::allocator base class to
* __gnu_cxx::new_allocator.
* Used to set the std::allocator base class to std::__new_allocator.
*
* @ingroup allocators
* @tparam _Tp Type of allocated object.
*/
template<typename _Tp>
using __allocator_base = __gnu_cxx::new_allocator<_Tp>;
using __allocator_base = __new_allocator<_Tp>;
}
#else
// Define new_allocator as the base class to std::allocator.
# define __allocator_base __gnu_cxx::new_allocator
// Define __new_allocator as the base class to std::allocator.
# define __allocator_base __new_allocator
#endif
#ifndef _GLIBCXX_SANITIZE_STD_ALLOCATOR

View File

@ -447,4 +447,7 @@ Dynamic exception specifications should be replaced with <code class="code">noex
</p><p>
The <code class="literal">bitmap</code>, <code class="literal">mt</code>, and <code class="literal">pool</code>
options for <code class="option">--enable-libstdcxx-allocator</code> were removed.
For the <code class="literal">new</code> option, <code class="classname">std::allocator</code>
no longer derives from <code class="classname">__gnu_cxx::new_allocator</code>;
they both derive from <code class="classname">std::__new_allocator</code> instead.
</p></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="abi.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="appendix_porting.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="backwards.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">ABI Policy and Guidelines </td><td width="20%" align="center"><a accesskey="h" href="../index.html">Home</a></td><td width="40%" align="right" valign="top"> Backwards Compatibility</td></tr></table></div></body></html>

View File

@ -140,9 +140,11 @@
<a class="link" href="http://gcc.gnu.org/viewcvs/gcc/trunk/libstdc++-v3/testsuite/performance/23_containers/producer_consumer/associative.cc?view=markup" target="_top">associative</a>
containers.
</p></li></ol></div><p>
The current default choice for
Since GCC 12 the default choice for
<code class="classname">allocator</code> is
<code class="classname">__gnu_cxx::new_allocator</code>.
<code class="classname">std::__new_allocator</code>.
Before GCC 12 it was the <code class="classname">__gnu_cxx::new_allocator</code>
extension (which has identical behaviour).
</p></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a id="allocator.caching"></a>Disabling Memory Caching</h5></div></div></div><p>
In use, <code class="classname">allocator</code> may allocate and
deallocate using implementation-specific strategies and

View File

@ -219,9 +219,11 @@
</orderedlist>
<para>
The current default choice for
Since GCC 12 the default choice for
<classname>allocator</classname> is
<classname>__gnu_cxx::new_allocator</classname>.
<classname>std::__new_allocator</classname>.
Before GCC 12 it was the <classname>__gnu_cxx::new_allocator</classname>
extension (which has identical behaviour).
</para>
</section>

View File

@ -1036,6 +1036,9 @@ Dynamic exception specifications should be replaced with <code>noexcept</code>.
<para>
The <literal>bitmap</literal>, <literal>mt</literal>, and <literal>pool</literal>
options for <option>--enable-libstdcxx-allocator</option> were removed.
For the <literal>new</literal> option, <classname>std::allocator</classname>
no longer derives from <classname>__gnu_cxx::new_allocator</classname>;
they both derive from <classname>std::__new_allocator</classname> instead.
</para>
</section>

View File

@ -159,6 +159,7 @@ bits_headers = \
${bits_srcdir}/mofunc_impl.h \
${bits_srcdir}/move.h \
${bits_srcdir}/move_only_function.h \
${bits_srcdir}/new_allocator.h \
${bits_srcdir}/node_handle.h \
${bits_srcdir}/ostream.tcc \
${bits_srcdir}/ostream_insert.h \

View File

@ -509,6 +509,7 @@ bits_headers = \
${bits_srcdir}/mofunc_impl.h \
${bits_srcdir}/move.h \
${bits_srcdir}/move_only_function.h \
${bits_srcdir}/new_allocator.h \
${bits_srcdir}/node_handle.h \
${bits_srcdir}/ostream.tcc \
${bits_srcdir}/ostream_insert.h \

View File

@ -0,0 +1,223 @@
// Allocator that wraps operator new -*- C++ -*-
// Copyright (C) 2001-2021 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 bits/new_allocator.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{memory}
*/
#ifndef _STD_NEW_ALLOCATOR_H
#define _STD_NEW_ALLOCATOR_H 1
#include <bits/c++config.h>
#include <new>
#include <bits/functexcept.h>
#include <bits/move.h>
#if __cplusplus >= 201103L
#include <type_traits>
#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* @brief An allocator that uses global new, as per C++03 [20.4.1].
* @ingroup allocators
*
* This is precisely the allocator defined in the C++ Standard.
* - all allocation calls operator new
* - all deallocation calls operator delete
*
* @tparam _Tp Type of allocated object.
*/
template<typename _Tp>
class __new_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 __new_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
__new_allocator() _GLIBCXX_USE_NOEXCEPT { }
_GLIBCXX20_CONSTEXPR
__new_allocator(const __new_allocator&) _GLIBCXX_USE_NOEXCEPT { }
template<typename _Tp1>
_GLIBCXX20_CONSTEXPR
__new_allocator(const __new_allocator<_Tp1>&) _GLIBCXX_USE_NOEXCEPT { }
#if __cplusplus <= 201703L
~__new_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
#if __has_builtin(__builtin_operator_new) >= 201802L
# define _GLIBCXX_OPERATOR_NEW __builtin_operator_new
# define _GLIBCXX_OPERATOR_DELETE __builtin_operator_delete
#else
# define _GLIBCXX_OPERATOR_NEW ::operator new
# define _GLIBCXX_OPERATOR_DELETE ::operator delete
#endif
// NB: __n is permitted to be 0. The C++ standard says nothing
// about what the return value is when __n == 0.
_GLIBCXX_NODISCARD _Tp*
allocate(size_type __n, const void* = static_cast<const void*>(0))
{
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3308. std::allocator<void>().allocate(n)
static_assert(sizeof(_Tp) != 0, "cannot allocate incomplete types");
#endif
if (__builtin_expect(__n > this->_M_max_size(), false))
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3190. allocator::allocate sometimes returns too little storage
if (__n > (std::size_t(-1) / sizeof(_Tp)))
std::__throw_bad_array_new_length();
std::__throw_bad_alloc();
}
#if __cpp_aligned_new
if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
std::align_val_t __al = std::align_val_t(alignof(_Tp));
return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp),
__al));
}
#endif
return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));
}
// __p is not permitted to be a null pointer.
void
deallocate(_Tp* __p, size_type __n __attribute__ ((__unused__)))
{
#if __cpp_sized_deallocation
# define _GLIBCXX_SIZED_DEALLOC(p, n) (p), (n) * sizeof(_Tp)
#else
# define _GLIBCXX_SIZED_DEALLOC(p, n) (p)
#endif
#if __cpp_aligned_new
if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
_GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n),
std::align_val_t(alignof(_Tp)));
return;
}
#endif
_GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n));
}
#undef _GLIBCXX_SIZED_DEALLOC
#undef _GLIBCXX_OPERATOR_DELETE
#undef _GLIBCXX_OPERATOR_NEW
#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) _Tp(__val); }
void
destroy(pointer __p) { __p->~_Tp(); }
#endif
#endif // ! C++20
template<typename _Up>
friend _GLIBCXX20_CONSTEXPR bool
operator==(const __new_allocator&, const __new_allocator<_Up>&)
_GLIBCXX_NOTHROW
{ return true; }
#if __cpp_impl_three_way_comparison < 201907L
template<typename _Up>
friend _GLIBCXX20_CONSTEXPR bool
operator!=(const __new_allocator&, const __new_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

View File

@ -40,7 +40,7 @@
#include <atomic> // atomic
#include <new> // placement new
#include <cstddef> // max_align_t
#include <ext/new_allocator.h>
#include <bits/new_allocator.h>
#include <debug/assertions.h>
/// @cond
@ -503,7 +503,7 @@ namespace pmr {
inline memory_resource*
new_delete_resource() noexcept
{
using type = resource_adaptor<__gnu_cxx::new_allocator<char>>;
using type = resource_adaptor<std::__new_allocator<char>>;
alignas(type) static unsigned char __buf[sizeof(type)];
static type* __r = new(__buf) type;
return __r;

View File

@ -29,13 +29,7 @@
#ifndef _NEW_ALLOCATOR_H
#define _NEW_ALLOCATOR_H 1
#include <bits/c++config.h>
#include <new>
#include <bits/functexcept.h>
#include <bits/move.h>
#if __cplusplus >= 201103L
#include <type_traits>
#endif
#include <bits/new_allocator.h>
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{
@ -52,168 +46,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @tparam _Tp Type of allocated object.
*/
template<typename _Tp>
class new_allocator
class new_allocator : public std::__new_allocator<_Tp>
{
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 new_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
new_allocator() _GLIBCXX_USE_NOEXCEPT { }
_GLIBCXX20_CONSTEXPR
new_allocator(const new_allocator&) _GLIBCXX_USE_NOEXCEPT { }
template<typename _Tp1>
_GLIBCXX20_CONSTEXPR
new_allocator(const new_allocator<_Tp1>&) _GLIBCXX_USE_NOEXCEPT { }
#if __cplusplus <= 201703L
~new_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
#if __has_builtin(__builtin_operator_new) >= 201802L
# define _GLIBCXX_OPERATOR_NEW __builtin_operator_new
# define _GLIBCXX_OPERATOR_DELETE __builtin_operator_delete
#else
# define _GLIBCXX_OPERATOR_NEW ::operator new
# define _GLIBCXX_OPERATOR_DELETE ::operator delete
#endif
// NB: __n is permitted to be 0. The C++ standard says nothing
// about what the return value is when __n == 0.
_GLIBCXX_NODISCARD _Tp*
allocate(size_type __n, const void* = static_cast<const void*>(0))
{
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3308. std::allocator<void>().allocate(n)
static_assert(sizeof(_Tp) != 0, "cannot allocate incomplete types");
#endif
if (__builtin_expect(__n > this->_M_max_size(), false))
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3190. allocator::allocate sometimes returns too little storage
if (__n > (std::size_t(-1) / sizeof(_Tp)))
std::__throw_bad_array_new_length();
std::__throw_bad_alloc();
}
#if __cpp_aligned_new
if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
std::align_val_t __al = std::align_val_t(alignof(_Tp));
return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp),
__al));
}
#endif
return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));
}
// __p is not permitted to be a null pointer.
void
deallocate(_Tp* __p, size_type __n __attribute__ ((__unused__)))
{
#if __cpp_sized_deallocation
# define _GLIBCXX_SIZED_DEALLOC(p, n) (p), (n) * sizeof(_Tp)
#else
# define _GLIBCXX_SIZED_DEALLOC(p, n) (p)
#endif
#if __cpp_aligned_new
if (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
{
_GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n),
std::align_val_t(alignof(_Tp)));
return;
}
#endif
_GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n));
}
#undef _GLIBCXX_SIZED_DEALLOC
#undef _GLIBCXX_OPERATOR_DELETE
#undef _GLIBCXX_OPERATOR_NEW
#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) _Tp(__val); }
void
destroy(pointer __p) { __p->~_Tp(); }
#endif
#endif // ! C++20
template<typename _Up>
friend _GLIBCXX20_CONSTEXPR bool
operator==(const new_allocator&, const new_allocator<_Up>&)
_GLIBCXX_NOTHROW
{ return true; }
#if __cpp_impl_three_way_comparison < 201907L
template<typename _Up>
friend _GLIBCXX20_CONSTEXPR bool
operator!=(const new_allocator&, const new_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

View File

@ -0,0 +1,45 @@
// { dg-do compile { target std_allocator_new } }
// { dg-add-options no_pch }
// PR libstdc++/64135
#define new_allocator 1
#define malloc_allocator 1
#define bitmap_allocator 1
#include <memory>
#if __cplusplus >= 201103L
#define STATIC_ASSERT(X) static_assert((X), #X)
#else
#define PASTE2(X, Y) X##Y
#define PASTE(X, Y) PASTE2(X, Y)
#define STATIC_ASSERT(X) char PASTE(_assertion_, __LINE__) [(X) ? 1 : -1]
#endif
#undef new_allocator
#undef malloc_allocator
#include <ext/new_allocator.h>
#include <ext/malloc_allocator.h>
struct N : __gnu_cxx::new_allocator<char> { };
struct A : std::allocator<char>, N { };
struct B : std::allocator<char> { N n; };
// Verify that layout was not changed by removing std::allocator inheritance
// from __gnu_cxx::new_allocator:
STATIC_ASSERT( sizeof(A) == 2 );
STATIC_ASSERT( sizeof(B) == 2 );
struct M : __gnu_cxx::malloc_allocator<char> { };
struct C : N, M { };
// Verify that malloc_allocator can be an overlapping subobject with
// __new_allocator:
STATIC_ASSERT( sizeof(M) == 1 );
STATIC_ASSERT( sizeof(C) == 1 );
struct D : std::allocator<char>, M { };
// This test uses { target std_allocator_new } so this is true too:
STATIC_ASSERT( sizeof(D) == 1 );