[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 05:58:11 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/18] [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/18] 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/18] `__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/18] 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/18] 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/18] 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/18] 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/18] `_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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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_; }
More information about the libcxx-commits
mailing list