From b66e5a95c0065fda3569a1bfd3766963a848a00d Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Fri, 10 Aug 2018 21:20:27 +0100 Subject: [PATCH] PR libstdc++/68210 adjust operator new and delete for LWG 206 Ensure that nothrow versions of new and delete call the ordinary versions of new or delete, instead of calling malloc or free directly. These files are all compiled with -std=gnu++14 so can use noexcept and nullptr to make the code more readable. PR libstdc++/68210 * doc/xml/manual/intro.xml: Document LWG 206 change. * libsupc++/del_op.cc: Replace _GLIBCXX_USE_NOEXCEPT with noexcept. * libsupc++/del_opa.cc: Likewise. * libsupc++/del_opant.cc: Likewise. * libsupc++/del_opnt.cc: Likewise. Call operator delete(ptr) instead of free(ptr). * libsupc++/del_ops.cc: Replace _GLIBCXX_USE_NOEXCEPT with noexcept. * libsupc++/del_opsa.cc: Likewise. * libsupc++/del_opva.cc: Likewise. * libsupc++/del_opvant.cc: Likewise. * libsupc++/del_opvnt.cc: Likewise. Call operator delete[](ptr) instead of operator delete(ptr). * libsupc++/del_opvs.cc: Replace _GLIBCXX_USE_NOEXCEPT with noexcept. * libsupc++/del_opvsa.cc: Likewise. * libsupc++/new_op.cc: Use __builtin_expect in check for zero size. * libsupc++/new_opa.cc: Use nullptr instead of literal 0. * libsupc++/new_opant.cc: Likewise. Replace _GLIBCXX_USE_NOEXCEPT with noexcept. * libsupc++/new_opnt.cc: Likewise. Call operator new(sz) instead of malloc(sz). * libsupc++/new_opvant.cc: Use nullptr and noexcept. * libsupc++/new_opvnt.cc: Likewise. Call operator new[](sz) instead of operator new(sz, nothrow). * testsuite/18_support/new_nothrow.cc: New test. From-SVN: r263478 --- libstdc++-v3/ChangeLog | 28 +++ libstdc++-v3/doc/xml/manual/intro.xml | 11 ++ libstdc++-v3/libsupc++/del_op.cc | 2 +- libstdc++-v3/libsupc++/del_opa.cc | 2 +- libstdc++-v3/libsupc++/del_opant.cc | 2 +- libstdc++-v3/libsupc++/del_opnt.cc | 7 +- libstdc++-v3/libsupc++/del_ops.cc | 2 +- libstdc++-v3/libsupc++/del_opsa.cc | 2 +- libstdc++-v3/libsupc++/del_opva.cc | 2 +- libstdc++-v3/libsupc++/del_opvant.cc | 2 +- libstdc++-v3/libsupc++/del_opvnt.cc | 4 +- libstdc++-v3/libsupc++/del_opvs.cc | 2 +- libstdc++-v3/libsupc++/del_opvsa.cc | 2 +- libstdc++-v3/libsupc++/new_op.cc | 2 +- libstdc++-v3/libsupc++/new_opa.cc | 6 +- libstdc++-v3/libsupc++/new_opant.cc | 4 +- libstdc++-v3/libsupc++/new_opnt.cc | 31 +-- libstdc++-v3/libsupc++/new_opvant.cc | 4 +- libstdc++-v3/libsupc++/new_opvnt.cc | 15 +- .../testsuite/18_support/new_nothrow.cc | 183 ++++++++++++++++++ 20 files changed, 268 insertions(+), 45 deletions(-) create mode 100644 libstdc++-v3/testsuite/18_support/new_nothrow.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index aa90b20410d4..fd547e260391 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,31 @@ +2018-08-10 Jonathan Wakely + + PR libstdc++/68210 + * doc/xml/manual/intro.xml: Document LWG 206 change. + * libsupc++/del_op.cc: Replace _GLIBCXX_USE_NOEXCEPT with noexcept. + * libsupc++/del_opa.cc: Likewise. + * libsupc++/del_opant.cc: Likewise. + * libsupc++/del_opnt.cc: Likewise. Call operator delete(ptr) instead + of free(ptr). + * libsupc++/del_ops.cc: Replace _GLIBCXX_USE_NOEXCEPT with noexcept. + * libsupc++/del_opsa.cc: Likewise. + * libsupc++/del_opva.cc: Likewise. + * libsupc++/del_opvant.cc: Likewise. + * libsupc++/del_opvnt.cc: Likewise. Call operator delete[](ptr) + instead of operator delete(ptr). + * libsupc++/del_opvs.cc: Replace _GLIBCXX_USE_NOEXCEPT with noexcept. + * libsupc++/del_opvsa.cc: Likewise. + * libsupc++/new_op.cc: Use __builtin_expect in check for zero size. + * libsupc++/new_opa.cc: Use nullptr instead of literal 0. + * libsupc++/new_opant.cc: Likewise. Replace _GLIBCXX_USE_NOEXCEPT + with noexcept. + * libsupc++/new_opnt.cc: Likewise. Call operator new(sz) instead of + malloc(sz). + * libsupc++/new_opvant.cc: Use nullptr and noexcept. + * libsupc++/new_opvnt.cc: Likewise. Call operator new[](sz) instead of + operator new(sz, nothrow). + * testsuite/18_support/new_nothrow.cc: New test. + 2018-08-10 Martin Liska * libsupc++/new_op.cc (new): Remove __builtin_expect as malloc diff --git a/libstdc++-v3/doc/xml/manual/intro.xml b/libstdc++-v3/doc/xml/manual/intro.xml index fea07e2bb5f8..cb187e1a2ed7 100644 --- a/libstdc++-v3/doc/xml/manual/intro.xml +++ b/libstdc++-v3/doc/xml/manual/intro.xml @@ -440,6 +440,17 @@ requirements of the license of GCC. Yes, it can, specifically if EOF is reached while skipping whitespace. + 206: + operator new(size_t, nothrow) may become + unlinked to ordinary operator new if ordinary + version replaced + + + The nothrow forms of new and delete were + changed to call the throwing forms, handling any exception by + catching it and returning a null pointer. + + 211: operator>>(istream&, string&) doesn't set failbit diff --git a/libstdc++-v3/libsupc++/del_op.cc b/libstdc++-v3/libsupc++/del_op.cc index 9a5cb82fdf09..ab3b617afa7a 100644 --- a/libstdc++-v3/libsupc++/del_op.cc +++ b/libstdc++-v3/libsupc++/del_op.cc @@ -44,7 +44,7 @@ _GLIBCXX_END_NAMESPACE_VERSION #pragma GCC diagnostic ignored "-Wsized-deallocation" _GLIBCXX_WEAK_DEFINITION void -operator delete(void* ptr) _GLIBCXX_USE_NOEXCEPT +operator delete(void* ptr) noexcept { std::free(ptr); } diff --git a/libstdc++-v3/libsupc++/del_opa.cc b/libstdc++-v3/libsupc++/del_opa.cc index 71f384df661c..2a1f0aba3a16 100644 --- a/libstdc++-v3/libsupc++/del_opa.cc +++ b/libstdc++-v3/libsupc++/del_opa.cc @@ -44,7 +44,7 @@ _GLIBCXX_END_NAMESPACE_VERSION #pragma GCC diagnostic ignored "-Wsized-deallocation" _GLIBCXX_WEAK_DEFINITION void -operator delete(void* ptr, std::align_val_t) _GLIBCXX_USE_NOEXCEPT +operator delete(void* ptr, std::align_val_t) noexcept { #if _GLIBCXX_HAVE_ALIGNED_ALLOC || _GLIBCXX_HAVE_POSIX_MEMALIGN \ || _GLIBCXX_HAVE_MEMALIGN diff --git a/libstdc++-v3/libsupc++/del_opant.cc b/libstdc++-v3/libsupc++/del_opant.cc index a4305a3146af..603d9f2e9d29 100644 --- a/libstdc++-v3/libsupc++/del_opant.cc +++ b/libstdc++-v3/libsupc++/del_opant.cc @@ -27,7 +27,7 @@ #include "new" _GLIBCXX_WEAK_DEFINITION void -operator delete (void *ptr, std::align_val_t al, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT +operator delete (void *ptr, std::align_val_t al, const std::nothrow_t&) noexcept { ::operator delete (ptr, al); } diff --git a/libstdc++-v3/libsupc++/del_opnt.cc b/libstdc++-v3/libsupc++/del_opnt.cc index 2bbcdc3dceb7..a2762659d9fe 100644 --- a/libstdc++-v3/libsupc++/del_opnt.cc +++ b/libstdc++-v3/libsupc++/del_opnt.cc @@ -41,7 +41,10 @@ _GLIBCXX_END_NAMESPACE_VERSION #include "new" _GLIBCXX_WEAK_DEFINITION void -operator delete (void *ptr, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT +operator delete (void *ptr, const std::nothrow_t&) noexcept { - std::free(ptr); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 206. operator new(size_t, nothrow) may become unlinked to ordinary + // operator new if ordinary version replaced + ::operator delete (ptr); } diff --git a/libstdc++-v3/libsupc++/del_ops.cc b/libstdc++-v3/libsupc++/del_ops.cc index 1fa24f22dc34..e452c7cf19eb 100644 --- a/libstdc++-v3/libsupc++/del_ops.cc +++ b/libstdc++-v3/libsupc++/del_ops.cc @@ -28,7 +28,7 @@ #include "new" _GLIBCXX_WEAK_DEFINITION void -operator delete(void* ptr, std::size_t) _GLIBCXX_USE_NOEXCEPT +operator delete(void* ptr, std::size_t) noexcept { ::operator delete (ptr); } diff --git a/libstdc++-v3/libsupc++/del_opsa.cc b/libstdc++-v3/libsupc++/del_opsa.cc index 1984fce9c906..2cadb4b6ae42 100644 --- a/libstdc++-v3/libsupc++/del_opsa.cc +++ b/libstdc++-v3/libsupc++/del_opsa.cc @@ -27,7 +27,7 @@ #include "new" _GLIBCXX_WEAK_DEFINITION void -operator delete(void* ptr, std::size_t, std::align_val_t al) _GLIBCXX_USE_NOEXCEPT +operator delete(void* ptr, std::size_t, std::align_val_t al) noexcept { ::operator delete (ptr, al); } diff --git a/libstdc++-v3/libsupc++/del_opva.cc b/libstdc++-v3/libsupc++/del_opva.cc index dc727b38c243..a539ccc81878 100644 --- a/libstdc++-v3/libsupc++/del_opva.cc +++ b/libstdc++-v3/libsupc++/del_opva.cc @@ -30,7 +30,7 @@ #pragma GCC diagnostic ignored "-Wsized-deallocation" _GLIBCXX_WEAK_DEFINITION void -operator delete[] (void *ptr, std::align_val_t al) _GLIBCXX_USE_NOEXCEPT +operator delete[] (void *ptr, std::align_val_t al) noexcept { ::operator delete (ptr, al); } diff --git a/libstdc++-v3/libsupc++/del_opvant.cc b/libstdc++-v3/libsupc++/del_opvant.cc index 86406b253b1e..892c8b7999b3 100644 --- a/libstdc++-v3/libsupc++/del_opvant.cc +++ b/libstdc++-v3/libsupc++/del_opvant.cc @@ -27,7 +27,7 @@ #include "new" _GLIBCXX_WEAK_DEFINITION void -operator delete[] (void *ptr, std::align_val_t al, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT +operator delete[] (void *ptr, std::align_val_t al, const std::nothrow_t&) noexcept { ::operator delete[] (ptr, al); } diff --git a/libstdc++-v3/libsupc++/del_opvnt.cc b/libstdc++-v3/libsupc++/del_opvnt.cc index ed06d2e82d50..f050526093b4 100644 --- a/libstdc++-v3/libsupc++/del_opvnt.cc +++ b/libstdc++-v3/libsupc++/del_opvnt.cc @@ -27,7 +27,7 @@ #include "new" _GLIBCXX_WEAK_DEFINITION void -operator delete[] (void *ptr, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT +operator delete[] (void *ptr, const std::nothrow_t&) noexcept { - ::operator delete (ptr); + ::operator delete[] (ptr); } diff --git a/libstdc++-v3/libsupc++/del_opvs.cc b/libstdc++-v3/libsupc++/del_opvs.cc index 2be94d6562c1..4b1e161fb257 100644 --- a/libstdc++-v3/libsupc++/del_opvs.cc +++ b/libstdc++-v3/libsupc++/del_opvs.cc @@ -28,7 +28,7 @@ #include "new" _GLIBCXX_WEAK_DEFINITION void -operator delete[] (void *ptr, std::size_t) _GLIBCXX_USE_NOEXCEPT +operator delete[] (void *ptr, std::size_t) noexcept { ::operator delete[] (ptr); } diff --git a/libstdc++-v3/libsupc++/del_opvsa.cc b/libstdc++-v3/libsupc++/del_opvsa.cc index 02f184524624..00ea369ee690 100644 --- a/libstdc++-v3/libsupc++/del_opvsa.cc +++ b/libstdc++-v3/libsupc++/del_opvsa.cc @@ -27,7 +27,7 @@ #include "new" _GLIBCXX_WEAK_DEFINITION void -operator delete[] (void *ptr, std::size_t, std::align_val_t al) _GLIBCXX_USE_NOEXCEPT +operator delete[] (void *ptr, std::size_t, std::align_val_t al) noexcept { ::operator delete[] (ptr, al); } diff --git a/libstdc++-v3/libsupc++/new_op.cc b/libstdc++-v3/libsupc++/new_op.cc index 3caa0bab2eaa..df77950cd97d 100644 --- a/libstdc++-v3/libsupc++/new_op.cc +++ b/libstdc++-v3/libsupc++/new_op.cc @@ -44,7 +44,7 @@ operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc) void *p; /* malloc (0) is unpredictable; avoid it. */ - if (sz == 0) + if (__builtin_expect (sz == 0, false)) sz = 1; while ((p = malloc (sz)) == 0) diff --git a/libstdc++-v3/libsupc++/new_opa.cc b/libstdc++-v3/libsupc++/new_opa.cc index a27ff843ca1c..aa3e5dc4ce5d 100644 --- a/libstdc++-v3/libsupc++/new_opa.cc +++ b/libstdc++-v3/libsupc++/new_opa.cc @@ -101,7 +101,6 @@ aligned_alloc (std::size_t al, std::size_t sz) _GLIBCXX_WEAK_DEFINITION void * operator new (std::size_t sz, std::align_val_t al) { - void *p; std::size_t align = (std::size_t)al; /* Alignment must be a power of two. */ @@ -125,8 +124,9 @@ operator new (std::size_t sz, std::align_val_t al) sz += align - rem; #endif - using __gnu_cxx::aligned_alloc; - while ((p = aligned_alloc (align, sz)) == 0) + void *p; + + while ((p = __gnu_cxx::aligned_alloc (align, sz)) == nullptr) { new_handler handler = std::get_new_handler (); if (! handler) diff --git a/libstdc++-v3/libsupc++/new_opant.cc b/libstdc++-v3/libsupc++/new_opant.cc index b4458402d028..f08ae0c2ac48 100644 --- a/libstdc++-v3/libsupc++/new_opant.cc +++ b/libstdc++-v3/libsupc++/new_opant.cc @@ -29,7 +29,7 @@ _GLIBCXX_WEAK_DEFINITION void* operator new(std::size_t sz, std::align_val_t al, const std::nothrow_t&) - _GLIBCXX_USE_NOEXCEPT + noexcept { __try { @@ -37,6 +37,6 @@ operator new(std::size_t sz, std::align_val_t al, const std::nothrow_t&) } __catch(...) { - return 0; + return nullptr; } } diff --git a/libstdc++-v3/libsupc++/new_opnt.cc b/libstdc++-v3/libsupc++/new_opnt.cc index faab44e66c27..ffe44b939bb5 100644 --- a/libstdc++-v3/libsupc++/new_opnt.cc +++ b/libstdc++-v3/libsupc++/new_opnt.cc @@ -32,28 +32,17 @@ using std::bad_alloc; extern "C" void *malloc (std::size_t); _GLIBCXX_WEAK_DEFINITION void * -operator new (std::size_t sz, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT +operator new (std::size_t sz, const std::nothrow_t&) noexcept { - void *p; - - /* malloc (0) is unpredictable; avoid it. */ - if (sz == 0) - sz = 1; - - while ((p = malloc (sz)) == 0) + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 206. operator new(size_t, nothrow) may become unlinked to ordinary + // operator new if ordinary version replaced + __try { - new_handler handler = std::get_new_handler (); - if (! handler) - return 0; - __try - { - handler (); - } - __catch(const bad_alloc&) - { - return 0; - } + return ::operator new(sz); + } + __catch (...) + { + return nullptr; } - - return p; } diff --git a/libstdc++-v3/libsupc++/new_opvant.cc b/libstdc++-v3/libsupc++/new_opvant.cc index faeb4dc2cb2b..4ba1268fbe9c 100644 --- a/libstdc++-v3/libsupc++/new_opvant.cc +++ b/libstdc++-v3/libsupc++/new_opvant.cc @@ -29,7 +29,7 @@ _GLIBCXX_WEAK_DEFINITION void* operator new[] (std::size_t sz, std::align_val_t al, const std::nothrow_t&) - _GLIBCXX_USE_NOEXCEPT + noexcept { __try { @@ -37,6 +37,6 @@ operator new[] (std::size_t sz, std::align_val_t al, const std::nothrow_t&) } __catch(...) { - return 0; + return nullptr; } } diff --git a/libstdc++-v3/libsupc++/new_opvnt.cc b/libstdc++-v3/libsupc++/new_opvnt.cc index 828a971dafbf..3678b8e1ac93 100644 --- a/libstdc++-v3/libsupc++/new_opvnt.cc +++ b/libstdc++-v3/libsupc++/new_opvnt.cc @@ -27,8 +27,17 @@ #include "new" _GLIBCXX_WEAK_DEFINITION void* -operator new[] (std::size_t sz, const std::nothrow_t& nothrow) - _GLIBCXX_USE_NOEXCEPT +operator new[] (std::size_t sz, const std::nothrow_t&) noexcept { - return ::operator new(sz, nothrow); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 206. operator new(size_t, nothrow) may become unlinked to ordinary + // operator new if ordinary version replaced + __try + { + return ::operator new[](sz); + } + __catch (...) + { + return nullptr; + } } diff --git a/libstdc++-v3/testsuite/18_support/new_nothrow.cc b/libstdc++-v3/testsuite/18_support/new_nothrow.cc new file mode 100644 index 000000000000..362dabf2bcf1 --- /dev/null +++ b/libstdc++-v3/testsuite/18_support/new_nothrow.cc @@ -0,0 +1,183 @@ +// Copyright (C) 2018 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 run } + +#include +#include +#include + +// PR libstdc++/68210 + +struct MyBadAlloc: std::bad_alloc { }; + +static bool new_fail; +static bool bad_alloc_thrown; +static unsigned new_called; +static unsigned delete_called; +static unsigned new_vec_called; +static unsigned delete_vec_called; +static unsigned new_handler_called; + +static void new_handler () +{ + if (new_handler_called++) + throw MyBadAlloc (); +} + +void* operator new (size_t n) +{ + static size_t cntr; + + ++new_called; + + for ( ; ; ) { + if (void *p = new_fail ? 0 : malloc (n + sizeof n)) { + *static_cast(p) = ++cntr; + return static_cast(p) + 1; + } + + if (std::new_handler h = std::set_new_handler (0)) { + std::set_new_handler (h); + h (); + } + else { + bad_alloc_thrown = true; + throw MyBadAlloc (); + } + } +} + +void operator delete (void *p) +{ + ++delete_called; + if (p) + free (static_cast(p) - 1); +} + +void* operator new[] (size_t n) +{ + ++new_vec_called; + return operator new(n); +} + +void operator delete[] (void *p) +{ + ++delete_vec_called; + operator delete(p); +} + +#if __cplusplus >= 201402L +void operator delete (void *p, std::size_t) +{ + ::operator delete(p); +} +void operator delete[] (void *p, std::size_t) +{ + ::operator delete[](p); +} +#endif + +void init() +{ + new_fail = false; + new_called = 0; + delete_called = 0; + new_vec_called = 0; + delete_vec_called = 0; + new_handler_called = 0; + std::set_new_handler (0); +} + +void +test01() +{ + init (); + + void *p = operator new (1, std::nothrow); + + VERIFY (p != 0); + VERIFY (1 == new_called); + VERIFY (0 == new_handler_called); + VERIFY (!bad_alloc_thrown); + + operator delete (p, std::nothrow); + VERIFY( 1 == delete_called ); + + new_fail = true; + p = operator new (1, std::nothrow); + + VERIFY (0 == p); + VERIFY (2 == new_called); + VERIFY (0 == new_handler_called); + VERIFY (bad_alloc_thrown); + + new_fail = true; + bad_alloc_thrown = false; + std::set_new_handler (new_handler); + p = operator new (1, std::nothrow); + + VERIFY (0 == p); + VERIFY (3 == new_called); + VERIFY (2 == new_handler_called); + VERIFY (!bad_alloc_thrown); +} + +void +test02() +{ + init (); + + void *p = operator new[] (1, std::nothrow); + + VERIFY (p != 0); + VERIFY (1 == new_called); + VERIFY (1 == new_vec_called); + VERIFY (0 == new_handler_called); + VERIFY (!bad_alloc_thrown); + + operator delete[] (p, std::nothrow); + VERIFY( 1 == delete_called ); + VERIFY( 1 == delete_vec_called ); + + new_fail = true; + p = operator new[] (1, std::nothrow); + + VERIFY (0 == p); + VERIFY (2 == new_called); + VERIFY (2 == new_vec_called); + VERIFY (0 == new_handler_called); + VERIFY (bad_alloc_thrown); + + new_fail = true; + bad_alloc_thrown = false; + std::set_new_handler (new_handler); + p = operator new[] (1, std::nothrow); + + VERIFY (0 == p); + VERIFY (3 == new_called); + VERIFY (3 == new_vec_called); + VERIFY (2 == new_handler_called); + VERIFY (!bad_alloc_thrown); +} + + +int main() +{ + test01(); + test02(); +}