[libcxx-commits] [libcxx] ffcb0a4 - [libc++] Update polymorphic_allocator to never contain a nullptr (#148423)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jul 16 01:04:00 PDT 2025


Author: Nikolas Klauser
Date: 2025-07-16T10:03:57+02:00
New Revision: ffcb0a4559b850c5ba51003ae1877c7c2ae9836f

URL: https://github.com/llvm/llvm-project/commit/ffcb0a4559b850c5ba51003ae1877c7c2ae9836f
DIFF: https://github.com/llvm/llvm-project/commit/ffcb0a4559b850c5ba51003ae1877c7c2ae9836f.diff

LOG: [libc++] Update polymorphic_allocator to never contain a nullptr (#148423)

According to `[mem.poly.allocator.ctor]` the pointer contained in
`polymorphic_allocator` can never be null. The default constructor uses
`get_default_resource()`, which never returns null and the constructor
taking a pointer explicitly has a precondition that the pointer is
non-null.

This patch adds a warning and an assertion in case a user passes a null
pointer to `polymorphic_allocator` as well as marking `resource()` to
never return null.

This also fixes some tests which contained UB.

Fixes #148420

Added: 
    libcxx/test/libcxx/mem/mem.res/ctor.nullptr.assert.pass.cpp
    libcxx/test/libcxx/mem/mem.res/nonnull.verify.cpp

Modified: 
    libcxx/include/__memory_resource/polymorphic_allocator.h
    libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/resource.pass.cpp
    libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/select_on_container_copy_construction.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__memory_resource/polymorphic_allocator.h b/libcxx/include/__memory_resource/polymorphic_allocator.h
index b95c6a37c5c11..1b8711f10811d 100644
--- a/libcxx/include/__memory_resource/polymorphic_allocator.h
+++ b/libcxx/include/__memory_resource/polymorphic_allocator.h
@@ -50,7 +50,9 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
 
   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator() noexcept : __res_(std::pmr::get_default_resource()) {}
 
-  _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(memory_resource* __r) noexcept : __res_(__r) {}
+  _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(memory_resource* _LIBCPP_DIAGNOSE_NULLPTR __r) noexcept : __res_(__r) {
+    _LIBCPP_ASSERT_NON_NULL(__r, "Attempted to pass a nullptr resource to polymorphic_alloator");
+  }
 
   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(const polymorphic_allocator&) = default;
 
@@ -174,7 +176,7 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
     return polymorphic_allocator();
   }
 
-  _LIBCPP_HIDE_FROM_ABI memory_resource* resource() const noexcept { return __res_; }
+  [[__gnu__::__returns_nonnull__]] _LIBCPP_HIDE_FROM_ABI memory_resource* resource() const noexcept { return __res_; }
 
   _LIBCPP_HIDE_FROM_ABI friend bool
   operator==(const polymorphic_allocator& __lhs, const polymorphic_allocator& __rhs) noexcept {

diff  --git a/libcxx/test/libcxx/mem/mem.res/ctor.nullptr.assert.pass.cpp b/libcxx/test/libcxx/mem/mem.res/ctor.nullptr.assert.pass.cpp
new file mode 100644
index 0000000000000..831985f2ce37f
--- /dev/null
+++ b/libcxx/test/libcxx/mem/mem.res/ctor.nullptr.assert.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory_resource>
+
+// Test hardening assertions for std::pmr::polymorphic_allocator.
+
+// REQUIRES: has-unix-headers
+// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
+// UNSUPPORTED: c++03, c++11, c++14
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// We're testing nullptr assertions
+// ADDITIONAL_COMPILE_FLAGS: -Wno-nonnull
+
+#include <memory_resource>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::pmr::polymorphic_allocator<int>(nullptr), "Attempted to pass a nullptr resource to polymorphic_alloator");
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/mem/mem.res/nonnull.verify.cpp b/libcxx/test/libcxx/mem/mem.res/nonnull.verify.cpp
new file mode 100644
index 0000000000000..c5a6c8a3db3cf
--- /dev/null
+++ b/libcxx/test/libcxx/mem/mem.res/nonnull.verify.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// Ensure that passing a nullptr to polymorphic_allocator is diagnosed
+
+#include <memory_resource>
+
+void test() {
+  std::pmr::polymorphic_allocator<int> alloc(nullptr); // expected-warning {{null passed}}
+}

diff  --git a/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/resource.pass.cpp b/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/resource.pass.cpp
index 23865f67a694b..22cb72414e975 100644
--- a/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/resource.pass.cpp
+++ b/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/resource.pass.cpp
@@ -17,11 +17,19 @@
 // memory_resource *
 // polymorphic_allocator<T>::resource() const
 
-#include <memory_resource>
 #include <cassert>
+#include <cstddef>
+#include <memory_resource>
+#include <new>
 
 #include "test_macros.h"
 
+struct resource : std::pmr::memory_resource {
+  void* do_allocate(size_t, size_t) override { TEST_THROW(std::bad_alloc()); }
+  void do_deallocate(void*, size_t, size_t) override { assert(false); }
+  bool do_is_equal(const std::pmr::memory_resource&) const noexcept override { return false; }
+};
+
 int main(int, char**) {
   typedef std::pmr::polymorphic_allocator<void> A;
   {
@@ -29,24 +37,19 @@ int main(int, char**) {
     ASSERT_SAME_TYPE(decltype(a.resource()), std::pmr::memory_resource*);
   }
   {
-    std::pmr::memory_resource* mptr = (std::pmr::memory_resource*)42;
-    A const a(mptr);
-    assert(a.resource() == mptr);
-  }
-  {
-    A const a(nullptr);
-    assert(a.resource() == nullptr);
-    assert(a.resource() == nullptr);
+    resource res;
+    A const a(&res);
+    assert(a.resource() == &res);
   }
   {
     A const a;
     assert(a.resource() == std::pmr::get_default_resource());
   }
   {
-    std::pmr::memory_resource* mptr = (std::pmr::memory_resource*)42;
-    std::pmr::set_default_resource(mptr);
+    resource res;
+    std::pmr::set_default_resource(&res);
     A const a;
-    assert(a.resource() == mptr);
+    assert(a.resource() == &res);
     assert(a.resource() == std::pmr::get_default_resource());
   }
 

diff  --git a/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/select_on_container_copy_construction.pass.cpp b/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/select_on_container_copy_construction.pass.cpp
index 58d6cccd244cf..ccac1178fe516 100644
--- a/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/select_on_container_copy_construction.pass.cpp
+++ b/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/select_on_container_copy_construction.pass.cpp
@@ -17,11 +17,19 @@
 // polymorphic_allocator
 // polymorphic_allocator<T>::select_on_container_copy_construction() const
 
-#include <memory_resource>
 #include <cassert>
+#include <cstddef>
+#include <memory_resource>
+#include <new>
 
 #include "test_macros.h"
 
+struct resource : std::pmr::memory_resource {
+  void* do_allocate(size_t, size_t) override { TEST_THROW(std::bad_alloc()); }
+  void do_deallocate(void*, size_t, size_t) override { assert(false); }
+  bool do_is_equal(const std::pmr::memory_resource&) const noexcept override { return false; }
+};
+
 int main(int, char**) {
   typedef std::pmr::polymorphic_allocator<void> A;
   {
@@ -29,22 +37,12 @@ int main(int, char**) {
     ASSERT_SAME_TYPE(decltype(a.select_on_container_copy_construction()), A);
   }
   {
-    std::pmr::memory_resource* mptr = (std::pmr::memory_resource*)42;
-    A const a(mptr);
-    assert(a.resource() == mptr);
-    A const other = a.select_on_container_copy_construction();
-    assert(other.resource() == std::pmr::get_default_resource());
-    assert(a.resource() == mptr);
-  }
-  {
-    std::pmr::memory_resource* mptr = (std::pmr::memory_resource*)42;
-    std::pmr::set_default_resource(mptr);
-    A const a(nullptr);
-    assert(a.resource() == nullptr);
+    resource res;
+    A const a(&res);
+    assert(a.resource() == &res);
     A const other = a.select_on_container_copy_construction();
     assert(other.resource() == std::pmr::get_default_resource());
-    assert(other.resource() == mptr);
-    assert(a.resource() == nullptr);
+    assert(a.resource() == &res);
   }
 
   return 0;


        


More information about the libcxx-commits mailing list