[libcxx-commits] [libcxx] d41c6d5 - [libc++] Rationalize our treatment of contiguous iterators and __unwrap_iter().

Arthur O'Dwyer via libcxx-commits libcxx-commits at lists.llvm.org
Wed Feb 3 13:29:22 PST 2021


Author: Arthur O'Dwyer
Date: 2021-02-03T16:28:38-05:00
New Revision: d41c6d51cbadbbd0f81c6ac0d6628d01b881e2a5

URL: https://github.com/llvm/llvm-project/commit/d41c6d51cbadbbd0f81c6ac0d6628d01b881e2a5
DIFF: https://github.com/llvm/llvm-project/commit/d41c6d51cbadbbd0f81c6ac0d6628d01b881e2a5.diff

LOG: [libc++] Rationalize our treatment of contiguous iterators and __unwrap_iter().

- Implement C++20's changes to `reverse_iterator`, so that it won't be
    accidentally counted as a contiguous iterator in C++20 mode.
- Implement C++20's changes to `move_iterator` as well.
- `move_iterator` should not be contiguous. This fixes a bug where
    we optimized `std::copy`-of-move-iterators in an observable way.
    Add a regression test for that bugfix.
- Add libcxx tests for `__is_cpp17_contiguous_iterator` of all relevant
    standard iterator types. Particularly check that vector::iterator
    is still considered contiguous in all C++ modes, even C++03.

After this patch, there continues to be no supported way to write your
own iterator type in C++17-and-earlier such that libc++ will consider it
"contiguous"; however, we now fully support the C++20 approach (in C++20
mode only). If you want user-defined contiguous iterators in C++17-and-earlier,
libc++'s position is "please upgrade to C++20."

Differential Revision: https://reviews.llvm.org/D94807

Added: 
    libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_trivial_optimization.pass.cpp

Modified: 
    libcxx/include/__memory/pointer_traits.h
    libcxx/include/algorithm
    libcxx/include/filesystem
    libcxx/include/iterator
    libcxx/include/memory
    libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp
    libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp
    libcxx/test/support/test_iterators.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index b2c5d34cb082..c26a7824c6dd 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -162,6 +162,70 @@ struct __rebind_pointer {
 #endif
 };
 
+// to_address
+
+template <bool _UsePointerTraits> struct __to_address_helper;
+
+template <> struct __to_address_helper<true> {
+    template <class _Pointer>
+    using __return_type = decltype(pointer_traits<_Pointer>::to_address(_VSTD::declval<const _Pointer&>()));
+
+    template <class _Pointer>
+    _LIBCPP_CONSTEXPR
+    static __return_type<_Pointer>
+    __do_it(const _Pointer &__p) _NOEXCEPT { return pointer_traits<_Pointer>::to_address(__p); }
+};
+
+template <class _Pointer, bool _Dummy = true>
+using __choose_to_address = __to_address_helper<_IsValidExpansion<__to_address_helper<_Dummy>::template __return_type, _Pointer>::value>;
+
+template <class _Tp>
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+_Tp*
+__to_address(_Tp* __p) _NOEXCEPT
+{
+    static_assert(!is_function<_Tp>::value, "_Tp is a function type");
+    return __p;
+}
+
+template <class _Pointer>
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
+typename __choose_to_address<_Pointer>::template __return_type<_Pointer>
+__to_address(const _Pointer& __p) _NOEXCEPT
+{
+    return __choose_to_address<_Pointer>::__do_it(__p);
+}
+
+template <> struct __to_address_helper<false> {
+    template <class _Pointer>
+    using __return_type = typename pointer_traits<_Pointer>::element_type*;
+
+    template <class _Pointer>
+    _LIBCPP_CONSTEXPR
+    static __return_type<_Pointer>
+    __do_it(const _Pointer &__p) _NOEXCEPT { return _VSTD::__to_address(__p.operator->()); }
+};
+
+
+#if _LIBCPP_STD_VER > 17
+template <class _Tp>
+inline _LIBCPP_INLINE_VISIBILITY constexpr
+_Tp*
+to_address(_Tp* __p) _NOEXCEPT
+{
+    static_assert(!is_function_v<_Tp>, "_Tp is a function type");
+    return __p;
+}
+
+template <class _Pointer>
+inline _LIBCPP_INLINE_VISIBILITY constexpr
+auto
+to_address(const _Pointer& __p) _NOEXCEPT
+{
+    return _VSTD::__to_address(__p);
+}
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 9bc31f852e2f..d8fbac731b19 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -1639,68 +1639,42 @@ search_n(_ForwardIterator __first, _ForwardIterator __last, _Size __count, const
                            __value_, __equal_to<__v, _Tp>());
 }
 
-// copy
-template <class _Iter>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-_Iter
-__unwrap_iter(_Iter __i)
-{
-    return __i;
-}
-
-template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-typename enable_if
-<
-    is_trivially_copy_assignable<_Tp>::value,
-    _Tp*
->::type
-__unwrap_iter(move_iterator<_Tp*> __i)
-{
-    return __i.base();
-}
+// __unwrap_iter
+
+// The job of __unwrap_iter is to lower iterators-that-are-tantamount-to-pointers
+// (such as vector<T>::iterator) into pointers, to reduce the number of template
+// instantiations and to enable pointer-based optimizations e.g. in std::copy.
+// In debug mode, we don't do this.
+
+template <class _Iter, bool = __is_cpp17_contiguous_iterator<_Iter>::value>
+struct __unwrap_iter_impl {
+    static _LIBCPP_CONSTEXPR _Iter
+    __apply(_Iter __i) _NOEXCEPT {
+        return __i;
+    }
+};
 
 #if _LIBCPP_DEBUG_LEVEL < 2
 
-template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-typename enable_if
-<
-    is_trivially_copy_assignable<_Tp>::value,
-    _Tp*
->::type
-__unwrap_iter(__wrap_iter<_Tp*> __i)
-{
-    return __i.base();
-}
-
-template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-typename enable_if
-<
-    is_trivially_copy_assignable<_Tp>::value,
-    const _Tp*
->::type
-__unwrap_iter(__wrap_iter<const _Tp*> __i)
-{
-    return __i.base();
-}
+template <class _Iter>
+struct __unwrap_iter_impl<_Iter, true> {
+    static _LIBCPP_CONSTEXPR decltype(_VSTD::__to_address(declval<_Iter>()))
+    __apply(_Iter __i) _NOEXCEPT {
+        return _VSTD::__to_address(__i);
+    }
+};
 
-#else
+#endif  // _LIBCPP_DEBUG_LEVEL < 2
 
-template <class _Tp>
+template<class _Iter, class _Impl = __unwrap_iter_impl<_Iter> >
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-typename enable_if
-<
-    is_trivially_copy_assignable<_Tp>::value,
-    __wrap_iter<_Tp*>
->::type
-__unwrap_iter(__wrap_iter<_Tp*> __i)
+decltype(_Impl::__apply(_VSTD::declval<_Iter>()))
+__unwrap_iter(_Iter __i) _NOEXCEPT
 {
-    return __i;
+    return _Impl::__apply(__i);
 }
 
-#endif  // _LIBCPP_DEBUG_LEVEL < 2
+// copy
 
 template <class _InputIterator, class _OutputIterator>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
@@ -1894,7 +1868,7 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 typename enable_if
 <
     is_same<typename remove_const<_Tp>::type, _Up>::value &&
-    is_trivially_copy_assignable<_Up>::value,
+    is_trivially_move_assignable<_Up>::value,
     _Up*
 >::type
 __move(_Tp* __first, _Tp* __last, _Up* __result)
@@ -1942,7 +1916,7 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 typename enable_if
 <
     is_same<typename remove_const<_Tp>::type, _Up>::value &&
-    is_trivially_copy_assignable<_Up>::value,
+    is_trivially_move_assignable<_Up>::value,
     _Up*
 >::type
 __move_backward(_Tp* __first, _Tp* __last, _Up* __result)

diff  --git a/libcxx/include/filesystem b/libcxx/include/filesystem
index 92e37e183def..b11de541b0fe 100644
--- a/libcxx/include/filesystem
+++ b/libcxx/include/filesystem
@@ -1495,7 +1495,7 @@ _LIBCPP_INLINE_VISIBILITY _LIBCPP_DEPRECATED_WITH_CHAR8_T
       "'char' or 'char8_t'");
 #if defined(_LIBCPP_WIN32API)
   using _Traits = __is_pathable<_Source>;
-  return u8path(__unwrap_iter(_Traits::__range_begin(__s)), __unwrap_iter(_Traits::__range_end(__s)));
+  return u8path(_VSTD::__unwrap_iter(_Traits::__range_begin(__s)), _VSTD::__unwrap_iter(_Traits::__range_end(__s)));
 #else
   return path(__s);
 #endif

diff  --git a/libcxx/include/iterator b/libcxx/include/iterator
index 00a3451e7a21..d2db7de0cabe 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -421,6 +421,7 @@ template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
 #include <cstddef>
 #include <initializer_list>
 #include <__memory/base.h>
+#include <__memory/pointer_traits.h>
 #include <version>
 
 #include <__debug>
@@ -439,9 +440,7 @@ struct _LIBCPP_TEMPLATE_VIS forward_iterator_tag       : public input_iterator_t
 struct _LIBCPP_TEMPLATE_VIS bidirectional_iterator_tag : public forward_iterator_tag {};
 struct _LIBCPP_TEMPLATE_VIS random_access_iterator_tag : public bidirectional_iterator_tag {};
 #if _LIBCPP_STD_VER > 17
-// TODO(EricWF)  contiguous_iterator_tag is provided as an extension prior to
-//  C++20 to allow optimizations for users providing wrapped iterator types.
-struct _LIBCPP_TEMPLATE_VIS contiguous_iterator_tag: public random_access_iterator_tag { };
+struct _LIBCPP_TEMPLATE_VIS contiguous_iterator_tag    : public random_access_iterator_tag {};
 #endif
 
 template <class _Iter>
@@ -517,6 +516,17 @@ public:
     static const bool value = sizeof(__test<_Tp>(nullptr)) == 1;
 };
 
+template <class _Tp>
+struct __has_iterator_concept
+{
+private:
+    struct __two {char __lx; char __lxx;};
+    template <class _Up> static __two __test(...);
+    template <class _Up> static char __test(typename _Up::iterator_concept* = nullptr);
+public:
+    static const bool value = sizeof(__test<_Tp>(nullptr)) == 1;
+};
+
 template <class _Iter, bool> struct __iterator_traits_impl {};
 
 template <class _Iter>
@@ -568,11 +578,19 @@ struct _LIBCPP_TEMPLATE_VIS iterator_traits<_Tp*>
 
 template <class _Tp, class _Up, bool = __has_iterator_category<iterator_traits<_Tp> >::value>
 struct __has_iterator_category_convertible_to
-    : public integral_constant<bool, is_convertible<typename iterator_traits<_Tp>::iterator_category, _Up>::value>
+    : _BoolConstant<is_convertible<typename iterator_traits<_Tp>::iterator_category, _Up>::value>
+{};
+
+template <class _Tp, class _Up>
+struct __has_iterator_category_convertible_to<_Tp, _Up, false> : false_type {};
+
+template <class _Tp, class _Up, bool = __has_iterator_concept<_Tp>::value>
+struct __has_iterator_concept_convertible_to
+    : _BoolConstant<is_convertible<typename _Tp::iterator_concept, _Up>::value>
 {};
 
 template <class _Tp, class _Up>
-struct __has_iterator_category_convertible_to<_Tp, _Up, false> : public false_type {};
+struct __has_iterator_concept_convertible_to<_Tp, _Up, false> : false_type {};
 
 template <class _Tp>
 struct __is_cpp17_input_iterator : public __has_iterator_category_convertible_to<_Tp, input_iterator_tag> {};
@@ -586,14 +604,26 @@ struct __is_cpp17_bidirectional_iterator : public __has_iterator_category_conver
 template <class _Tp>
 struct __is_cpp17_random_access_iterator : public __has_iterator_category_convertible_to<_Tp, random_access_iterator_tag> {};
 
+// __is_cpp17_contiguous_iterator determines if an iterator is contiguous,
+// either because it advertises itself as such (in C++20) or because it
+// is a pointer type or a known trivial wrapper around a pointer type,
+// such as __wrap_iter<T*>.
+//
 #if _LIBCPP_STD_VER > 17
 template <class _Tp>
-struct __is_cpp17_contiguous_iterator : public __has_iterator_category_convertible_to<_Tp, contiguous_iterator_tag> {};
+struct __is_cpp17_contiguous_iterator : _Or<
+    __has_iterator_category_convertible_to<_Tp, contiguous_iterator_tag>,
+    __has_iterator_concept_convertible_to<_Tp, contiguous_iterator_tag>
+> {};
 #else
 template <class _Tp>
-struct __is_cpp17_contiguous_iterator : public false_type {};
+struct __is_cpp17_contiguous_iterator : false_type {};
 #endif
 
+// Any native pointer which is an iterator is also a contiguous iterator.
+template <class _Up>
+struct __is_cpp17_contiguous_iterator<_Up*> : true_type {};
+
 
 template <class _Tp>
 struct __is_exactly_cpp17_input_iterator
@@ -759,6 +789,14 @@ public:
     typedef typename iterator_traits<_Iter>::
diff erence_type 
diff erence_type;
     typedef typename iterator_traits<_Iter>::reference       reference;
     typedef typename iterator_traits<_Iter>::pointer         pointer;
+    typedef _If<__is_cpp17_random_access_iterator<_Iter>::value,
+        random_access_iterator_tag,
+        typename iterator_traits<_Iter>::iterator_category>  iterator_category;
+#if _LIBCPP_STD_VER > 17
+    typedef _If<__is_cpp17_random_access_iterator<_Iter>::value,
+        random_access_iterator_tag,
+        bidirectional_iterator_tag>                          iterator_concept;
+#endif
 
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
     reverse_iterator() : __t(), current() {}
@@ -1206,10 +1244,16 @@ private:
     _Iter __i;
 public:
     typedef _Iter                                            iterator_type;
-    typedef typename iterator_traits<iterator_type>::iterator_category iterator_category;
     typedef typename iterator_traits<iterator_type>::value_type value_type;
     typedef typename iterator_traits<iterator_type>::
diff erence_type 
diff erence_type;
     typedef iterator_type pointer;
+    typedef _If<__is_cpp17_random_access_iterator<_Iter>::value,
+        random_access_iterator_tag,
+        typename iterator_traits<_Iter>::iterator_category>  iterator_category;
+#if _LIBCPP_STD_VER > 17
+    typedef input_iterator_tag                               iterator_concept;
+#endif
+
 #ifndef _LIBCPP_CXX03_LANG
     typedef typename iterator_traits<iterator_type>::reference __reference;
     typedef typename conditional<
@@ -1393,40 +1437,21 @@ template <class _B1, class _B2> _B2 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_
 template <class _Ip, class _Op> _Op _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 move(_Ip, _Ip, _Op);
 template <class _B1, class _B2> _B2 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 move_backward(_B1, _B1, _B2);
 
-#if _LIBCPP_DEBUG_LEVEL < 2
-
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-typename enable_if
-<
-    is_trivially_copy_assignable<_Tp>::value,
-    _Tp*
->::type
-__unwrap_iter(__wrap_iter<_Tp*>);
-
-#else
-
-template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-typename enable_if
-<
-    is_trivially_copy_assignable<_Tp>::value,
-    __wrap_iter<_Tp*>
->::type
-__unwrap_iter(__wrap_iter<_Tp*> __i);
-
-#endif
-
 template <class _Iter>
 class __wrap_iter
 {
 public:
     typedef _Iter                                                      iterator_type;
-    typedef typename iterator_traits<iterator_type>::iterator_category iterator_category;
     typedef typename iterator_traits<iterator_type>::value_type        value_type;
     typedef typename iterator_traits<iterator_type>::
diff erence_type   
diff erence_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 > 17
+    typedef _If<__is_cpp17_contiguous_iterator<_Iter>::value,
+                contiguous_iterator_tag, iterator_category>            iterator_concept;
+#endif
+
 private:
     iterator_type __i;
 public:
@@ -1603,27 +1628,19 @@ private:
     template <class _B1, class _B2> friend _LIBCPP_CONSTEXPR_AFTER_CXX17 _B2 copy_backward(_B1, _B1, _B2);
     template <class _Ip, class _Op> friend _LIBCPP_CONSTEXPR_AFTER_CXX17 _Op move(_Ip, _Ip, _Op);
     template <class _B1, class _B2> friend _LIBCPP_CONSTEXPR_AFTER_CXX17 _B2 move_backward(_B1, _B1, _B2);
+};
 
-#if _LIBCPP_DEBUG_LEVEL < 2
-    template <class _Tp>
-    _LIBCPP_CONSTEXPR friend
-    typename enable_if
-    <
-        is_trivially_copy_assignable<_Tp>::value,
-        _Tp*
-    >::type
-    __unwrap_iter(__wrap_iter<_Tp*>);
-#else
-  template <class _Tp>
-  inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR friend
-  typename enable_if
-  <
-      is_trivially_copy_assignable<_Tp>::value,
-      __wrap_iter<_Tp*>
-  >::type
-  __unwrap_iter(__wrap_iter<_Tp*> __i);
+#if _LIBCPP_STD_VER <= 17
+template <class _It>
+struct __is_cpp17_contiguous_iterator<__wrap_iter<_It> > : __is_cpp17_contiguous_iterator<_It> {};
 #endif
-};
+
+template <class _Iter>
+_LIBCPP_CONSTEXPR
+_EnableIf<__is_cpp17_contiguous_iterator<_Iter>::value, decltype(_VSTD::__to_address(declval<_Iter>()))>
+__to_address(__wrap_iter<_Iter> __w) _NOEXCEPT {
+    return _VSTD::__to_address(__w.base());
+}
 
 template <class _Iter1, class _Iter2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG

diff  --git a/libcxx/include/memory b/libcxx/include/memory
index 39d0f5bee6a5..ade8349dd3f2 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -722,68 +722,6 @@ _ValueType __libcpp_acquire_load(_ValueType const* __value) {
 #endif
 }
 
-template <bool _UsePointerTraits> struct __to_address_helper;
-
-template <> struct __to_address_helper<true> {
-    template <class _Pointer>
-    using __return_type = decltype(pointer_traits<_Pointer>::to_address(_VSTD::declval<const _Pointer&>()));
-
-    template <class _Pointer>
-    _LIBCPP_CONSTEXPR
-    static __return_type<_Pointer>
-    __do_it(const _Pointer &__p) _NOEXCEPT { return pointer_traits<_Pointer>::to_address(__p); }
-};
-
-template <class _Pointer, bool _Dummy = true>
-using __choose_to_address = __to_address_helper<_IsValidExpansion<__to_address_helper<_Dummy>::template __return_type, _Pointer>::value>;
-
-
-template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-_Tp*
-__to_address(_Tp* __p) _NOEXCEPT
-{
-    static_assert(!is_function<_Tp>::value, "_Tp is a function type");
-    return __p;
-}
-
-template <class _Pointer>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
-typename __choose_to_address<_Pointer>::template __return_type<_Pointer>
-__to_address(const _Pointer& __p) _NOEXCEPT {
-  return __choose_to_address<_Pointer>::__do_it(__p);
-}
-
-template <> struct __to_address_helper<false> {
-    template <class _Pointer>
-    using __return_type = typename pointer_traits<_Pointer>::element_type*;
-
-    template <class _Pointer>
-    _LIBCPP_CONSTEXPR
-    static __return_type<_Pointer>
-    __do_it(const _Pointer &__p) _NOEXCEPT { return _VSTD::__to_address(__p.operator->()); }
-};
-
-
-#if _LIBCPP_STD_VER > 17
-template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY constexpr
-_Tp*
-to_address(_Tp* __p) _NOEXCEPT
-{
-    static_assert(!is_function_v<_Tp>, "_Tp is a function type");
-    return __p;
-}
-
-template <class _Pointer>
-inline _LIBCPP_INLINE_VISIBILITY constexpr
-auto
-to_address(const _Pointer& __p) _NOEXCEPT
-{
-    return _VSTD::__to_address(__p);
-}
-#endif
-
 template <class _Tp> class allocator;
 
 #if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_ALLOCATOR_MEMBERS)

diff  --git a/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp b/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp
new file mode 100644
index 000000000000..2ea592338a0b
--- /dev/null
+++ b/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp
@@ -0,0 +1,258 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+
+// <iterator>
+
+// __is_cpp17_contiguous_iterator<_Tp>
+
+// __is_cpp17_contiguous_iterator determines if an iterator is contiguous,
+// either because it advertises itself as such (in C++20) or because it
+// is a pointer type or a known trivial wrapper around a pointer type,
+// such as __wrap_iter<T*>.
+//
+
+#include <cassert>
+#include <deque>
+#include <initializer_list>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+#if TEST_STD_VER >= 17
+#include <string_view>
+#endif
+
+#if TEST_STD_VER >= 20
+#include <span>
+#endif
+
+#if TEST_STD_VER >= 11
+#define DELETE_FUNCTION = delete
+#else
+#define DELETE_FUNCTION
+#endif
+
+class T;  // incomplete
+
+class my_input_iterator
+{
+    struct tag : std::input_iterator_tag {};
+    typedef my_input_iterator Self;
+    int *state_;
+public:
+    typedef tag iterator_category;
+    typedef int value_type;
+    typedef int 
diff erence_type;
+    typedef int* pointer;
+    typedef int& reference;
+
+    my_input_iterator();
+    reference operator*() const;
+    pointer operator->() const;
+
+    Self& operator++();
+    Self operator++(int);
+    friend bool operator==(const Self&, const Self&);
+    friend bool operator!=(const Self&, const Self&);
+};
+
+class my_random_access_iterator
+{
+    struct tag : std::random_access_iterator_tag {};
+    typedef my_random_access_iterator Self;
+    int *state_;
+public:
+    typedef tag iterator_category;
+    typedef int value_type;
+    typedef int 
diff erence_type;
+    typedef int* pointer;
+    typedef int& reference;
+
+    my_random_access_iterator();
+    reference operator*() const;
+    pointer operator->() const;
+    reference operator[](
diff erence_type) const;
+
+    Self& operator++();
+    Self operator++(int);
+    Self& operator--();
+    Self operator--(int);
+    friend Self& operator+=(Self&, 
diff erence_type);
+    friend Self& operator-=(Self&, 
diff erence_type);
+    friend Self operator+(Self, 
diff erence_type);
+    friend Self operator+(
diff erence_type, Self);
+    friend Self operator-(Self, 
diff erence_type);
+    friend 
diff erence_type operator-(Self, Self);
+    friend bool operator==(const Self&, const Self&);
+    friend bool operator!=(const Self&, const Self&);
+    friend bool operator<(const Self&, const Self&);
+    friend bool operator>(const Self&, const Self&);
+    friend bool operator<=(const Self&, const Self&);
+    friend bool operator>=(const Self&, const Self&);
+};
+
+#if TEST_STD_VER >= 20
+class my_contiguous_iterator
+{
+    struct tag : std::contiguous_iterator_tag {};
+    typedef my_contiguous_iterator Self;
+    int *state_;
+public:
+    typedef tag iterator_category;
+    typedef int value_type;
+    typedef int 
diff erence_type;
+    typedef int* pointer;
+    typedef int& reference;
+    typedef int element_type;  // enable to_address via pointer_traits
+
+    my_contiguous_iterator();
+    reference operator*() const;
+    pointer operator->() const;
+    reference operator[](
diff erence_type) const;
+
+    Self& operator++();
+    Self operator++(int);
+    Self& operator--();
+    Self operator--(int);
+    friend Self& operator+=(Self&, 
diff erence_type);
+    friend Self& operator-=(Self&, 
diff erence_type);
+    friend Self operator+(Self, 
diff erence_type);
+    friend Self operator+(
diff erence_type, Self);
+    friend Self operator-(Self, 
diff erence_type);
+    friend 
diff erence_type operator-(Self, Self);
+    friend bool operator==(const Self&, const Self&);
+    friend bool operator!=(const Self&, const Self&);
+    friend bool operator<(const Self&, const Self&);
+    friend bool operator>(const Self&, const Self&);
+    friend bool operator<=(const Self&, const Self&);
+    friend bool operator>=(const Self&, const Self&);
+};
+#endif
+
+int main(int, char**)
+{
+//  basic tests
+    static_assert(( std::__is_cpp17_contiguous_iterator<char *>::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<const char *>::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<int *>::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<int **>::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<T *>::value), "");
+
+    static_assert((!std::__is_cpp17_contiguous_iterator<my_input_iterator>::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<my_random_access_iterator>::value), "");
+#if TEST_STD_VER >= 20
+    static_assert(( std::__is_cpp17_contiguous_iterator<my_contiguous_iterator>::value), "");
+#endif
+
+    // move_iterator changes value category, which makes it pretty sketchy to use in optimized codepaths
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::move_iterator<char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::move_iterator<const char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::move_iterator<int *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::move_iterator<T *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::move_iterator<my_random_access_iterator> >::value), "");
+#if TEST_STD_VER >= 20
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::move_iterator<my_contiguous_iterator> >::value), "");
+#endif
+
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::reverse_iterator<char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::reverse_iterator<const char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::reverse_iterator<int *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::reverse_iterator<T *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::reverse_iterator<my_random_access_iterator> >::value), "");
+#if TEST_STD_VER >= 20
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::reverse_iterator<my_contiguous_iterator> >::value), "");
+#endif
+
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::__wrap_iter<char *> >::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::__wrap_iter<const char *> >::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::__wrap_iter<int *> >::value), "");
+
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::__wrap_iter<T *> >::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::__wrap_iter<T *> > >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::reverse_iterator<T *> > >::value), "");
+
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::__wrap_iter<my_random_access_iterator> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::__wrap_iter<my_random_access_iterator> > >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::reverse_iterator<my_random_access_iterator> > >::value), "");
+
+#if TEST_STD_VER >= 20
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::__wrap_iter<my_contiguous_iterator> >::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::__wrap_iter<my_contiguous_iterator> > >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::reverse_iterator<my_contiguous_iterator> > >::value), "");
+#endif
+
+//  iterators in the libc++ test suite
+    static_assert((!std::__is_cpp17_contiguous_iterator<output_iterator       <char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<input_iterator        <char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<forward_iterator      <char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<bidirectional_iterator<char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<random_access_iterator<char *> >::value), "");
+#if TEST_STD_VER >= 20
+    static_assert(( std::__is_cpp17_contiguous_iterator<contiguous_iterator   <char *> >::value), "");
+#endif
+    static_assert((!std::__is_cpp17_contiguous_iterator<ThrowingIterator      <char *> >::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<NonThrowingIterator   <char *> >::value), "");
+
+//
+//  iterators from libc++'s containers
+//
+
+//  vector
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::vector<int>::iterator>                   ::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::vector<int>::const_iterator>             ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::vector<int>::reverse_iterator>           ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::vector<int>::const_reverse_iterator>     ::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::vector<int>::iterator> >::value), "");
+
+//  string
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::string::iterator>              ::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::string::const_iterator>        ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::string::reverse_iterator>      ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::string::const_reverse_iterator>::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::wstring::iterator>              ::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::wstring::const_iterator>        ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::wstring::reverse_iterator>      ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::wstring::const_reverse_iterator>::value), "");
+
+//  deque is random-access but not contiguous
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::deque<int>::iterator>                   ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::deque<int>::const_iterator>             ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::deque<int>::reverse_iterator>           ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::deque<int>::const_reverse_iterator>     ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::deque<int>::iterator> >::value), "");
+
+//  vector<bool> is random-access but not contiguous
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::vector<bool>::iterator>                   ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::vector<bool>::const_iterator>             ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::vector<bool>::reverse_iterator>           ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::vector<bool>::const_reverse_iterator>     ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::__wrap_iter<std::vector<bool>::iterator> >::value), "");
+
+#if TEST_STD_VER >= 11
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::initializer_list<int>::iterator>      ::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::initializer_list<int>::const_iterator>::value), "");
+#endif
+
+#if TEST_STD_VER >= 17
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::string_view::iterator>      ::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::string_view::const_iterator>::value), "");
+#endif
+
+#if TEST_STD_VER >= 20
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::span<      int>::iterator>        ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::span<      int>::reverse_iterator>::value), "");
+    static_assert(( std::__is_cpp17_contiguous_iterator<std::span<const int>::iterator>        ::value), "");
+    static_assert((!std::__is_cpp17_contiguous_iterator<std::span<const int>::reverse_iterator>::value), "");
+#endif
+
+    return 0;
+}

diff  --git a/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp b/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp
index f6164db9d984..f4cd35911643 100644
--- a/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp
+++ b/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp
@@ -130,36 +130,42 @@ int main(int, char**)
     static_assert(( std::__is_cpp17_forward_iterator      <char *>::value), "" );
     static_assert(( std::__is_cpp17_bidirectional_iterator<char *>::value), "" );
     static_assert(( std::__is_cpp17_random_access_iterator<char *>::value), "" );
+    static_assert(( std::__is_cpp17_contiguous_iterator   <char *>::value), "" );
     static_assert((!std::__is_exactly_cpp17_input_iterator<char *>::value), "" );
 
     static_assert(( std::__is_cpp17_input_iterator        <input_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_forward_iterator      <input_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_bidirectional_iterator<input_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_random_access_iterator<input_iterator<char *> >::value), "" );
+    static_assert((!std::__is_cpp17_contiguous_iterator   <input_iterator<char *> >::value), "" );
     static_assert(( std::__is_exactly_cpp17_input_iterator<input_iterator<char *> >::value), "" );
 
     static_assert(( std::__is_cpp17_input_iterator        <forward_iterator<char *> >::value), "" );
     static_assert(( std::__is_cpp17_forward_iterator      <forward_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_bidirectional_iterator<forward_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_random_access_iterator<forward_iterator<char *> >::value), "" );
+    static_assert((!std::__is_cpp17_contiguous_iterator   <forward_iterator<char *> >::value), "" );
     static_assert((!std::__is_exactly_cpp17_input_iterator<forward_iterator<char *> >::value), "" );
 
     static_assert(( std::__is_cpp17_input_iterator        <bidirectional_iterator<char *> >::value), "" );
     static_assert(( std::__is_cpp17_forward_iterator      <bidirectional_iterator<char *> >::value), "" );
     static_assert(( std::__is_cpp17_bidirectional_iterator<bidirectional_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_random_access_iterator<bidirectional_iterator<char *> >::value), "" );
+    static_assert((!std::__is_cpp17_contiguous_iterator   <bidirectional_iterator<char *> >::value), "" );
     static_assert((!std::__is_exactly_cpp17_input_iterator<bidirectional_iterator<char *> >::value), "" );
 
     static_assert(( std::__is_cpp17_input_iterator        <random_access_iterator<char *> >::value), "" );
     static_assert(( std::__is_cpp17_forward_iterator      <random_access_iterator<char *> >::value), "" );
     static_assert(( std::__is_cpp17_bidirectional_iterator<random_access_iterator<char *> >::value), "" );
     static_assert(( std::__is_cpp17_random_access_iterator<random_access_iterator<char *> >::value), "" );
+    static_assert((!std::__is_cpp17_contiguous_iterator   <random_access_iterator<char *> >::value), "" );
     static_assert((!std::__is_exactly_cpp17_input_iterator<random_access_iterator<char *> >::value), "" );
 
     static_assert(( std::__is_cpp17_input_iterator        <my_input_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_forward_iterator      <my_input_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_bidirectional_iterator<my_input_iterator<char *> >::value), "" );
     static_assert((!std::__is_cpp17_random_access_iterator<my_input_iterator<char *> >::value), "" );
+    static_assert((!std::__is_cpp17_contiguous_iterator   <my_input_iterator<char *> >::value), "" );
     static_assert(( std::__is_exactly_cpp17_input_iterator<my_input_iterator<char *> >::value), "" );
 
 //

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_trivial_optimization.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_trivial_optimization.pass.cpp
new file mode 100644
index 000000000000..9b65c4a220d9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_trivial_optimization.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Older compilers don't support std::is_constant_evaluated
+// UNSUPPORTED: clang-4, clang-5, clang-6, clang-7, clang-8
+// UNSUPPORTED: apple-clang-9, apple-clang-10
+// UNSUPPORTED: c++03
+
+// <algorithm>
+
+// We optimize std::copy(_backward) and std::move(_backward) into memmove
+// when the iterator is trivial and contiguous and the type in question
+// is also trivially (copyable, movable). This test verifies that the
+// optimization never eliminates an actually non-trivial copy or move.
+
+#include <algorithm>
+#include <iterator>
+#include <cassert>
+
+#include "test_macros.h"
+#include "test_iterators.h"
+
+struct TMBNTC {
+    int *p;
+    constexpr TMBNTC(int& copies) : p(&copies) {}
+    constexpr TMBNTC(const TMBNTC&) = default;
+    TEST_CONSTEXPR_CXX14 TMBNTC& operator=(TMBNTC&&) = default;
+    TEST_CONSTEXPR_CXX14 TMBNTC& operator=(const TMBNTC&) { ++*p; return *this; }
+};
+
+TEST_CONSTEXPR_CXX20 bool
+test_trivial_moveassign_but_no_trivial_copyassign()
+{
+    int copies = 0;
+    TMBNTC ia[] = { copies, copies, copies, copies };
+    TMBNTC ib[] = { copies, copies, copies, copies };
+    std::copy(ia, ia+4, ib);
+    assert(copies == 4);
+    copies = 0;
+    std::copy_backward(ia, ia+4, ib+4);
+    assert(copies == 4);
+
+    copies = 0;
+    std::copy(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib);
+    assert(copies == 0);
+    std::copy_backward(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib+4);
+    assert(copies == 0);
+
+    std::move(ia, ia+4, ib);
+    assert(copies == 0);
+    std::move_backward(ia, ia+4, ib+4);
+    assert(copies == 0);
+
+    return true;
+}
+
+struct TCBNTM {
+    int *p;
+    constexpr TCBNTM(int& moves) : p(&moves) {}
+    constexpr TCBNTM(const TCBNTM&) = default;
+    TEST_CONSTEXPR_CXX14 TCBNTM& operator=(TCBNTM&&) { ++*p; return *this; }
+    TEST_CONSTEXPR_CXX14 TCBNTM& operator=(const TCBNTM&) = default;
+};
+
+TEST_CONSTEXPR_CXX20 bool
+test_trivial_copyassign_but_no_trivial_moveassign()
+{
+    int moves = 0;
+    TCBNTM ia[] = { moves, moves, moves, moves };
+    TCBNTM ib[] = { moves, moves, moves, moves };
+    std::move(ia, ia+4, ib);
+    assert(moves == 4);
+    moves = 0;
+    std::move_backward(ia, ia+4, ib+4);
+    assert(moves == 4);
+
+    moves = 0;
+    std::copy(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib);
+    assert(moves == 4);
+    moves = 0;
+    std::copy_backward(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib+4);
+    assert(moves == 4);
+
+    moves = 0;
+    std::copy(ia, ia+4, ib);
+    assert(moves == 0);
+    std::copy_backward(ia, ia+4, ib+4);
+    assert(moves == 0);
+
+    return true;
+}
+
+int main(int, char**)
+{
+    test_trivial_moveassign_but_no_trivial_copyassign();
+    test_trivial_copyassign_but_no_trivial_moveassign();
+
+#if TEST_STD_VER > 17
+    static_assert(test_trivial_moveassign_but_no_trivial_copyassign());
+    static_assert(test_trivial_copyassign_but_no_trivial_moveassign());
+#endif
+
+    return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp
index 25dc47e7e7b6..9515e1fbd910 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp
@@ -54,7 +54,15 @@ test()
 #else
     static_assert((std::is_same<typename R::reference, typename T::reference>::value), "");
 #endif
+#if TEST_STD_VER > 17
+    if constexpr (std::is_same_v<typename T::iterator_category, std::contiguous_iterator_tag>) {
+        static_assert((std::is_same<typename R::iterator_category, std::random_access_iterator_tag>::value), "");
+    } else {
+        static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
+    }
+#else
     static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
+#endif
 }
 
 int main(int, char**)
@@ -94,5 +102,14 @@ int main(int, char**)
     }
 #endif
 
+#if TEST_STD_VER > 17
+    test<contiguous_iterator<char*>>();
+    static_assert(std::is_same_v<typename std::move_iterator<forward_iterator<char*>>::iterator_concept, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename std::move_iterator<bidirectional_iterator<char*>>::iterator_concept, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename std::move_iterator<random_access_iterator<char*>>::iterator_concept, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename std::move_iterator<contiguous_iterator<char*>>::iterator_concept, std::input_iterator_tag>);
+    static_assert(std::is_same_v<typename std::move_iterator<char*>::iterator_concept, std::input_iterator_tag>);
+#endif
+
   return 0;
 }

diff  --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp
index 14f08a8b371b..0406bc23efc9 100644
--- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp
@@ -49,7 +49,15 @@ test()
     static_assert((std::is_same<typename R::
diff erence_type, typename T::
diff erence_type>::value), "");
     static_assert((std::is_same<typename R::reference, typename T::reference>::value), "");
     static_assert((std::is_same<typename R::pointer, typename std::iterator_traits<It>::pointer>::value), "");
+#if TEST_STD_VER > 17
+    if constexpr (std::is_same_v<typename T::iterator_category, std::contiguous_iterator_tag>) {
+        static_assert((std::is_same<typename R::iterator_category, std::random_access_iterator_tag>::value), "");
+    } else {
+        static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
+    }
+#else
     static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
+#endif
 }
 
 int main(int, char**)
@@ -58,5 +66,13 @@ int main(int, char**)
     test<random_access_iterator<char*> >();
     test<char*>();
 
+#if TEST_STD_VER > 17
+    test<contiguous_iterator<char*>>();
+    static_assert(std::is_same_v<typename std::reverse_iterator<bidirectional_iterator<char*>>::iterator_concept, std::bidirectional_iterator_tag>);
+    static_assert(std::is_same_v<typename std::reverse_iterator<random_access_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<typename std::reverse_iterator<contiguous_iterator<char*>>::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::is_same_v<typename std::reverse_iterator<char*>::iterator_concept, std::random_access_iterator_tag>);
+#endif
+
     return 0;
 }

diff  --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index bc07e20fbafe..71ef2d58628b 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -311,6 +311,85 @@ operator-(const random_access_iterator<T>& x, const random_access_iterator<U>& y
     return x.base() - y.base();
 }
 
+#if TEST_STD_VER >= 20
+template <class It>
+class contiguous_iterator
+{
+    It it_;
+
+    template <class U> friend class contiguous_iterator;
+public:
+    typedef          std::contiguous_iterator_tag              iterator_category;
+    typedef typename std::iterator_traits<It>::value_type      value_type;
+    typedef typename std::iterator_traits<It>::
diff erence_type 
diff erence_type;
+    typedef It                                                 pointer;
+    typedef typename std::iterator_traits<It>::reference       reference;
+    typedef typename std::iterator_traits<It>::value_type      element_type;
+
+    TEST_CONSTEXPR_CXX14 It base() const {return it_;}
+
+    TEST_CONSTEXPR_CXX14 contiguous_iterator() : it_() {}
+    explicit TEST_CONSTEXPR_CXX14 contiguous_iterator(It it) : it_(it) {}
+    template <class U>
+        TEST_CONSTEXPR_CXX14 contiguous_iterator(const contiguous_iterator<U>& u) :it_(u.it_) {}
+
+    TEST_CONSTEXPR_CXX14 reference operator*() const {return *it_;}
+    TEST_CONSTEXPR_CXX14 pointer operator->() const {return it_;}
+
+    TEST_CONSTEXPR_CXX14 contiguous_iterator& operator++() {++it_; return *this;}
+    TEST_CONSTEXPR_CXX14 contiguous_iterator operator++(int)
+        {contiguous_iterator tmp(*this); ++(*this); return tmp;}
+
+    TEST_CONSTEXPR_CXX14 contiguous_iterator& operator--() {--it_; return *this;}
+    TEST_CONSTEXPR_CXX14 contiguous_iterator operator--(int)
+        {contiguous_iterator tmp(*this); --(*this); return tmp;}
+
+    TEST_CONSTEXPR_CXX14 contiguous_iterator& operator+=(
diff erence_type n) {it_ += n; return *this;}
+    TEST_CONSTEXPR_CXX14 contiguous_iterator operator+(
diff erence_type n) const
+        {contiguous_iterator tmp(*this); tmp += n; return tmp;}
+    friend TEST_CONSTEXPR_CXX14 contiguous_iterator operator+(
diff erence_type n, contiguous_iterator x)
+        {x += n; return x;}
+    TEST_CONSTEXPR_CXX14 contiguous_iterator& operator-=(
diff erence_type n) {return *this += -n;}
+    TEST_CONSTEXPR_CXX14 contiguous_iterator operator-(
diff erence_type n) const
+        {contiguous_iterator tmp(*this); tmp -= n; return tmp;}
+
+    TEST_CONSTEXPR_CXX14 reference operator[](
diff erence_type n) const {return it_[n];}
+
+    template <class T>
+    void operator,(T const &) DELETE_FUNCTION;
+
+    friend TEST_CONSTEXPR_CXX14
+    
diff erence_type operator-(const contiguous_iterator& x, const contiguous_iterator& y) {
+        return x.base() - y.base();
+    }
+
+    friend TEST_CONSTEXPR_CXX14
+    
diff erence_type operator<(const contiguous_iterator& x, const contiguous_iterator& y) {
+        return x.base() < y.base();
+    }
+    friend TEST_CONSTEXPR_CXX14
+    
diff erence_type operator>(const contiguous_iterator& x, const contiguous_iterator& y) {
+        return x.base() > y.base();
+    }
+    friend TEST_CONSTEXPR_CXX14
+    
diff erence_type operator<=(const contiguous_iterator& x, const contiguous_iterator& y) {
+        return x.base() <= y.base();
+    }
+    friend TEST_CONSTEXPR_CXX14
+    
diff erence_type operator>=(const contiguous_iterator& x, const contiguous_iterator& y) {
+        return x.base() >= y.base();
+    }
+    friend TEST_CONSTEXPR_CXX14
+    
diff erence_type operator==(const contiguous_iterator& x, const contiguous_iterator& y) {
+        return x.base() == y.base();
+    }
+    friend TEST_CONSTEXPR_CXX14
+    
diff erence_type operator!=(const contiguous_iterator& x, const contiguous_iterator& y) {
+        return x.base() != y.base();
+    }
+};
+#endif
+
 template <class Iter>
 inline TEST_CONSTEXPR_CXX14 Iter base(output_iterator<Iter> i) { return i.base(); }
 
@@ -326,6 +405,11 @@ inline TEST_CONSTEXPR_CXX14 Iter base(bidirectional_iterator<Iter> i) { return i
 template <class Iter>
 inline TEST_CONSTEXPR_CXX14 Iter base(random_access_iterator<Iter> i) { return i.base(); }
 
+#if TEST_STD_VER >= 20
+template <class Iter>
+inline TEST_CONSTEXPR_CXX14 Iter base(contiguous_iterator<Iter> i) { return i.base(); }
+#endif
+
 template <class Iter>    // everything else
 inline TEST_CONSTEXPR_CXX14 Iter base(Iter i) { return i; }
 


        


More information about the libcxx-commits mailing list