[libcxx-commits] [libcxx] [libc++][WIP] ADL-proof `std::__wrap_iter` (PR #107766)

Mital Ashok via libcxx-commits libcxx-commits at lists.llvm.org
Sun Sep 8 09:07:22 PDT 2024


https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/107766

>From ffff9dfb0ef8547207b70e5161ef4727bff66cee Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Sun, 8 Sep 2024 16:51:58 +0100
Subject: [PATCH] wip

---
 libcxx/include/__configuration/abi.h          |   8 +
 libcxx/include/__format/buffer.h              |   2 +-
 libcxx/include/__iterator/iterator_traits.h   |  10 +
 libcxx/include/__iterator/wrap_iter.h         | 223 +++++++++++-------
 libcxx/include/__memory/pointer_traits.h      |  38 ++-
 libcxx/include/array                          |   8 +-
 libcxx/include/regex                          |  21 ++
 libcxx/include/span                           |   4 +-
 libcxx/include/string                         |  32 ++-
 libcxx/include/string_view                    |   2 +-
 libcxx/include/vector                         |  20 +-
 .../iterators/contiguous_iterators.pass.cpp   |  39 ++-
 .../libcxx/iterators/unwrap_iter.pass.cpp     |   8 +-
 .../iterators/wrap_iter_adl.compile.pass.cpp  |  58 +++++
 ...range_concept_conformance.compile.pass.cpp |  48 ++--
 ...rator_concept_conformance.compile.pass.cpp | 131 +++++-----
 libcxx/test/support/nasty_string.h            |   7 +
 17 files changed, 454 insertions(+), 205 deletions(-)
 create mode 100644 libcxx/test/libcxx/iterators/wrap_iter_adl.compile.pass.cpp

diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 8efbb42d1d8470..a368586ceb8adf 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -98,6 +98,14 @@
 // and WCHAR_MAX. This ABI setting determines whether we should instead track whether the fill
 // value has been initialized using a separate boolean, which changes the ABI.
 #  define _LIBCPP_ABI_IOS_ALLOW_ARBITRARY_FILL_VALUE
+// Change std::__wrap_iter<T> to not associate the namespaces associated with
+// T when unqualified lookup (e.g., with operators) is done. Also make it so
+// only std::__wrap_iter<pointer> and std::__wrap_iter<const_pointer> can
+// be used together (for comparison, conversion, etc.) instead of with any
+// wrapped iterator. So comprison between std::__wrap_iter<fancy_pointer<T>>
+// and std::__wrap_iter<T*> will no longer compile even if fancy_pointer<T>
+// is comparable with T*.
+#  define _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
 #elif _LIBCPP_ABI_VERSION == 1
 #  if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
 // Enable compiling copies of now inline methods into the dylib to support
diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h
index ce9ac0c81e315a..048af2a00f8ae3 100644
--- a/libcxx/include/__format/buffer.h
+++ b/libcxx/include/__format/buffer.h
@@ -258,7 +258,7 @@ concept __enable_direct_output =
     (same_as<_OutIt, _CharT*>
      // TODO(hardening): the following check might not apply to hardened iterators and might need to be wrapped in an
      // `#ifdef`.
-     || same_as<_OutIt, __wrap_iter<_CharT*>>);
+     || same_as<_OutIt, __wrap_mut_iter<_CharT*, const _CharT*>>);
 
 /// Write policy for directly writing to the underlying output.
 template <class _OutIt, __fmt_char_type _CharT>
diff --git a/libcxx/include/__iterator/iterator_traits.h b/libcxx/include/__iterator/iterator_traits.h
index 4d9ad480cc4a29..a06f9660c8d739 100644
--- a/libcxx/include/__iterator/iterator_traits.h
+++ b/libcxx/include/__iterator/iterator_traits.h
@@ -453,6 +453,14 @@ template <class _Tp>
 struct __libcpp_is_contiguous_iterator
     : _Or< __has_iterator_category_convertible_to<_Tp, contiguous_iterator_tag>,
            __has_iterator_concept_convertible_to<_Tp, contiguous_iterator_tag> > {};
+#elif defined(_LIBCPP_ABI_WRAP_ITER_ADL_PROOF)
+template <class _It, class = void>
+struct __is_contiguous_wrap_iter : false_type {};
+template <class _It>
+struct __is_contiguous_wrap_iter<_It, __void_t<typename _Tp::__is_wrap_iter> > : true_type {};
+
+template <class _Tp>
+struct __libcpp_is_contiguous_iterator : __is_contiguous_wrap_iter<_Tp> {};
 #else
 template <class _Tp>
 struct __libcpp_is_contiguous_iterator : false_type {};
@@ -462,8 +470,10 @@ struct __libcpp_is_contiguous_iterator : false_type {};
 template <class _Up>
 struct __libcpp_is_contiguous_iterator<_Up*> : true_type {};
 
+#ifndef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
 template <class _Iter>
 class __wrap_iter;
+#endif
 
 template <class _Tp>
 using __has_exactly_input_iterator_category =
diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h
index 549d8ff2dbd7db..f61d7a6ede6665 100644
--- a/libcxx/include/__iterator/wrap_iter.h
+++ b/libcxx/include/__iterator/wrap_iter.h
@@ -16,9 +16,11 @@
 #include <__iterator/iterator_traits.h>
 #include <__memory/addressof.h>
 #include <__memory/pointer_traits.h>
+#include <__type_traits/conditional.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_convertible.h>
+#include <__type_traits/void_t.h>
 #include <cstddef>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -27,15 +29,25 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+template <class _MutableIter, class _ConstIter>
+struct __wrap_iter_impl {
+  template <bool _IsConst>
+#else
 template <class _Iter>
-class __wrap_iter {
-public:
-  typedef _Iter iterator_type;
-  typedef typename iterator_traits<iterator_type>::value_type value_type;
-  typedef typename iterator_traits<iterator_type>::difference_type difference_type;
-  typedef typename iterator_traits<iterator_type>::pointer pointer;
-  typedef typename iterator_traits<iterator_type>::reference reference;
-  typedef typename iterator_traits<iterator_type>::iterator_category iterator_category;
+#endif
+  class __wrap_iter {
+
+  public:
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+    using _Iter = __conditional_t<_IsConst, _ConstIter, _MutableIter>;
+#endif
+    typedef _Iter iterator_type;
+    typedef typename iterator_traits<iterator_type>::value_type value_type;
+    typedef typename iterator_traits<iterator_type>::difference_type difference_type;
+    typedef typename iterator_traits<iterator_type>::pointer pointer;
+    typedef typename iterator_traits<iterator_type>::reference reference;
+    typedef typename iterator_traits<iterator_type>::iterator_category iterator_category;
 #if _LIBCPP_STD_VER >= 20
   typedef contiguous_iterator_tag iterator_concept;
 #endif
@@ -45,9 +57,15 @@ class __wrap_iter {
 
 public:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __wrap_iter() _NOEXCEPT : __i_() {}
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+  template <bool _B, __enable_if_t<!_B, int> = 0>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __wrap_iter(const __wrap_iter<_B>& __u) _NOEXCEPT
+      : __i_(__u.base()) {}
+#else
   template <class _Up, __enable_if_t<is_convertible<_Up, iterator_type>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __wrap_iter(const __wrap_iter<_Up>& __u) _NOEXCEPT
       : __i_(__u.base()) {}
+#endif
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 reference operator*() const _NOEXCEPT { return *__i_; }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pointer operator->() const _NOEXCEPT {
     return std::__to_address(__i_);
@@ -96,7 +114,13 @@ class __wrap_iter {
 private:
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __wrap_iter(iterator_type __x) _NOEXCEPT : __i_(__x) {}
 
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+  using __is_wrap_iter = void;
+
+  template <bool _OtherConst>
+#else
   template <class _Up>
+#endif
   friend class __wrap_iter;
   template <class _CharT, class _Traits, class _Alloc>
   friend class basic_string;
@@ -108,85 +132,99 @@ class __wrap_iter {
   friend class _LIBCPP_TEMPLATE_VIS span;
   template <class _Tp, size_t _Size>
   friend struct array;
-};
+  };
+
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+#  define _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD                                                                       \
+    template <bool _Iter1>                                                                                             \
+    friend
+#  define _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD                                                                         \
+    template <bool _Iter1, bool _Iter2>                                                                                \
+    friend
+#else
+#  define _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD template <class _Iter1>
+#  define _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD template <class _Iter1, class _Iter2>
+#endif
 
-template <class _Iter1>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
-  return __x.base() == __y.base();
-}
+  _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
+    return __x.base() == __y.base();
+  }
 
-template <class _Iter1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
-  return __x.base() == __y.base();
-}
+  _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
+    return __x.base() == __y.base();
+  }
 
-template <class _Iter1>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
-operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
-  return __x.base() < __y.base();
-}
+  _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
+  operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
+    return __x.base() < __y.base();
+  }
 
-template <class _Iter1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
-operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
-  return __x.base() < __y.base();
-}
+  _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
+  operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
+    return __x.base() < __y.base();
+  }
 
 #if _LIBCPP_STD_VER <= 17
-template <class _Iter1>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
-  return !(__x == __y);
-}
+  _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
+    return !(__x == __y);
+  }
 
-template <class _Iter1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
-  return !(__x == __y);
-}
-template <class _Iter1>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
-  return __y < __x;
-}
+  _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
+    return !(__x == __y);
+  }
+  _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
+    return __y < __x;
+  }
 
-template <class _Iter1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
-  return __y < __x;
-}
+  _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
+    return __y < __x;
+  }
 
-template <class _Iter1>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
-  return !(__x < __y);
-}
+  _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
+    return !(__x < __y);
+  }
 
-template <class _Iter1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
-  return !(__x < __y);
-}
+  _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
+    return !(__x < __y);
+  }
 
-template <class _Iter1>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
-  return !(__y < __x);
-}
+  _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT {
+    return !(__y < __x);
+  }
 
-template <class _Iter1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
-operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
-  return !(__y < __x);
-}
+  _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+  operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT {
+    return !(__y < __x);
+  }
 
 #else
-template <class _Iter1, class _Iter2>
+_LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD
 _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
 operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) noexcept {
-  if constexpr (three_way_comparable_with<_Iter1, _Iter2, strong_ordering>) {
+  if constexpr (three_way_comparable_with<typename __wrap_iter<_Iter1>::iterator_type,
+                                          typename __wrap_iter<_Iter2>::iterator_type,
+                                          strong_ordering>) {
     return __x.base() <=> __y.base();
   } else {
     if (__x.base() < __y.base())
@@ -200,31 +238,38 @@ operator<=>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) noex
 }
 #endif // _LIBCPP_STD_VER >= 20
 
-template <class _Iter1, class _Iter2>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14
+  _LIBCPP_WRAP_ITER_PAIR_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14
 #ifndef _LIBCPP_CXX03_LANG
-    auto
-    operator-(const __wrap_iter<_Iter1>& __x,
-              const __wrap_iter<_Iter2>& __y) _NOEXCEPT->decltype(__x.base() - __y.base())
+  auto
+  operator-(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT->decltype(__x.base() - __y.base())
 #else
 typename __wrap_iter<_Iter1>::difference_type
 operator-(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT
 #endif // C++03
-{
+  {
   return __x.base() - __y.base();
-}
+  }
 
-template <class _Iter1>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __wrap_iter<_Iter1>
-operator+(typename __wrap_iter<_Iter1>::difference_type __n, __wrap_iter<_Iter1> __x) _NOEXCEPT {
-  __x += __n;
-  return __x;
-}
+  _LIBCPP_WRAP_ITER_SINGLE_TEMPLATE_HEAD
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __wrap_iter<_Iter1>
+  operator+(typename __wrap_iter<_Iter1>::difference_type __n, __wrap_iter<_Iter1> __x) _NOEXCEPT {
+    __x += __n;
+    return __x;
+  }
 
-#if _LIBCPP_STD_VER <= 17
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+};
+
+template <class _MutableIter, class _ConstIter>
+using __wrap_mut_iter = typename __wrap_iter_impl<_MutableIter, _ConstIter>::template __wrap_iter<false>;
+template <class _MutableIter, class _ConstIter>
+using __wrap_const_iter = typename __wrap_iter_impl<_MutableIter, _ConstIter>::template __wrap_iter<true>;
+#else
+#  if _LIBCPP_STD_VER <= 17
 template <class _It>
 struct __libcpp_is_contiguous_iterator<__wrap_iter<_It> > : true_type {};
-#endif
+#  endif
 
 template <class _It>
 struct _LIBCPP_TEMPLATE_VIS pointer_traits<__wrap_iter<_It> > {
@@ -237,6 +282,12 @@ struct _LIBCPP_TEMPLATE_VIS pointer_traits<__wrap_iter<_It> > {
   }
 };
 
+template <class _MutableIter, class _ConstIter>
+using __wrap_mut_iter = __wrap_iter<_MutableIter>;
+template <class _MutableIter, class _ConstIter>
+using __wrap_const_iter = __wrap_iter<_ConstIter>;
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ITERATOR_WRAP_ITER_H
diff --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index 8e08eb74413ee5..0e4196625af4e9 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -43,6 +43,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 _LIBCPP_CLASS_TRAITS_HAS_XXX(__has_pointer, pointer);
 _LIBCPP_CLASS_TRAITS_HAS_XXX(__has_element_type, element_type);
+_LIBCPP_CLASS_TRAITS_HAS_XXX(__is_wrap_iter, __is_wrap_iter);
 
 template <class _Ptr, bool = __has_element_type<_Ptr>::value>
 struct __pointer_traits_element_type {};
@@ -115,11 +116,21 @@ struct __pointer_traits_rebind<_Sp<_Tp, _Args...>, _Up, false> {
   typedef _Sp<_Up, _Args...> type;
 };
 
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+template <class _Ptr, bool, class = void>
+struct __pointer_traits_impl {};
+
+template <class _Ptr>
+struct __pointer_traits_impl<_Ptr,
+                             false,
+#else
 template <class _Ptr, class = void>
 struct __pointer_traits_impl {};
 
 template <class _Ptr>
-struct __pointer_traits_impl<_Ptr, __void_t<typename __pointer_traits_element_type<_Ptr>::type> > {
+struct __pointer_traits_impl<_Ptr,
+#endif
+                             __void_t<typename __pointer_traits_element_type<_Ptr>::type> > {
   typedef _Ptr pointer;
   typedef typename __pointer_traits_element_type<pointer>::type element_type;
   typedef typename __pointer_traits_difference_type<pointer>::type difference_type;
@@ -144,8 +155,25 @@ struct __pointer_traits_impl<_Ptr, __void_t<typename __pointer_traits_element_ty
   }
 };
 
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+template <class _Ptr>
+struct _LIBCPP_TEMPLATE_VIS pointer_traits;
+
+template <class _WrapIt>
+struct __pointer_traits_impl<_WrapIt, true, void> {
+  using pointer         = _WrapIt;
+  using element_type    = typename pointer_traits<typename _WrapIt::iterator_type>::element_type;
+  using difference_type = typename pointer_traits<typename _WrapIt::iterator_type>::difference_type;
+
+  inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR static element_type* to_address(pointer __w) _NOEXCEPT;
+};
+
+template <class _Ptr>
+struct _LIBCPP_TEMPLATE_VIS pointer_traits : __pointer_traits_impl<_Ptr, __is_wrap_iter<_Ptr>::value> {};
+#else
 template <class _Ptr>
 struct _LIBCPP_TEMPLATE_VIS pointer_traits : __pointer_traits_impl<_Ptr> {};
+#endif
 
 template <class _Tp>
 struct _LIBCPP_TEMPLATE_VIS pointer_traits<_Tp*> {
@@ -237,6 +265,14 @@ struct __to_address_helper<_Pointer,
   }
 };
 
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+template <class _WrapIt>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR typename __pointer_traits_impl<_WrapIt, true, void>::element_type*
+__pointer_traits_impl<_WrapIt, true, void>::to_address(pointer __w) _NOEXCEPT {
+  return std::__to_address(__w);
+}
+#endif
+
 #if _LIBCPP_STD_VER >= 20
 template <class _Tp>
 inline _LIBCPP_HIDE_FROM_ABI constexpr auto to_address(_Tp* __p) noexcept {
diff --git a/libcxx/include/array b/libcxx/include/array
index 588664ace0162a..36fb4963b875c8 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -177,8 +177,8 @@ struct _LIBCPP_TEMPLATE_VIS array {
   using pointer         = value_type*;
   using const_pointer   = const value_type*;
 #if defined(_LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY)
-  using iterator       = __wrap_iter<pointer>;
-  using const_iterator = __wrap_iter<const_pointer>;
+  using iterator       = __wrap_mut_iter<pointer, const_pointer>;
+  using const_iterator = __wrap_const_iter<pointer, const_pointer>;
 #else
   using iterator       = pointer;
   using const_iterator = const_pointer;
@@ -277,8 +277,8 @@ struct _LIBCPP_TEMPLATE_VIS array<_Tp, 0> {
   using pointer         = value_type*;
   using const_pointer   = const value_type*;
 #if defined(_LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY)
-  using iterator       = __wrap_iter<pointer>;
-  using const_iterator = __wrap_iter<const_pointer>;
+  using iterator       = __wrap_mut_iter<pointer, const_pointer>;
+  using const_iterator = __wrap_const_iter<pointer, const_pointer>;
 #else
   using iterator       = pointer;
   using const_iterator = const_pointer;
diff --git a/libcxx/include/regex b/libcxx/include/regex
index 08aebc2266f5de..0e3e90cc5c634a 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -2632,6 +2632,16 @@ private:
                            const basic_regex<_Cp, _Tp>& __e,
                            regex_constants::match_flag_type __flags);
 
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+  template <class _WrapIter, class _Ap, class _Cp, class _Tp, typename _WrapIter::__is_wrap_iter*>
+  friend bool
+  regex_search(_WrapIter __first,
+               _WrapIter __last,
+               match_results<_WrapIter, _Ap>& __m,
+               const basic_regex<_Cp, _Tp>& __e,
+               regex_constants::match_flag_type __flags);
+
+#else
   template <class _Iter, class _Ap, class _Cp, class _Tp>
   friend bool
   regex_search(__wrap_iter<_Iter> __first,
@@ -2639,6 +2649,7 @@ private:
                match_results<__wrap_iter<_Iter>, _Ap>& __m,
                const basic_regex<_Cp, _Tp>& __e,
                regex_constants::match_flag_type __flags);
+#endif
 
   template <class, class>
   friend class __lookahead;
@@ -5149,6 +5160,15 @@ regex_search(_BidirectionalIterator __first,
   return __r;
 }
 
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+template <class _WrapIter, class _Allocator, class _CharT, class _Traits, typename _WrapIter::__is_wrap_iter* = nullptr>
+inline _LIBCPP_HIDE_FROM_ABI bool
+regex_search(_WrapIter __first,
+             _WrapIter __last,
+             match_results<_WrapIter, _Allocator>& __m,
+             const basic_regex<_CharT, _Traits>& __e,
+             regex_constants::match_flag_type __flags = regex_constants::match_default) {
+#else
 template <class _Iter, class _Allocator, class _CharT, class _Traits>
 inline _LIBCPP_HIDE_FROM_ABI bool
 regex_search(__wrap_iter<_Iter> __first,
@@ -5156,6 +5176,7 @@ regex_search(__wrap_iter<_Iter> __first,
              match_results<__wrap_iter<_Iter>, _Allocator>& __m,
              const basic_regex<_CharT, _Traits>& __e,
              regex_constants::match_flag_type __flags = regex_constants::match_default) {
+#endif
   match_results<const _CharT*> __mc;
   bool __r = __e.__search(__first.base(), __last.base(), __mc, __flags);
   __m.__assign(__first, __last, __mc, __flags & regex_constants::__no_update_pos);
diff --git a/libcxx/include/span b/libcxx/include/span
index a32f7a372e2ae1..a691ba7e8623e4 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -239,7 +239,7 @@ public:
 #  ifdef _LIBCPP_ABI_BOUNDED_ITERATORS
   using iterator = __bounded_iter<pointer>;
 #  else
-  using iterator = __wrap_iter<pointer>;
+  using iterator = __wrap_mut_iter<pointer, pointer>;
 #  endif
   using reverse_iterator = std::reverse_iterator<iterator>;
 
@@ -420,7 +420,7 @@ public:
 #  ifdef _LIBCPP_ABI_BOUNDED_ITERATORS
   using iterator = __bounded_iter<pointer>;
 #  else
-  using iterator = __wrap_iter<pointer>;
+  using iterator = __wrap_mut_iter<pointer, pointer>;
 #  endif
   using reverse_iterator = std::reverse_iterator<iterator>;
 
diff --git a/libcxx/include/string b/libcxx/include/string
index 3480b57375c118..a4ccaaec4c9c21 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -732,6 +732,17 @@ operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs, basic_strin
 extern template _LIBCPP_EXPORTED_FROM_ABI string operator+
     <char, char_traits<char>, allocator<char> >(char const*, string const&);
 
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+template <class _Iter, class = void>
+struct __string_is_trivial_iterator : public false_type {};
+
+template <class _Tp>
+struct __string_is_trivial_iterator<_Tp*, void> : public is_arithmetic<_Tp> {};
+
+template <class _WrapIter>
+struct __string_is_trivial_iterator<_WrapIter, __void_t<typename _WrapIter::__is_wrap_iter> >
+    : public __string_is_trivial_iterator<typename _WrapIter::iterator_type> {};
+#else
 template <class _Iter>
 struct __string_is_trivial_iterator : public false_type {};
 
@@ -740,6 +751,7 @@ struct __string_is_trivial_iterator<_Tp*> : public is_arithmetic<_Tp> {};
 
 template <class _Iter>
 struct __string_is_trivial_iterator<__wrap_iter<_Iter> > : public __string_is_trivial_iterator<_Iter> {};
+#endif
 
 template <class _CharT, class _Traits, class _Tp>
 struct __can_be_converted_to_string_view
@@ -828,11 +840,11 @@ public:
   // Users might provide custom allocators, and prior to C++20 we have no existing way to detect whether the allocator's
   // pointer type is contiguous (though it has to be by the Standard). Using the wrapper type ensures the iterator is
   // considered contiguous.
-  typedef __bounded_iter<__wrap_iter<pointer> > iterator;
-  typedef __bounded_iter<__wrap_iter<const_pointer> > const_iterator;
+  typedef __bounded_iter<__wrap_mut_iter<pointer, const_pointer> > iterator;
+  typedef __bounded_iter<__wrap_const_iter<pointer, const_pointer> > const_iterator;
 #else
-  typedef __wrap_iter<pointer> iterator;
-  typedef __wrap_iter<const_pointer> const_iterator;
+  typedef __wrap_mut_iter<pointer, const_pointer> iterator;
+  typedef __wrap_const_iter<pointer, const_pointer> const_iterator;
 #endif
   typedef std::reverse_iterator<iterator> reverse_iterator;
   typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -958,9 +970,9 @@ private:
     // a stricter check, since correct code can never rely on being able to access newly-added elements via an existing
     // iterator.
     return std::__make_bounded_iter(
-        std::__wrap_iter<pointer>(__p),
-        std::__wrap_iter<pointer>(__get_pointer()),
-        std::__wrap_iter<pointer>(__get_pointer() + size()));
+        __wrap_mut_iter<pointer, const_pointer>(__p),
+        __wrap_mut_iter<pointer, const_pointer>(__get_pointer()),
+        __wrap_mut_iter<pointer, const_pointer>(__get_pointer() + size()));
 #else
     return iterator(__p);
 #endif // _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING
@@ -970,9 +982,9 @@ private:
 #ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING
     // Bound the iterator according to the size (and not the capacity, unlike vector).
     return std::__make_bounded_iter(
-        std::__wrap_iter<const_pointer>(__p),
-        std::__wrap_iter<const_pointer>(__get_pointer()),
-        std::__wrap_iter<const_pointer>(__get_pointer() + size()));
+        __wrap_const_iter<pointer, const_pointer>(__p),
+        __wrap_const_iter<pointer, const_pointer>(__get_pointer()),
+        __wrap_const_iter<pointer, const_pointer>(__get_pointer() + size()));
 #else
     return const_iterator(__p);
 #endif // _LIBCPP_ABI_BOUNDED_ITERATORS_IN_STRING
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index cf97e3a9be314d..451b50fb7a7b71 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -284,7 +284,7 @@ public:
 #if defined(_LIBCPP_ABI_BOUNDED_ITERATORS)
   using const_iterator = __bounded_iter<const_pointer>;
 #elif defined(_LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW)
-  using const_iterator = __wrap_iter<const_pointer>;
+  using const_iterator = __wrap_const_iter<const_pointer, const_pointer>;
 #else
   using const_iterator = const_pointer;
 #endif
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 2442852c764a63..2bf296fef5d063 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -404,11 +404,11 @@ public:
   // Users might provide custom allocators, and prior to C++20 we have no existing way to detect whether the allocator's
   // pointer type is contiguous (though it has to be by the Standard). Using the wrapper type ensures the iterator is
   // considered contiguous.
-  typedef __bounded_iter<__wrap_iter<pointer> > iterator;
-  typedef __bounded_iter<__wrap_iter<const_pointer> > const_iterator;
+  typedef __bounded_iter<__wrap_mut_iter<pointer, const_pointer> > iterator;
+  typedef __bounded_iter<__wrap_const_iter<pointer, const_pointer> > const_iterator;
 #else
-  typedef __wrap_iter<pointer> iterator;
-  typedef __wrap_iter<const_pointer> const_iterator;
+  typedef __wrap_mut_iter<pointer, const_pointer> iterator;
+  typedef __wrap_const_iter<pointer, const_pointer> const_iterator;
 #endif
   typedef std::reverse_iterator<iterator> reverse_iterator;
   typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
@@ -847,9 +847,9 @@ private:
     // don't have a way to update existing valid iterators when the container is resized and thus have to go with
     // a laxer approach.
     return std::__make_bounded_iter(
-        std::__wrap_iter<pointer>(__p),
-        std::__wrap_iter<pointer>(this->__begin_),
-        std::__wrap_iter<pointer>(this->__end_cap()));
+        __wrap_mut_iter<pointer, const_pointer>(__p),
+        __wrap_mut_iter<pointer, const_pointer>(this->__begin_),
+        __wrap_mut_iter<pointer, const_pointer>(this->__end_cap()));
 #else
     return iterator(__p);
 #endif // _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
@@ -859,9 +859,9 @@ private:
 #ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
     // Bound the iterator according to the capacity, rather than the size.
     return std::__make_bounded_iter(
-        std::__wrap_iter<const_pointer>(__p),
-        std::__wrap_iter<const_pointer>(this->__begin_),
-        std::__wrap_iter<const_pointer>(this->__end_cap()));
+        __wrap_const_iter<pointer, const_pointer>(__p),
+        __wrap_const_iter<pointer, const_pointer>(this->__begin_),
+        __wrap_const_iter<pointer, const_pointer>(this->__end_cap()));
 #else
     return const_iterator(__p);
 #endif // _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
diff --git a/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp b/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp
index f00ca4e8794031..a5b1123b269f71 100644
--- a/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp
+++ b/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp
@@ -181,20 +181,35 @@ int main(int, char**)
     static_assert((!std::__libcpp_is_contiguous_iterator<std::reverse_iterator<my_contiguous_iterator> >::value), "");
 #endif
 
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<char *> >::value), "");
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<const char *> >::value), "");
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<int *> >::value), "");
+    static_assert((std::__libcpp_is_contiguous_iterator<std::__wrap_mut_iter<char*, const char*> >::value), "");
+    static_assert((std::__libcpp_is_contiguous_iterator<std::__wrap_const_iter<char*, const char*> >::value), "");
+    static_assert((std::__libcpp_is_contiguous_iterator<std::__wrap_mut_iter<int*, const int*> >::value), "");
 
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<T *> >::value), "");
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<std::__wrap_iter<T *> > >::value), "");
+    static_assert((std::__libcpp_is_contiguous_iterator<std::__wrap_mut_iter<T*, const T*> >::value), "");
+    static_assert(
+        (std::__libcpp_is_contiguous_iterator<
+            std::__wrap_mut_iter<std::__wrap_mut_iter<T*, const T*>, std::__wrap_mut_iter<T*, const T*> > >::value),
+        "");
 
     // Here my_random_access_iterator is standing in for some user's fancy pointer type, written pre-C++20.
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<my_random_access_iterator> >::value), "");
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<std::__wrap_iter<my_random_access_iterator> > >::value), "");
+    static_assert((std::__libcpp_is_contiguous_iterator<
+                      std::__wrap_mut_iter<my_random_access_iterator, my_random_access_iterator> >::value),
+                  "");
+    static_assert(
+        (std::__libcpp_is_contiguous_iterator<
+            std::__wrap_mut_iter<std::__wrap_mut_iter<my_random_access_iterator, my_random_access_iterator>,
+                                 std::__wrap_mut_iter<my_random_access_iterator, my_random_access_iterator> > >::value),
+        "");
 
 #if TEST_STD_VER >= 20
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<my_contiguous_iterator> >::value), "");
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<std::__wrap_iter<my_contiguous_iterator> > >::value), "");
+    static_assert((std::__libcpp_is_contiguous_iterator<
+                      std::__wrap_mut_iter<my_contiguous_iterator, my_contiguous_iterator> >::value),
+                  "");
+    static_assert(
+        (std::__libcpp_is_contiguous_iterator<
+            std::__wrap_mut_iter<std::__wrap_mut_iter<my_contiguous_iterator, my_contiguous_iterator>,
+                                 std::__wrap_mut_iter<my_contiguous_iterator, my_contiguous_iterator> > >::value),
+        "");
 #endif
 
 //  iterators in the libc++ test suite
@@ -218,9 +233,11 @@ int main(int, char**)
     static_assert(( std::__libcpp_is_contiguous_iterator<std::vector<int>::const_iterator>             ::value), "");
     static_assert((!std::__libcpp_is_contiguous_iterator<std::vector<int>::reverse_iterator>           ::value), "");
     static_assert((!std::__libcpp_is_contiguous_iterator<std::vector<int>::const_reverse_iterator>     ::value), "");
-    static_assert(( std::__libcpp_is_contiguous_iterator<std::__wrap_iter<std::vector<int>::iterator> >::value), "");
+    static_assert((std::__libcpp_is_contiguous_iterator<
+                      std::__wrap_mut_iter<std::vector<int>::iterator, std::vector<int>::const_iterator> >::value),
+                  "");
 
-//  string
+    //  string
     static_assert(( std::__libcpp_is_contiguous_iterator<std::string::iterator>              ::value), "");
     static_assert(( std::__libcpp_is_contiguous_iterator<std::string::const_iterator>        ::value), "");
     static_assert((!std::__libcpp_is_contiguous_iterator<std::string::reverse_iterator>      ::value), "");
diff --git a/libcxx/test/libcxx/iterators/unwrap_iter.pass.cpp b/libcxx/test/libcxx/iterators/unwrap_iter.pass.cpp
index 8ef2be2b010743..c2956cd6120ecc 100644
--- a/libcxx/test/libcxx/iterators/unwrap_iter.pass.cpp
+++ b/libcxx/test/libcxx/iterators/unwrap_iter.pass.cpp
@@ -26,11 +26,13 @@ template <class Iter>
 using rev_rev_iter = rev_iter<rev_iter<Iter> >;
 
 static_assert(std::is_same<UnwrapT<int*>, int*>::value, "");
-static_assert(std::is_same<UnwrapT<std::__wrap_iter<int*> >, int*>::value, "");
+static_assert(std::is_same<UnwrapT<std::__wrap_mut_iter<int*, const int*> >, int*>::value, "");
 static_assert(std::is_same<UnwrapT<rev_iter<int*> >, std::reverse_iterator<int*> >::value, "");
 static_assert(std::is_same<UnwrapT<rev_rev_iter<int*> >, int*>::value, "");
-static_assert(std::is_same<UnwrapT<rev_rev_iter<std::__wrap_iter<int*> > >, int*>::value, "");
-static_assert(std::is_same<UnwrapT<rev_rev_iter<rev_iter<std::__wrap_iter<int*> > > >, rev_iter<std::__wrap_iter<int*> > >::value, "");
+static_assert(std::is_same<UnwrapT<rev_rev_iter<std::__wrap_mut_iter<int*, const int*> > >, int*>::value, "");
+static_assert(std::is_same<UnwrapT<rev_rev_iter<rev_iter<std::__wrap_mut_iter<int*, const int*> > > >,
+                           rev_iter<std::__wrap_mut_iter<int*, const int*> > >::value,
+              "");
 
 static_assert(std::is_same<UnwrapT<random_access_iterator<int*> >, random_access_iterator<int*> >::value, "");
 static_assert(std::is_same<UnwrapT<rev_iter<random_access_iterator<int*> > >, rev_iter<random_access_iterator<int*> > >::value, "");
diff --git a/libcxx/test/libcxx/iterators/wrap_iter_adl.compile.pass.cpp b/libcxx/test/libcxx/iterators/wrap_iter_adl.compile.pass.cpp
new file mode 100644
index 00000000000000..d2cfbf16870ff6
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/wrap_iter_adl.compile.pass.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Check that whats used for __wrap_iter<T> does not try to look into T
+// for ADL
+
+#include <__iterator/wrap_iter.h>
+#include <vector>
+#include <array>
+#include <iterator>
+
+#include "test_macros.h"
+
+#ifdef _LIBCPP_ABI_WRAP_ITER_ADL_PROOF
+
+struct incomplete;
+template <class T>
+struct holder {
+  T t;
+};
+
+struct nefarious {
+  friend void operator<=>(std::vector<nefarious>::iterator, std::vector<nefarious>::iterator)            = delete;
+  friend void operator==(std::vector<nefarious>::const_iterator, std::vector<nefarious>::const_iterator) = delete;
+};
+
+template <class Container>
+void test() {
+  using iterator       = typename Container::iterator;
+  using const_iterator = typename Container::const_iterator;
+
+  (void)(iterator() < iterator());
+  (void)(iterator() < const_iterator());
+  (void)(const_iterator() < const_iterator());
+  (void)(iterator() == iterator());
+  (void)(iterator() == const_iterator());
+  (void)(const_iterator() == const_iterator());
+
+#  if TEST_STD_VER >= 20
+  static_assert(std::contiguous_iterator<iterator>);
+  static_assert(std::contiguous_iterator<const_iterator>);
+  static_assert(std::ranges::contiguous_range<Container>);
+  static_assert(std::ranges::contiguous_range<const Container>);
+#  endif
+}
+
+void tests() {
+  test<std::vector<holder<incomplete>*>>();
+  test<std::array<holder<incomplete>*, 10>>();
+  test<std::vector<nefarious>>();
+  test<std::array<nefarious, 10>>();
+}
+#endif
diff --git a/libcxx/test/std/strings/basic.string/range_concept_conformance.compile.pass.cpp b/libcxx/test/std/strings/basic.string/range_concept_conformance.compile.pass.cpp
index b6afc20e3cccb0..4712f85078a306 100644
--- a/libcxx/test/std/strings/basic.string/range_concept_conformance.compile.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/range_concept_conformance.compile.pass.cpp
@@ -15,20 +15,34 @@
 #include <concepts>
 #include <ranges>
 
-static_assert(std::same_as<std::ranges::iterator_t<std::string>, std::string::iterator>);
-static_assert(std::ranges::common_range<std::string>);
-static_assert(std::ranges::random_access_range<std::string>);
-static_assert(std::ranges::contiguous_range<std::string>);
-static_assert(!std::ranges::view<std::string>);
-static_assert(std::ranges::sized_range<std::string>);
-static_assert(!std::ranges::borrowed_range<std::string>);
-static_assert(std::ranges::viewable_range<std::string>);
-
-static_assert(std::same_as<std::ranges::iterator_t<std::string const>, std::string::const_iterator>);
-static_assert(std::ranges::common_range<std::string const>);
-static_assert(std::ranges::random_access_range<std::string const>);
-static_assert(std::ranges::contiguous_range<std::string const>);
-static_assert(!std::ranges::view<std::string const>);
-static_assert(std::ranges::sized_range<std::string const>);
-static_assert(!std::ranges::borrowed_range<std::string const>);
-static_assert(!std::ranges::viewable_range<std::string const>);
+#include "nasty_string.h"
+#include "test_macros.h"
+
+template <class String>
+void test() {
+  static_assert(std::same_as<std::ranges::iterator_t<String>, typename String::iterator>);
+  static_assert(std::ranges::common_range<String>);
+  static_assert(std::ranges::random_access_range<String>);
+  static_assert(std::ranges::contiguous_range<String>);
+  static_assert(!std::ranges::view<String>);
+  static_assert(std::ranges::sized_range<String>);
+  static_assert(!std::ranges::borrowed_range<String>);
+  static_assert(std::ranges::viewable_range<String>);
+
+  static_assert(std::same_as<std::ranges::iterator_t<String const>, typename String::const_iterator>);
+  static_assert(std::ranges::common_range<String const>);
+  static_assert(std::ranges::random_access_range<String const>);
+  static_assert(std::ranges::contiguous_range<String const>);
+  static_assert(!std::ranges::view<String const>);
+  static_assert(std::ranges::sized_range<String const>);
+  static_assert(!std::ranges::borrowed_range<String const>);
+  static_assert(!std::ranges::viewable_range<String const>);
+}
+
+void tests() {
+  test<std::string>();
+  test<std::wstring>();
+#ifndef TEST_HAS_NO_NASTY_STRING
+  test<nasty_string>();
+#endif
+}
diff --git a/libcxx/test/std/strings/basic.string/string.iterators/iterator_concept_conformance.compile.pass.cpp b/libcxx/test/std/strings/basic.string/string.iterators/iterator_concept_conformance.compile.pass.cpp
index bde2f9e9cde327..77212e24d7a644 100644
--- a/libcxx/test/std/strings/basic.string/string.iterators/iterator_concept_conformance.compile.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.iterators/iterator_concept_conformance.compile.pass.cpp
@@ -14,64 +14,77 @@
 
 #include <iterator>
 
-using iterator               = std::string::iterator;
-using const_iterator         = std::string::const_iterator;
-using reverse_iterator       = std::string::reverse_iterator;
-using const_reverse_iterator = std::string::const_reverse_iterator;
-using value_type             = char;
+#include "nasty_string.h"
 
-static_assert(std::contiguous_iterator<iterator>);
-static_assert(std::indirectly_writable<iterator, value_type>);
-static_assert(std::sentinel_for<iterator, iterator>);
-static_assert(std::sentinel_for<iterator, const_iterator>);
-static_assert(!std::sentinel_for<iterator, reverse_iterator>);
-static_assert(!std::sentinel_for<iterator, const_reverse_iterator>);
-static_assert(std::sized_sentinel_for<iterator, iterator>);
-static_assert(std::sized_sentinel_for<iterator, const_iterator>);
-static_assert(!std::sized_sentinel_for<iterator, reverse_iterator>);
-static_assert(!std::sized_sentinel_for<iterator, const_reverse_iterator>);
-static_assert(std::indirectly_movable<iterator, iterator>);
-static_assert(std::indirectly_movable_storable<iterator, iterator>);
-static_assert(!std::indirectly_movable<iterator, const_iterator>);
-static_assert(!std::indirectly_movable_storable<iterator, const_iterator>);
-static_assert(std::indirectly_movable<iterator, reverse_iterator>);
-static_assert(std::indirectly_movable_storable<iterator, reverse_iterator>);
-static_assert(!std::indirectly_movable<iterator, const_reverse_iterator>);
-static_assert(!std::indirectly_movable_storable<iterator, const_reverse_iterator>);
-static_assert(std::indirectly_copyable<iterator, iterator>);
-static_assert(std::indirectly_copyable_storable<iterator, iterator>);
-static_assert(!std::indirectly_copyable<iterator, const_iterator>);
-static_assert(!std::indirectly_copyable_storable<iterator, const_iterator>);
-static_assert(std::indirectly_copyable<iterator, reverse_iterator>);
-static_assert(std::indirectly_copyable_storable<iterator, reverse_iterator>);
-static_assert(!std::indirectly_copyable<iterator, const_reverse_iterator>);
-static_assert(!std::indirectly_copyable_storable<iterator, const_reverse_iterator>);
-static_assert(std::indirectly_swappable<iterator, iterator>);
+template <class String>
+void test() {
+  using iterator               = typename String::iterator;
+  using const_iterator         = typename String::const_iterator;
+  using reverse_iterator       = typename String::reverse_iterator;
+  using const_reverse_iterator = typename String::const_reverse_iterator;
+  using value_type             = typename String::value_type;
 
-static_assert(std::contiguous_iterator<const_iterator>);
-static_assert(!std::indirectly_writable<const_iterator, value_type>);
-static_assert(std::sentinel_for<const_iterator, iterator>);
-static_assert(std::sentinel_for<const_iterator, const_iterator>);
-static_assert(!std::sentinel_for<const_iterator, reverse_iterator>);
-static_assert(!std::sentinel_for<const_iterator, const_reverse_iterator>);
-static_assert(std::sized_sentinel_for<const_iterator, iterator>);
-static_assert(std::sized_sentinel_for<const_iterator, const_iterator>);
-static_assert(!std::sized_sentinel_for<const_iterator, reverse_iterator>);
-static_assert(!std::sized_sentinel_for<const_iterator, const_reverse_iterator>);
-static_assert(std::indirectly_movable<const_iterator, iterator>);
-static_assert(std::indirectly_movable_storable<const_iterator, iterator>);
-static_assert(!std::indirectly_movable<const_iterator, const_iterator>);
-static_assert(!std::indirectly_movable_storable<const_iterator, const_iterator>);
-static_assert(std::indirectly_movable<const_iterator, reverse_iterator>);
-static_assert(std::indirectly_movable_storable<const_iterator, reverse_iterator>);
-static_assert(!std::indirectly_movable<const_iterator, const_reverse_iterator>);
-static_assert(!std::indirectly_movable_storable<const_iterator, const_reverse_iterator>);
-static_assert(std::indirectly_copyable<const_iterator, iterator>);
-static_assert(std::indirectly_copyable_storable<const_iterator, iterator>);
-static_assert(!std::indirectly_copyable<const_iterator, const_iterator>);
-static_assert(!std::indirectly_copyable_storable<const_iterator, const_iterator>);
-static_assert(std::indirectly_copyable<const_iterator, reverse_iterator>);
-static_assert(std::indirectly_copyable_storable<const_iterator, reverse_iterator>);
-static_assert(!std::indirectly_copyable<const_iterator, const_reverse_iterator>);
-static_assert(!std::indirectly_copyable_storable<const_iterator, const_reverse_iterator>);
-static_assert(!std::indirectly_swappable<const_iterator, const_iterator>);
+  static_assert(std::contiguous_iterator<iterator>);
+  static_assert(std::indirectly_writable<iterator, value_type>);
+  static_assert(std::sentinel_for<iterator, iterator>);
+  static_assert(std::sentinel_for<iterator, const_iterator>);
+  static_assert(!std::sentinel_for<iterator, reverse_iterator>);
+  static_assert(!std::sentinel_for<iterator, const_reverse_iterator>);
+  static_assert(std::sized_sentinel_for<iterator, iterator>);
+  static_assert(std::sized_sentinel_for<iterator, const_iterator>);
+  static_assert(!std::sized_sentinel_for<iterator, reverse_iterator>);
+  static_assert(!std::sized_sentinel_for<iterator, const_reverse_iterator>);
+  static_assert(std::indirectly_movable<iterator, iterator>);
+  static_assert(std::indirectly_movable_storable<iterator, iterator>);
+  static_assert(!std::indirectly_movable<iterator, const_iterator>);
+  static_assert(!std::indirectly_movable_storable<iterator, const_iterator>);
+  static_assert(std::indirectly_movable<iterator, reverse_iterator>);
+  static_assert(std::indirectly_movable_storable<iterator, reverse_iterator>);
+  static_assert(!std::indirectly_movable<iterator, const_reverse_iterator>);
+  static_assert(!std::indirectly_movable_storable<iterator, const_reverse_iterator>);
+  static_assert(std::indirectly_copyable<iterator, iterator>);
+  static_assert(std::indirectly_copyable_storable<iterator, iterator>);
+  static_assert(!std::indirectly_copyable<iterator, const_iterator>);
+  static_assert(!std::indirectly_copyable_storable<iterator, const_iterator>);
+  static_assert(std::indirectly_copyable<iterator, reverse_iterator>);
+  static_assert(std::indirectly_copyable_storable<iterator, reverse_iterator>);
+  static_assert(!std::indirectly_copyable<iterator, const_reverse_iterator>);
+  static_assert(!std::indirectly_copyable_storable<iterator, const_reverse_iterator>);
+  static_assert(std::indirectly_swappable<iterator, iterator>);
+
+  static_assert(std::contiguous_iterator<const_iterator>);
+  static_assert(!std::indirectly_writable<const_iterator, value_type>);
+  static_assert(std::sentinel_for<const_iterator, iterator>);
+  static_assert(std::sentinel_for<const_iterator, const_iterator>);
+  static_assert(!std::sentinel_for<const_iterator, reverse_iterator>);
+  static_assert(!std::sentinel_for<const_iterator, const_reverse_iterator>);
+  static_assert(std::sized_sentinel_for<const_iterator, iterator>);
+  static_assert(std::sized_sentinel_for<const_iterator, const_iterator>);
+  static_assert(!std::sized_sentinel_for<const_iterator, reverse_iterator>);
+  static_assert(!std::sized_sentinel_for<const_iterator, const_reverse_iterator>);
+  static_assert(std::indirectly_movable<const_iterator, iterator>);
+  static_assert(std::indirectly_movable_storable<const_iterator, iterator>);
+  static_assert(!std::indirectly_movable<const_iterator, const_iterator>);
+  static_assert(!std::indirectly_movable_storable<const_iterator, const_iterator>);
+  static_assert(std::indirectly_movable<const_iterator, reverse_iterator>);
+  static_assert(std::indirectly_movable_storable<const_iterator, reverse_iterator>);
+  static_assert(!std::indirectly_movable<const_iterator, const_reverse_iterator>);
+  static_assert(!std::indirectly_movable_storable<const_iterator, const_reverse_iterator>);
+  static_assert(std::indirectly_copyable<const_iterator, iterator>);
+  static_assert(std::indirectly_copyable_storable<const_iterator, iterator>);
+  static_assert(!std::indirectly_copyable<const_iterator, const_iterator>);
+  static_assert(!std::indirectly_copyable_storable<const_iterator, const_iterator>);
+  static_assert(std::indirectly_copyable<const_iterator, reverse_iterator>);
+  static_assert(std::indirectly_copyable_storable<const_iterator, reverse_iterator>);
+  static_assert(!std::indirectly_copyable<const_iterator, const_reverse_iterator>);
+  static_assert(!std::indirectly_copyable_storable<const_iterator, const_reverse_iterator>);
+  static_assert(!std::indirectly_swappable<const_iterator, const_iterator>);
+}
+
+void tests() {
+  test<std::string>();
+  test<std::wstring>();
+#if !defined(TEST_HAS_NO_NASTY_STRING)
+  test<nasty_string>();
+#endif
+}
diff --git a/libcxx/test/support/nasty_string.h b/libcxx/test/support/nasty_string.h
index ea9d83ccf282aa..e526ccd3365b6f 100644
--- a/libcxx/test/support/nasty_string.h
+++ b/libcxx/test/support/nasty_string.h
@@ -40,8 +40,15 @@
 #ifndef TEST_HAS_NO_NASTY_STRING
 // Make sure the char-like operations in strings do not depend on the char-like type.
 struct nasty_char {
+#  if defined(_LIBCPP_VERSION) && !defined(_LIBCPP_ABI_WRAP_ITER_ADL_PROOF)
+  // T, T would win overload resolution when comparing two wrap_iters, so
+  // make sure this is less specific
+  template <typename T, typename U>
+  friend auto operator<=>(T, U) = delete;
+#  else
   template <typename T>
   friend auto operator<=>(T, T) = delete;
+#  endif
 
   template <typename T>
   friend void operator+(T&&) = delete;



More information about the libcxx-commits mailing list