[libcxx-commits] [libcxx] [libc++] Fix strict aliasing violation for `deque::const_iterator` (PR #136067)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Mon Mar 9 18:37:37 PDT 2026


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/136067

>From 608000748c1e1feec392376de74f0b00f2bdd705 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 17 Apr 2025 09:09:28 +0800
Subject: [PATCH 01/19] [libc++] Fix strict aliasing violation for
 `deque::const_iterator`

When the allocators use fancy pointers, the internal map of `deque`
stores `fancy_ptr<T>` objects, and the previous strategy accessed these
objects via `const fancy_ptr<const T>` lvalues, which usually caused
core language undefined behavior. Now `const_iterator` stores
`fancy_ptr<const fancy_ptr<T>>` instead of
`fancy_ptr<const fancy_ptr<const T>>`, and ABI break can happen when
such two types have incompatible layouts.

This is necessary for reducing undefined behavior and `constexpr`
support for `deque` in C++26, and I currently don't want to provide any
way to opt-out of that behavior.

Also removes redundant identity `static_cast` before and after type
change.

The existing test coverage seems to be sufficient.
---
 libcxx/docs/ReleaseNotes/21.rst |  8 ++++++++
 libcxx/include/deque            | 17 ++++++++---------
 2 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index 2091a713ea200..84d7d8cf7aab3 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -114,6 +114,14 @@ ABI Affecting Changes
   comparison between shared libraries, since all RTTI has the correct visibility now. There is no behaviour change on
   Clang.
 
+- The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the
+  (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map
+  stores ``fancy_ptr<T>`` objects, and the previous strategy accessed these objects via ``const fancy_ptr<const T>``
+  lvalues, which usually caused core language undefined behavior. Now ``const_iterator`` stores
+  ``fancy_ptr<const fancy_ptr<T>>`` instead of ``fancy_ptr<const fancy_ptr<const T>>``, and ABI break can happen when
+  such two types have incompatible layouts. This is necessary for reducing undefined behavior and ``constexpr`` support
+  for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
+
 
 Build System Changes
 --------------------
diff --git a/libcxx/include/deque b/libcxx/include/deque
index e9846af5e5848..8df2a046e618d 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -504,13 +504,12 @@ public:
   using pointer       = typename __alloc_traits::pointer;
   using const_pointer = typename __alloc_traits::const_pointer;
 
-  using __pointer_allocator _LIBCPP_NODEBUG       = __rebind_alloc<__alloc_traits, pointer>;
-  using __const_pointer_allocator _LIBCPP_NODEBUG = __rebind_alloc<__alloc_traits, const_pointer>;
-  using __map _LIBCPP_NODEBUG                     = __split_buffer<pointer, __pointer_allocator>;
-  using __map_alloc_traits _LIBCPP_NODEBUG        = allocator_traits<__pointer_allocator>;
-  using __map_pointer _LIBCPP_NODEBUG             = typename __map_alloc_traits::pointer;
-  using __map_const_pointer _LIBCPP_NODEBUG       = typename allocator_traits<__const_pointer_allocator>::const_pointer;
-  using __map_const_iterator _LIBCPP_NODEBUG      = typename __map::const_iterator;
+  using __pointer_allocator _LIBCPP_NODEBUG  = __rebind_alloc<__alloc_traits, pointer>;
+  using __map _LIBCPP_NODEBUG                = __split_buffer<pointer, __pointer_allocator>;
+  using __map_alloc_traits _LIBCPP_NODEBUG   = allocator_traits<__pointer_allocator>;
+  using __map_pointer _LIBCPP_NODEBUG        = typename __map_alloc_traits::pointer;
+  using __map_const_pointer _LIBCPP_NODEBUG  = typename allocator_traits<__pointer_allocator>::const_pointer;
+  using __map_const_iterator _LIBCPP_NODEBUG = typename __map::const_iterator;
 
   using reference       = value_type&;
   using const_reference = const value_type&;
@@ -721,7 +720,7 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT {
-    __map_const_pointer __mp = static_cast<__map_const_pointer>(__map_.begin() + __start_ / __block_size);
+    __map_const_pointer __mp = __map_.begin() + __start_ / __block_size;
     return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __start_ % __block_size);
   }
 
@@ -733,7 +732,7 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT {
     size_type __p            = size() + __start_;
-    __map_const_pointer __mp = static_cast<__map_const_pointer>(__map_.begin() + __p / __block_size);
+    __map_const_pointer __mp = __map_.begin() + __p / __block_size;
     return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __p % __block_size);
   }
 

>From c62445517a611ca143f64dfb7027cc4102fd130f Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 5 Jun 2025 16:56:01 +0800
Subject: [PATCH 02/19] Switch to use internally rebinding

---
 libcxx/include/deque | 63 +++++++++++++++++++++++++++++++-------------
 1 file changed, 44 insertions(+), 19 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index 8df2a046e618d..60a7b44b1500a 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -227,12 +227,14 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/disjunction.h>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
+#  include <__type_traits/is_const.h>
 #  include <__type_traits/is_convertible.h>
 #  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/is_swappable.h>
 #  include <__type_traits/is_trivially_relocatable.h>
+#  include <__type_traits/remove_reference.h>
 #  include <__type_traits/type_identity.h>
 #  include <__utility/forward.h>
 #  include <__utility/move.h>
@@ -269,6 +271,15 @@ struct __deque_block_size {
   static const _DiffType value = sizeof(_ValueType) < 256 ? 4096 / sizeof(_ValueType) : 16;
 };
 
+// When using fancy pointers, _MapPointer can be FancyPtr<const FancyPtr<const _ValueType>>, which causes strict
+// aliasing violation in direct dereferencing because the internal map stores FancyPtr<_ValueType> objects.
+// We need to transform the type to something like FancyPtr<const FancyPtr<_ValueType>>.
+template <class _ValueType, class _MapPointer>
+using __get_deque_map_iterator _LIBCPP_NODEBUG =
+    conditional_t<is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value,
+                  __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >,
+                  _MapPointer>;
+
 template <class _ValueType,
           class _Pointer,
           class _Reference,
@@ -284,7 +295,7 @@ template <class _ValueType,
 #  endif
           >
 class __deque_iterator {
-  typedef _MapPointer __map_iterator;
+  using __map_iterator _LIBCPP_NODEBUG = __get_deque_map_iterator<_ValueType, _MapPointer>;
 
 public:
   typedef _Pointer pointer;
@@ -461,7 +472,7 @@ private:
 
 public:
   using __is_segmented_iterator _LIBCPP_NODEBUG = true_type;
-  using __segment_iterator _LIBCPP_NODEBUG      = _MapPointer;
+  using __segment_iterator _LIBCPP_NODEBUG      = __get_deque_map_iterator<_ValueType, _MapPointer>;
   using __local_iterator _LIBCPP_NODEBUG        = _Pointer;
 
   static _LIBCPP_HIDE_FROM_ABI __segment_iterator __segment(_Iterator __iter) { return __iter.__m_iter_; }
@@ -504,18 +515,27 @@ public:
   using pointer       = typename __alloc_traits::pointer;
   using const_pointer = typename __alloc_traits::const_pointer;
 
-  using __pointer_allocator _LIBCPP_NODEBUG  = __rebind_alloc<__alloc_traits, pointer>;
-  using __map _LIBCPP_NODEBUG                = __split_buffer<pointer, __pointer_allocator>;
-  using __map_alloc_traits _LIBCPP_NODEBUG   = allocator_traits<__pointer_allocator>;
-  using __map_pointer _LIBCPP_NODEBUG        = typename __map_alloc_traits::pointer;
-  using __map_const_pointer _LIBCPP_NODEBUG  = typename allocator_traits<__pointer_allocator>::const_pointer;
-  using __map_const_iterator _LIBCPP_NODEBUG = typename __map::const_iterator;
+  using __pointer_allocator _LIBCPP_NODEBUG       = __rebind_alloc<__alloc_traits, pointer>;
+  using __const_pointer_allocator _LIBCPP_NODEBUG = __rebind_alloc<__alloc_traits, const_pointer>;
+  using __map _LIBCPP_NODEBUG                     = __split_buffer<pointer, __pointer_allocator>;
+  using __map_alloc_traits _LIBCPP_NODEBUG        = allocator_traits<__pointer_allocator>;
+  using __map_pointer _LIBCPP_NODEBUG             = typename __map_alloc_traits::pointer;
+  using __map_const_iterator _LIBCPP_NODEBUG      = typename __map::const_iterator;
+
+  // Direct dereferencing __map_const_pointer possibly causes strict aliasing violation.
+  // We need to transform it to something like __map_alloc_traits::const_pointer for the const_iterator.
+  using __map_const_pointer _LIBCPP_NODEBUG = typename allocator_traits<__const_pointer_allocator>::const_pointer;
+  using __map_proper_const_pointer _LIBCPP_NODEBUG = typename __map_alloc_traits::const_pointer;
+
+  using __iter_proper_const_pointer _LIBCPP_NODEBUG = __get_deque_map_iterator<_Tp, __map_const_pointer>;
+  static_assert(is_same<decltype(*__iter_proper_const_pointer()), const pointer&>::value,
+                "The fancy pointer types provided by the allocators cannot be restored by pointer_traits::rebind.");
 
   using reference       = value_type&;
   using const_reference = const value_type&;
 
-  using iterator = __deque_iterator<value_type, pointer, reference, __map_pointer, difference_type>;
-  using const_iterator =
+  using iterator       = __deque_iterator<value_type, pointer, reference, __map_pointer, difference_type>;
+  using const_iterator = // Use of __map_const_pointer is merely kept for API stability.
       __deque_iterator<value_type, const_pointer, const_reference, __map_const_pointer, difference_type>;
   using reverse_iterator       = std::reverse_iterator<iterator>;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
@@ -596,6 +616,10 @@ private:
     deque* const __base_;
   };
 
+  static __iter_proper_const_pointer __map_const_ptr_to_iter_const_ptr(__map_proper_const_pointer __mp) _NOEXCEPT {
+    return __mp ? pointer_traits<__iter_proper_const_pointer>::pointer_to(*__mp) : nullptr;
+  }
+
   static const difference_type __block_size;
 
   __map __map_;
@@ -720,8 +744,9 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT {
-    __map_const_pointer __mp = __map_.begin() + __start_ / __block_size;
-    return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __start_ % __block_size);
+    __map_proper_const_pointer __mp = __map_.begin() + __start_ / __block_size;
+    return const_iterator(
+        __map_const_ptr_to_iter_const_ptr(__mp), __map_.empty() ? 0 : *__mp + __start_ % __block_size);
   }
 
   _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT {
@@ -731,9 +756,9 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT {
-    size_type __p            = size() + __start_;
-    __map_const_pointer __mp = __map_.begin() + __p / __block_size;
-    return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __p % __block_size);
+    size_type __p                   = size() + __start_;
+    __map_proper_const_pointer __mp = __map_.begin() + __p / __block_size;
+    return const_iterator(__map_const_ptr_to_iter_const_ptr(__mp), __map_.empty() ? 0 : *__mp + __p % __block_size);
   }
 
   _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
@@ -2330,7 +2355,7 @@ deque<_Tp, _Allocator>::__move_and_check(iterator __f, iterator __l, iterator __
       __fe = __fb + __bs;
     }
     if (__fb <= __vt && __vt < __fe)
-      __vt = (const_iterator(static_cast<__map_const_pointer>(__f.__m_iter_), __vt) -= __f - __r).__ptr_;
+      __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__f.__m_iter_), __vt) -= __f - __r).__ptr_;
     __r = std::move(__fb, __fe, __r);
     __n -= __bs;
     __f += __bs;
@@ -2357,7 +2382,7 @@ deque<_Tp, _Allocator>::__move_backward_and_check(iterator __f, iterator __l, it
       __lb = __le - __bs;
     }
     if (__lb <= __vt && __vt < __le)
-      __vt = (const_iterator(static_cast<__map_const_pointer>(__l.__m_iter_), __vt) += __r - __l - 1).__ptr_;
+      __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__l.__m_iter_), __vt) += __r - __l - 1).__ptr_;
     __r = std::move_backward(__lb, __le, __r);
     __n -= __bs;
     __l -= __bs - 1;
@@ -2383,7 +2408,7 @@ void deque<_Tp, _Allocator>::__move_construct_and_check(iterator __f, iterator _
       __fe = __fb + __bs;
     }
     if (__fb <= __vt && __vt < __fe)
-      __vt = (const_iterator(static_cast<__map_const_pointer>(__f.__m_iter_), __vt) += __r - __f).__ptr_;
+      __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__f.__m_iter_), __vt) += __r - __f).__ptr_;
     for (; __fb != __fe; ++__fb, ++__r, ++__size())
       __alloc_traits::construct(__a, std::addressof(*__r), std::move(*__fb));
     __n -= __bs;
@@ -2415,7 +2440,7 @@ void deque<_Tp, _Allocator>::__move_construct_backward_and_check(
       __lb = __le - __bs;
     }
     if (__lb <= __vt && __vt < __le)
-      __vt = (const_iterator(static_cast<__map_const_pointer>(__l.__m_iter_), __vt) -= __l - __r + 1).__ptr_;
+      __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__l.__m_iter_), __vt) -= __l - __r + 1).__ptr_;
     while (__le != __lb) {
       __alloc_traits::construct(__a, std::addressof(*--__r), std::move(*--__le));
       --__start_;

>From 1c1a4a9fec9ffa0ac9a5177623af593fa8263242 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 5 Jun 2025 17:25:18 +0800
Subject: [PATCH 03/19] `__conditional_t`

---
 libcxx/include/deque | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index 47e15cd53098c..c4096abc7fa17 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -277,9 +277,9 @@ struct __deque_block_size {
 // We need to transform the type to something like FancyPtr<const FancyPtr<_ValueType>>.
 template <class _ValueType, class _MapPointer>
 using __get_deque_map_iterator _LIBCPP_NODEBUG =
-    conditional_t<is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value,
-                  __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >,
-                  _MapPointer>;
+    __conditional_t<is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value,
+                    __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >,
+                    _MapPointer>;
 
 template <class _ValueType,
           class _Pointer,

>From bbaa690569739bdd09d8650298afcfb10027a70e Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 5 Jun 2025 18:42:15 +0800
Subject: [PATCH 04/19] Fix allocators and fancy pointers in
 `deque/abi.compile.pass.cpp`

---
 .../sequences/deque/abi.compile.pass.cpp      | 77 ++++++++++++++++++-
 1 file changed, 73 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
index 30586d8b2422c..09a721980c723 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
@@ -10,6 +10,8 @@
 
 #include <cstdint>
 #include <deque>
+#include <iterator>
+#include <type_traits>
 
 #include "min_allocator.h"
 #include "test_allocator.h"
@@ -18,6 +20,73 @@
 template <class T>
 class small_pointer {
   std::uint16_t offset;
+
+public:
+  using value_type        = typename std::remove_cv<T>::type;
+  using difference_type   = std::int16_t;
+  using reference         = T&;
+  using pointer           = T*;
+  using iterator_category = std::random_access_iterator_tag;
+
+  template <class CT,
+            typename std::enable_if<std::is_same<const T, CT>::value && !std::is_const<T>::value, int>::type = 0>
+  operator small_pointer<CT>() const;
+  template <
+      class Void,
+      typename std::enable_if<std::is_same<Void, void>::value && std::is_convertible<T*, void*>::value, int>::type = 0>
+  operator small_pointer<Void>() const;
+  template <
+      class CVoid,
+      typename std::enable_if<std::is_same<CVoid, const void>::value && std::is_convertible<T*, const void*>::value,
+                              int>::type = 0>
+  operator small_pointer<CVoid>() const;
+
+  T& operator*() const;
+  T* operator->() const;
+  T& operator[](difference_type) const;
+
+  small_pointer& operator++();
+  small_pointer operator++(int);
+  small_pointer& operator--();
+  small_pointer operator--(int);
+  small_pointer& operator+=(difference_type);
+  small_pointer& operator-=(difference_type);
+
+  friend small_pointer operator+(small_pointer, difference_type);
+  friend small_pointer operator+(difference_type, small_pointer);
+  friend small_pointer operator-(small_pointer, difference_type);
+  friend difference_type operator-(small_pointer, small_pointer);
+
+  friend bool operator==(small_pointer, small_pointer);
+#if TEST_STD_VER < 20
+  friend bool operator!=(small_pointer, small_pointer);
+#endif
+  friend bool operator<(small_pointer, small_pointer);
+  friend bool operator>=(small_pointer, small_pointer);
+  friend bool operator>(small_pointer, small_pointer);
+  friend bool operator>=(small_pointer, small_pointer);
+
+  small_pointer pointer_to(T&);
+};
+
+template <>
+class small_pointer<const void> {
+  std::uint16_t offset;
+
+public:
+  template <class CT, typename std::enable_if<std::is_convertible<CT*, const void*>::value, int>::type = 0>
+  explicit operator small_pointer<CT>() const;
+};
+
+template <>
+class small_pointer<void*> {
+  std::uint16_t offset;
+
+public:
+  operator small_pointer<const void>() const;
+
+  template <class T, typename std::enable_if<std::is_convertible<T*, void*>::value, int>::type = 0>
+  explicit operator small_pointer<T>() const;
 };
 
 template <class T>
@@ -33,8 +102,8 @@ class small_iter_allocator {
   template <class U>
   small_iter_allocator(small_iter_allocator<U>) TEST_NOEXCEPT {}
 
-  T* allocate(std::size_t n);
-  void deallocate(T* p, std::size_t);
+  pointer allocate(std::size_t n);
+  void deallocate(pointer p, std::size_t);
 
   friend bool operator==(small_iter_allocator, small_iter_allocator) { return true; }
   friend bool operator!=(small_iter_allocator, small_iter_allocator) { return false; }
@@ -53,8 +122,8 @@ class final_small_iter_allocator final {
   template <class U>
   final_small_iter_allocator(final_small_iter_allocator<U>) TEST_NOEXCEPT {}
 
-  T* allocate(std::size_t n);
-  void deallocate(T* p, std::size_t);
+  pointer allocate(std::size_t n);
+  void deallocate(pointer p, std::size_t);
 
   friend bool operator==(final_small_iter_allocator, final_small_iter_allocator) { return true; }
   friend bool operator!=(final_small_iter_allocator, final_small_iter_allocator) { return false; }

>From 94dbfb400b42d66855b04798c1b55983243bfd4a Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 5 Jun 2025 19:32:32 +0800
Subject: [PATCH 05/19] Make GCC happy

---
 .../sequences/deque/abi.compile.pass.cpp      | 20 +++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
index 09a721980c723..dfe86d750d2fd 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
@@ -52,19 +52,19 @@ class small_pointer {
   small_pointer& operator+=(difference_type);
   small_pointer& operator-=(difference_type);
 
-  friend small_pointer operator+(small_pointer, difference_type);
-  friend small_pointer operator+(difference_type, small_pointer);
-  friend small_pointer operator-(small_pointer, difference_type);
-  friend difference_type operator-(small_pointer, small_pointer);
+  friend small_pointer operator+(small_pointer, difference_type) { return small_pointer{}; }
+  friend small_pointer operator+(difference_type, small_pointer) { return small_pointer{}; }
+  friend small_pointer operator-(small_pointer, difference_type) { return small_pointer{}; }
+  friend difference_type operator-(small_pointer, small_pointer) { return 0; }
 
-  friend bool operator==(small_pointer, small_pointer);
+  friend bool operator==(small_pointer, small_pointer) { return true; }
 #if TEST_STD_VER < 20
-  friend bool operator!=(small_pointer, small_pointer);
+  friend bool operator!=(small_pointer, small_pointer) { return false; }
 #endif
-  friend bool operator<(small_pointer, small_pointer);
-  friend bool operator>=(small_pointer, small_pointer);
-  friend bool operator>(small_pointer, small_pointer);
-  friend bool operator>=(small_pointer, small_pointer);
+  friend bool operator<(small_pointer, small_pointer) { return false; }
+  friend bool operator>=(small_pointer, small_pointer) { return true; }
+  friend bool operator>(small_pointer, small_pointer) { return false; }
+  friend bool operator>=(small_pointer, small_pointer) { return true; }
 
   small_pointer pointer_to(T&);
 };

>From ba6007fc45114a1c7126eb2973aff066ed87f963 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 5 Jun 2025 19:39:54 +0800
Subject: [PATCH 06/19] Make them meeting Cpp17NullablePointer

---
 .../sequences/deque/abi.compile.pass.cpp      | 43 +++++++++++++++++--
 1 file changed, 40 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
index dfe86d750d2fd..5f30f37b5507c 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
@@ -8,6 +8,7 @@
 
 // UNSUPPORTED: libcpp-abi-no-compressed-pair-padding
 
+#include <cstddef>
 #include <cstdint>
 #include <deque>
 #include <iterator>
@@ -28,6 +29,9 @@ class small_pointer {
   using pointer           = T*;
   using iterator_category = std::random_access_iterator_tag;
 
+  small_pointer() : offset() {}
+  small_pointer(std::nullptr_t) : offset() {}
+
   template <class CT,
             typename std::enable_if<std::is_same<const T, CT>::value && !std::is_const<T>::value, int>::type = 0>
   operator small_pointer<CT>() const;
@@ -41,6 +45,8 @@ class small_pointer {
                               int>::type = 0>
   operator small_pointer<CVoid>() const;
 
+  explicit operator bool() const;
+
   T& operator*() const;
   T* operator->() const;
   T& operator[](difference_type) const;
@@ -52,9 +58,9 @@ class small_pointer {
   small_pointer& operator+=(difference_type);
   small_pointer& operator-=(difference_type);
 
-  friend small_pointer operator+(small_pointer, difference_type) { return small_pointer{}; }
-  friend small_pointer operator+(difference_type, small_pointer) { return small_pointer{}; }
-  friend small_pointer operator-(small_pointer, difference_type) { return small_pointer{}; }
+  friend small_pointer operator+(small_pointer, difference_type) { return small_pointer(); }
+  friend small_pointer operator+(difference_type, small_pointer) { return small_pointer(); }
+  friend small_pointer operator-(small_pointer, difference_type) { return small_pointer(); }
   friend difference_type operator-(small_pointer, small_pointer) { return 0; }
 
   friend bool operator==(small_pointer, small_pointer) { return true; }
@@ -66,6 +72,13 @@ class small_pointer {
   friend bool operator>(small_pointer, small_pointer) { return false; }
   friend bool operator>=(small_pointer, small_pointer) { return true; }
 
+  friend bool operator==(small_pointer, std::nullptr_t) { return true; }
+#if TEST_STD_VER < 20
+  friend bool operator==(std::nullptr_t, small_pointer) { return true; }
+  friend bool operator!=(small_pointer, std::nullptr_t) { return false; }
+  friend bool operator!=(std::nullptr_t, small_pointer) { return false; }
+#endif
+
   small_pointer pointer_to(T&);
 };
 
@@ -74,8 +87,20 @@ class small_pointer<const void> {
   std::uint16_t offset;
 
 public:
+  small_pointer() : offset() {}
+  small_pointer(std::nullptr_t) : offset() {}
+
   template <class CT, typename std::enable_if<std::is_convertible<CT*, const void*>::value, int>::type = 0>
   explicit operator small_pointer<CT>() const;
+
+  explicit operator bool() const;
+
+  friend bool operator==(small_pointer, std::nullptr_t) { return true; }
+#if TEST_STD_VER < 20
+  friend bool operator==(std::nullptr_t, small_pointer) { return true; }
+  friend bool operator!=(small_pointer, std::nullptr_t) { return false; }
+  friend bool operator!=(std::nullptr_t, small_pointer) { return false; }
+#endif
 };
 
 template <>
@@ -83,10 +108,22 @@ class small_pointer<void*> {
   std::uint16_t offset;
 
 public:
+  small_pointer() : offset() {}
+  small_pointer(std::nullptr_t) : offset() {}
+
   operator small_pointer<const void>() const;
 
   template <class T, typename std::enable_if<std::is_convertible<T*, void*>::value, int>::type = 0>
   explicit operator small_pointer<T>() const;
+
+  explicit operator bool() const;
+
+  friend bool operator==(small_pointer, std::nullptr_t) { return true; }
+#if TEST_STD_VER < 20
+  friend bool operator==(std::nullptr_t, small_pointer) { return true; }
+  friend bool operator!=(small_pointer, std::nullptr_t) { return false; }
+  friend bool operator!=(std::nullptr_t, small_pointer) { return false; }
+#endif
 };
 
 template <class T>

>From 6aee963eb2c71c4dc82d710a1075988a0189f6db Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 5 Jun 2025 19:53:34 +0800
Subject: [PATCH 07/19] Fix typo

---
 .../test/libcxx/containers/sequences/deque/abi.compile.pass.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
index 5f30f37b5507c..2de92a4c9c8b5 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
@@ -68,7 +68,7 @@ class small_pointer {
   friend bool operator!=(small_pointer, small_pointer) { return false; }
 #endif
   friend bool operator<(small_pointer, small_pointer) { return false; }
-  friend bool operator>=(small_pointer, small_pointer) { return true; }
+  friend bool operator<=(small_pointer, small_pointer) { return true; }
   friend bool operator>(small_pointer, small_pointer) { return false; }
   friend bool operator>=(small_pointer, small_pointer) { return true; }
 

>From cedbf8a3c8985d38f16b8762e90b8ce5082c1854 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 6 Jun 2025 09:07:18 +0800
Subject: [PATCH 08/19] `_LIBCPP_HIDE_FROM_ABI`

---
 libcxx/include/deque | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index c4096abc7fa17..348daab1a4343 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -621,7 +621,8 @@ private:
     deque* const __base_;
   };
 
-  static __iter_proper_const_pointer __map_const_ptr_to_iter_const_ptr(__map_proper_const_pointer __mp) _NOEXCEPT {
+  static _LIBCPP_HIDE_FROM_ABI __iter_proper_const_pointer
+  __map_const_ptr_to_iter_const_ptr(__map_proper_const_pointer __mp) _NOEXCEPT {
     return __mp ? pointer_traits<__iter_proper_const_pointer>::pointer_to(*__mp) : nullptr;
   }
 

>From a976380473c4587d538489896ad44fbbb8fec4c5 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 12 Aug 2025 11:48:55 +0800
Subject: [PATCH 09/19] Retarget LLVM22

---
 libcxx/docs/ReleaseNotes/21.rst | 8 --------
 libcxx/docs/ReleaseNotes/22.rst | 8 ++++++++
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index 1590ea6628c0d..91123ffa3e34b 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -148,14 +148,6 @@ ABI Affecting Changes
   comparison between shared libraries, since all RTTI has the correct visibility now. There is no behaviour change on
   Clang.
 
-- The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the
-  (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map
-  stores ``fancy_ptr<T>`` objects, and the previous strategy accessed these objects via ``const fancy_ptr<const T>``
-  lvalues, which usually caused core language undefined behavior. Now ``const_iterator`` stores
-  ``fancy_ptr<const fancy_ptr<T>>`` instead of ``fancy_ptr<const fancy_ptr<const T>>``, and ABI break can happen when
-  such two types have incompatible layouts. This is necessary for reducing undefined behavior and ``constexpr`` support
-  for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
-
 
 Build System Changes
 --------------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 8b8dce5083149..414cea4fd33cd 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -58,5 +58,13 @@ Announcements About Future Releases
 ABI Affecting Changes
 ---------------------
 
+- The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the
+  (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map
+  stores ``fancy_ptr<T>`` objects, and the previous strategy accessed these objects via ``const fancy_ptr<const T>``
+  lvalues, which usually caused core language undefined behavior. Now ``const_iterator`` stores
+  ``fancy_ptr<const fancy_ptr<T>>`` instead of ``fancy_ptr<const fancy_ptr<const T>>``, and ABI break can happen when
+  such two types have incompatible layouts. This is necessary for reducing undefined behavior and ``constexpr`` support
+  for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
+
 Build System Changes
 --------------------

>From 56fcc316a452e67b41b54db8d3c14ecf5f5ce0a6 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 13 Aug 2025 15:02:29 +0800
Subject: [PATCH 10/19] Address review comments

---
 libcxx/docs/ReleaseNotes/22.rst               |   2 +-
 libcxx/include/deque                          |   2 +-
 .../sequences/deque/abi.compile.pass.cpp      | 111 +-----------------
 3 files changed, 6 insertions(+), 109 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 414cea4fd33cd..92dbc0cd8c727 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -61,7 +61,7 @@ ABI Affecting Changes
 - The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the
   (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map
   stores ``fancy_ptr<T>`` objects, and the previous strategy accessed these objects via ``const fancy_ptr<const T>``
-  lvalues, which usually caused core language undefined behavior. Now ``const_iterator`` stores
+  lvalues, caused undefined behavior. Now ``const_iterator`` stores
   ``fancy_ptr<const fancy_ptr<T>>`` instead of ``fancy_ptr<const fancy_ptr<const T>>``, and ABI break can happen when
   such two types have incompatible layouts. This is necessary for reducing undefined behavior and ``constexpr`` support
   for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 428cc538a5538..fb6c81a81633a 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -535,7 +535,7 @@ public:
   using const_reference = const value_type&;
 
   using iterator       = __deque_iterator<value_type, pointer, reference, __map_pointer, difference_type>;
-  using const_iterator = // Use of __map_const_pointer is merely kept for API stability.
+  using const_iterator = // Use of __map_const_pointer is merely kept for stability of const_iterator's true name.
       __deque_iterator<value_type, const_pointer, const_reference, __map_const_pointer, difference_type>;
   using reverse_iterator       = std::reverse_iterator<iterator>;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
diff --git a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
index 2de92a4c9c8b5..d65163cc09df9 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
@@ -8,11 +8,8 @@
 
 // UNSUPPORTED: libcpp-abi-no-compressed-pair-padding
 
-#include <cstddef>
 #include <cstdint>
 #include <deque>
-#include <iterator>
-#include <type_traits>
 
 #include "min_allocator.h"
 #include "test_allocator.h"
@@ -23,107 +20,7 @@ class small_pointer {
   std::uint16_t offset;
 
 public:
-  using value_type        = typename std::remove_cv<T>::type;
-  using difference_type   = std::int16_t;
-  using reference         = T&;
-  using pointer           = T*;
-  using iterator_category = std::random_access_iterator_tag;
-
-  small_pointer() : offset() {}
-  small_pointer(std::nullptr_t) : offset() {}
-
-  template <class CT,
-            typename std::enable_if<std::is_same<const T, CT>::value && !std::is_const<T>::value, int>::type = 0>
-  operator small_pointer<CT>() const;
-  template <
-      class Void,
-      typename std::enable_if<std::is_same<Void, void>::value && std::is_convertible<T*, void*>::value, int>::type = 0>
-  operator small_pointer<Void>() const;
-  template <
-      class CVoid,
-      typename std::enable_if<std::is_same<CVoid, const void>::value && std::is_convertible<T*, const void*>::value,
-                              int>::type = 0>
-  operator small_pointer<CVoid>() const;
-
-  explicit operator bool() const;
-
   T& operator*() const;
-  T* operator->() const;
-  T& operator[](difference_type) const;
-
-  small_pointer& operator++();
-  small_pointer operator++(int);
-  small_pointer& operator--();
-  small_pointer operator--(int);
-  small_pointer& operator+=(difference_type);
-  small_pointer& operator-=(difference_type);
-
-  friend small_pointer operator+(small_pointer, difference_type) { return small_pointer(); }
-  friend small_pointer operator+(difference_type, small_pointer) { return small_pointer(); }
-  friend small_pointer operator-(small_pointer, difference_type) { return small_pointer(); }
-  friend difference_type operator-(small_pointer, small_pointer) { return 0; }
-
-  friend bool operator==(small_pointer, small_pointer) { return true; }
-#if TEST_STD_VER < 20
-  friend bool operator!=(small_pointer, small_pointer) { return false; }
-#endif
-  friend bool operator<(small_pointer, small_pointer) { return false; }
-  friend bool operator<=(small_pointer, small_pointer) { return true; }
-  friend bool operator>(small_pointer, small_pointer) { return false; }
-  friend bool operator>=(small_pointer, small_pointer) { return true; }
-
-  friend bool operator==(small_pointer, std::nullptr_t) { return true; }
-#if TEST_STD_VER < 20
-  friend bool operator==(std::nullptr_t, small_pointer) { return true; }
-  friend bool operator!=(small_pointer, std::nullptr_t) { return false; }
-  friend bool operator!=(std::nullptr_t, small_pointer) { return false; }
-#endif
-
-  small_pointer pointer_to(T&);
-};
-
-template <>
-class small_pointer<const void> {
-  std::uint16_t offset;
-
-public:
-  small_pointer() : offset() {}
-  small_pointer(std::nullptr_t) : offset() {}
-
-  template <class CT, typename std::enable_if<std::is_convertible<CT*, const void*>::value, int>::type = 0>
-  explicit operator small_pointer<CT>() const;
-
-  explicit operator bool() const;
-
-  friend bool operator==(small_pointer, std::nullptr_t) { return true; }
-#if TEST_STD_VER < 20
-  friend bool operator==(std::nullptr_t, small_pointer) { return true; }
-  friend bool operator!=(small_pointer, std::nullptr_t) { return false; }
-  friend bool operator!=(std::nullptr_t, small_pointer) { return false; }
-#endif
-};
-
-template <>
-class small_pointer<void*> {
-  std::uint16_t offset;
-
-public:
-  small_pointer() : offset() {}
-  small_pointer(std::nullptr_t) : offset() {}
-
-  operator small_pointer<const void>() const;
-
-  template <class T, typename std::enable_if<std::is_convertible<T*, void*>::value, int>::type = 0>
-  explicit operator small_pointer<T>() const;
-
-  explicit operator bool() const;
-
-  friend bool operator==(small_pointer, std::nullptr_t) { return true; }
-#if TEST_STD_VER < 20
-  friend bool operator==(std::nullptr_t, small_pointer) { return true; }
-  friend bool operator!=(small_pointer, std::nullptr_t) { return false; }
-  friend bool operator!=(std::nullptr_t, small_pointer) { return false; }
-#endif
 };
 
 template <class T>
@@ -139,8 +36,8 @@ class small_iter_allocator {
   template <class U>
   small_iter_allocator(small_iter_allocator<U>) TEST_NOEXCEPT {}
 
-  pointer allocate(std::size_t n);
-  void deallocate(pointer p, std::size_t);
+  T* allocate(std::size_t n);
+  void deallocate(T* p, std::size_t);
 
   friend bool operator==(small_iter_allocator, small_iter_allocator) { return true; }
   friend bool operator!=(small_iter_allocator, small_iter_allocator) { return false; }
@@ -159,8 +56,8 @@ class final_small_iter_allocator final {
   template <class U>
   final_small_iter_allocator(final_small_iter_allocator<U>) TEST_NOEXCEPT {}
 
-  pointer allocate(std::size_t n);
-  void deallocate(pointer p, std::size_t);
+  T* allocate(std::size_t n);
+  void deallocate(T* p, std::size_t);
 
   friend bool operator==(final_small_iter_allocator, final_small_iter_allocator) { return true; }
   friend bool operator!=(final_small_iter_allocator, final_small_iter_allocator) { return false; }

>From 3d5b2f70ea9d8aa67e0b9c3939cd838516aac964 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sun, 14 Dec 2025 19:51:57 +0800
Subject: [PATCH 11/19] Address @ldionne and @philnik777's review comments

---
 libcxx/docs/ReleaseNotes/22.rst |  8 ++--
 libcxx/include/deque            | 72 ++++++++++++++++++---------------
 2 files changed, 44 insertions(+), 36 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 7d6c8d2a9cb3b..3c995e3e23807 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -113,10 +113,10 @@ ABI Affecting Changes
 - The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the
   (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map
   stores ``fancy_ptr<T>`` objects, and the previous strategy accessed these objects via ``const fancy_ptr<const T>``
-  lvalues, caused undefined behavior. Now ``const_iterator`` stores
-  ``fancy_ptr<const fancy_ptr<T>>`` instead of ``fancy_ptr<const fancy_ptr<const T>>``, and ABI break can happen when
-  such two types have incompatible layouts. This is necessary for reducing undefined behavior and ``constexpr`` support
-  for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
+  lvalues, causing undefined behavior. Now ``const_iterator`` stores
+  ``fancy_ptr<const fancy_ptr<T>>`` instead of ``fancy_ptr<const fancy_ptr<const T>>``, which is technically an ABI
+  break when these two types have incompatible layouts. This is necessary for reducing undefined behavior and for adding
+  ``constexpr`` support for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
 
 - The internal types ``__map_value_compare``, ``__unordered_map_hasher``, ``__unordered_map_equal``,
   ``__hash_map_hasher`` and ``__hash_map_equal`` have been refactored to use ``_LIBCPP_COMPRESSED_ELEMENT`` instead of
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 2d9a6a9332236..0d51085232010 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -269,9 +269,6 @@ struct __deque_block_size {
   static const _DiffType value = sizeof(_ValueType) < 256 ? 4096 / sizeof(_ValueType) : 16;
 };
 
-// When using fancy pointers, _MapPointer can be FancyPtr<const FancyPtr<const _ValueType>>, which causes strict
-// aliasing violation in direct dereferencing because the internal map stores FancyPtr<_ValueType> objects.
-// We need to transform the type to something like FancyPtr<const FancyPtr<_ValueType>>.
 template <class _ValueType, class _MapPointer>
 using __get_deque_map_iterator _LIBCPP_NODEBUG =
     __conditional_t<is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value,
@@ -293,7 +290,32 @@ template <class _ValueType,
 #  endif
           >
 class __deque_iterator {
-  using __map_iterator _LIBCPP_NODEBUG = __get_deque_map_iterator<_ValueType, _MapPointer>;
+  // When using fancy pointers, _MapPointer can be FancyPtr<const FancyPtr<const _ValueType>>, which causes strict
+  // aliasing violation in direct dereferencing because the internal map stores FancyPtr<_ValueType> objects.
+  // We need to transform the type to something like FancyPtr<const FancyPtr<_ValueType>>.
+  using __map_iterator _LIBCPP_NODEBUG =
+      __conditional_t<is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value,
+                      __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >,
+                      _MapPointer>;
+
+#  ifndef _LIBCPP_ABI_DEQUE_CONST_ITERATOR
+  static_assert(
+      sizeof(__map_iterator) == sizeof(_MapPointer) && _LIBCPP_ALIGNOF(__map_iterator) == _LIBCPP_ALIGNOF(_MapPointer),
+      "It looks like you are using fancy pointers such that <const FancyPtr<const value_type>> is not "
+      "layout-compatible with FancyPtr<const FancyPtr<value_type>>. "
+      "This means that your ABI is being broken between LLVM 21 and LLVM 22 if deque::const_iterator is "
+      "used. If you don't care about your ABI being broken, define the _LIBCPP_ABI_DEQUE_CONST_ITERATOR macro to "
+      "silence this diagnostic.");
+#  endif
+
+  template <bool _IsSamePointer = is_same<_MapPointer, __map_iterator>::value, __enable_if_t<_IsSamePointer, int> = 0>
+  static [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI __map_iterator __to_stored_pointer(_MapPointer __mp) _NOEXCEPT {
+    return __mp;
+  }
+  template <bool _IsSamePointer = is_same<_MapPointer, __map_iterator>::value, __enable_if_t<!_IsSamePointer, int> = 0>
+  static [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI __map_iterator __to_stored_pointer(_MapPointer __mp) _NOEXCEPT {
+    return __mp ? pointer_traits<__map_iterator>::pointer_to(*__mp) : nullptr;
+  }
 
 public:
   typedef _Pointer pointer;
@@ -448,8 +470,8 @@ public:
 #  endif // _LIBCPP_STD_VER >= 20
 
 private:
-  _LIBCPP_HIDE_FROM_ABI explicit __deque_iterator(__map_iterator __m, pointer __p) _NOEXCEPT
-      : __m_iter_(__m),
+  _LIBCPP_HIDE_FROM_ABI explicit __deque_iterator(_MapPointer __m, pointer __p) _NOEXCEPT
+      : __m_iter_(__to_stored_pointer(__m)),
         __ptr_(__p) {}
 
   template <class _Tp, class _Ap>
@@ -520,22 +542,14 @@ public:
   using __map _LIBCPP_NODEBUG                     = __split_buffer<pointer, __pointer_allocator>;
   using __map_alloc_traits _LIBCPP_NODEBUG        = allocator_traits<__pointer_allocator>;
   using __map_pointer _LIBCPP_NODEBUG             = typename __map_alloc_traits::pointer;
+  using __map_const_pointer _LIBCPP_NODEBUG       = typename allocator_traits<__const_pointer_allocator>::const_pointer;
   using __map_const_iterator _LIBCPP_NODEBUG      = typename __map::const_iterator;
 
-  // Direct dereferencing __map_const_pointer possibly causes strict aliasing violation.
-  // We need to transform it to something like __map_alloc_traits::const_pointer for the const_iterator.
-  using __map_const_pointer _LIBCPP_NODEBUG = typename allocator_traits<__const_pointer_allocator>::const_pointer;
-  using __map_proper_const_pointer _LIBCPP_NODEBUG = typename __map_alloc_traits::const_pointer;
-
-  using __iter_proper_const_pointer _LIBCPP_NODEBUG = __get_deque_map_iterator<_Tp, __map_const_pointer>;
-  static_assert(is_same<decltype(*__iter_proper_const_pointer()), const pointer&>::value,
-                "The fancy pointer types provided by the allocators cannot be restored by pointer_traits::rebind.");
-
   using reference       = value_type&;
   using const_reference = const value_type&;
 
-  using iterator       = __deque_iterator<value_type, pointer, reference, __map_pointer, difference_type>;
-  using const_iterator = // Use of __map_const_pointer is merely kept for stability of const_iterator's true name.
+  using iterator = __deque_iterator<value_type, pointer, reference, __map_pointer, difference_type>;
+  using const_iterator =
       __deque_iterator<value_type, const_pointer, const_reference, __map_const_pointer, difference_type>;
   using reverse_iterator       = std::reverse_iterator<iterator>;
   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
@@ -616,11 +630,6 @@ private:
     deque* const __base_;
   };
 
-  static _LIBCPP_HIDE_FROM_ABI __iter_proper_const_pointer
-  __map_const_ptr_to_iter_const_ptr(__map_proper_const_pointer __mp) _NOEXCEPT {
-    return __mp ? pointer_traits<__iter_proper_const_pointer>::pointer_to(*__mp) : nullptr;
-  }
-
   static const difference_type __block_size;
 
   __map __map_;
@@ -745,9 +754,8 @@ public:
   }
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT {
-    __map_proper_const_pointer __mp = __map_.begin() + __start_ / __block_size;
-    return const_iterator(
-        __map_const_ptr_to_iter_const_ptr(__mp), __map_.empty() ? 0 : *__mp + __start_ % __block_size);
+    __map_const_pointer __mp = static_cast<__map_const_pointer>(__map_.begin() + __start_ / __block_size);
+    return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __start_ % __block_size);
   }
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT {
@@ -757,9 +765,9 @@ public:
   }
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT {
-    size_type __p                   = size() + __start_;
-    __map_proper_const_pointer __mp = __map_.begin() + __p / __block_size;
-    return const_iterator(__map_const_ptr_to_iter_const_ptr(__mp), __map_.empty() ? 0 : *__mp + __p % __block_size);
+    size_type __p            = size() + __start_;
+    __map_const_pointer __mp = static_cast<__map_const_pointer>(__map_.begin() + __p / __block_size);
+    return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __p % __block_size);
   }
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator(end()); }
@@ -2250,7 +2258,7 @@ deque<_Tp, _Allocator>::__move_and_check(iterator __f, iterator __l, iterator __
       __fe = __fb + __bs;
     }
     if (__fb <= __vt && __vt < __fe)
-      __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__f.__m_iter_), __vt) -= __f - __r).__ptr_;
+      __vt = (const_iterator(static_cast<__map_const_pointer>(__f.__m_iter_), __vt) -= __f - __r).__ptr_;
     __r = std::move(__fb, __fe, __r);
     __n -= __bs;
     __f += __bs;
@@ -2277,7 +2285,7 @@ deque<_Tp, _Allocator>::__move_backward_and_check(iterator __f, iterator __l, it
       __lb = __le - __bs;
     }
     if (__lb <= __vt && __vt < __le)
-      __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__l.__m_iter_), __vt) += __r - __l - 1).__ptr_;
+      __vt = (const_iterator(static_cast<__map_const_pointer>(__l.__m_iter_), __vt) += __r - __l - 1).__ptr_;
     __r = std::move_backward(__lb, __le, __r);
     __n -= __bs;
     __l -= __bs - 1;
@@ -2303,7 +2311,7 @@ void deque<_Tp, _Allocator>::__move_construct_and_check(iterator __f, iterator _
       __fe = __fb + __bs;
     }
     if (__fb <= __vt && __vt < __fe)
-      __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__f.__m_iter_), __vt) += __r - __f).__ptr_;
+      __vt = (const_iterator(static_cast<__map_const_pointer>(__f.__m_iter_), __vt) += __r - __f).__ptr_;
     for (; __fb != __fe; ++__fb, ++__r, ++__size())
       __alloc_traits::construct(__a, std::addressof(*__r), std::move(*__fb));
     __n -= __bs;
@@ -2335,7 +2343,7 @@ void deque<_Tp, _Allocator>::__move_construct_backward_and_check(
       __lb = __le - __bs;
     }
     if (__lb <= __vt && __vt < __le)
-      __vt = (const_iterator(__map_const_ptr_to_iter_const_ptr(__l.__m_iter_), __vt) -= __l - __r + 1).__ptr_;
+      __vt = (const_iterator(static_cast<__map_const_pointer>(__l.__m_iter_), __vt) -= __l - __r + 1).__ptr_;
     while (__le != __lb) {
       __alloc_traits::construct(__a, std::addressof(*--__r), std::move(*--__le));
       --__start_;

>From 2447af23dbdbb1df6db5f91bb5b76894840bda64 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sun, 14 Dec 2025 20:04:08 +0800
Subject: [PATCH 12/19] Fix-up `[[__nodiscard__]]`

---
 libcxx/include/deque | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index 9c6145a063621..31c342d9a49df 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -298,6 +298,7 @@ class __deque_iterator {
                       __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >,
                       _MapPointer>;
 
+// TODO(LLVM 24): Remove this check
 #  ifndef _LIBCPP_ABI_DEQUE_CONST_ITERATOR
   static_assert(
       sizeof(__map_iterator) == sizeof(_MapPointer) && _LIBCPP_ALIGNOF(__map_iterator) == _LIBCPP_ALIGNOF(_MapPointer),
@@ -309,11 +310,11 @@ class __deque_iterator {
 #  endif
 
   template <bool _IsSamePointer = is_same<_MapPointer, __map_iterator>::value, __enable_if_t<_IsSamePointer, int> = 0>
-  static [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI __map_iterator __to_stored_pointer(_MapPointer __mp) _NOEXCEPT {
+  [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI __map_iterator __to_stored_pointer(_MapPointer __mp) _NOEXCEPT {
     return __mp;
   }
   template <bool _IsSamePointer = is_same<_MapPointer, __map_iterator>::value, __enable_if_t<!_IsSamePointer, int> = 0>
-  static [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI __map_iterator __to_stored_pointer(_MapPointer __mp) _NOEXCEPT {
+  [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI __map_iterator __to_stored_pointer(_MapPointer __mp) _NOEXCEPT {
     return __mp ? pointer_traits<__map_iterator>::pointer_to(*__mp) : nullptr;
   }
 

>From 940e57f1c9df3fcbadec484e52e1045b6602ec4d Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sun, 14 Dec 2025 23:00:11 +0800
Subject: [PATCH 13/19] Fix-up the strategy

---
 libcxx/include/deque | 41 +++++++++++++++++------------------------
 1 file changed, 17 insertions(+), 24 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index 31c342d9a49df..3ec8a4729233c 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -269,6 +269,9 @@ struct __deque_block_size {
   static const _DiffType value = sizeof(_ValueType) < 256 ? 4096 / sizeof(_ValueType) : 16;
 };
 
+// When using fancy pointers, _MapPointer can be FancyPtr<const FancyPtr<const _ValueType>>, which causes strict
+// aliasing violation in direct dereferencing because the internal map stores FancyPtr<_ValueType> objects.
+// We need to transform the type to something like FancyPtr<const FancyPtr<_ValueType>>.
 template <class _ValueType, class _MapPointer>
 using __get_deque_map_iterator _LIBCPP_NODEBUG =
     __conditional_t<is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value,
@@ -290,13 +293,7 @@ template <class _ValueType,
 #  endif
           >
 class __deque_iterator {
-  // When using fancy pointers, _MapPointer can be FancyPtr<const FancyPtr<const _ValueType>>, which causes strict
-  // aliasing violation in direct dereferencing because the internal map stores FancyPtr<_ValueType> objects.
-  // We need to transform the type to something like FancyPtr<const FancyPtr<_ValueType>>.
-  using __map_iterator _LIBCPP_NODEBUG =
-      __conditional_t<is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value,
-                      __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >,
-                      _MapPointer>;
+  using __map_iterator _LIBCPP_NODEBUG = __get_deque_map_iterator<_ValueType, _MapPointer>;
 
 // TODO(LLVM 24): Remove this check
 #  ifndef _LIBCPP_ABI_DEQUE_CONST_ITERATOR
@@ -309,15 +306,6 @@ class __deque_iterator {
       "silence this diagnostic.");
 #  endif
 
-  template <bool _IsSamePointer = is_same<_MapPointer, __map_iterator>::value, __enable_if_t<_IsSamePointer, int> = 0>
-  [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI __map_iterator __to_stored_pointer(_MapPointer __mp) _NOEXCEPT {
-    return __mp;
-  }
-  template <bool _IsSamePointer = is_same<_MapPointer, __map_iterator>::value, __enable_if_t<!_IsSamePointer, int> = 0>
-  [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI __map_iterator __to_stored_pointer(_MapPointer __mp) _NOEXCEPT {
-    return __mp ? pointer_traits<__map_iterator>::pointer_to(*__mp) : nullptr;
-  }
-
 public:
   typedef _Pointer pointer;
   typedef _DiffType difference_type;
@@ -471,8 +459,13 @@ public:
 #  endif // _LIBCPP_STD_VER >= 20
 
 private:
+  _LIBCPP_HIDE_FROM_ABI explicit __deque_iterator(__map_iterator __m, pointer __p) _NOEXCEPT
+      : __m_iter_(__m),
+        __ptr_(__p) {}
+
+  template <bool _IsSamePointer = is_same<_MapPointer, __map_iterator>::value, __enable_if_t<!_IsSamePointer, int> = 0>
   _LIBCPP_HIDE_FROM_ABI explicit __deque_iterator(_MapPointer __m, pointer __p) _NOEXCEPT
-      : __m_iter_(__to_stored_pointer(__m)),
+      : __m_iter_(__m ? pointer_traits<__map_iterator>::pointer_to(*__m) : nullptr),
         __ptr_(__p) {}
 
   template <class _Tp, class _Ap>
@@ -755,7 +748,7 @@ public:
   }
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT {
-    __map_const_pointer __mp = static_cast<__map_const_pointer>(__map_.begin() + __start_ / __block_size);
+    auto __mp = __map_.begin() + __start_ / __block_size;
     return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __start_ % __block_size);
   }
 
@@ -766,8 +759,8 @@ public:
   }
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT {
-    size_type __p            = size() + __start_;
-    __map_const_pointer __mp = static_cast<__map_const_pointer>(__map_.begin() + __p / __block_size);
+    size_type __p = size() + __start_;
+    auto __mp     = __map_.begin() + __p / __block_size;
     return const_iterator(__mp, __map_.empty() ? 0 : *__mp + __p % __block_size);
   }
 
@@ -2259,7 +2252,7 @@ deque<_Tp, _Allocator>::__move_and_check(iterator __f, iterator __l, iterator __
       __fe = __fb + __bs;
     }
     if (__fb <= __vt && __vt < __fe)
-      __vt = (const_iterator(static_cast<__map_const_pointer>(__f.__m_iter_), __vt) -= __f - __r).__ptr_;
+      __vt = (const_iterator(__f.__m_iter_, __vt) -= __f - __r).__ptr_;
     __r = std::move(__fb, __fe, __r);
     __n -= __bs;
     __f += __bs;
@@ -2286,7 +2279,7 @@ deque<_Tp, _Allocator>::__move_backward_and_check(iterator __f, iterator __l, it
       __lb = __le - __bs;
     }
     if (__lb <= __vt && __vt < __le)
-      __vt = (const_iterator(static_cast<__map_const_pointer>(__l.__m_iter_), __vt) += __r - __l - 1).__ptr_;
+      __vt = (const_iterator(__l.__m_iter_, __vt) += __r - __l - 1).__ptr_;
     __r = std::move_backward(__lb, __le, __r);
     __n -= __bs;
     __l -= __bs - 1;
@@ -2312,7 +2305,7 @@ void deque<_Tp, _Allocator>::__move_construct_and_check(iterator __f, iterator _
       __fe = __fb + __bs;
     }
     if (__fb <= __vt && __vt < __fe)
-      __vt = (const_iterator(static_cast<__map_const_pointer>(__f.__m_iter_), __vt) += __r - __f).__ptr_;
+      __vt = (const_iterator(__f.__m_iter_, __vt) += __r - __f).__ptr_;
     for (; __fb != __fe; ++__fb, ++__r, ++__size())
       __alloc_traits::construct(__a, std::addressof(*__r), std::move(*__fb));
     __n -= __bs;
@@ -2344,7 +2337,7 @@ void deque<_Tp, _Allocator>::__move_construct_backward_and_check(
       __lb = __le - __bs;
     }
     if (__lb <= __vt && __vt < __le)
-      __vt = (const_iterator(static_cast<__map_const_pointer>(__l.__m_iter_), __vt) -= __l - __r + 1).__ptr_;
+      __vt = (const_iterator(__l.__m_iter_, __vt) -= __l - __r + 1).__ptr_;
     while (__le != __lb) {
       __alloc_traits::construct(__a, std::addressof(*--__r), std::move(*--__le));
       --__start_;

>From 00c716311ec7d238f5cb901086a6b91a2c4572d9 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 16 Dec 2025 09:43:12 +0800
Subject: [PATCH 14/19] Remove unneeded constructor

---
 libcxx/include/deque | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index 3ec8a4729233c..88c3b4a0434b1 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -463,11 +463,6 @@ private:
       : __m_iter_(__m),
         __ptr_(__p) {}
 
-  template <bool _IsSamePointer = is_same<_MapPointer, __map_iterator>::value, __enable_if_t<!_IsSamePointer, int> = 0>
-  _LIBCPP_HIDE_FROM_ABI explicit __deque_iterator(_MapPointer __m, pointer __p) _NOEXCEPT
-      : __m_iter_(__m ? pointer_traits<__map_iterator>::pointer_to(*__m) : nullptr),
-        __ptr_(__p) {}
-
   template <class _Tp, class _Ap>
   friend class deque;
   template <class _Vp, class _Pp, class _Rp, class _MP, class _Dp, _Dp>

>From 752f2f4326f8f3e289cde37a6a8188dce2fcadc3 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 11 Feb 2026 13:36:25 +0800
Subject: [PATCH 15/19] Retarget LLVM 23

---
 libcxx/docs/ReleaseNotes/22.rst | 7 -------
 libcxx/docs/ReleaseNotes/23.rst | 8 ++++++++
 libcxx/include/deque            | 4 ++--
 3 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index a4e631e958de2..623bf409d7ebb 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -128,13 +128,6 @@ ABI Affecting Changes
 - The ABI flag ``_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER`` has been split off from
   ``_LIBCPP_ABI_NO_ITERATOR_BASES``. If you are using this flag and care about ABI stability, you should set
   ``_LIBCPP_ABI_NO_REVERSE_ITERATOR_SECOND_MEMBER`` as well.
-- The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the
-  (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map
-  stores ``fancy_ptr<T>`` objects, and the previous strategy accessed these objects via ``const fancy_ptr<const T>``
-  lvalues, causing undefined behavior. Now ``const_iterator`` stores
-  ``fancy_ptr<const fancy_ptr<T>>`` instead of ``fancy_ptr<const fancy_ptr<const T>>``, which is technically an ABI
-  break when these two types have incompatible layouts. This is necessary for reducing undefined behavior and for adding
-  ``constexpr`` support for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
 
 - The internal types ``__map_value_compare``, ``__unordered_map_hasher``, ``__unordered_map_equal``,
   ``__hash_map_hasher`` and ``__hash_map_equal`` have been refactored to use ``_LIBCPP_COMPRESSED_ELEMENT`` instead of
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index a1d62a367f33c..6f3dac2bfcb85 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -70,5 +70,13 @@ ABI Affecting Changes
   defining ``_LIBCPP_DEPRECATED_ABI_BITSET_CONST_SUBSCRIPT_RETURN_REF``. 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.
 
+- The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the
+  (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map
+  stores ``fancy_ptr<T>`` objects, and the previous strategy accessed these objects via ``const fancy_ptr<const T>``
+  lvalues, causing undefined behavior. Now ``const_iterator`` stores ``fancy_ptr<const fancy_ptr<T>>`` instead of
+  ``fancy_ptr<const fancy_ptr<const T>>``, which is technically an ABI break when these two types have incompatible
+  layouts. This is necessary for reducing undefined behavior and for adding ``constexpr`` support for ``deque`` in
+  C++26, so we do not provide any way to opt-out of that behavior.
+
 Build System Changes
 --------------------
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 1d06827c7a1af..02369ab45fda9 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -296,13 +296,13 @@ template <class _ValueType,
 class __deque_iterator {
   using __map_iterator _LIBCPP_NODEBUG = __get_deque_map_iterator<_ValueType, _MapPointer>;
 
-// TODO(LLVM 24): Remove this check
+// TODO(LLVM 25): Remove this check
 #  ifndef _LIBCPP_ABI_DEQUE_CONST_ITERATOR
   static_assert(
       sizeof(__map_iterator) == sizeof(_MapPointer) && _LIBCPP_ALIGNOF(__map_iterator) == _LIBCPP_ALIGNOF(_MapPointer),
       "It looks like you are using fancy pointers such that <const FancyPtr<const value_type>> is not "
       "layout-compatible with FancyPtr<const FancyPtr<value_type>>. "
-      "This means that your ABI is being broken between LLVM 21 and LLVM 22 if deque::const_iterator is "
+      "This means that your ABI is being broken between LLVM 22 and LLVM 23 if deque::const_iterator is "
       "used. If you don't care about your ABI being broken, define the _LIBCPP_ABI_DEQUE_CONST_ITERATOR macro to "
       "silence this diagnostic.");
 #  endif

>From beea87ef0b46c9dc9aa5f9cebf36755489cdb48d Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 11 Feb 2026 13:36:42 +0800
Subject: [PATCH 16/19] Expand `__get_deque_map_iterator`

---
 libcxx/include/deque | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index 02369ab45fda9..507da913c1dd9 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -273,11 +273,21 @@ struct __deque_block_size {
 // When using fancy pointers, _MapPointer can be FancyPtr<const FancyPtr<const _ValueType>>, which causes strict
 // aliasing violation in direct dereferencing because the internal map stores FancyPtr<_ValueType> objects.
 // We need to transform the type to something like FancyPtr<const FancyPtr<_ValueType>>.
+template <class _MapPointer>
+inline const bool __is_deque_map_pointer_to_const =
+    is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value;
+
+template <class _ValueType, class _MapPointer, bool _IsConst = __is_deque_map_pointer_to_const<_MapPointer> >
+struct __get_deque_map_iterator_impl {
+  using type _LIBCPP_NODEBUG = __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >;
+};
+template <class _ValueType, class _MapPointer>
+struct __get_deque_map_iterator_impl<_ValueType, _MapPointer, false> {
+  using type _LIBCPP_NODEBUG = _MapPointer;
+};
+
 template <class _ValueType, class _MapPointer>
-using __get_deque_map_iterator _LIBCPP_NODEBUG =
-    __conditional_t<is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value,
-                    __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >,
-                    _MapPointer>;
+using __get_deque_map_iterator _LIBCPP_NODEBUG = typename __get_deque_map_iterator_impl<_ValueType, _MapPointer>::type;
 
 template <class _ValueType,
           class _Pointer,

>From 0fed0fb112b39c467926a89f732b0e2aa65427c8 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 9 Mar 2026 20:37:11 +0800
Subject: [PATCH 17/19] Address review comments

- Also change `deque::iterator`.
- Revert changes in the test.
- Update Release notes.
---
 libcxx/docs/ReleaseNotes/23.rst               | 11 ++---
 libcxx/include/deque                          | 43 +++++--------------
 .../sequences/deque/abi.compile.pass.cpp      |  3 --
 3 files changed, 17 insertions(+), 40 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 6f3dac2bfcb85..73b31960051dc 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -72,11 +72,12 @@ ABI Affecting Changes
 
 - The ``const_iterator`` member type of ``std::deque`` is now corrected to hold a (possibly fancy) pointer to the
   (possibly fancy) pointer allocated in the internal map. E.g. when the allocators use fancy pointers, the internal map
-  stores ``fancy_ptr<T>`` objects, and the previous strategy accessed these objects via ``const fancy_ptr<const T>``
-  lvalues, causing undefined behavior. Now ``const_iterator`` stores ``fancy_ptr<const fancy_ptr<T>>`` instead of
-  ``fancy_ptr<const fancy_ptr<const T>>``, which is technically an ABI break when these two types have incompatible
-  layouts. This is necessary for reducing undefined behavior and for adding ``constexpr`` support for ``deque`` in
-  C++26, so we do not provide any way to opt-out of that behavior.
+  stores ``FancyPtr<T>`` objects, and the previous strategy accessed these objects via ``const FancyPtr<const T>``
+  lvalues, causing undefined behavior. Now ``const_iterator`` stores ``FancyPtr<const FancyPtr<T>>`` instead of
+  ``FancyPtr<const FancyPtr<const T>>``, which is technically an ABI break when these two types have incompatible
+  layouts.  The ``iterator`` member type is also changed to store ``FancyPtr<const FancyPtr<T>>`` because the internal
+  map is never modified via an ``iterator``. This is necessary for reducing undefined behavior and for adding
+  ``constexpr`` support for ``deque`` in C++26, so we do not provide any way to opt-out of that behavior.
 
 Build System Changes
 --------------------
diff --git a/libcxx/include/deque b/libcxx/include/deque
index 507da913c1dd9..d6132b334d648 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -225,14 +225,12 @@ template <class T, class Allocator, class Predicate>
 #  include <__type_traits/container_traits.h>
 #  include <__type_traits/enable_if.h>
 #  include <__type_traits/is_allocator.h>
-#  include <__type_traits/is_const.h>
 #  include <__type_traits/is_convertible.h>
 #  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/is_swappable.h>
 #  include <__type_traits/is_trivially_relocatable.h>
-#  include <__type_traits/remove_reference.h>
 #  include <__type_traits/type_identity.h>
 #  include <__utility/exception_guard.h>
 #  include <__utility/forward.h>
@@ -270,25 +268,6 @@ struct __deque_block_size {
   static const _DiffType value = sizeof(_ValueType) < 256 ? 4096 / sizeof(_ValueType) : 16;
 };
 
-// When using fancy pointers, _MapPointer can be FancyPtr<const FancyPtr<const _ValueType>>, which causes strict
-// aliasing violation in direct dereferencing because the internal map stores FancyPtr<_ValueType> objects.
-// We need to transform the type to something like FancyPtr<const FancyPtr<_ValueType>>.
-template <class _MapPointer>
-inline const bool __is_deque_map_pointer_to_const =
-    is_const<__libcpp_remove_reference_t<decltype(**_MapPointer())> >::value;
-
-template <class _ValueType, class _MapPointer, bool _IsConst = __is_deque_map_pointer_to_const<_MapPointer> >
-struct __get_deque_map_iterator_impl {
-  using type _LIBCPP_NODEBUG = __rebind_pointer_t<_MapPointer, const __rebind_pointer_t<_MapPointer, _ValueType> >;
-};
-template <class _ValueType, class _MapPointer>
-struct __get_deque_map_iterator_impl<_ValueType, _MapPointer, false> {
-  using type _LIBCPP_NODEBUG = _MapPointer;
-};
-
-template <class _ValueType, class _MapPointer>
-using __get_deque_map_iterator _LIBCPP_NODEBUG = typename __get_deque_map_iterator_impl<_ValueType, _MapPointer>::type;
-
 template <class _ValueType,
           class _Pointer,
           class _Reference,
@@ -304,17 +283,17 @@ template <class _ValueType,
 #  endif
           >
 class __deque_iterator {
-  using __map_iterator _LIBCPP_NODEBUG = __get_deque_map_iterator<_ValueType, _MapPointer>;
+  using __map_iterator _LIBCPP_NODEBUG = __rebind_pointer_t<const __rebind_pointer_t<_Pointer, _ValueType> >;
 
 // TODO(LLVM 25): Remove this check
-#  ifndef _LIBCPP_ABI_DEQUE_CONST_ITERATOR
+#  ifndef _LIBCPP_ABI_DEQUE_ITERATORS
   static_assert(
       sizeof(__map_iterator) == sizeof(_MapPointer) && _LIBCPP_ALIGNOF(__map_iterator) == _LIBCPP_ALIGNOF(_MapPointer),
-      "It looks like you are using fancy pointers such that <const FancyPtr<const value_type>> is not "
-      "layout-compatible with FancyPtr<const FancyPtr<value_type>>. "
-      "This means that your ABI is being broken between LLVM 22 and LLVM 23 if deque::const_iterator is "
-      "used. If you don't care about your ABI being broken, define the _LIBCPP_ABI_DEQUE_CONST_ITERATOR macro to "
-      "silence this diagnostic.");
+      "It looks like you are using fancy pointers such that FancyPtr<const FancyPtr<const value_type>> is not "
+      "layout-compatible with FancyPtr<FancyPtr<value_type>> or FancyPtr<const FancyPtr<value_type>>. "
+      "This means that your ABI is being broken between LLVM 22 and LLVM 23 if deque::iterator or "
+      "deque::const_iterator is used. If you don't care about your ABI being broken, define the "
+      "_LIBCPP_ABI_DEQUE_ITERATORS macro to silence this diagnostic.");
 #  endif
 
 public:
@@ -491,7 +470,7 @@ private:
       __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer, _DiffType, _BlockSize>;
 
 public:
-  using __segment_iterator _LIBCPP_NODEBUG = __get_deque_map_iterator<_ValueType, _MapPointer>;
+  using __segment_iterator _LIBCPP_NODEBUG = __rebind_pointer_t<const __rebind_pointer_t<_Pointer, _ValueType> >;
   using __local_iterator _LIBCPP_NODEBUG   = _Pointer;
 
   static _LIBCPP_HIDE_FROM_ABI __segment_iterator __segment(_Iterator __iter) { return __iter.__m_iter_; }
@@ -757,7 +736,7 @@ public:
   // iterators:
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT {
-    __map_pointer __mp = __map_.begin() + __start_ / __block_size;
+    auto __mp = static_cast<const __map&>(__map_).begin() + __start_ / __block_size;
     return iterator(__mp, __map_.empty() ? 0 : *__mp + __start_ % __block_size);
   }
 
@@ -767,8 +746,8 @@ public:
   }
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT {
-    size_type __p      = size() + __start_;
-    __map_pointer __mp = __map_.begin() + __p / __block_size;
+    size_type __p = size() + __start_;
+    auto __mp     = static_cast<const __map&>(__map_).begin() + __p / __block_size;
     return iterator(__mp, __map_.empty() ? 0 : *__mp + __p % __block_size);
   }
 
diff --git a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
index 00acd5057db7c..7eaf64ea09d13 100644
--- a/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
+++ b/libcxx/test/libcxx/containers/sequences/deque/abi.compile.pass.cpp
@@ -18,9 +18,6 @@
 template <class T>
 class small_pointer {
   std::uint16_t offset;
-
-public:
-  T& operator*() const;
 };
 
 template <class T>

>From ae675ec1368a816fbdf7c5dc59ae93670680a241 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 9 Mar 2026 20:57:34 +0800
Subject: [PATCH 18/19] Fix pointer rebinding

---
 libcxx/include/deque | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index d6132b334d648..902a35e52f4cc 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -283,7 +283,7 @@ template <class _ValueType,
 #  endif
           >
 class __deque_iterator {
-  using __map_iterator _LIBCPP_NODEBUG = __rebind_pointer_t<const __rebind_pointer_t<_Pointer, _ValueType> >;
+  using __map_iterator _LIBCPP_NODEBUG = __rebind_pointer_t<_Pointer, const __rebind_pointer_t<_Pointer, _ValueType> >;
 
 // TODO(LLVM 25): Remove this check
 #  ifndef _LIBCPP_ABI_DEQUE_ITERATORS
@@ -470,8 +470,9 @@ private:
       __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer, _DiffType, _BlockSize>;
 
 public:
-  using __segment_iterator _LIBCPP_NODEBUG = __rebind_pointer_t<const __rebind_pointer_t<_Pointer, _ValueType> >;
-  using __local_iterator _LIBCPP_NODEBUG   = _Pointer;
+  using __segment_iterator _LIBCPP_NODEBUG =
+      __rebind_pointer_t<_Pointer, const __rebind_pointer_t<_Pointer, _ValueType> >;
+  using __local_iterator _LIBCPP_NODEBUG = _Pointer;
 
   static _LIBCPP_HIDE_FROM_ABI __segment_iterator __segment(_Iterator __iter) { return __iter.__m_iter_; }
   static _LIBCPP_HIDE_FROM_ABI __local_iterator __local(_Iterator __iter) { return __iter.__ptr_; }

>From adf464c3f48559e8b85a3036e8db417a64e92ef5 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 10 Mar 2026 09:36:18 +0800
Subject: [PATCH 19/19] Simplify member type alias and remove unnecessary casts

---
 libcxx/include/deque | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/deque b/libcxx/include/deque
index 902a35e52f4cc..c8c6889f1a165 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -470,9 +470,8 @@ private:
       __deque_iterator<_ValueType, _Pointer, _Reference, _MapPointer, _DiffType, _BlockSize>;
 
 public:
-  using __segment_iterator _LIBCPP_NODEBUG =
-      __rebind_pointer_t<_Pointer, const __rebind_pointer_t<_Pointer, _ValueType> >;
-  using __local_iterator _LIBCPP_NODEBUG = _Pointer;
+  using __segment_iterator _LIBCPP_NODEBUG = typename _Iterator::__map_iterator;
+  using __local_iterator _LIBCPP_NODEBUG   = _Pointer;
 
   static _LIBCPP_HIDE_FROM_ABI __segment_iterator __segment(_Iterator __iter) { return __iter.__m_iter_; }
   static _LIBCPP_HIDE_FROM_ABI __local_iterator __local(_Iterator __iter) { return __iter.__ptr_; }
@@ -737,7 +736,7 @@ public:
   // iterators:
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT {
-    auto __mp = static_cast<const __map&>(__map_).begin() + __start_ / __block_size;
+    auto __mp = __map_.begin() + __start_ / __block_size;
     return iterator(__mp, __map_.empty() ? 0 : *__mp + __start_ % __block_size);
   }
 
@@ -748,7 +747,7 @@ public:
 
   [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT {
     size_type __p = size() + __start_;
-    auto __mp     = static_cast<const __map&>(__map_).begin() + __p / __block_size;
+    auto __mp     = __map_.begin() + __p / __block_size;
     return iterator(__mp, __map_.empty() ? 0 : *__mp + __p % __block_size);
   }
 



More information about the libcxx-commits mailing list