[libcxx-commits] [libcxx] [libcxx][test-support] Implement fancy_pointer_allocator (PR #196187)

Nikita Belenkiy via libcxx-commits libcxx-commits at lists.llvm.org
Fri May 22 05:02:14 PDT 2026


https://github.com/kitsnet updated https://github.com/llvm/llvm-project/pull/196187

>From 48c3bdf008b47c51658fcc4bcac8f11e94732df6 Mon Sep 17 00:00:00 2001
From: Nikita Belenkiy <nikita.nb.belenkiy at partner.bmw.de>
Date: Tue, 5 May 2026 22:17:44 +0200
Subject: [PATCH 1/2] [libcxx][test-support] Improve thread_unsafe_shared_ptr

Adapting test support class thread_unsafe_shared_ptr for future use
with fancy_pointer_allocator, in particular, in constexpr contexts:

* Implementing the rule of 5;
* Annotating noexcept methods (and removing the workaround in the respective test);
* Making deallocation of its control block ignored by DisableAllocationGuard;
* Adding workaround for (expected later) module export problem (https://llvm.org/PR120108)
---
 .../string.cons/move_noexcept.pass.cpp        |  4 --
 libcxx/test/support/module.modulemap          |  5 +-
 libcxx/test/support/test_allocator.h          | 60 +++++++++++++++----
 3 files changed, 52 insertions(+), 17 deletions(-)

diff --git a/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
index d807cac2c72c8..89502f6652655 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
@@ -32,11 +32,7 @@ int main(int, char**) {
   }
   {
     typedef std::basic_string<char, std::char_traits<char>, limited_allocator<char, 10>> C;
-#if TEST_STD_VER <= 14
-    static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-#else
     static_assert(std::is_nothrow_move_constructible<C>::value, "");
-#endif
   }
 
   return 0;
diff --git a/libcxx/test/support/module.modulemap b/libcxx/test/support/module.modulemap
index 0af147d75ee5d..07acbb6b2e2fd 100644
--- a/libcxx/test/support/module.modulemap
+++ b/libcxx/test/support/module.modulemap
@@ -5,7 +5,10 @@ module test_config {
 
 module test {
   module double_move_tracker    { header "double_move_tracker.h" }
-  module test_allocator         { header "test_allocator.h" }
+  module test_allocator {
+    header "test_allocator.h"
+    export * // TODO: Workaround for https://llvm.org/PR120108
+  }
   module test_iterators         { header "test_iterators.h" }
   module type_algorithms        { header "type_algorithms.h" }
 }
diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h
index f8b622d7f9520..1d05169e1df90 100644
--- a/libcxx/test/support/test_allocator.h
+++ b/libcxx/test/support/test_allocator.h
@@ -393,21 +393,30 @@ namespace detail {
 template <class T>
 class thread_unsafe_shared_ptr {
 public:
-  thread_unsafe_shared_ptr() = default;
-
-  TEST_CONSTEXPR_CXX14 thread_unsafe_shared_ptr(const thread_unsafe_shared_ptr& other) : block(other.block) {
+  // as it's internal and technically not nullable, we don't care about null pointer state
+  // and don't need destructive move
+  TEST_CONSTEXPR_CXX14 thread_unsafe_shared_ptr(const thread_unsafe_shared_ptr& other) TEST_NOEXCEPT
+      : block(other.block) {
     ++block->ref_count;
   }
-
-  TEST_CONSTEXPR_CXX20 ~thread_unsafe_shared_ptr() {
-    --block->ref_count;
-    if (block->ref_count != 0)
-      return;
-    typedef std::allocator_traits<std::allocator<control_block> > allocator_traits;
-    std::allocator<control_block> alloc;
-    allocator_traits::destroy(alloc, block);
-    allocator_traits::deallocate(alloc, block, 1);
+  TEST_CONSTEXPR_CXX14 thread_unsafe_shared_ptr(thread_unsafe_shared_ptr&& other) TEST_NOEXCEPT : block(other.block) {
+    ++block->ref_count;
   }
+  TEST_CONSTEXPR_CXX14 thread_unsafe_shared_ptr& operator=(const thread_unsafe_shared_ptr& other) TEST_NOEXCEPT {
+    // self-assignment safe order
+    ++other.block->ref_count;
+    detach_control_block();
+    block = other.block;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX20 thread_unsafe_shared_ptr& operator=(thread_unsafe_shared_ptr&& other) TEST_NOEXCEPT {
+    // self-assignment safe order
+    ++other.block->ref_count;
+    detach_control_block();
+    block = other.block;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX20 ~thread_unsafe_shared_ptr() TEST_NOEXCEPT { detach_control_block(); }
 
   TEST_CONSTEXPR const T& operator*() const { return block->content; }
   TEST_CONSTEXPR const T* operator->() const { return &block->content; }
@@ -424,6 +433,33 @@ class thread_unsafe_shared_ptr {
     T content;
   };
 
+  thread_unsafe_shared_ptr() = default;
+
+  TEST_CONSTEXPR_CXX20 void detach_control_block() {
+    --block->ref_count;
+    if (block->ref_count != 0)
+      return;
+    typedef std::allocator_traits<std::allocator<control_block> > allocator_traits;
+    std::allocator<control_block> alloc;
+    allocator_traits::destroy(alloc, block);
+#ifdef COUNT_NEW_H
+    // We are called from test code instrumented by counting operator new/operator delete,
+    // which might be surprised by unexpected deallocations we introduce.
+    // Instead of changing all the potentially affected tests, we will just temporarily
+    // disable that counting here.
+    bool disable_allocations = false;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      disable_allocations                  = globalMemCounter.disable_allocations;
+      globalMemCounter.disable_allocations = false;
+    }
+#endif
+    allocator_traits::deallocate(alloc, block, 1);
+#ifdef COUNT_NEW_H
+    if (!TEST_IS_CONSTANT_EVALUATED)
+      globalMemCounter.disable_allocations = disable_allocations;
+#endif
+  }
+
   control_block* block = nullptr;
 
   template <class U, class... Args>

>From d8f41f9ede0660fbc2de6c53dcf684dfdb4a124e Mon Sep 17 00:00:00 2001
From: Nikita Belenkiy <nikita.nb.belenkiy at partner.bmw.de>
Date: Wed, 6 May 2026 22:48:01 +0200
Subject: [PATCH 2/2] [libcxx][test-support] Implement fancy_pointer_allocator

In order to make sure that our further modifications of basic_string will make
it able to handle fancy pointers such as boost::interprocess::offset_ptr,
we need some testable fancy pointers. Unfortunately, offset pointers
themselves are a bad idea for that, as they cannot be constant evaluated.

Instead, we are implementing a test fancy pointer that has nontrivial
special member functions that are C++20 constexpr and have side effects that
can be checked on correctness even in constant evaluated test contexts,
backed by a similarly C++20 constexpr fancy pointer allocator.

The idea is that the allocator has a state that counts how many pointer
instances originated from the allocator exist at the moment, the pointers
in their special methods update this shared state, and the state checks
at its destruction that there exactly 0 pointers are recorded as still
existing. The state is unique per instance of default-constructed
allocator but shared between the instances of copy/move-constructed
allocators.

The resulting test fancy pointer is more demanding than an offset pointer:
an offset pointer may have a trivial destructor, but our test pointer
has a destructor that relies on still existing state. Unfortunately,
our containers are generally laid out in such a way that their allocator
member is destructed earlier than their storage pointer members. To use
the fancy_pointer_allocator in testing, we either need to modify the
containers that they null the storage pointers at the end of destructor
(which will be naturally done for basic_string, which is our main target)
or write our tests in such a way that there is a copy of container's
allocator that survives the container's lifetime.
---
 libcxx/test/support/test_allocator.h | 351 +++++++++++++++++++++++++++
 1 file changed, 351 insertions(+)

diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h
index 1d05169e1df90..09c27e72e7e0e 100644
--- a/libcxx/test/support/test_allocator.h
+++ b/libcxx/test/support/test_allocator.h
@@ -10,6 +10,7 @@
 #define TEST_ALLOCATOR_H
 
 #include <type_traits>
+#include <iterator>
 #include <new>
 #include <memory>
 #include <utility>
@@ -551,4 +552,354 @@ struct SocccAllocator {
   using propagate_on_container_swap            = std::false_type;
 };
 
+template <class T>
+class fancy_pointer {
+public:
+  // For the std::pointer_traits interface.
+  using pointer         = T*;
+  using element_type    = T;
+  using difference_type = std::ptrdiff_t;
+
+  // For the std::iterator_traits interface.
+  using value_type        = typename std::remove_cv<T>::type;
+  using reference         = typename std::add_lvalue_reference<T>::type;
+  using iterator_category = std::random_access_iterator_tag;
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer() TEST_NOEXCEPT : ptr_(nullptr), ctr_(nullptr) {}
+  TEST_CONSTEXPR_CXX20 fancy_pointer(nullptr_t) TEST_NOEXCEPT : ptr_(nullptr), ctr_(nullptr) {}
+  TEST_CONSTEXPR_CXX20 explicit fancy_pointer(T* p, int* c = nullptr) TEST_NOEXCEPT : ptr_(p), ctr_(c) {
+    if (ctr_)
+      ++*ctr_;
+  }
+  template <typename T2>
+  TEST_CONSTEXPR_CXX20 explicit fancy_pointer(const fancy_pointer<T2>& other) TEST_NOEXCEPT
+      : ptr_(static_cast<T*>(other.ptr_)),
+        ctr_(other.ctr_) {
+    if (ctr_)
+      ++*ctr_;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer(const fancy_pointer& other) TEST_NOEXCEPT : ptr_(other.ptr_), ctr_(other.ctr_) {
+    if (ctr_)
+      ++*ctr_;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer(fancy_pointer&& other) TEST_NOEXCEPT : ptr_(other.ptr_), ctr_(other.ctr_) {
+    if (ctr_)
+      ++*ctr_;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer& operator=(const fancy_pointer& other) TEST_NOEXCEPT {
+    if (ctr_)
+      --*ctr_;
+    ptr_ = other.ptr_;
+    ctr_ = other.ctr_;
+    if (ctr_)
+      ++*ctr_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer& operator=(fancy_pointer&& other) TEST_NOEXCEPT {
+    if (ctr_)
+      --*ctr_;
+    ptr_ = other.ptr_;
+    ctr_ = other.ctr_;
+    if (ctr_)
+      ++*ctr_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX20 ~fancy_pointer() TEST_NOEXCEPT {
+    if (ctr_)
+      --*ctr_;
+  }
+
+  TEST_CONSTEXPR_CXX20 operator fancy_pointer<const T>() const TEST_NOEXCEPT {
+    return fancy_pointer<const T>(ptr_, ctr_);
+  }
+
+  TEST_CONSTEXPR_CXX20 operator T*() const TEST_NOEXCEPT { return ptr_; }
+
+  TEST_CONSTEXPR_CXX20 T& operator*() const TEST_NOEXCEPT { return *ptr_; }
+  TEST_CONSTEXPR_CXX20 T* operator->() const TEST_NOEXCEPT { return ptr_; }
+  TEST_CONSTEXPR_CXX20 explicit operator bool() const TEST_NOEXCEPT { return ptr_; }
+  TEST_CONSTEXPR_CXX20 T* get() const TEST_NOEXCEPT { return ptr_; }
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer& operator++() TEST_NOEXCEPT {
+    ++ptr_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer operator++(int) TEST_NOEXCEPT {
+    fancy_pointer tmp(*this);
+    ++ptr_;
+    return tmp;
+  }
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer& operator--() TEST_NOEXCEPT {
+    --ptr_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer operator--(int) TEST_NOEXCEPT {
+    fancy_pointer tmp(*this);
+    --ptr_;
+    return tmp;
+  }
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer& operator+=(difference_type n) TEST_NOEXCEPT {
+    ptr_ += n;
+    return *this;
+  }
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer& operator-=(difference_type n) TEST_NOEXCEPT {
+    ptr_ -= n;
+    return *this;
+  }
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer operator+(difference_type n) const TEST_NOEXCEPT {
+    fancy_pointer tmp(*this);
+    tmp += n;
+    return tmp;
+  }
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer operator-(difference_type n) const TEST_NOEXCEPT {
+    fancy_pointer tmp(*this);
+    tmp -= n;
+    return tmp;
+  }
+
+  TEST_CONSTEXPR_CXX20 reference operator[](difference_type n) const TEST_NOEXCEPT { return ptr_[n]; }
+
+  friend TEST_CONSTEXPR_CXX20 fancy_pointer operator+(difference_type x, fancy_pointer y) TEST_NOEXCEPT {
+    return y + x;
+  }
+
+  static TEST_CONSTEXPR_CXX20 fancy_pointer pointer_to(reference ref) TEST_NOEXCEPT { return fancy_pointer(&ref); }
+
+  T* ptr_;
+  int* ctr_;
+};
+
+template <>
+class fancy_pointer<void> {
+public:
+  using T = void;
+
+  // For the std::pointer_traits interface.
+  using pointer         = T*;
+  using element_type    = T;
+  using difference_type = std::ptrdiff_t;
+
+  // For the std::iterator_traits interface.
+  using value_type        = typename std::remove_cv<T>::type;
+  using reference         = typename std::add_lvalue_reference<T>::type;
+  using iterator_category = std::random_access_iterator_tag;
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer() TEST_NOEXCEPT : ptr_(nullptr), ctr_(nullptr) {}
+  TEST_CONSTEXPR_CXX20 fancy_pointer(nullptr_t) TEST_NOEXCEPT : ptr_(nullptr), ctr_(nullptr) {}
+  TEST_CONSTEXPR_CXX20 explicit fancy_pointer(T* p, int* c = nullptr) TEST_NOEXCEPT : ptr_(p), ctr_(c) {
+    if (ctr_)
+      ++*ctr_;
+  }
+  template <typename T2>
+  TEST_CONSTEXPR_CXX20 explicit fancy_pointer(const fancy_pointer<T2>& other) TEST_NOEXCEPT
+      : ptr_(static_cast<T*>(other.ptr_), other.ctr_) {
+    if (ctr_)
+      ++*ctr_;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer(const fancy_pointer& other) TEST_NOEXCEPT : ptr_(other.ptr_), ctr_(other.ctr_) {
+    if (ctr_)
+      ++*ctr_;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer(fancy_pointer&& other) TEST_NOEXCEPT : ptr_(other.ptr_), ctr_(other.ctr_) {
+    if (ctr_)
+      ++*ctr_;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer& operator=(const fancy_pointer& other) TEST_NOEXCEPT {
+    if (ctr_)
+      --*ctr_;
+    ptr_ = other.ptr_;
+    ctr_ = other.ctr_;
+    if (ctr_)
+      ++*ctr_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX20 fancy_pointer& operator=(fancy_pointer&& other) TEST_NOEXCEPT {
+    if (ctr_)
+      --*ctr_;
+    ptr_ = other.ptr_;
+    ctr_ = other.ctr_;
+    if (ctr_)
+      ++*ctr_;
+    return *this;
+  }
+  TEST_CONSTEXPR_CXX20 ~fancy_pointer() TEST_NOEXCEPT {
+    if (ctr_)
+      --*ctr_;
+  }
+
+  TEST_CONSTEXPR_CXX14 explicit operator bool() const TEST_NOEXCEPT { return ptr_; }
+  TEST_CONSTEXPR_CXX14 T* get() const TEST_NOEXCEPT { return ptr_; }
+
+  T* ptr_;
+  int* ctr_;
+};
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 typename fancy_pointer<T>::difference_type
+operator-(fancy_pointer<T> x, fancy_pointer<T> y) TEST_NOEXCEPT {
+  return x.ptr_ - y.ptr_;
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 typename fancy_pointer<T>::difference_type
+operator-(fancy_pointer<const T> x, fancy_pointer<T> y) TEST_NOEXCEPT {
+  return x.ptr_ - y.ptr_;
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 typename fancy_pointer<T>::difference_type
+operator-(fancy_pointer<T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return x.ptr_ - y.ptr_;
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 typename fancy_pointer<T>::difference_type
+operator-(fancy_pointer<const T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return x.ptr_ - y.ptr_;
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator==(fancy_pointer<T> x, fancy_pointer<T> y) TEST_NOEXCEPT {
+  return x.get() == y.get();
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator==(fancy_pointer<const T> x, fancy_pointer<T> y) TEST_NOEXCEPT {
+  return x.get() == y.get();
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator==(fancy_pointer<T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return x.get() == y.get();
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator==(fancy_pointer<const T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return x.get() == y.get();
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator!=(fancy_pointer<T> x, fancy_pointer<T> y) TEST_NOEXCEPT {
+  return !(x == y);
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator!=(fancy_pointer<const T> x, fancy_pointer<T> y) TEST_NOEXCEPT {
+  return !(x == y);
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator!=(fancy_pointer<T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return !(x == y);
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator!=(fancy_pointer<const T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return !(x == y);
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator>(fancy_pointer<const T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return x.get() == y.get();
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator<(fancy_pointer<const T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return y < x;
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator<=(fancy_pointer<const T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return !(y < x);
+}
+
+template <typename T>
+TEST_CONSTEXPR_CXX20 bool operator>=(fancy_pointer<const T> x, fancy_pointer<const T> y) TEST_NOEXCEPT {
+  return !(x < y);
+}
+
+struct fancy_pointer_statistics {
+  int alive_count = 0;
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer_statistics() {};
+  fancy_pointer_statistics(const fancy_pointer_statistics&)            = delete;
+  fancy_pointer_statistics(fancy_pointer_statistics&&)                 = delete;
+  fancy_pointer_statistics& operator=(const fancy_pointer_statistics&) = delete;
+  fancy_pointer_statistics& operator=(fancy_pointer_statistics&&)      = delete;
+  TEST_CONSTEXPR_CXX20 ~fancy_pointer_statistics() {
+    assert(alive_count == 0 && "unmatched constructor/destructor/assignment?");
+  }
+};
+
+template <class T>
+class fancy_pointer_allocator : public std::allocator<T> {
+  using base = std::allocator<T>;
+
+public:
+  using value_type      = T;
+  using pointer         = fancy_pointer<T>;
+  using const_pointer   = fancy_pointer<const T>;
+  using reference       = value_type&;
+  using const_reference = const value_type&;
+  using size_type       = std::size_t;
+  using difference_type = std::ptrdiff_t;
+
+  template <class U>
+  struct rebind {
+    using other = fancy_pointer_allocator<U>;
+  };
+
+  using propagate_on_container_copy_assignment = std::true_type;
+  using propagate_on_container_move_assignment = std::true_type;
+  using propagate_on_container_swap            = std::true_type;
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer_allocator() TEST_NOEXCEPT
+      : stats_(detail::make_thread_unsafe_shared<fancy_pointer_statistics>()) {}
+
+  template <class U>
+  TEST_CONSTEXPR_CXX20 explicit fancy_pointer_allocator(fancy_pointer_allocator<U> other) TEST_NOEXCEPT
+      : stats_(other.stats_) {}
+
+  TEST_CONSTEXPR_CXX20 fancy_pointer_allocator(const fancy_pointer_allocator&)            = default;
+  TEST_CONSTEXPR_CXX20 fancy_pointer_allocator(fancy_pointer_allocator&&)                 = default;
+  TEST_CONSTEXPR_CXX20 fancy_pointer_allocator& operator=(const fancy_pointer_allocator&) = default;
+  TEST_CONSTEXPR_CXX20 fancy_pointer_allocator& operator=(fancy_pointer_allocator&&)      = default;
+  TEST_CONSTEXPR_CXX20 ~fancy_pointer_allocator()                                         = default;
+
+  TEST_CONSTEXPR_CXX20 pointer allocate(size_t n) { return pointer(base::allocate(n), &stats_->alive_count); }
+  TEST_CONSTEXPR_CXX20 void deallocate(pointer p, size_t n) { base::deallocate(p, n); }
+
+#if TEST_STD_VER > 20
+  TEST_CONSTEXPR_CXX20 std::allocation_result<pointer> allocate_at_least(std::size_t n) {
+    return {pointer{base::allocate(n), &stats_->alive_count}, n};
+  }
+#endif
+
+  detail::thread_unsafe_shared_ptr<fancy_pointer_statistics> stats_;
+};
+
+template <class T, class U>
+TEST_CONSTEXPR_CXX20 inline bool
+operator==(fancy_pointer_allocator<T> const& LHS, fancy_pointer_allocator<U> const& RHS) {
+  return LHS.stats_.get() == RHS.stats_.get();
+}
+
+template <class T, class U>
+TEST_CONSTEXPR_CXX20 inline bool
+operator!=(fancy_pointer_allocator<T> const& LHS, fancy_pointer_allocator<U> const& RHS) {
+  return !(LHS == RHS);
+}
+
+#if 0 // to be enabled after merge of https://github.com/llvm/llvm-project/pull/195839
+template <typename T>
+struct are_default_allocators_always_equal<fancy_pointer_allocator<T> > {
+  enum { value = 0 };
+};
+#endif
+
 #endif // TEST_ALLOCATOR_H



More information about the libcxx-commits mailing list