P0722R3:  Implement library support for destroying delete

Summary:
This provides the `std::destroying_delete_t` declaration in C++2a and after. (Even when the compiler doesn't support the language feature).

However, the feature test macro `__cpp_lib_destroying_delete` is only defined when we have both language support and  C++2a.


Reviewers: ldionne, ckennelly, serge-sans-paille, EricWF

Reviewed By: EricWF

Subscribers: dexonsmith, riccibruno, christof, jwakely, jdoerfert, mclow.lists, ldionne, libcxx-commits

Differential Revision: https://reviews.llvm.org/D55840

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@361572 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/docs/FeatureTestMacroTable.rst b/docs/FeatureTestMacroTable.rst
index 0748c4c..2200a99 100644
--- a/docs/FeatureTestMacroTable.rst
+++ b/docs/FeatureTestMacroTable.rst
@@ -182,7 +182,7 @@
     ------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_swap_algorithms``           *unimplemented*  
     ------------------------------------------------- -----------------
-    ``__cpp_lib_destroying_delete``                   *unimplemented*  
+    ``__cpp_lib_destroying_delete``                   ``201806L``      
     ------------------------------------------------- -----------------
     ``__cpp_lib_erase_if``                            ``201811L``      
     ------------------------------------------------- -----------------
diff --git a/include/new b/include/new
index 4cf4c4c..85e4c4b 100644
--- a/include/new
+++ b/include/new
@@ -33,6 +33,12 @@
 };
 
 enum class align_val_t : size_t {}; // C++17
+
+struct destroying_delete_t { // C++20
+  explicit destroying_delete_t() = default;
+};
+inline constexpr destroying_delete_t destroying_delete{}; // C++20
+
 struct nothrow_t {};
 extern const nothrow_t nothrow;
 typedef void (*new_handler)();
@@ -158,6 +164,15 @@
 #endif
 #endif
 
+#if _LIBCPP_STD_VER > 17
+// Enable the declaration even if the compiler doesn't support the language
+// feature.
+struct destroying_delete_t {
+  explicit destroying_delete_t() = default;
+};
+_LIBCPP_INLINE_VAR constexpr destroying_delete_t destroying_delete{};
+#endif // _LIBCPP_STD_VER > 17
+
 }  // std
 
 #if defined(_LIBCPP_CXX03_LANG)
diff --git a/include/version b/include/version
index 102da67..fe9cfed 100644
--- a/include/version
+++ b/include/version
@@ -220,7 +220,9 @@
 // # define __cpp_lib_concepts                             201806L
 // # define __cpp_lib_constexpr_misc                       201811L
 // # define __cpp_lib_constexpr_swap_algorithms            201806L
-// # define __cpp_lib_destroying_delete                    201806L
+# if _LIBCPP_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L
+#   define __cpp_lib_destroying_delete                  201806L
+# endif
 # define __cpp_lib_erase_if                             201811L
 // # define __cpp_lib_generic_unordered_lookup             201811L
 # define __cpp_lib_interpolate                          201902L
diff --git a/test/std/language.support/support.dynamic/destroying_delete_t.pass.cpp b/test/std/language.support/support.dynamic/destroying_delete_t.pass.cpp
new file mode 100644
index 0000000..d544b0e
--- /dev/null
+++ b/test/std/language.support/support.dynamic/destroying_delete_t.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+//                     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.
+//
+//===----------------------------------------------------------------------===//
+
+// struct destroying_delete_t {
+//   explicit destroying_delete_t() = default;
+// };
+// inline constexpr destroying_delete_t destroying_delete{};
+
+// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17
+
+#include <new>
+
+#include <cassert>
+#include "test_macros.h"
+
+struct A {
+  void *data;
+  A();
+  ~A();
+
+  static A* New();
+  void operator delete(A*, std::destroying_delete_t);
+};
+
+bool A_constructed = false;
+bool A_destroyed = false;
+bool A_destroying_deleted = false;
+
+A::A() {
+  A_constructed = true;
+}
+
+A::~A() {
+  A_destroyed = true;
+}
+
+A* A::New() {
+  return new(::operator new(sizeof(A))) A();
+}
+
+void A::operator delete(A* a, std::destroying_delete_t) {
+  A_destroying_deleted = true;
+  ::operator delete(a);
+}
+
+#ifndef __cpp_lib_destroying_delete
+#error "Expected __cpp_lib_destroying_delete to be defined"
+#elif __cpp_lib_destroying_delete < 201806L
+#error "Unexpected value of __cpp_lib_destroying_delete"
+#endif
+
+int main() {
+  // Ensure that we call the destroying delete and not the destructor.
+  A* ap = A::New();
+  assert(A_constructed);
+  delete ap;
+  assert(!A_destroyed);
+  assert(A_destroying_deleted);
+}
diff --git a/test/std/language.support/support.limits/support.limits.general/new.version.pass.cpp b/test/std/language.support/support.limits/support.limits.general/new.version.pass.cpp
index 5f012cd..294c29a 100644
--- a/test/std/language.support/support.limits/support.limits.general/new.version.pass.cpp
+++ b/test/std/language.support/support.limits/support.limits.general/new.version.pass.cpp
@@ -72,16 +72,16 @@
 
 #elif TEST_STD_VER > 17
 
-# if !defined(_LIBCPP_VERSION)
+# if TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L
 #   ifndef __cpp_lib_destroying_delete
 #     error "__cpp_lib_destroying_delete should be defined in c++2a"
 #   endif
 #   if __cpp_lib_destroying_delete != 201806L
 #     error "__cpp_lib_destroying_delete should have the value 201806L in c++2a"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_destroying_delete
-#     error "__cpp_lib_destroying_delete should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_destroying_delete should not be defined when TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L is not defined!"
 #   endif
 # endif
 
diff --git a/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
index 0ed0a51..3503051 100644
--- a/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
+++ b/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
@@ -1662,16 +1662,16 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L
 #   ifndef __cpp_lib_destroying_delete
 #     error "__cpp_lib_destroying_delete should be defined in c++2a"
 #   endif
 #   if __cpp_lib_destroying_delete != 201806L
 #     error "__cpp_lib_destroying_delete should have the value 201806L in c++2a"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_destroying_delete
-#     error "__cpp_lib_destroying_delete should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_destroying_delete should not be defined when TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L is not defined!"
 #   endif
 # endif
 
diff --git a/utils/generate_feature_test_macro_components.py b/utils/generate_feature_test_macro_components.py
index f5e770d..0384d32 100755
--- a/utils/generate_feature_test_macro_components.py
+++ b/utils/generate_feature_test_macro_components.py
@@ -483,7 +483,14 @@
      "c++2a": 201806L,
    },
    "headers": ["new"],
-   "unimplemented": True,
+   "depends":
+      "TEST_STD_VER > 17"
+      " && defined(__cpp_impl_destroying_delete)"
+      " && __cpp_impl_destroying_delete >= 201806L",
+   "internal_depends":
+      "_LIBCPP_STD_VER > 17"
+      " && defined(__cpp_impl_destroying_delete)"
+      " && __cpp_impl_destroying_delete >= 201806L",
    },
   {"name": "__cpp_lib_three_way_comparison",
    "values": {