Fix handling of usual deallocation functions in various configuratios.
Clang allows users to enable or disable various types of allocation
and deallocation regardless of the C++ dialect. When extended new/delete
overloads are enabled in older dialects, we need to treat them as if
they're usual.
Also, disabling one usual deallocation form shouldn't
disable any others. For example, disabling aligned allocation in C++2a
should have no effect on destroying delete.
llvm-svn: 352980
diff --git a/clang/test/SemaCXX/extended-usual-deallocation-functions.cpp b/clang/test/SemaCXX/extended-usual-deallocation-functions.cpp
new file mode 100644
index 0000000..87b68d3
--- /dev/null
+++ b/clang/test/SemaCXX/extended-usual-deallocation-functions.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -fexceptions -std=c++2a -fsized-deallocation -fno-aligned-allocation -verify %s
+// RUN: %clang_cc1 -fexceptions -std=c++17 -fsized-deallocation -fno-aligned-allocation -verify %s
+// RUN: %clang_cc1 -fexceptions -std=c++14 -fsized-deallocation -faligned-allocation -DHAS_ALIGN -verify %s
+// RUN: %clang_cc1 -fexceptions -std=c++11 -fsized-deallocation -faligned-allocation -DHAS_ALIGN -verify %s
+
+// Test that we handle aligned deallocation, sized deallocation, and destroying
+// delete as usual deallocation functions even if they are used as extensions
+// prior to C++17.
+
+namespace std {
+using size_t = decltype(sizeof(0));
+enum class align_val_t : size_t;
+
+struct destroying_delete_t {
+ struct __construct { explicit __construct() = default; };
+ explicit destroying_delete_t(__construct) {}
+};
+
+inline constexpr destroying_delete_t destroying_delete(destroying_delete_t::__construct());
+}
+
+// FIXME: Should destroying delete really be on in all dialects by default?
+struct A {
+ void operator delete(void*) = delete;
+ void operator delete(A*, std::destroying_delete_t) = delete; // expected-note {{deleted}}
+};
+void ATest(A* a) { delete a; } // expected-error {{deleted}}
+
+struct B {
+ void operator delete(void*) = delete; // expected-note {{deleted}}
+ void operator delete(void*, std::size_t) = delete;
+};
+void BTest(B *b) { delete b; }// expected-error {{deleted}}
+
+
+struct alignas(32) C {
+#ifndef HAS_ALIGN
+ // expected-note@+2 {{deleted}}
+#endif
+ void operator delete(void*) = delete;
+#ifdef HAS_ALIGN
+ // expected-note@+2 {{deleted}}
+#endif
+ void operator delete(void*, std::align_val_t) = delete;
+};
+void CTest(C *c) { delete c; } // expected-error {{deleted}}
+
+struct D {
+ void operator delete(void*) = delete;
+ void operator delete(D*, std::destroying_delete_t) = delete; // expected-note {{deleted}}
+ void operator delete(D*, std::destroying_delete_t, std::size_t) = delete;
+ void operator delete(D*, std::destroying_delete_t, std::align_val_t) = delete;
+ void operator delete(D*, std::destroying_delete_t, std::size_t, std::align_val_t) = delete;
+};
+void DTest(D *d) { delete d; } // expected-error {{deleted}}
+
+struct alignas(64) E {
+ void operator delete(void*) = delete;
+ void operator delete(E*, std::destroying_delete_t) = delete;
+ void operator delete(E*, std::destroying_delete_t, std::size_t) = delete;
+ void operator delete(E*, std::destroying_delete_t, std::align_val_t) = delete;
+ void operator delete(E*, std::destroying_delete_t, std::size_t, std::align_val_t) = delete;
+#ifdef HAS_ALIGN
+ // expected-note@-3 {{deleted}}
+#else
+ // expected-note@-7 {{deleted}}
+#endif
+};
+void ETest(E *e) { delete e; } // expected-error {{deleted}}