[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
Thu Jun 5 04:53:48 PDT 2025


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 1/7] [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 2/7] 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 3/7] `__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 4/7] 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 5/7] 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 6/7] 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 7/7] 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; }
 



More information about the libcxx-commits mailing list