From patchwork Thu Dec 2 17:05:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 48426 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 53EC4385DC07 for ; Thu, 2 Dec 2021 17:23:32 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 53EC4385DC07 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1638465812; bh=B8zECEw9KX+t605Axg3T/7i5EqhWNfq9Vq7ljmU8sys=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=PJgR8AxqhK/z+4xGnA88AhePZRk/PwVIQ/gGn9K8Wgyu7YdiC2/IeVlu/eauUoJ7p IPVL6vASiXmX0S1Em0HRZxobzraTjZwf0JNKZE/BTutYw7yWehEfURLRjShZfs0paY rUT2cR+fpQ2kRu540+xb0OI6IVNDExZxB3/u7M+I= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id ACF0B385C41C for ; Thu, 2 Dec 2021 17:05:07 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org ACF0B385C41C Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-455-CP-1BOUUPJKINB8lz0N0ng-1; Thu, 02 Dec 2021 12:05:04 -0500 X-MC-Unique: CP-1BOUUPJKINB8lz0N0ng-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id ECF7969738; Thu, 2 Dec 2021 17:05:02 +0000 (UTC) Received: from localhost (unknown [10.33.36.16]) by smtp.corp.redhat.com (Postfix) with ESMTP id 754A05F4ED; Thu, 2 Dec 2021 17:05:02 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [PATCH] libstdc++: Fix non-reserved name in std::allocator base class [PR64135] Date: Thu, 2 Dec 2021 17:05:01 +0000 Message-Id: <20211202170501.367073-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-14.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jonathan Wakely via Gcc-patches From: Jonathan Wakely Reply-To: Jonathan Wakely Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" I'd like to push this to trunk to fix PR64135. It think this fixes the use of a non-reserved name, while preserving the ABI as far as class layout goes. It will break any programs which rely on std::allocator having a __gnu_cxx::new_allocator base class, e.g. so that is_convertible<__gnu_cxx::new_allocator&, std::allocator&> is true, but I don't see why anybody would do that. ... 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 instead of . (__allocator_base): Use std::__new_allocator instead of __gnu_cxx::new_allocator. * 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. --- .../config/allocator/new_allocator_base.h | 11 +- libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/bits/new_allocator.h | 223 ++++++++++++++++++ .../include/experimental/memory_resource | 4 +- libstdc++-v3/include/ext/new_allocator.h | 157 +----------- .../testsuite/20_util/allocator/64135.cc | 45 ++++ 7 files changed, 279 insertions(+), 163 deletions(-) create mode 100644 libstdc++-v3/include/bits/new_allocator.h create mode 100644 libstdc++-v3/testsuite/20_util/allocator/64135.cc diff --git a/libstdc++-v3/config/allocator/new_allocator_base.h b/libstdc++-v3/config/allocator/new_allocator_base.h index 7c52fef63de..a139f2fb668 100644 --- a/libstdc++-v3/config/allocator/new_allocator_base.h +++ b/libstdc++-v3/config/allocator/new_allocator_base.h @@ -30,7 +30,7 @@ #ifndef _GLIBCXX_CXX_ALLOCATOR_H #define _GLIBCXX_CXX_ALLOCATOR_H 1 -#include +#include #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 - 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 diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 25a8d9c8a41..f1cf79615c8 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -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 \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 47a5d985049..4e4a240831a 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -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 \ diff --git a/libstdc++-v3/include/bits/new_allocator.h b/libstdc++-v3/include/bits/new_allocator.h new file mode 100644 index 00000000000..4d85612720d --- /dev/null +++ b/libstdc++-v3/include/bits/new_allocator.h @@ -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 +// . + +/** @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 +#include +#include +#include +#if __cplusplus >= 201103L +#include +#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 + 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 + 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 + _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(0)) + { +#if __cplusplus >= 201103L + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3308. std::allocator().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 + void + construct(_Up* __p, _Args&&... __args) + noexcept(std::is_nothrow_constructible<_Up, _Args...>::value) + { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } + + template + 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 + friend _GLIBCXX20_CONSTEXPR bool + operator==(const __new_allocator&, const __new_allocator<_Up>&) + _GLIBCXX_NOTHROW + { return true; } + +#if __cpp_impl_three_way_comparison < 201907L + template + 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 diff --git a/libstdc++-v3/include/experimental/memory_resource b/libstdc++-v3/include/experimental/memory_resource index 82d324c83b6..37ac95fc4b1 100644 --- a/libstdc++-v3/include/experimental/memory_resource +++ b/libstdc++-v3/include/experimental/memory_resource @@ -40,7 +40,7 @@ #include // atomic #include // placement new #include // max_align_t -#include +#include #include /// @cond @@ -503,7 +503,7 @@ namespace pmr { inline memory_resource* new_delete_resource() noexcept { - using type = resource_adaptor<__gnu_cxx::new_allocator>; + using type = resource_adaptor>; alignas(type) static unsigned char __buf[sizeof(type)]; static type* __r = new(__buf) type; return __r; diff --git a/libstdc++-v3/include/ext/new_allocator.h b/libstdc++-v3/include/ext/new_allocator.h index 7c48c820c62..5cb1b97c2b2 100644 --- a/libstdc++-v3/include/ext/new_allocator.h +++ b/libstdc++-v3/include/ext/new_allocator.h @@ -29,13 +29,7 @@ #ifndef _NEW_ALLOCATOR_H #define _NEW_ALLOCATOR_H 1 -#include -#include -#include -#include -#if __cplusplus >= 201103L -#include -#endif +#include namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) { @@ -52,168 +46,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @tparam _Tp Type of allocated object. */ template - 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 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 - _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(0)) - { -#if __cplusplus >= 201103L - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 3308. std::allocator().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 - void - construct(_Up* __p, _Args&&... __args) - noexcept(std::is_nothrow_constructible<_Up, _Args...>::value) - { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } - - template - 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 - friend _GLIBCXX20_CONSTEXPR bool - operator==(const new_allocator&, const new_allocator<_Up>&) - _GLIBCXX_NOTHROW - { return true; } - -#if __cpp_impl_three_way_comparison < 201907L - template - 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 diff --git a/libstdc++-v3/testsuite/20_util/allocator/64135.cc b/libstdc++-v3/testsuite/20_util/allocator/64135.cc new file mode 100644 index 00000000000..b0a49e9b3f0 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/allocator/64135.cc @@ -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 + +#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 +#include + +struct N : __gnu_cxx::new_allocator { }; + +struct A : std::allocator, N { }; +struct B : std::allocator { 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 { }; +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, M { }; + +// This test uses { target std_allocator_new } so this is true too: +STATIC_ASSERT( sizeof(D) == 1 );