From 0ae4f23fdcd8a278c58ea3ae2f062220763bcb66 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Fri, 14 Oct 2016 06:46:30 +0000 Subject: [PATCH] Implement P0035R4 -- Add C++17 aligned allocation functions Summary: This patch implements the library side of P0035R4. The implementation is thanks to @rsmith. In addition to the C++17 implementation, the library implementation can be explicitly turned on using `-faligned-allocation` in all dialects. Reviewers: mclow.lists, rsmith Subscribers: rsmith, cfe-commits Differential Revision: https://reviews.llvm.org/D25591 llvm-svn: 284206 --- libcxx/include/new | 79 ++++++++++--- libcxx/lib/abi/CHANGELOG.TXT | 16 +++ libcxx/lib/abi/x86_64-linux-gnu.abilist | 11 ++ libcxx/src/new.cpp | 126 +++++++++++++++++++-- .../support.dynamic/new_faligned_allocation.sh.cpp | 77 +++++++++++++ libcxx/test/libcxx/test/config.py | 3 + .../support.dynamic/align_val_t.pass.cpp | 34 ++++++ .../delete_align_val_t_replace.pass.cpp | 83 ++++++++++++++ .../new.delete.array/new_align_val_t.pass.cpp | 77 +++++++++++++ .../new_align_val_t_nothrow.pass.cpp | 79 +++++++++++++ .../new_align_val_t_nothrow_replace.pass.cpp | 84 ++++++++++++++ .../new_align_val_t_replace.pass.cpp | 82 ++++++++++++++ .../delete_align_val_t_replace.pass.cpp | 83 ++++++++++++++ .../new.delete.single/new_align_val_t.pass.cpp | 77 +++++++++++++ .../new_align_val_t_nothrow.pass.cpp | 79 +++++++++++++ .../new_align_val_t_nothrow_replace.pass.cpp | 85 ++++++++++++++ .../new_align_val_t_replace.pass.cpp | 82 ++++++++++++++ 17 files changed, 1135 insertions(+), 22 deletions(-) create mode 100644 libcxx/test/libcxx/language.support/support.dynamic/new_faligned_allocation.sh.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/align_val_t.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/delete_align_val_t_replace.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_nothrow.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_nothrow_replace.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_replace.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/delete_align_val_t_replace.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_nothrow.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_nothrow_replace.pass.cpp create mode 100644 libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_replace.pass.cpp diff --git a/libcxx/include/new b/libcxx/include/new index e77d3e6..c42ea08 100644 --- a/libcxx/include/new +++ b/libcxx/include/new @@ -27,18 +27,19 @@ public: virtual const char* what() const noexcept; }; -class bad_array_length : public bad_alloc // C++14 +class bad_array_length : public bad_alloc // FIXME: Not part of C++ { public: bad_array_length() noexcept; }; -class bad_array_new_length : public bad_alloc +class bad_array_new_length : public bad_alloc // C++14 { public: bad_array_new_length() noexcept; }; +enum class align_val_t : size_t {}; // C++17 struct nothrow_t {}; extern const nothrow_t nothrow; typedef void (*new_handler)(); @@ -48,16 +49,34 @@ new_handler get_new_handler() noexcept; } // std void* operator new(std::size_t size); // replaceable +void* operator new(std::size_t size, std::align_val_t alignment); // replaceable, C++17 void* operator new(std::size_t size, const std::nothrow_t&) noexcept; // replaceable +void* operator new(std::size_t size, std::align_val_t alignment, + const std::nothrow_t&) noexcept; // replaceable, C++17 void operator delete(void* ptr) noexcept; // replaceable void operator delete(void* ptr, std::size_t size) noexcept; // replaceable, C++14 +void operator delete(void* ptr, std::align_val_t alignment) noexcept; // replaceable, C++17 +void operator delete(void* ptr, std::size_t size, + std::align_val_t alignment) noexcept; // replaceable, C++17 void operator delete(void* ptr, const std::nothrow_t&) noexcept; // replaceable +void operator delete(void* ptr, std:align_val_t alignment, + const std::nothrow_t&) noexcept; // replaceable, C++17 void* operator new[](std::size_t size); // replaceable +void* operator new[](std::size_t size, + std::align_val_t alignment) noexcept; // replaceable, C++17 void* operator new[](std::size_t size, const std::nothrow_t&) noexcept; // replaceable +void* operator new[](std::size_t size, std::align_val_t alignment, + const std::nothrow_t&) noexcept; // replaceable, C++17 void operator delete[](void* ptr) noexcept; // replaceable void operator delete[](void* ptr, std::size_t size) noexcept; // replaceable, C++14 +void operator delete[](void* ptr, + std::align_val_t alignment) noexcept; // replaceable, C++17 +void operator delete[](void* ptr, std::size_t size, + std::align_val_t alignment) noexcept; // replaceable, C++17 void operator delete[](void* ptr, const std::nothrow_t&) noexcept; // replaceable +void operator delete[](void* ptr, std::align_val_t alignment, + const std::nothrow_t&) noexcept; // replaceable, C++17 void* operator new (std::size_t size, void* ptr) noexcept; void* operator new[](std::size_t size, void* ptr) noexcept; @@ -79,6 +98,16 @@ void operator delete[](void* ptr, void*) noexcept; #pragma GCC system_header #endif +#if !(defined(_LIBCPP_BUILDING_NEW) || _LIBCPP_STD_VER >= 14 || \ + (defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309)) +# define _LIBCPP_HAS_NO_SIZED_DEALLOCATION +#endif + +#if !(defined(_LIBCPP_BUILDING_NEW) || _LIBCPP_STD_VER > 14 || \ + (defined(__cpp_aligned_new) && __cpp_aligned_new >= 201606)) +# define _LIBCPP_HAS_NO_ALIGNED_ALLOCATION +#endif + namespace std // purposefully not using versioning namespace { @@ -117,6 +146,14 @@ public: #endif // defined(_LIBCPP_BUILDING_NEW) || (_LIBCPP_STD_VER > 11) +#ifndef _LIBCPP_HAS_NO_ALIGNED_ALLOCATION +#ifndef _LIBCPP_CXX03_LANG +enum class _LIBCPP_ENUM_VIS align_val_t : size_t { }; +#else +enum align_val_t { __zero = 0, __max = (size_t)-1 }; +#endif +#endif + struct _LIBCPP_TYPE_VIS nothrow_t {}; extern _LIBCPP_FUNC_VIS const nothrow_t nothrow; typedef void (*new_handler)(); @@ -131,32 +168,46 @@ _LIBCPP_FUNC_VIS new_handler get_new_handler() _NOEXCEPT; # define _LIBCPP_NEW_DELETE_VIS _LIBCPP_FUNC_VIS #endif -_LIBCPP_NEW_DELETE_VIS void* operator new(std::size_t __sz) #if !__has_feature(cxx_noexcept) - throw(std::bad_alloc) +#define _THROW_BAD_ALLOC throw(std::bad_alloc) +#else +#define _THROW_BAD_ALLOC #endif -; + +_LIBCPP_NEW_DELETE_VIS void* operator new(std::size_t __sz) _THROW_BAD_ALLOC; _LIBCPP_NEW_DELETE_VIS void* operator new(std::size_t __sz, const std::nothrow_t&) _NOEXCEPT _NOALIAS; _LIBCPP_NEW_DELETE_VIS void operator delete(void* __p) _NOEXCEPT; _LIBCPP_NEW_DELETE_VIS void operator delete(void* __p, const std::nothrow_t&) _NOEXCEPT; -#if defined(_LIBCPP_BUILDING_NEW) || _LIBCPP_STD_VER >= 14 || \ - (defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309) +#ifndef _LIBCPP_HAS_NO_SIZED_DEALLOCATION _LIBCPP_NEW_DELETE_VIS void operator delete(void* __p, std::size_t __sz) _NOEXCEPT; #endif -_LIBCPP_NEW_DELETE_VIS void* operator new[](std::size_t __sz) -#if !__has_feature(cxx_noexcept) - throw(std::bad_alloc) -#endif -; +_LIBCPP_NEW_DELETE_VIS void* operator new[](std::size_t __sz) _THROW_BAD_ALLOC; _LIBCPP_NEW_DELETE_VIS void* operator new[](std::size_t __sz, const std::nothrow_t&) _NOEXCEPT _NOALIAS; _LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p) _NOEXCEPT; _LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p, const std::nothrow_t&) _NOEXCEPT; -#if defined(_LIBCPP_BUILDING_NEW) || _LIBCPP_STD_VER >= 14 || \ - (defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309) +#ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION _LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p, std::size_t __sz) _NOEXCEPT; #endif +#ifndef _LIBCPP_HAS_NO_ALIGNED_ALLOCATION +_LIBCPP_NEW_DELETE_VIS void* operator new(std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC; +_LIBCPP_NEW_DELETE_VIS void* operator new(std::size_t __sz, std::align_val_t, const std::nothrow_t&) _NOEXCEPT _NOALIAS; +_LIBCPP_NEW_DELETE_VIS void operator delete(void* __p, std::align_val_t) _NOEXCEPT; +_LIBCPP_NEW_DELETE_VIS void operator delete(void* __p, std::align_val_t, const std::nothrow_t&) _NOEXCEPT; +#ifndef _LIBCPP_HAS_NO_SIZED_DEALLOCATION +_LIBCPP_NEW_DELETE_VIS void operator delete(void* __p, std::size_t __sz, std::align_val_t) _NOEXCEPT; +#endif + +_LIBCPP_NEW_DELETE_VIS void* operator new[](std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC; +_LIBCPP_NEW_DELETE_VIS void* operator new[](std::size_t __sz, std::align_val_t, const std::nothrow_t&) _NOEXCEPT _NOALIAS; +_LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p, std::align_val_t) _NOEXCEPT; +_LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p, std::align_val_t, const std::nothrow_t&) _NOEXCEPT; +#ifndef _LIBCPP_HAS_NO_SIZED_DEALLOCATION +_LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p, std::size_t __sz, std::align_val_t) _NOEXCEPT; +#endif +#endif + inline _LIBCPP_INLINE_VISIBILITY void* operator new (std::size_t, void* __p) _NOEXCEPT {return __p;} inline _LIBCPP_INLINE_VISIBILITY void* operator new[](std::size_t, void* __p) _NOEXCEPT {return __p;} inline _LIBCPP_INLINE_VISIBILITY void operator delete (void*, void*) _NOEXCEPT {} diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT index c98a3e0..87a4ee5 100644 --- a/libcxx/lib/abi/CHANGELOG.TXT +++ b/libcxx/lib/abi/CHANGELOG.TXT @@ -16,6 +16,22 @@ New entries should be added directly below the "Version" header. Version 4.0 ----------- +* rTBD - Implement C++17 aligned allocation in + + x86_64-linux-gnu + ---------------- + Symbol added: posix_memalign@GLIBC_2.2.5 + Symbol added: _ZdaPvSt11align_val_t + Symbol added: _ZdlPvSt11align_val_t + Symbol added: _ZnamSt11align_val_t + Symbol added: _ZdaPvmSt11align_val_t + Symbol added: _ZdlPvmSt11align_val_t + Symbol added: _ZdlPvSt11align_val_tRKSt9nothrow_t + Symbol added: _ZnwmSt11align_val_tRKSt9nothrow_t + Symbol added: _ZnamSt11align_val_tRKSt9nothrow_t + Symbol added: _ZdaPvSt11align_val_tRKSt9nothrow_t + Symbol added: _ZnwmSt11align_val_t + * r283980 - Implement C++17 x86_64-linux-gnu diff --git a/libcxx/lib/abi/x86_64-linux-gnu.abilist b/libcxx/lib/abi/x86_64-linux-gnu.abilist index 7820b2d..8efa8a3 100644 --- a/libcxx/lib/abi/x86_64-linux-gnu.abilist +++ b/libcxx/lib/abi/x86_64-linux-gnu.abilist @@ -1874,14 +1874,24 @@ {'type': 'FUNC', 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev'} {'type': 'FUNC', 'name': '_ZdaPv'} {'type': 'FUNC', 'name': '_ZdaPvRKSt9nothrow_t'} +{'type': 'FUNC', 'name': '_ZdaPvSt11align_val_t'} +{'type': 'FUNC', 'name': '_ZdaPvSt11align_val_tRKSt9nothrow_t'} {'type': 'FUNC', 'name': '_ZdaPvm'} +{'type': 'FUNC', 'name': '_ZdaPvmSt11align_val_t'} {'type': 'FUNC', 'name': '_ZdlPv'} {'type': 'FUNC', 'name': '_ZdlPvRKSt9nothrow_t'} +{'type': 'FUNC', 'name': '_ZdlPvSt11align_val_t'} +{'type': 'FUNC', 'name': '_ZdlPvSt11align_val_tRKSt9nothrow_t'} {'type': 'FUNC', 'name': '_ZdlPvm'} +{'type': 'FUNC', 'name': '_ZdlPvmSt11align_val_t'} {'type': 'FUNC', 'name': '_Znam'} {'type': 'FUNC', 'name': '_ZnamRKSt9nothrow_t'} +{'type': 'FUNC', 'name': '_ZnamSt11align_val_t'} +{'type': 'FUNC', 'name': '_ZnamSt11align_val_tRKSt9nothrow_t'} {'type': 'FUNC', 'name': '_Znwm'} {'type': 'FUNC', 'name': '_ZnwmRKSt9nothrow_t'} +{'type': 'FUNC', 'name': '_ZnwmSt11align_val_t'} +{'type': 'FUNC', 'name': '_ZnwmSt11align_val_tRKSt9nothrow_t'} {'type': 'FUNC', 'name': '__assert_fail@GLIBC_2.2.5'} {'type': 'FUNC', 'name': '__ctype_get_mb_cur_max@GLIBC_2.2.5'} {'type': 'FUNC', 'name': '__cxa_allocate_exception'} @@ -1943,6 +1953,7 @@ {'type': 'FUNC', 'name': 'nanosleep@GLIBC_2.2.5'} {'type': 'FUNC', 'name': 'newlocale@GLIBC_2.3'} {'type': 'FUNC', 'name': 'open@GLIBC_2.2.5'} +{'type': 'FUNC', 'name': 'posix_memalign@GLIBC_2.2.5'} {'type': 'FUNC', 'name': 'pthread_cond_broadcast@GLIBC_2.3.2'} {'type': 'FUNC', 'name': 'pthread_cond_destroy@GLIBC_2.3.2'} {'type': 'FUNC', 'name': 'pthread_cond_signal@GLIBC_2.3.2'} diff --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp index d5db461..8e002d4 100644 --- a/libcxx/src/new.cpp +++ b/libcxx/src/new.cpp @@ -39,10 +39,7 @@ _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void * -operator new(std::size_t size) -#if !__has_feature(cxx_noexcept) - throw(std::bad_alloc) -#endif +operator new(std::size_t size) _THROW_BAD_ALLOC { if (size == 0) size = 1; @@ -65,6 +62,34 @@ operator new(std::size_t size) } _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void * +operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC +{ + if (size == 0) + size = 1; + if (static_cast(alignment) < sizeof(void*)) + alignment = std::align_val_t(sizeof(void*)); + void* p; + while (::posix_memalign(&p, static_cast(alignment), size) != 0) + { + // If posix_memalign fails and there is a new_handler, + // call it to try free up memory. + std::new_handler nh = std::get_new_handler(); + if (nh) + nh(); + else { +#ifndef _LIBCPP_NO_EXCEPTIONS + throw std::bad_alloc(); +#else + p = nullptr; // posix_memalign doesn't initialize 'p' on failure + break; +#endif + } + } + return p; +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void* operator new(size_t size, const std::nothrow_t&) _NOEXCEPT { @@ -85,16 +110,39 @@ operator new(size_t size, const std::nothrow_t&) _NOEXCEPT _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void* -operator new[](size_t size) -#if !__has_feature(cxx_noexcept) - throw(std::bad_alloc) -#endif +operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT +{ + void* p = 0; +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + p = ::operator new(size, alignment); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + } +#endif // _LIBCPP_NO_EXCEPTIONS + return p; +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void* +operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); } _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void* +operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC +{ + return ::operator new(size, alignment); +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void* operator new[](size_t size, const std::nothrow_t&) _NOEXCEPT { void* p = 0; @@ -113,6 +161,25 @@ operator new[](size_t size, const std::nothrow_t&) _NOEXCEPT } _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void* +operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT +{ + void* p = 0; +#ifndef _LIBCPP_NO_EXCEPTIONS + try + { +#endif // _LIBCPP_NO_EXCEPTIONS + p = ::operator new[](size, alignment); +#ifndef _LIBCPP_NO_EXCEPTIONS + } + catch (...) + { + } +#endif // _LIBCPP_NO_EXCEPTIONS + return p; +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void operator delete(void* ptr) _NOEXCEPT { @@ -122,6 +189,14 @@ operator delete(void* ptr) _NOEXCEPT _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void +operator delete(void* ptr, std::align_val_t) _NOEXCEPT +{ + if (ptr) + ::free(ptr); +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void operator delete(void* ptr, const std::nothrow_t&) _NOEXCEPT { ::operator delete(ptr); @@ -129,6 +204,13 @@ operator delete(void* ptr, const std::nothrow_t&) _NOEXCEPT _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void +operator delete(void* ptr, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT +{ + ::operator delete(ptr, alignment); +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void operator delete(void* ptr, size_t) _NOEXCEPT { ::operator delete(ptr); @@ -136,6 +218,13 @@ operator delete(void* ptr, size_t) _NOEXCEPT _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void +operator delete(void* ptr, size_t, std::align_val_t alignment) _NOEXCEPT +{ + ::operator delete(ptr, alignment); +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void operator delete[] (void* ptr) _NOEXCEPT { ::operator delete(ptr); @@ -143,6 +232,13 @@ operator delete[] (void* ptr) _NOEXCEPT _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void +operator delete[] (void* ptr, std::align_val_t alignment) _NOEXCEPT +{ + ::operator delete(ptr, alignment); +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void operator delete[] (void* ptr, const std::nothrow_t&) _NOEXCEPT { ::operator delete[](ptr); @@ -150,11 +246,25 @@ operator delete[] (void* ptr, const std::nothrow_t&) _NOEXCEPT _LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS void +operator delete[] (void* ptr, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT +{ + ::operator delete[](ptr, alignment); +} + +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void operator delete[] (void* ptr, size_t) _NOEXCEPT { ::operator delete[](ptr); } +_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS +void +operator delete[] (void* ptr, size_t, std::align_val_t alignment) _NOEXCEPT +{ + ::operator delete[](ptr, alignment); +} + #endif // !__GLIBCXX__ namespace std diff --git a/libcxx/test/libcxx/language.support/support.dynamic/new_faligned_allocation.sh.cpp b/libcxx/test/libcxx/language.support/support.dynamic/new_faligned_allocation.sh.cpp new file mode 100644 index 0000000..04b4000 --- /dev/null +++ b/libcxx/test/libcxx/language.support/support.dynamic/new_faligned_allocation.sh.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// test libc++'s implementation of align_val_t, and the relevent new/delete +// overloads in all dialects when -faligned-allocation is present. + +// REQUIRES: -faligned-allocation + +// RUN: %build -faligned-allocation +// RUN: %run + +#include +#include +#include +#include + +#include "test_macros.h" + +int main() { + { + static_assert(std::is_enum::value, ""); + typedef std::underlying_type::type UT; + static_assert((std::is_same::value), ""); + } + { + static_assert((!std::is_constructible::value), ""); +#if TEST_STD_VER >= 11 + static_assert(!std::is_constructible::value, ""); +#else + static_assert((std::is_constructible::value), ""); +#endif + } + { + std::align_val_t a = std::align_val_t(0); + std::align_val_t b = std::align_val_t(32); + assert(a != b); + assert(a == std::align_val_t(0)); + assert(b == std::align_val_t(32)); + } + { + void *ptr = ::operator new(1, std::align_val_t(128)); + assert(ptr); + assert(reinterpret_cast(ptr) % 128 == 0); + ::operator delete(ptr, std::align_val_t(128)); + } + { + void *ptr = ::operator new(1, std::align_val_t(128), std::nothrow); + assert(ptr); + assert(reinterpret_cast(ptr) % 128 == 0); + ::operator delete(ptr, std::align_val_t(128), std::nothrow); + } + { + void *ptr = ::operator new[](1, std::align_val_t(128)); + assert(ptr); + assert(reinterpret_cast(ptr) % 128 == 0); + ::operator delete[](ptr, std::align_val_t(128)); + } + { + void *ptr = ::operator new[](1, std::align_val_t(128), std::nothrow); + assert(ptr); + assert(reinterpret_cast(ptr) % 128 == 0); + ::operator delete[](ptr, std::align_val_t(128), std::nothrow); + } +#ifndef TEST_HAS_NO_RTTI + { + // Check that libc++ doesn't define align_val_t in a versioning namespace. + // And that it mangles the same in C++03 through C++17 + assert(typeid(std::align_val_t).name() == std::string("St11align_val_t")); + } +#endif +} \ No newline at end of file diff --git a/libcxx/test/libcxx/test/config.py b/libcxx/test/libcxx/test/config.py index a786c3f..9b1ded4 100644 --- a/libcxx/test/libcxx/test/config.py +++ b/libcxx/test/libcxx/test/config.py @@ -313,6 +313,9 @@ class Configuration(object): if self.cxx.hasCompileFlag('-fsized-deallocation'): self.config.available_features.add('fsized-deallocation') + if self.cxx.hasCompileFlag('-faligned-allocation'): + self.config.available_features.add('-faligned-allocation') + if self.get_lit_bool('has_libatomic', False): self.config.available_features.add('libatomic') diff --git a/libcxx/test/std/language.support/support.dynamic/align_val_t.pass.cpp b/libcxx/test/std/language.support/support.dynamic/align_val_t.pass.cpp new file mode 100644 index 0000000..0a19de9 --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/align_val_t.pass.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// enum class align_val_t : size_t {} + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +#include + +#include "test_macros.h" + +int main() { + { + static_assert(std::is_enum::value, ""); + static_assert(std::is_same::type, std::size_t>::value, ""); + static_assert(!std::is_constructible::value, ""); + static_assert(!std::is_constructible::value, ""); + } + { + constexpr auto a = std::align_val_t(0); + constexpr auto b = std::align_val_t(32); + constexpr auto c = std::align_val_t(-1); + static_assert(a != b, ""); + static_assert(a == std::align_val_t(0), ""); + static_assert(b == std::align_val_t(32), ""); + static_assert(static_cast(c) == (std::size_t)-1, ""); + } +} \ No newline at end of file diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/delete_align_val_t_replace.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/delete_align_val_t_replace.pass.cpp new file mode 100644 index 0000000..6d7ef7a --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/delete_align_val_t_replace.pass.cpp @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// test aligned operator delete replacement. + +// UNSUPPORTED: sanitizer-new-delete, c++98, c++03, c++11, c++14 + +// Older Clang versions do not support this +// XFAIL: clang-3, apple-clang + +// None of the current GCC compilers support this. +// XFAIL: gcc-4, gcc-5, gcc-6 + +#include +#include +#include +#include + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +int unsized_delete_called = 0; +int unsized_delete_nothrow_called = 0; +int aligned_delete_called = 0; + +void reset() { + unsized_delete_called = 0; + unsized_delete_nothrow_called = 0; + aligned_delete_called = 0; +} + +void operator delete(void* p) throw() +{ + ++unsized_delete_called; + std::free(p); +} + +void operator delete(void* p, const std::nothrow_t&) throw() +{ + ++unsized_delete_nothrow_called; + std::free(p); +} + +void operator delete [] (void* p, std::align_val_t a) throw() +{ + ++aligned_delete_called; + std::free(p); +} + +struct alignas(OverAligned) A {}; +struct alignas(std::max_align_t) B {}; + +int main() +{ + { + B *x = new B; + assert(0 == unsized_delete_called); + assert(0 == unsized_delete_nothrow_called); + assert(0 == aligned_delete_called); + + delete x; + assert(1 == unsized_delete_called); + assert(0 == unsized_delete_nothrow_called); + assert(0 == aligned_delete_called); + } + reset(); + { + A *x = new A; + assert(0 == unsized_delete_called); + assert(0 == unsized_delete_nothrow_called); + assert(0 == aligned_delete_called); + + delete x; + assert(0 == unsized_delete_called); + assert(0 == unsized_delete_nothrow_called); + assert(1 == aligned_delete_called); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t.pass.cpp new file mode 100644 index 0000000..69be9b3 --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// test operator new + +// asan and msan will not call the new handler. +// UNSUPPORTED: sanitizer-new-delete + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +int new_handler_called = 0; + +void new_handler() +{ + ++new_handler_called; + std::set_new_handler(0); +} + +int A_constructed = 0; + +struct alignas(OverAligned) A +{ + A() { ++A_constructed;} + ~A() { --A_constructed;} +}; + +void test_throw_max_size() { +#ifndef TEST_HAS_NO_EXCEPTIONS + std::set_new_handler(new_handler); + try + { + void* vp = operator new[] (std::numeric_limits::max(), + static_cast(32)); + ((void)vp); + assert(false); + } + catch (std::bad_alloc&) + { + assert(new_handler_called == 1); + } + catch (...) + { + assert(false); + } +#endif +} + +int main() +{ + { + A* ap = new A[2]; + assert(ap); + assert(reinterpret_cast(ap) % OverAligned == 0); + assert(A_constructed == 2); + delete [] ap; + assert(A_constructed == 0); + } + { + test_throw_max_size(); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_nothrow.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_nothrow.pass.cpp new file mode 100644 index 0000000..76b5034 --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_nothrow.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// test operator new (nothrow) + +// asan and msan will not call the new handler. +// UNSUPPORTED: sanitizer-new-delete + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +int new_handler_called = 0; + +void new_handler() +{ + ++new_handler_called; + std::set_new_handler(0); +} + +int A_constructed = 0; + +struct alignas(OverAligned) A +{ + A() { ++A_constructed; } + ~A() { --A_constructed; } +}; + +void test_max_alloc() { + std::set_new_handler(new_handler); + auto do_test = []() { + void* vp = operator new [](std::numeric_limits::max(), + std::align_val_t(OverAligned), + std::nothrow); + assert(new_handler_called == 1); + assert(vp == 0); + }; +#ifndef TEST_HAS_NO_EXCEPTIONS + try + { + do_test(); + } + catch (...) + { + assert(false); + } +#else + do_test(); +#endif +} + +int main() +{ + { + A* ap = new(std::nothrow) A[3]; + assert(ap); + assert(reinterpret_cast(ap) % OverAligned == 0); + assert(A_constructed == 3); + delete [] ap; + assert(!A_constructed); + } + { + test_max_alloc(); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_nothrow_replace.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_nothrow_replace.pass.cpp new file mode 100644 index 0000000..299be7d --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_nothrow_replace.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// test operator new nothrow by replacing only operator new + +// UNSUPPORTED: sanitizer-new-delete + +// TODO Investigate why UBSAN prevents nothrow new from calling our replacement. +// XFAIL: ubsan + +#include +#include +#include +#include +#include + + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +int A_constructed = 0; + +struct alignas(OverAligned) A +{ + A() {++A_constructed;} + ~A() {--A_constructed;} +}; + +int B_constructed = 0; + +struct B { + std::max_align_t member; + B() { ++B_constructed; } + ~B() { --B_constructed; } +}; + +int new_called = 0; +alignas(OverAligned) char Buff[OverAligned * 3]; + +void* operator new[](std::size_t s, std::align_val_t a) throw(std::bad_alloc) +{ + assert(!new_called); + assert(s <= sizeof(Buff)); + assert(static_cast(a) == OverAligned); + ++new_called; + return Buff; +} + +void operator delete[](void* p, std::align_val_t a) throw() +{ + assert(p == Buff); + assert(static_cast(a) == OverAligned); + assert(new_called); + --new_called; +} + +int main() +{ + { + A* ap = new (std::nothrow) A[2]; + assert(ap); + assert(A_constructed == 2); + assert(new_called); + delete [] ap; + assert(A_constructed == 0); + assert(!new_called); + } + { + B* bp = new (std::nothrow) B[2]; + assert(bp); + assert(B_constructed == 2); + assert(!new_called); + delete [] bp; + assert(!new_called); + assert(!B_constructed); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_replace.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_replace.pass.cpp new file mode 100644 index 0000000..6ea23ff --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.array/new_align_val_t_replace.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// test operator new replacement + +// UNSUPPORTED: sanitizer-new-delete + +#include +#include +#include +#include +#include +#include + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +int A_constructed = 0; + +struct alignas(OverAligned) A { + A() { ++A_constructed;} + ~A() { --A_constructed;} +}; + + +int B_constructed = 0; + +struct alignas(std::max_align_t) B +{ + std::max_align_t member; + B() { ++B_constructed;} + ~B() { --B_constructed;} +}; + +int new_called = 0; + +alignas(OverAligned) char DummyData[OverAligned * 4]; + +void* operator new[](std::size_t s, std::align_val_t a) throw(std::bad_alloc) +{ + assert(new_called == 0); // We already allocated + assert(s <= sizeof(DummyData)); + assert(static_cast(a) == OverAligned); + ++new_called; + return DummyData; +} + +void operator delete[](void* p, std::align_val_t a) throw() +{ + assert(new_called == 1); + --new_called; + assert(p == DummyData); +} + + +int main() +{ + { + A* ap = new A[3]; + assert(ap); + assert(A_constructed == 3); + assert(new_called); + delete [] ap; + assert(!A_constructed); + assert(!new_called); + } + { + B* bp = new B[3]; + assert(bp); + assert(B_constructed == 3); + assert(!new_called); + delete [] bp; + assert(!new_called); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/delete_align_val_t_replace.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/delete_align_val_t_replace.pass.cpp new file mode 100644 index 0000000..11d2901 --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/delete_align_val_t_replace.pass.cpp @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// test aligned operator delete replacement. + +// UNSUPPORTED: sanitizer-new-delete, c++98, c++03, c++11, c++14 + +// Older Clang versions do not support this +// XFAIL: clang-3, apple-clang + +// None of the current GCC compilers support this. +// XFAIL: gcc-4, gcc-5, gcc-6 + +#include +#include +#include +#include + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +int unsized_delete_called = 0; +int unsized_delete_nothrow_called = 0; +int aligned_delete_called = 0; + +void reset() { + unsized_delete_called = 0; + unsized_delete_nothrow_called = 0; + aligned_delete_called = 0; +} + +void operator delete(void* p) throw() +{ + ++unsized_delete_called; + std::free(p); +} + +void operator delete(void* p, const std::nothrow_t&) throw() +{ + ++unsized_delete_nothrow_called; + std::free(p); +} + +void operator delete(void* p, std::align_val_t a) throw() +{ + ++aligned_delete_called; + std::free(p); +} + +struct alignas(OverAligned) A {}; +struct alignas(std::max_align_t) B {}; + +int main() +{ + { + B *x = new B; + assert(0 == unsized_delete_called); + assert(0 == unsized_delete_nothrow_called); + assert(0 == aligned_delete_called); + + delete x; + assert(1 == unsized_delete_called); + assert(0 == unsized_delete_nothrow_called); + assert(0 == aligned_delete_called); + } + reset(); + { + A *x = new A; + assert(0 == unsized_delete_called); + assert(0 == unsized_delete_nothrow_called); + assert(0 == aligned_delete_called); + + delete x; + assert(0 == unsized_delete_called); + assert(0 == unsized_delete_nothrow_called); + assert(1 == aligned_delete_called); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t.pass.cpp new file mode 100644 index 0000000..1be78fb --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// test operator new + +// asan and msan will not call the new handler. +// UNSUPPORTED: sanitizer-new-delete + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +int new_handler_called = 0; + +void new_handler() +{ + ++new_handler_called; + std::set_new_handler(0); +} + +bool A_constructed = false; + +struct alignas(OverAligned) A +{ + A() {A_constructed = true;} + ~A() {A_constructed = false;} +}; + +void test_throw_max_size() { +#ifndef TEST_HAS_NO_EXCEPTIONS + std::set_new_handler(new_handler); + try + { + void* vp = operator new (std::numeric_limits::max(), + static_cast(32)); + ((void)vp); + assert(false); + } + catch (std::bad_alloc&) + { + assert(new_handler_called == 1); + } + catch (...) + { + assert(false); + } +#endif +} + +int main() +{ + { + A* ap = new A; + assert(ap); + assert(reinterpret_cast(ap) % OverAligned == 0); + assert(A_constructed); + delete ap; + assert(!A_constructed); + } + { + test_throw_max_size(); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_nothrow.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_nothrow.pass.cpp new file mode 100644 index 0000000..89e5dc8 --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_nothrow.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// test operator new (nothrow) + +// asan and msan will not call the new handler. +// UNSUPPORTED: sanitizer-new-delete + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +int new_handler_called = 0; + +void new_handler() +{ + ++new_handler_called; + std::set_new_handler(0); +} + +bool A_constructed = false; + +struct alignas(OverAligned) A +{ + A() {A_constructed = true;} + ~A() {A_constructed = false;} +}; + +void test_max_alloc() { + std::set_new_handler(new_handler); + auto do_test = []() { + void* vp = operator new (std::numeric_limits::max(), + std::align_val_t(OverAligned), + std::nothrow); + assert(new_handler_called == 1); + assert(vp == 0); + }; +#ifndef TEST_HAS_NO_EXCEPTIONS + try + { + do_test(); + } + catch (...) + { + assert(false); + } +#else + do_test(); +#endif +} + +int main() +{ + { + A* ap = new(std::nothrow) A; + assert(ap); + assert(reinterpret_cast(ap) % OverAligned == 0); + assert(A_constructed); + delete ap; + assert(!A_constructed); + } + { + test_max_alloc(); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_nothrow_replace.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_nothrow_replace.pass.cpp new file mode 100644 index 0000000..7a52e98 --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_nothrow_replace.pass.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// test operator new nothrow by replacing only operator new + +// UNSUPPORTED: sanitizer-new-delete + +// TODO Investigate why UBSAN prevents nothrow new from calling our replacement. +// XFAIL: ubsan + +#include +#include +#include +#include +#include + + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +bool A_constructed = false; + +struct alignas(OverAligned) A +{ + A() {A_constructed = true;} + ~A() {A_constructed = false;} +}; + +bool B_constructed = false; + +struct B { + std::max_align_t member; + B() { B_constructed = true; } + ~B() { B_constructed = false; } +}; + +int new_called = 0; +alignas(OverAligned) char Buff[OverAligned * 2]; + +void* operator new(std::size_t s, std::align_val_t a) throw(std::bad_alloc) +{ + assert(!new_called); + assert(s <= sizeof(Buff)); + assert(static_cast(a) == OverAligned); + ++new_called; + return Buff; +} + +void operator delete(void* p, std::align_val_t a) throw() +{ + assert(p == Buff); + assert(static_cast(a) == OverAligned); + assert(new_called); + --new_called; +} + + +int main() +{ + { + A* ap = new (std::nothrow) A; + assert(ap); + assert(A_constructed); + assert(new_called); + delete ap; + assert(!A_constructed); + assert(!new_called); + } + { + B* bp = new (std::nothrow) B; + assert(bp); + assert(B_constructed); + assert(!new_called); + delete bp; + assert(!new_called); + assert(!B_constructed); + } +} diff --git a/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_replace.pass.cpp b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_replace.pass.cpp new file mode 100644 index 0000000..dd6fc67 --- /dev/null +++ b/libcxx/test/std/language.support/support.dynamic/new.delete/new.delete.single/new_align_val_t_replace.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// test operator new replacement + +// UNSUPPORTED: sanitizer-new-delete + +#include +#include +#include +#include +#include +#include + +constexpr auto OverAligned = alignof(std::max_align_t) * 2; + +bool A_constructed = false; + +struct alignas(OverAligned) A { + A() {A_constructed = true;} + ~A() {A_constructed = false;} +}; + + +bool B_constructed = false; + +struct alignas(std::max_align_t) B +{ + std::max_align_t member; + B() {B_constructed = true;} + ~B() {B_constructed = false;} +}; + +int new_called = 0; + +alignas(OverAligned) char DummyData[OverAligned]; + +void* operator new(std::size_t s, std::align_val_t a) throw(std::bad_alloc) +{ + assert(new_called == 0); // We already allocated + assert(s <= sizeof(DummyData)); + assert(static_cast(a) == OverAligned); + ++new_called; + return DummyData; +} + +void operator delete(void* p, std::align_val_t a) throw() +{ + assert(new_called == 1); + --new_called; + assert(p == DummyData); +} + + +int main() +{ + { + A* ap = new A; + assert(ap); + assert(A_constructed); + assert(new_called); + delete ap; + assert(!A_constructed); + assert(!new_called); + } + { + B* bp = new B; + assert(bp); + assert(B_constructed); + assert(!new_called); + delete bp; + assert(!new_called); + } +} -- 2.7.4