From 15b56edd626f62b87bb82dd5b6a91fd076691f71 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Fri, 28 Nov 2025 14:03:19 +0100 Subject: [PATCH] [libc++] Make std::allocator always trivially default constructible --- libcxx/docs/ReleaseNotes/22.rst | 4 +++ libcxx/include/__memory/allocator.h | 33 ++++++------------- .../allocator_triviality.compile.pass.cpp | 24 ++++++++++++++ ...triviality.deprecated_abi.compile.pass.cpp | 27 +++++++++++++++ .../allocator_void.trivial.compile.pass.cpp | 26 --------------- .../make_optional_explicit.pass.cpp | 3 -- ...ptional_explicit_initializer_list.pass.cpp | 3 -- 7 files changed, 65 insertions(+), 55 deletions(-) create mode 100644 libcxx/test/libcxx/memory/allocator_triviality.compile.pass.cpp create mode 100644 libcxx/test/libcxx/memory/allocator_triviality.deprecated_abi.compile.pass.cpp delete mode 100644 libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 9f1e3d570f254..d9722e4aa0cf7 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -121,5 +121,9 @@ ABI Affecting Changes - ``ranges::iota_view`` is now aware of ``__int128``. This causes ``iota_view::difference_type`` to change from ``long long`` to ``__int128`` in some cases. +- ``std::allocator`` is now trivially default constructible. The behaviour can be reverted by defining + ``_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR``. Please inform the libc++ team if you need this flag, since it will + be removed in LLVM 24 if there is no evidence that it's required. + Build System Changes -------------------- diff --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h index 52f4122a9bf5f..1c96a2ab64578 100644 --- a/libcxx/include/__memory/allocator.h +++ b/libcxx/include/__memory/allocator.h @@ -14,7 +14,6 @@ #include <__cstddef/ptrdiff_t.h> #include <__cstddef/size_t.h> #include <__memory/addressof.h> -#include <__memory/allocate_at_least.h> #include <__memory/allocator_traits.h> #include <__new/allocate.h> #include <__new/exceptions.h> @@ -51,33 +50,21 @@ class allocator { }; #endif // _LIBCPP_STD_VER <= 17 -// This class provides a non-trivial default constructor to the class that derives from it -// if the condition is satisfied. -// -// The second template parameter exists to allow giving a unique type to __non_trivial_if, -// which makes it possible to avoid breaking the ABI when making this a base class of an -// existing class. Without that, imagine we have classes D1 and D2, both of which used to -// have no base classes, but which now derive from __non_trivial_if. The layout of a class -// that inherits from both D1 and D2 will change because the two __non_trivial_if base -// classes are not allowed to share the same address. -// -// By making those __non_trivial_if base classes unique, we work around this problem and -// it is safe to start deriving from __non_trivial_if in existing classes. -template -struct __non_trivial_if {}; +template +struct __non_trivially_default_constructible_if {}; template -struct __non_trivial_if { - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __non_trivial_if() _NOEXCEPT {} +struct __non_trivially_default_constructible_if { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __non_trivially_default_constructible_if() {} }; -// allocator -// -// Note: For ABI compatibility between C++20 and previous standards, we make -// allocator trivial in C++20. - template -class allocator : private __non_trivial_if::value, allocator<_Tp> > { +class allocator +// TODO(LLVM 24): Remove the opt-out +#ifdef _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR + : __non_trivially_default_constructible_if::value, allocator<_Tp> > +#endif +{ static_assert(!is_const<_Tp>::value, "std::allocator does not support const types"); static_assert(!is_volatile<_Tp>::value, "std::allocator does not support volatile types"); diff --git a/libcxx/test/libcxx/memory/allocator_triviality.compile.pass.cpp b/libcxx/test/libcxx/memory/allocator_triviality.compile.pass.cpp new file mode 100644 index 0000000000000..ff298963e074a --- /dev/null +++ b/libcxx/test/libcxx/memory/allocator_triviality.compile.pass.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// + +// Make sure that std::allocator is trivial. + +// + +#include +#include +#include + +static_assert(std::is_trivially_default_constructible >::value, ""); +static_assert(std::is_trivially_default_constructible >::value, ""); +static_assert(std::is_trivially_default_constructible >::value, ""); + +static_assert(std::is_trivially_copyable >::value, ""); +static_assert(std::is_trivially_copyable >::value, ""); +static_assert(std::is_trivially_copyable >::value, ""); diff --git a/libcxx/test/libcxx/memory/allocator_triviality.deprecated_abi.compile.pass.cpp b/libcxx/test/libcxx/memory/allocator_triviality.deprecated_abi.compile.pass.cpp new file mode 100644 index 0000000000000..be2a1840ec903 --- /dev/null +++ b/libcxx/test/libcxx/memory/allocator_triviality.deprecated_abi.compile.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// + +// Make sure that std::allocator is not trivial if _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR if defined. +// std::allocator _should_ still be trivial, since it has always been trivial. + +// + +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR + +#include +#include +#include + +static_assert(!std::is_trivially_default_constructible >::value, ""); +static_assert(!std::is_trivially_default_constructible >::value, ""); +static_assert(std::is_trivially_default_constructible >::value, ""); + +static_assert(std::is_trivially_copyable >::value, ""); +static_assert(std::is_trivially_copyable >::value, ""); +static_assert(std::is_trivially_copyable >::value, ""); diff --git a/libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp b/libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp deleted file mode 100644 index b7dfc190e8e91..0000000000000 --- a/libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// Make sure that std::allocator is trivial. This was the case before C++20 -// with the std::allocator explicit specialization, and this test makes sure -// that we maintain that property across all standards. -// -// This is important since triviality has implications on how the type is passed -// as a function argument in the ABI. - -#include -#include - -typedef std::allocator A1; -struct A2 : std::allocator { }; - -static_assert(std::is_trivially_default_constructible::value, ""); -static_assert(std::is_trivially_copyable::value, ""); - -static_assert(std::is_trivially_default_constructible::value, ""); -static_assert(std::is_trivially_copyable::value, ""); diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp index 5dd1d6f0b3380..b08fce2b701e2 100644 --- a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit.pass.cpp @@ -12,9 +12,6 @@ // template // constexpr optional make_optional(Args&&... args); -// GCC crashes on this file, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120577 -// XFAIL: gcc-15 - #include #include #include diff --git a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit_initializer_list.pass.cpp b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit_initializer_list.pass.cpp index 5ddb229ad9268..80371d6333712 100644 --- a/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit_initializer_list.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.specalg/make_optional_explicit_initializer_list.pass.cpp @@ -12,9 +12,6 @@ // template // constexpr optional make_optional(initializer_list il, Args&&... args); -// GCC crashes on this file, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120577 -// XFAIL: gcc-15 - #include #include #include