More on adding sized deallocation functions in libc++: Continuing from r229281, this adds version guards and test cases.
llvm-svn: 229968
Cr-Mirrored-From: sso://chromium.googlesource.com/_direct/external/github.com/llvm/llvm-project
Cr-Mirrored-Commit: e4864e0bc2cf9f6a0e450d654f9775cf15e5aa7d
diff --git a/include/new b/include/new
index 7268b4b..29c1a83 100644
--- a/include/new
+++ b/include/new
@@ -139,9 +139,11 @@
;
_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, std::size_t __sz) _NOEXCEPT;
_LIBCPP_NEW_DELETE_VIS void operator delete(void* __p, const std::nothrow_t&) _NOEXCEPT;
+#if defined(_LIBCPP_BUILDING_NEW) || _LIBCPP_STD_VER >= 14
+_LIBCPP_NEW_DELETE_VIS void operator delete(void* __p, std::size_t __sz) _NOEXCEPT;
_LIBCPP_NEW_DELETE_VIS void operator delete(void* __p, std::size_t __sz, const std::nothrow_t&) _NOEXCEPT;
+#endif
_LIBCPP_NEW_DELETE_VIS void* operator new[](std::size_t __sz)
#if !__has_feature(cxx_noexcept)
@@ -150,9 +152,11 @@
;
_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, std::size_t __sz) _NOEXCEPT;
_LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p, const std::nothrow_t&) _NOEXCEPT;
+#if defined(_LIBCPP_BUILDING_NEW) || _LIBCPP_STD_VER >= 14
+_LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p, std::size_t __sz) _NOEXCEPT;
_LIBCPP_NEW_DELETE_VIS void operator delete[](void* __p, std::size_t __sz, const std::nothrow_t&) _NOEXCEPT;
+#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;}
diff --git a/src/new.cpp b/src/new.cpp
index a9b3733..c46d073 100644
--- a/src/new.cpp
+++ b/src/new.cpp
@@ -126,13 +126,6 @@
_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, const std::nothrow_t&) _NOEXCEPT
{
::operator delete(ptr);
@@ -140,13 +133,20 @@
_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS
void
-operator delete(void* ptr, size_t, const std::nothrow_t&) _NOEXCEPT
+operator delete(void* ptr, size_t) _NOEXCEPT
{
::operator delete(ptr);
}
_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS
void
+operator delete(void* ptr, size_t, const std::nothrow_t& nt) _NOEXCEPT
+{
+ ::operator delete(ptr, nt);
+}
+
+_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS
+void
operator delete[] (void* ptr) _NOEXCEPT
{
::operator delete(ptr);
@@ -154,13 +154,6 @@
_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, const std::nothrow_t&) _NOEXCEPT
{
::operator delete[](ptr);
@@ -168,11 +161,18 @@
_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS
void
-operator delete[] (void* ptr, size_t, const std::nothrow_t&) _NOEXCEPT
+operator delete[] (void* ptr, size_t) _NOEXCEPT
{
::operator delete[](ptr);
}
+_LIBCPP_WEAK _LIBCPP_NEW_DELETE_VIS
+void
+operator delete[] (void* ptr, size_t, const std::nothrow_t& nt) _NOEXCEPT
+{
+ ::operator delete[](ptr, nt);
+}
+
#endif // !__GLIBCXX__
namespace std
diff --git a/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp b/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.pass.cpp
new file mode 100644
index 0000000..57e6cd6
--- /dev/null
+++ b/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array.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.
+//
+//===----------------------------------------------------------------------===//
+
+// test sized operator delete[] by replacing unsized operator delete[].
+
+// UNSUPPORTED: asan, msan
+
+#include <new>
+#include <cstddef>
+#include <cstdlib>
+#include <cassert>
+#include <limits>
+
+int delete_called = 0;
+int delete_nothrow_called = 0;
+
+void operator delete[](void* p) throw()
+{
+ ++delete_called;
+ delete_nothrow_called;
+ std::free(p);
+}
+
+void operator delete[](void* p, const std::nothrow_t&) throw()
+{
+ ++delete_nothrow_called;
+ std::free(p);
+}
+
+int new_handler_called = 0;
+
+void new_handler()
+{
+ ++new_handler_called;
+ std::set_new_handler(0);
+}
+
+int A_constructed = 0;
+
+struct A
+{
+ A() {++A_constructed;}
+ ~A() {--A_constructed;}
+};
+
+int main()
+{
+ std::set_new_handler(new_handler);
+ try
+ {
+ void* vp = operator new [] (std::numeric_limits<std::size_t>::max());
+ assert(false);
+ }
+ catch (std::bad_alloc&)
+ {
+ assert(new_handler_called == 1);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ A* ap = new A[3];
+ assert(ap);
+ assert(A_constructed == 3);
+ assert(!delete_called);
+ assert(!delete_nothrow_called);
+ delete [] ap;
+ assert(A_constructed == 0);
+ assert(delete_called == 1);
+ assert(!delete_nothrow_called);
+}
diff --git a/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_nothrow.pass.cpp b/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_nothrow.pass.cpp
new file mode 100644
index 0000000..05dc013
--- /dev/null
+++ b/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_nothrow.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 nothrow sized operator delete[] by replacing
+// nothrow unsized operator delete[].
+
+// UNSUPPORTED: asan, msan
+
+#include <new>
+#include <cstddef>
+#include <cstdlib>
+#include <cassert>
+#include <limits>
+
+int delete_called = 0;
+int delete_nothrow_called = 0;
+
+void operator delete[](void* p) throw()
+{
+ ++delete_called;
+ std::free(p);
+}
+
+void operator delete[](void* p, const std::nothrow_t&) throw()
+{
+ ++delete_nothrow_called;
+ std::free(p);
+}
+
+int new_handler_called = 0;
+
+void new_handler()
+{
+ ++new_handler_called;
+ std::set_new_handler(0);
+}
+
+bool A_constructed = false;
+
+struct A
+{
+ A() {A_constructed = true;}
+ ~A() {A_constructed = false;}
+};
+
+struct BadA : public A {
+ BadA() { throw std::bad_alloc(); }
+};
+
+int main()
+{
+ std::set_new_handler(new_handler);
+ try
+ {
+ void*volatile vp = operator new [] (std::numeric_limits<std::size_t>::max(), std::nothrow);
+ assert(new_handler_called == 1);
+ assert(vp == 0);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ try
+ {
+ A* ap = new(std::nothrow) BadA [3];
+ assert(false);
+ }
+ catch (...)
+ {
+ assert(!A_constructed);
+ assert(!delete_called);
+ assert(delete_nothrow_called == 1);
+ }
+}
diff --git a/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_nothrow_replace.pass.cpp b/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_nothrow_replace.pass.cpp
new file mode 100644
index 0000000..6a5320b
--- /dev/null
+++ b/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_nothrow_replace.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 nothrow sized operator delete[] replacement.
+
+// Note that sized delete operator definitions below are simply ignored
+// when sized deallocation is not supported, e.g., prior to C++14.
+
+// UNSUPPORTED: asan, msan
+
+#include <new>
+#include <cstddef>
+#include <cstdlib>
+#include <cassert>
+#include <limits>
+
+int unsized_delete_called = 0;
+int unsized_delete_nothrow_called = 0;
+int sized_delete_called = 0;
+int sized_delete_nothrow_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::size_t) throw()
+{
+ ++sized_delete_called;
+ std::free(p);
+}
+
+void operator delete[](void* p, std::size_t, const std::nothrow_t&) throw()
+{
+ ++sized_delete_nothrow_called;
+ std::free(p);
+}
+
+int new_handler_called = 0;
+
+void new_handler()
+{
+ ++new_handler_called;
+ std::set_new_handler(0);
+}
+
+bool A_constructed = false;
+
+struct A
+{
+ A() {A_constructed = true;}
+ ~A() {A_constructed = false;}
+};
+
+struct BadA : public A {
+ BadA() { throw std::bad_alloc(); }
+};
+
+int main()
+{
+ std::set_new_handler(new_handler);
+ try
+ {
+ void*volatile vp = operator new [] (std::numeric_limits<std::size_t>::max(), std::nothrow);
+ assert(new_handler_called == 1);
+ assert(vp == 0);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ try
+ {
+ A* ap = new(std::nothrow) BadA [3];
+ assert(false);
+ }
+ catch (...)
+ {
+ assert(!A_constructed);
+#if _LIBCPP_STD_VER >= 14
+ // FIXME: Do we need a version of [Expr.Delete]#10 for nothrow
+ // deallocation functions (selecting sized ones whenever available)?
+ // It is not required by the standard. If it were, the following would
+ // be the expected behaviour (instead of the current one):
+ // assert(!unsized_delete_nothrow_called);
+ // assert(sized_delete_nothrow_called == 1);
+ assert(unsized_delete_nothrow_called == 1);
+ assert(!sized_delete_nothrow_called);
+#else // if _LIBCPP_STD_VER < 14
+ assert(unsized_delete_nothrow_called == 1);
+ assert(!sized_delete_nothrow_called);
+#endif
+ assert(!unsized_delete_called);
+ assert(!sized_delete_called);
+ }
+}
diff --git a/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_replace.pass.cpp b/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_replace.pass.cpp
new file mode 100644
index 0000000..752cd70
--- /dev/null
+++ b/test/std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_replace.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 sized operator delete[] replacement.
+
+// Note that sized delete operator definitions below are simply ignored
+// when sized deallocation is not supported, e.g., prior to C++14.
+
+// UNSUPPORTED: asan, msan
+
+#include <new>
+#include <cstddef>
+#include <cstdlib>
+#include <cassert>
+#include <limits>
+
+int unsized_delete_called = 0;
+int unsized_delete_nothrow_called = 0;
+int sized_delete_called = 0;
+int sized_delete_nothrow_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::size_t) throw()
+{
+ ++sized_delete_called;
+ std::free(p);
+}
+
+void operator delete[](void* p, std::size_t, const std::nothrow_t&) throw()
+{
+ ++sized_delete_nothrow_called;
+ std::free(p);
+}
+
+int new_handler_called = 0;
+
+void new_handler()
+{
+ ++new_handler_called;
+ std::set_new_handler(0);
+}
+
+int A_constructed = 0;
+
+struct A
+{
+ A() {++A_constructed;}
+ ~A() {--A_constructed;}
+};
+
+int main()
+{
+ std::set_new_handler(new_handler);
+ try
+ {
+ void* vp = operator new [] (std::numeric_limits<std::size_t>::max());
+ assert(false);
+ }
+ catch (std::bad_alloc&)
+ {
+ assert(new_handler_called == 1);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ A* ap = new A[3];
+ assert(ap);
+ assert(A_constructed == 3);
+ assert(!unsized_delete_called);
+ assert(!unsized_delete_nothrow_called);
+ assert(!sized_delete_called);
+ assert(!sized_delete_nothrow_called);
+ delete [] ap;
+ assert(A_constructed == 0);
+#if _LIBCPP_STD_VER >= 14
+ assert(!unsized_delete_called);
+ assert(sized_delete_called == 1);
+#else // if _LIBCPP_STD_VER < 14
+ assert(unsized_delete_called == 1);
+ assert(!sized_delete_called);
+#endif
+ assert(!unsized_delete_nothrow_called);
+ assert(!sized_delete_nothrow_called);
+}
diff --git a/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp b/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.pass.cpp
new file mode 100644
index 0000000..6e3a235
--- /dev/null
+++ b/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete.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.
+//
+//===----------------------------------------------------------------------===//
+
+// test sized operator delete by replacing unsized operator delete.
+
+// UNSUPPORTED: asan, msan
+
+#include <new>
+#include <cstddef>
+#include <cstdlib>
+#include <cassert>
+#include <limits>
+
+int delete_called = 0;
+int delete_nothrow_called = 0;
+
+void operator delete(void* p) throw()
+{
+ ++delete_called;
+ delete_nothrow_called;
+ std::free(p);
+}
+
+void operator delete(void* p, const std::nothrow_t&) throw()
+{
+ ++delete_nothrow_called;
+ std::free(p);
+}
+
+int new_handler_called = 0;
+
+void new_handler()
+{
+ ++new_handler_called;
+ std::set_new_handler(0);
+}
+
+bool A_constructed = false;
+
+struct A
+{
+ A() {A_constructed = true;}
+ ~A() {A_constructed = false;}
+};
+
+int main()
+{
+ std::set_new_handler(new_handler);
+ try
+ {
+ void* vp = operator new (std::numeric_limits<std::size_t>::max());
+ assert(false);
+ }
+ catch (std::bad_alloc&)
+ {
+ assert(new_handler_called == 1);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ A* ap = new A;
+ assert(ap);
+ assert(A_constructed);
+ assert(!delete_called);
+ assert(!delete_nothrow_called);
+ delete ap;
+ assert(!A_constructed);
+ assert(delete_called == 1);
+ assert(!delete_nothrow_called);
+}
diff --git a/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_nothrow.pass.cpp b/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_nothrow.pass.cpp
new file mode 100644
index 0000000..690e8af
--- /dev/null
+++ b/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_nothrow.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 nothrow sized operator delete by replacing
+// nothrow unsized operator delete.
+
+// UNSUPPORTED: asan, msan
+
+#include <new>
+#include <cstddef>
+#include <cstdlib>
+#include <cassert>
+#include <limits>
+
+int delete_called = 0;
+int delete_nothrow_called = 0;
+
+void operator delete(void* p) throw()
+{
+ ++delete_called;
+ std::free(p);
+}
+
+void operator delete(void* p, const std::nothrow_t&) throw()
+{
+ ++delete_nothrow_called;
+ std::free(p);
+}
+
+int new_handler_called = 0;
+
+void new_handler()
+{
+ ++new_handler_called;
+ std::set_new_handler(0);
+}
+
+bool A_constructed = false;
+
+struct A
+{
+ A() {A_constructed = true;}
+ ~A() {A_constructed = false;}
+};
+
+struct BadA : public A {
+ BadA() { throw std::bad_alloc(); }
+};
+
+int main()
+{
+ std::set_new_handler(new_handler);
+ try
+ {
+ void* volatile vp = operator new (std::numeric_limits<std::size_t>::max(), std::nothrow);
+ assert(new_handler_called == 1);
+ assert(vp == 0);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ try
+ {
+ A* ap = new(std::nothrow) BadA;
+ assert(false);
+ }
+ catch (...)
+ {
+ assert(!A_constructed);
+ assert(!delete_called);
+ assert(delete_nothrow_called == 1);
+ }
+}
diff --git a/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_nothrow_replace.pass.cpp b/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_nothrow_replace.pass.cpp
new file mode 100644
index 0000000..b5eb9f2
--- /dev/null
+++ b/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_nothrow_replace.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 nothrow sized operator delete replacement.
+
+// Note that sized delete operator definitions below are simply ignored
+// when sized deallocation is not supported, e.g., prior to C++14.
+
+// UNSUPPORTED: asan, msan
+
+#include <new>
+#include <cstddef>
+#include <cstdlib>
+#include <cassert>
+#include <limits>
+
+int unsized_delete_called = 0;
+int unsized_delete_nothrow_called = 0;
+int sized_delete_called = 0;
+int sized_delete_nothrow_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::size_t) throw()
+{
+ ++sized_delete_called;
+ std::free(p);
+}
+
+void operator delete(void* p, std::size_t, const std::nothrow_t&) throw()
+{
+ ++sized_delete_nothrow_called;
+ std::free(p);
+}
+
+int new_handler_called = 0;
+
+void new_handler()
+{
+ ++new_handler_called;
+ std::set_new_handler(0);
+}
+
+bool A_constructed = false;
+
+struct A
+{
+ A() {A_constructed = true;}
+ ~A() {A_constructed = false;}
+};
+
+struct BadA : public A {
+ BadA() { throw std::bad_alloc(); }
+};
+
+int main()
+{
+ std::set_new_handler(new_handler);
+ try
+ {
+ void*volatile vp = operator new (std::numeric_limits<std::size_t>::max(), std::nothrow);
+ assert(new_handler_called == 1);
+ assert(vp == 0);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ try
+ {
+ A* ap = new(std::nothrow) BadA;
+ assert(false);
+ }
+ catch (...)
+ {
+ assert(!A_constructed);
+#if _LIBCPP_STD_VER >= 14
+ // FIXME: Do we need a version of [Expr.Delete]#10 for nothrow
+ // deallocation functions (selecting sized ones whenever available)?
+ // It is not required by the standard. If it were, the following would
+ // be the expected behaviour (instead of the current one):
+ // assert(!unsized_delete_nothrow_called);
+ // assert(sized_delete_nothrow_called == 1);
+ assert(unsized_delete_nothrow_called == 1);
+ assert(!sized_delete_nothrow_called);
+#else // if _LIBCPP_STD_VER < 14
+ assert(unsized_delete_nothrow_called == 1);
+ assert(!sized_delete_nothrow_called);
+#endif
+ assert(!unsized_delete_called);
+ assert(!sized_delete_called);
+ }
+}
diff --git a/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_replace.pass.cpp b/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_replace.pass.cpp
new file mode 100644
index 0000000..4e0d7c7
--- /dev/null
+++ b/test/std/language.support/support.dynamic/new.delete/new.delete.single/sized_delete_replace.pass.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 sized operator delete replacement.
+
+// Note that sized delete operator definitions below are simply ignored
+// when sized deallocation is not supported, e.g., prior to C++14.
+
+// UNSUPPORTED: asan, msan
+
+#include <new>
+#include <cstddef>
+#include <cstdlib>
+#include <cassert>
+#include <limits>
+
+int unsized_delete_called = 0;
+int unsized_delete_nothrow_called = 0;
+int sized_delete_called = 0;
+int sized_delete_nothrow_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::size_t) throw()
+{
+ ++sized_delete_called;
+ std::free(p);
+}
+
+void operator delete(void* p, std::size_t, const std::nothrow_t&) throw()
+{
+ ++sized_delete_nothrow_called;
+ std::free(p);
+}
+
+int new_handler_called = 0;
+
+void new_handler()
+{
+ ++new_handler_called;
+ std::set_new_handler(0);
+}
+
+bool A_constructed = false;
+
+struct A
+{
+ A() {A_constructed = true;}
+ ~A() {A_constructed = false;}
+};
+
+int main()
+{
+ std::set_new_handler(new_handler);
+ try
+ {
+ void* vp = operator new (std::numeric_limits<std::size_t>::max());
+ assert(false);
+ }
+ catch (std::bad_alloc&)
+ {
+ assert(new_handler_called == 1);
+ }
+ catch (...)
+ {
+ assert(false);
+ }
+ A* ap = new A;
+ assert(ap);
+ assert(A_constructed);
+ assert(!unsized_delete_called);
+ assert(!unsized_delete_nothrow_called);
+ assert(!sized_delete_called);
+ assert(!sized_delete_nothrow_called);
+ delete ap;
+ assert(!A_constructed);
+#if _LIBCPP_STD_VER >= 14
+ assert(!unsized_delete_called);
+ assert(sized_delete_called == 1);
+#else // if _LIBCPP_STD_VER < 14
+ assert(unsized_delete_called == 1);
+ assert(!sized_delete_called);
+#endif
+ assert(!unsized_delete_nothrow_called);
+ assert(!sized_delete_nothrow_called);
+}