Index: docs/FeatureTestMacroTable.rst =================================================================== --- docs/FeatureTestMacroTable.rst +++ 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`` ------------------------------------------------- ----------------- Index: docs/UsingLibcxx.rst =================================================================== --- docs/UsingLibcxx.rst +++ docs/UsingLibcxx.rst @@ -248,6 +248,14 @@ warning saying that `std::auto_ptr` is deprecated. If the macro is defined, no warning will be emitted. By default, this macro is not defined. +**_LIBCPP_ENABLE_DESTROYING_DELETE_EXT**: + This macro enables library support for C++20's destroying delete as an extension in older dialects. + When the macro is enabled, ```` always declares ``std::destroying_delete_t`` and + ``std::destroying_delete``. Note that the compiler must also support destroying delete as an + extension in order for the language feature to actually be used. All versions of Clang that support + destroying delete support using it in older dialects. + + C++17 Specific Configuration Macros ----------------------------------- **_LIBCPP_ENABLE_CXX17_REMOVED_FEATURES**: Index: include/new =================================================================== --- include/new +++ 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,19 @@ #endif #endif +#if _LIBCPP_STD_VER > 17 || defined(_LIBCPP_ENABLE_DESTROYING_DELETE_EXT) +struct destroying_delete_t { +#ifndef _LIBCPP_CXX03_LANG + explicit destroying_delete_t() = default; +#endif +}; +#ifndef _LIBCPP_CXX03_LANG +_LIBCPP_INLINE_VAR constexpr destroying_delete_t destroying_delete{}; +#else +const destroying_delete_t destroying_delete = {}; +#endif +#endif // _LIBCPP_HAS_NO_LIBRARY_DESTROYING_DELETE + } // std #if defined(_LIBCPP_CXX03_LANG) Index: include/version =================================================================== --- include/version +++ include/version @@ -219,7 +219,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(_LIBCPP_ENABLE_DESTROYING_DELETE_EXT)) && 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_is_constant_evaluated 201811L Index: test/libcxx/language.support/support.dynamic/destroying_delete_extension.pass.cpp =================================================================== --- /dev/null +++ test/libcxx/language.support/support.dynamic/destroying_delete_extension.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// 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 that when _LIBCPP_ENABLE_DESTROYING_DELETE_EXT is defined the library +// declares `std::destroying_delete_t` and `std::destroying_delete`. +// Additionally test that when the compiler supports destroying delete as an +// extension, then destroying delete works as if it were C++20 + +// MODULES_DEFINES: _LIBCPP_ENABLE_DESTROYING_DELETE_EXT +#define _LIBCPP_ENABLE_DESTROYING_DELETE_EXT + +#include + +#include +#include "test_macros.h" +#if TEST_STD_VER >= 11 +#include "test_convertible.hpp" +#endif + +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); +} + +// Test that we only define __cpp_lib_destroying_delete when the compiler +// provides the extension too. +#ifdef __cpp_impl_destroying_delete +# 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 +#endif // !defined(__cpp_impl_destroying_delete) + + + +void test_destroying_delete_library_extension() { + ASSERT_SAME_TYPE(decltype(std::destroying_delete), const std::destroying_delete_t); + static_assert(std::is_default_constructible::value, ""); +#if TEST_STD_VER > 03 + constexpr auto copy = std::destroying_delete; + ((void)copy); + static_assert(!test_convertible(), ""); +#endif +} + +void test_destroying_delete_language_extension() { + // Ensure that we call the destroying delete and not the destructor. + A* ap = A::New(); + assert(A_constructed); + delete ap; +#ifdef __cpp_lib_destroying_delete + assert(!A_destroyed); + assert(A_destroying_deleted); +#else + assert(A_destroyed); + assert(!A_destroying_deleted); +#endif +} + +int main() { + test_destroying_delete_library_extension(); + test_destroying_delete_language_extension(); +} Index: test/std/language.support/support.dynamic/destroying_delete_t.pass.cpp =================================================================== --- /dev/null +++ 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 + +#include + +#include +#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); +} Index: test/std/language.support/support.limits/support.limits.general/new.version.pass.cpp =================================================================== --- test/std/language.support/support.limits/support.limits.general/new.version.pass.cpp +++ 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(_LIBCPP_ENABLE_DESTROYING_DELETE_EXT)) && 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(_LIBCPP_ENABLE_DESTROYING_DELETE_EXT)) && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L is not defined!" # endif # endif Index: test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp =================================================================== --- test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -1649,16 +1649,16 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) +# if (TEST_STD_VER > 17 || defined(_LIBCPP_ENABLE_DESTROYING_DELETE_EXT)) && 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(_LIBCPP_ENABLE_DESTROYING_DELETE_EXT)) && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L is not defined!" # endif # endif Index: utils/generate_feature_test_macro_components.py =================================================================== --- utils/generate_feature_test_macro_components.py +++ utils/generate_feature_test_macro_components.py @@ -483,7 +483,14 @@ "c++2a": 201806L, }, "headers": ["new"], - "unimplemented": True, + "depends": + "(TEST_STD_VER > 17 || defined(_LIBCPP_ENABLE_DESTROYING_DELETE_EXT))" + " && defined(__cpp_impl_destroying_delete)" + " && __cpp_impl_destroying_delete >= 201806L", + "internal_depends": + "(_LIBCPP_STD_VER > 17 || defined(_LIBCPP_ENABLE_DESTROYING_DELETE_EXT))" + " && defined(__cpp_impl_destroying_delete)" + " && __cpp_impl_destroying_delete >= 201806L", }, {"name": "__cpp_lib_three_way_comparison", "values": {