[libcxx-commits] [libcxx] [libc++] Make std::allocator always trivially default constructible (PR #169914)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Dec 9 07:54:07 PST 2025
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/169914
>From dddba1113d13566f16c7ba0ba41069d9b35c3f29 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
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 | 23 +++++++++++++
...triviality.deprecated_abi.compile.pass.cpp | 26 +++++++++++++++
.../allocator_void.trivial.compile.pass.cpp | 26 ---------------
.../make_optional_explicit.pass.cpp | 3 --
...ptional_explicit_initializer_list.pass.cpp | 3 --
7 files changed, 63 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..30bfc267dceb1 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<void> {
};
#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 <bool _Cond, class _Unique>
-struct __non_trivial_if {};
+template <bool, class _Unique>
+struct __non_trivially_default_constructible_if {};
template <class _Unique>
-struct __non_trivial_if<true, _Unique> {
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __non_trivial_if() _NOEXCEPT {}
+struct __non_trivially_default_constructible_if<true, _Unique> {
+ _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<void> trivial in C++20.
-
template <class _Tp>
-class allocator : private __non_trivial_if<!is_void<_Tp>::value, allocator<_Tp> > {
+class allocator
+// TODO(LLVM 24): Remove the opt-out
+#ifdef _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR
+ : __non_trivially_default_constructible_if<!is_void<_Tp>::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..c8ee25277c27f
--- /dev/null
+++ b/libcxx/test/libcxx/memory/allocator_triviality.compile.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<T> is trivial.
+
+// <memory>
+
+#include <memory>
+#include <string>
+
+static_assert(std::is_trivially_default_constructible<std::allocator<char>>::value, "");
+static_assert(std::is_trivially_default_constructible<std::allocator<std::string>>::value, "");
+static_assert(std::is_trivially_default_constructible<std::allocator<void>>::value, "");
+
+static_assert(std::is_trivially_copyable<std::allocator<char>>::value, "");
+static_assert(std::is_trivially_copyable<std::allocator<std::string>>::value, "");
+static_assert(std::is_trivially_copyable<std::allocator<void>>::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..c8c955e3dab79
--- /dev/null
+++ b/libcxx/test/libcxx/memory/allocator_triviality.deprecated_abi.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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<T> is not trivial if _LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR if defined.
+// std::allocator<void> _should_ still be trivial, since it has always been trivial.
+
+// <memory>
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DEPRECATED_ABI_NON_TRIVIAL_ALLOCATOR
+
+#include <memory>
+#include <string>
+
+static_assert(!std::is_trivially_default_constructible<std::allocator<char>>::value, "");
+static_assert(!std::is_trivially_default_constructible<std::allocator<std::string>>::value, "");
+static_assert(std::is_trivially_default_constructible<std::allocator<void>>::value, "");
+
+static_assert(std::is_trivially_copyable<std::allocator<char>>::value, "");
+static_assert(std::is_trivially_copyable<std::allocator<std::string>>::value, "");
+static_assert(std::is_trivially_copyable<std::allocator<void>>::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<void> is trivial. This was the case before C++20
-// with the std::allocator<void> 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 <memory>
-#include <type_traits>
-
-typedef std::allocator<void> A1;
-struct A2 : std::allocator<void> { };
-
-static_assert(std::is_trivially_default_constructible<A1>::value, "");
-static_assert(std::is_trivially_copyable<A1>::value, "");
-
-static_assert(std::is_trivially_default_constructible<A2>::value, "");
-static_assert(std::is_trivially_copyable<A2>::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 <class T, class... Args>
// constexpr optional<T> make_optional(Args&&... args);
-// GCC crashes on this file, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120577
-// XFAIL: gcc-15
-
#include <cassert>
#include <memory>
#include <optional>
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 <class T, class U, class... Args>
// constexpr optional<T> make_optional(initializer_list<U> il, Args&&... args);
-// GCC crashes on this file, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120577
-// XFAIL: gcc-15
-
#include <cassert>
#include <memory>
#include <optional>
More information about the libcxx-commits
mailing list