[libcxx-commits] [libcxx] [libcxx][string] Allow fancy pointers in basic_string (PR #199264)

Nikita Belenkiy via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 15 04:51:11 PDT 2026


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

>From 34ac338104d6a3adc7bfa53c875c1d57d2a5f100 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/3] [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
---
 libcxx/test/support/test_allocator.h | 45 ++++++++++++++++++++--------
 1 file changed, 33 insertions(+), 12 deletions(-)

diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h
index 60845fa35a086..ece1a27259b76 100644
--- a/libcxx/test/support/test_allocator.h
+++ b/libcxx/test/support/test_allocator.h
@@ -421,21 +421,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) // TODO: 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; }
@@ -452,6 +461,18 @@ 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);
+    allocator_traits::deallocate(alloc, block, 1);
+  }
+
   control_block* block = nullptr;
 
   template <class U, class... Args>

>From b042a01ed8c28a3c8ee85d24b260f4a7b257b8bb 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/3] [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/module.modulemap |   5 +-
 libcxx/test/support/test_allocator.h | 351 +++++++++++++++++++++++++++
 2 files changed, 355 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/support/module.modulemap b/libcxx/test/support/module.modulemap
index 0d83f08b6dd22..07b24b78750e3 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 test_text_encoding     { header "test_text_encoding.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 ece1a27259b76..c5f30a1cffad4 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>
@@ -574,4 +575,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

>From 9224de4d513574566f7d77e7d699b31b63ba80b5 Mon Sep 17 00:00:00 2001
From: Nikita Belenkiy <nikita.nb.belenkiy at partner.bmw.de>
Date: Fri, 22 May 2026 20:40:22 +0200
Subject: [PATCH 3/3] [libcxx][string] Allow fancy pointers in basic_string

The rationale for this change is to allow allocation of strings in interprocess shared memory
using fancy pointers like boost::interprocess::offset_ptr.

Currently, basic_string does not support custom allocators which use fancy pointers
that are not trivially constructible and destructible as the pointer type. This happens
because in its internal representation, fancy pointers are stored in a union, which
deletes the default constructor of such a union.

In order to allow fancy pointers, this PR does:
* create an alternative version of the underlying union for fancy pointers, with
  explicitly defined special member functions. The original version (compile-time selected)
  is kept to avoid performance regression with non-fancy pointers;
* add explicit __construct_at() to start lifetime of the fancy pointer in constant expressions;
* simplifies the logic of self-move-assignment handling;
* adds tests using fanci_polinter_allocator in libcxx/test/libcxx/strings/

New tests in libcxx/test/std/strings/ also exist and pass, but, being much more numerous,
will be provided in a separate PR to simplify review of this PR.

Also, the PR code initially contained fixes for deficiencies in short string ASAN annotations,
but as short string ASAN annotations are currently disabled and are going to be removed or
reworked, I have removed these fixes to avoid potential merge conflicts.

Fixes: https://github.com/llvm/llvm-project/issues/20882
---
 libcxx/include/string                         | 124 +++++++++++++-----
 .../basic.string/alignof.compile.pass.cpp     |  15 +++
 .../basic.string/sizeof.compile.pass.cpp      |  35 +++++
 .../string.access/assert.back.pass.cpp        |   2 +
 .../string.access/assert.cback.pass.cpp       |   2 +
 .../string.access/assert.cfront.pass.cpp      |   2 +
 .../string.access/assert.cindex.pass.cpp      |   2 +
 .../string.access/assert.front.pass.cpp       |   2 +
 .../string.access/assert.index.pass.cpp       |   2 +
 .../string.cons/copy_shrunk_long.pass.cpp     |   2 +
 .../assert.iterator.add.pass.cpp              |   2 +
 .../assert.iterator.decrement.pass.cpp        |   2 +
 .../assert.iterator.dereference.pass.cpp      |   2 +
 .../assert.iterator.increment.pass.cpp        |   2 +
 .../assert.iterator.index.pass.cpp            |   2 +
 .../debug.iterator.compare.pass.cpp           |   2 +
 .../debug.iterator.subtract.pass.cpp          |   2 +
 .../assert.erase_iter.null.pass.cpp           |   2 +
 .../string.modifiers/assert.pop_back.pass.cpp |   2 +
 .../debug.erase.iter.pass.cpp                 |   2 +
 .../debug.erase.iter_iter.pass.cpp            |   2 +
 .../debug.insert.iter_char.pass.cpp           |   2 +
 .../debug.insert.iter_iter_iter.pass.cpp      |   2 +
 .../debug.insert.iter_size_char.pass.cpp      |   2 +
 24 files changed, 186 insertions(+), 30 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index c485038312d99..c86571ea2fe26 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -640,8 +640,10 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #  include <__type_traits/is_relocatable.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/is_standard_layout.h>
+#  include <__type_traits/is_trivially_assignable.h>
 #  include <__type_traits/is_trivially_constructible.h>
 #  include <__type_traits/is_trivially_copyable.h>
+#  include <__type_traits/is_trivially_destructible.h>
 #  include <__type_traits/remove_cv.h>
 #  include <__type_traits/remove_cvref.h>
 #  include <__utility/assume.h>
@@ -749,7 +751,7 @@ public:
   using const_pointer                  = typename __alloc_traits::const_pointer;
 
   // A basic_string contains the following members which may be trivially relocatable:
-  // - pointer: is currently assumed to be trivially relocatable, but is still checked in case that changes
+  // - pointer: may or may not be trivially relocatable, so it's checked
   // - size_type: is always trivially relocatable, since it has to be an integral type
   // - value_type: is always trivially relocatable, since it has to be trivial
   // - unsigned char: is a fundamental type, so it's trivially relocatable
@@ -878,16 +880,95 @@ private:
 
   static_assert(sizeof(__short) == (sizeof(value_type) * (__min_cap + 1)), "__short has an unexpected size.");
 
-  union __rep {
+  template <typename _LongT, typename = void>
+  union __rep_t {
     __short __s;
-    __long __l;
+    _LongT __l;
 
-    __rep() = default;
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep(__short __r) : __s(__r) {}
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep(__long __r) : __l(__r) {}
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep(__uninitialized_tag) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __begin_long_lifetime() {}
+
+    __rep_t() = default;
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__short __r) : __s(__r) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__long __r) : __l(__r) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__uninitialized_tag) {}
   };
 
+  template <typename _LongT>
+  union __rep_t<_LongT,
+                typename std::enable_if<!is_trivially_constructible<_LongT>::value ||
+                                        !is_trivially_copy_assignable<_LongT>::value ||
+                                        !is_trivially_destructible<_LongT>::value >::type> {
+    __short __s;
+    _LongT __l;
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_long() const _NOEXCEPT {
+      if (__libcpp_is_constant_evaluated() && __builtin_constant_p(__l.__is_long_)) {
+        return __l.__is_long_;
+      }
+      return __s.__is_long_;
+    }
+
+    // If __long is not trivially constructible (this occurs if the pointer type inside __long is not trivially
+    // constructible) then the lifetime of __l must be explicitly started with std::__construct_at before it can be
+    // assigned to. If __long is trivially constructible, then the lifetime of __l can be implicitly started with a
+    // simple assignment and therefore does not need to be explicitly initialised here.
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __begin_long_lifetime() {
+#  if _LIBCPP_STD_VER >= 17
+      if constexpr (!__is_trivially_constructible(_LongT)) {
+#  else
+      if (!__is_trivially_constructible(_LongT)) {
+#  endif // _LIBCPP_STD_VER >= 17
+        std::__construct_at(std::addressof(__l));
+      }
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t() : __s() {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__short __r) : __s(__r) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(_LongT __r) : __l(__r) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__uninitialized_tag) : __s() {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(const __rep_t& __other) {
+      if (__other.__is_long()) {
+        __begin_long_lifetime();
+        __l = __other.__l;
+      } else
+        __s = __other.__s;
+    }
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__rep_t&& __other) {
+      if (__other.__is_long()) {
+        __begin_long_lifetime();
+        __l = std::move(__other.__l);
+      } else
+        __s = __other.__s;
+    }
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t& operator=(const __rep_t& __other) {
+      if (__is_long())
+        __l.~_LongT();
+      if (__other.__is_long()) {
+        __begin_long_lifetime();
+        __l = __other.__l;
+      } else
+        __s = __other.__s;
+      return *this;
+    }
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t& operator=(__rep_t&& __other) {
+      if (__is_long())
+        __l.~_LongT();
+      if (__other.__is_long()) {
+        __begin_long_lifetime();
+        __l = std::move(__other.__l);
+      } else
+        __s = __other.__s;
+      return *this;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__rep_t() {
+      if (__is_long())
+        __l.~_LongT();
+    }
+  };
+
+  using __rep _LIBCPP_NODEBUG = __rep_t<__long>;
+
   _LIBCPP_COMPRESSED_PAIR(__rep, __rep_, allocator_type, __alloc_);
 
   // annotate the string with its size() at scope exit. The string has to be in a valid state at that point.
@@ -935,7 +1016,7 @@ private:
     return std::__make_bounded_iter(__p, __get_pointer(), __get_pointer() + size());
 #  else
     return const_iterator(__p);
-#  endif                    // _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING
+#  endif // _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING
   }
 
 public:
@@ -1146,6 +1227,8 @@ public:
 #  ifndef _LIBCPP_CXX03_LANG
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
   operator=(basic_string&& __str) noexcept(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+    if (this == std::addressof(__str))
+      return *this;
     __move_assign(__str, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
     return *this;
   }
@@ -2222,6 +2305,7 @@ private:
       __annotate_new(__size);
       return __get_short_pointer();
     } else {
+      __rep_.__begin_long_lifetime();
       __rep_.__l = __allocate_long_buffer(__alloc_, __size);
       __annotate_new(__size);
       return __get_long_pointer();
@@ -2870,31 +2954,10 @@ basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, tr
     }
 #    endif
   }
-  size_type __str_old_size = __str.size();
-  bool __str_was_short     = !__str.__is_long();
 
   __move_assign_alloc(__str);
   __rep_ = __str.__rep_;
-  __str.__set_short_size(0);
-  traits_type::assign(__str.__get_short_pointer()[0], value_type());
-
-  if (__str_was_short && this != std::addressof(__str))
-    __str.__annotate_shrink(__str_old_size);
-  else
-    // ASan annotations: was long, so object memory is unpoisoned as new.
-    // Or is same as *this, and __annotate_delete() was called.
-    __str.__annotate_new(0);
-
-  // ASan annotations: Guard against `std::string s; s = std::move(s);`
-  // You can find more here: https://en.cppreference.com/w/cpp/utility/move
-  // Quote: "Unless otherwise specified, all standard library objects that have been moved
-  // from are placed in a "valid but unspecified state", meaning the object's class
-  // invariants hold (so functions without preconditions, such as the assignment operator,
-  // can be safely used on the object after it was moved from):"
-  // Quote: "v = std::move(v); // the value of v is unspecified"
-  if (!__is_long() && std::addressof(__str) != this)
-    // If it is long string, delete was never called on original __str's buffer.
-    __annotate_new(__get_short_size());
+  __str.__rep_ = __rep();
 }
 
 #  endif
@@ -3378,6 +3441,7 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocat
   // We're a long string and we're shrinking into the small buffer.
   if (__fits_in_sso(__size)) {
     __annotation_guard __g(*this);
+    __rep_ = __rep();
     __set_short_size(__size);
     traits_type::copy(std::__to_address(__get_short_pointer()), std::__to_address(__ptr), __size + 1);
     __alloc_traits::deallocate(__alloc_, __ptr, __cap);
diff --git a/libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp b/libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp
index 49cbde14c568b..f78a7ab35326b 100644
--- a/libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp
@@ -54,6 +54,9 @@ class small_iter_allocator {
 template <class CharT>
 using min_string = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>;
 
+template <class CharT>
+using fancy_string = std::basic_string<CharT, std::char_traits<CharT>, fancy_pointer_allocator<CharT>>;
+
 template <class CharT>
 using test_string = std::basic_string<CharT, std::char_traits<CharT>, test_allocator<CharT>>;
 
@@ -64,6 +67,7 @@ using small_string = std::basic_string<CharT, std::char_traits<CharT>, small_ite
 
 static_assert(alignof(std::string) == 8, "");
 static_assert(alignof(min_string<char>) == 8, "");
+static_assert(alignof(fancy_string<char>) == 8, "");
 static_assert(alignof(test_string<char>) == 8, "");
 static_assert(alignof(small_string<char>) == 2, "");
 
@@ -71,11 +75,13 @@ static_assert(alignof(small_string<char>) == 2, "");
 #    if __WCHAR_WIDTH__ == 32
 static_assert(alignof(std::wstring) == 8, "");
 static_assert(alignof(min_string<wchar_t>) == 8, "");
+static_assert(alignof(fancy_string<wchar_t>) == 8, "");
 static_assert(alignof(test_string<wchar_t>) == 8, "");
 static_assert(alignof(small_string<wchar_t>) == 4, "");
 #    elif __WCHAR_WIDTH__ == 16
 static_assert(alignof(std::wstring) == 8, "");
 static_assert(alignof(min_string<wchar_t>) == 8, "");
+static_assert(alignof(fancy_string<wchar_t>) == 8, "");
 static_assert(alignof(test_string<wchar_t>) == 8, "");
 static_assert(alignof(small_string<wchar_t>) == 2, "");
 #    else
@@ -86,6 +92,7 @@ static_assert(alignof(small_string<wchar_t>) == 2, "");
 #  ifndef TEST_HAS_NO_CHAR8_T
 static_assert(alignof(std::u8string) == 8, "");
 static_assert(alignof(min_string<char8_t>) == 8, "");
+static_assert(alignof(fancy_string<char8_t>) == 8, "");
 static_assert(alignof(test_string<char8_t>) == 8, "");
 static_assert(alignof(small_string<char8_t>) == 2, "");
 #  endif
@@ -95,6 +102,8 @@ static_assert(alignof(std::u16string) == 8, "");
 static_assert(alignof(std::u32string) == 8, "");
 static_assert(alignof(min_string<char16_t>) == 8, "");
 static_assert(alignof(min_string<char32_t>) == 8, "");
+static_assert(alignof(fancy_string<char16_t>) == 8, "");
+static_assert(alignof(fancy_string<char32_t>) == 8, "");
 static_assert(alignof(test_string<char16_t>) == 8, "");
 static_assert(alignof(test_string<char32_t>) == 8, "");
 static_assert(alignof(small_string<char16_t>) == 2, "");
@@ -105,6 +114,7 @@ static_assert(alignof(small_string<char32_t>) == 4, "");
 
 static_assert(alignof(std::string) == 4, "");
 static_assert(alignof(min_string<char>) == 4, "");
+static_assert(alignof(fancy_string<char>) == 4, "");
 static_assert(alignof(test_string<char>) == 4, "");
 static_assert(alignof(small_string<char>) == 2, "");
 
@@ -112,11 +122,13 @@ static_assert(alignof(small_string<char>) == 2, "");
 #    if __WCHAR_WIDTH__ == 32
 static_assert(alignof(std::wstring) == 4, "");
 static_assert(alignof(min_string<wchar_t>) == 4, "");
+static_assert(alignof(fancy_string<wchar_t>) == 4, "");
 static_assert(alignof(test_string<wchar_t>) == 4, "");
 static_assert(alignof(small_string<wchar_t>) == 4, "");
 #    elif __WCHAR_WIDTH__ == 16
 static_assert(alignof(std::wstring) == 4, "");
 static_assert(alignof(min_string<wchar_t>) == 4, "");
+static_assert(alignof(fancy_string<wchar_t>) == 4, "");
 static_assert(alignof(test_string<wchar_t>) == 4, "");
 static_assert(alignof(small_string<wchar_t>) == 2, "");
 #    else
@@ -127,6 +139,7 @@ static_assert(alignof(small_string<wchar_t>) == 2, "");
 #  ifndef TEST_HAS_NO_CHAR8_T
 static_assert(alignof(std::u8string) == 4, "");
 static_assert(alignof(min_string<char8_t>) == 4, "");
+static_assert(alignof(fancy_string<char8_t>) == 4, "");
 static_assert(alignof(test_string<char8_t>) == 4, "");
 static_assert(alignof(small_string<char8_t>) == 2, "");
 #  endif
@@ -136,6 +149,8 @@ static_assert(alignof(std::u16string) == 4, "");
 static_assert(alignof(std::u32string) == 4, "");
 static_assert(alignof(min_string<char16_t>) == 4, "");
 static_assert(alignof(min_string<char32_t>) == 4, "");
+static_assert(alignof(fancy_string<char16_t>) == 4, "");
+static_assert(alignof(fancy_string<char32_t>) == 4, "");
 static_assert(alignof(test_string<char16_t>) == 4, "");
 static_assert(alignof(test_string<char32_t>) == 4, "");
 static_assert(alignof(small_string<char32_t>) == 4, "");
diff --git a/libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp b/libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp
index 31f1a94c73216..aeb07649d7bf9 100644
--- a/libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp
@@ -52,6 +52,9 @@ class small_iter_allocator {
 template <class CharT>
 using min_string = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT> >;
 
+template <class CharT>
+using fancy_string = std::basic_string<CharT, std::char_traits<CharT>, fancy_pointer_allocator<CharT> >;
+
 template <class CharT>
 using test_string = std::basic_string<CharT, std::char_traits<CharT>, test_allocator<CharT> >;
 
@@ -62,6 +65,9 @@ using small_string = std::basic_string<CharT, std::char_traits<CharT>, small_ite
 
 static_assert(sizeof(std::string) == 24, "");
 static_assert(sizeof(min_string<char>) == 24, "");
+#  if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char>) == 40, "");
+#  endif
 static_assert(sizeof(test_string<char>) == 32, "");
 static_assert(sizeof(small_string<char>) == 6, "");
 
@@ -69,11 +75,17 @@ static_assert(sizeof(small_string<char>) == 6, "");
 #    if __WCHAR_WIDTH__ == 32
 static_assert(sizeof(std::wstring) == 24, "");
 static_assert(sizeof(min_string<wchar_t>) == 24, "");
+#      if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<wchar_t>) == 40, "");
+#      endif
 static_assert(sizeof(test_string<wchar_t>) == 32, "");
 static_assert(sizeof(small_string<wchar_t>) == 12, "");
 #    elif __WCHAR_WIDTH__ == 16
 static_assert(sizeof(std::wstring) == 24, "");
 static_assert(sizeof(min_string<wchar_t>) == 24, "");
+#      if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<wchar_t>) == 40, "");
+#      endif
 static_assert(sizeof(test_string<wchar_t>) == 32, "");
 static_assert(sizeof(small_string<wchar_t>) == 6, "");
 #    else
@@ -84,6 +96,9 @@ static_assert(sizeof(small_string<wchar_t>) == 6, "");
 #  ifndef TEST_HAS_NO_CHAR8_T
 static_assert(sizeof(std::u8string) == 24, "");
 static_assert(sizeof(min_string<char8_t>) == 24, "");
+#    if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char8_t>) == 40, "");
+#    endif
 static_assert(sizeof(test_string<char8_t>) == 32, "");
 static_assert(sizeof(small_string<char8_t>) == 6, "");
 #  endif
@@ -93,6 +108,10 @@ static_assert(sizeof(std::u16string) == 24, "");
 static_assert(sizeof(std::u32string) == 24, "");
 static_assert(sizeof(min_string<char16_t>) == 24, "");
 static_assert(sizeof(min_string<char32_t>) == 24, "");
+#    if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char16_t>) == 40, "");
+static_assert(sizeof(fancy_string<char32_t>) == 40, "");
+#    endif
 static_assert(sizeof(test_string<char16_t>) == 32, "");
 static_assert(sizeof(test_string<char32_t>) == 32, "");
 static_assert(sizeof(small_string<char16_t>) == 6, "");
@@ -103,6 +122,9 @@ static_assert(sizeof(small_string<char32_t>) == 12, "");
 
 static_assert(sizeof(std::string) == 12, "");
 static_assert(sizeof(min_string<char>) == 12, "");
+#  if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char>) == 20, "");
+#  endif
 static_assert(sizeof(test_string<char>) == 24, "");
 static_assert(sizeof(small_string<char>) == 6, "");
 
@@ -110,11 +132,17 @@ static_assert(sizeof(small_string<char>) == 6, "");
 #    if __WCHAR_WIDTH__ == 32
 static_assert(sizeof(std::wstring) == 12, "");
 static_assert(sizeof(min_string<wchar_t>) == 12, "");
+#      if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<wchar_t>) == 20, "");
+#      endif
 static_assert(sizeof(test_string<wchar_t>) == 24, "");
 static_assert(sizeof(small_string<wchar_t>) == 12, "");
 #    elif __WCHAR_WIDTH__ == 16
 static_assert(sizeof(std::wstring) == 12, "");
 static_assert(sizeof(min_string<wchar_t>) == 12, "");
+#      if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<wchar_t>) == 20, "");
+#      endif
 static_assert(sizeof(test_string<wchar_t>) == 24, "");
 static_assert(sizeof(small_string<wchar_t>) == 6, "");
 #    else
@@ -125,6 +153,9 @@ static_assert(sizeof(small_string<wchar_t>) == 6, "");
 #  ifndef TEST_HAS_NO_CHAR8_T
 static_assert(sizeof(std::u8string) == 12, "");
 static_assert(sizeof(min_string<char8_t>) == 12, "");
+#    if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char8_t>) == 20, "");
+#    endif
 static_assert(sizeof(test_string<char8_t>) == 24, "");
 static_assert(sizeof(small_string<char>) == 6, "");
 #  endif
@@ -134,6 +165,10 @@ static_assert(sizeof(std::u16string) == 12, "");
 static_assert(sizeof(std::u32string) == 12, "");
 static_assert(sizeof(min_string<char16_t>) == 12, "");
 static_assert(sizeof(min_string<char32_t>) == 12, "");
+#    if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char16_t>) == 20, "");
+static_assert(sizeof(fancy_string<char32_t>) == 20, "");
+#    endif
 static_assert(sizeof(test_string<char16_t>) == 24, "");
 static_assert(sizeof(test_string<char32_t>) == 24, "");
 static_assert(sizeof(small_string<char16_t>) == 6, "");
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp
index 5e3f8545b565b..15ccbb92b4aa6 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -26,6 +27,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp
index 36969a99b79fb..67c2eb80b509a 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -26,6 +27,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp
index d1673fa765e7b..6c8615c80815e 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -26,6 +27,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cindex.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cindex.pass.cpp
index 22705d483bd98..362786f6b393f 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cindex.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cindex.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -28,6 +29,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.front.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.front.pass.cpp
index 950cba5820cd7..74b49100978cb 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.front.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.front.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -27,6 +28,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.index.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.index.pass.cpp
index b8b1be075b186..cc7c98319ddd1 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.index.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.index.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -28,6 +29,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp
index d4a0b318f36d7..f346a2cd2b38a 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp
@@ -35,10 +35,12 @@ int main(int, char**) {
   test<std::basic_string<char, std::char_traits<char>, test_allocator<char> > >();
 #if TEST_STD_VER >= 11
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 #endif
 #if TEST_STD_VER > 17
   static_assert(test<std::basic_string<char, std::char_traits<char>, test_allocator<char>>>());
   static_assert(test<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>());
+  static_assert(test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >());
 #endif
 
   return 0;
diff --git a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.add.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.add.pass.cpp
index 5bfb376bcacf0..5551ecb5da4fe 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.add.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.add.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class C>
 void test() {
@@ -32,6 +33,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.decrement.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.decrement.pass.cpp
index 447105f04a255..a7af44d5b0586 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.decrement.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.decrement.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class C>
 void test() {
@@ -31,6 +32,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.dereference.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.dereference.pass.cpp
index f0fce12580664..e6f85a95d6aad 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.dereference.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.dereference.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class C>
 void test() {
@@ -28,6 +29,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.increment.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.increment.pass.cpp
index 5ba020b87d2c1..9300a40956bb7 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.increment.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.increment.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class C>
 void test() {
@@ -31,6 +32,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.index.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.index.pass.cpp
index 9688edc9bdcbf..2269169e02bf5 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.index.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.index.pass.cpp
@@ -18,6 +18,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class C>
 void test() {
@@ -32,6 +33,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.compare.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.compare.pass.cpp
index c55410edf4eba..aa99d18a0c201 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.compare.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.compare.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -28,6 +29,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.subtract.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.subtract.pass.cpp
index d3274cbd90cc5..2f60065dc324b 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.subtract.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.subtract.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -28,6 +29,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.erase_iter.null.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.erase_iter.null.pass.cpp
index 161f9cf7be52d..ca4596f76c33e 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.erase_iter.null.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.erase_iter.null.pass.cpp
@@ -16,6 +16,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -27,6 +28,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.pop_back.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.pop_back.pass.cpp
index b7863b990227a..973464d3e2284 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.pop_back.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.pop_back.pass.cpp
@@ -15,6 +15,7 @@
 #include <string>
 
 #include "check_assertion.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -24,6 +25,7 @@ void test() {
 
 int main(int, char**) {
   test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter.pass.cpp
index 93fd6caf0ba23..4fc4be6bfaf67 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -30,6 +31,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter_iter.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter_iter.pass.cpp
index ed97e21d9411e..29954095f3b22 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter_iter.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter_iter.pass.cpp
@@ -17,6 +17,7 @@
 
 #include "check_assertion.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -56,6 +57,7 @@ void test() {
 int main(int, char**) {
   test<std::string>();
   test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_char.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_char.pass.cpp
index 561f79e0e18fb..946839aea6612 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_char.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_char.pass.cpp
@@ -22,6 +22,7 @@
 #include <string>
 
 #include "check_assertion.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -34,6 +35,7 @@ void test() {
 
 int main(int, char**) {
   test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_iter_iter.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_iter_iter.pass.cpp
index eef525349972b..baff22ee85a44 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_iter_iter.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_iter_iter.pass.cpp
@@ -17,6 +17,7 @@
 #include <string>
 
 #include "check_assertion.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -30,6 +31,7 @@ void test() {
 
 int main(int, char**) {
   test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }
diff --git a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_size_char.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_size_char.pass.cpp
index 4b7532678f2e5..4b1ed1b82ebad 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_size_char.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_size_char.pass.cpp
@@ -16,6 +16,7 @@
 #include <string>
 
 #include "check_assertion.h"
+#include "test_allocator.h"
 
 template <class S>
 void test() {
@@ -27,6 +28,7 @@ void test() {
 
 int main(int, char**) {
   test<std::string>();
+  test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
 
   return 0;
 }



More information about the libcxx-commits mailing list