[libcxx-commits] [libcxx] 79a2b4b - [libc++][ranges] Finish LWG issues directly related to the One Ranges Proposal.

Konstantin Varlamov via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jun 28 12:01:05 PDT 2022


Author: Konstantin Varlamov
Date: 2022-06-28T12:00:15-07:00
New Revision: 79a2b4ba98a1eecc214b68fc31483ebbd7cf8c8a

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

LOG: [libc++][ranges] Finish LWG issues directly related to the One Ranges Proposal.

- P1252 ("Ranges Design Cleanup") -- deprecate
  `move_iterator::operator->` starting from C++20; add range comparisons
  to the `<functional>` synopsis. This restores
  `move_iterator::operator->` that was incorrectly deleted in D117656;
  it's still defined in the latest draft, see
  http://eel.is/c++draft/depr.move.iter.elem. Note that changes to
  `*_result` types from 6.1 in the paper are no longer relevant now that
  these types are aliases;
- P2106 ("Alternative wording for GB315 and GB316") -- add a few
  `*_result` types to the synopsis in `<algorithm>` (some algorithms are
  not implemented yet and thus some of the proposal still cannot be
  marked as done);

Also mark already done issues as done (or as nothing to do):
- P2091 ("Fixing Issues With Range Access CPOs") was already implemented
  (this patch adds tests for some ill-formed cases);
- LWG 3247 ("`ranges::iter_move` should perform ADL-only lookup of
  `iter_move`") was already implemented;
- LWG 3300 ("Non-array ssize overload is underconstrained") doesn't
  affect the implementation;
- LWG 3335 ("Resolve C++20 NB comments US 273 and GB 274") was already
  implemented;
- LWG 3355 ("The memory algorithms should support move-only input
  iterators introduced by P1207") was already implemented (except for
  testing).

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

Added: 
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/deprecated.verify.cpp
    libcxx/test/std/ranges/range.access/begin.verify.cpp
    libcxx/test/std/ranges/range.access/data.verify.cpp
    libcxx/test/std/ranges/range.access/empty.verify.cpp
    libcxx/test/std/ranges/range.access/end.verify.cpp
    libcxx/test/std/ranges/range.access/rbegin.verify.cpp
    libcxx/test/std/ranges/range.access/rend.verify.cpp
    libcxx/test/std/ranges/range.access/size.verify.cpp
    libcxx/test/std/ranges/range.access/ssize.verify.cpp

Modified: 
    libcxx/docs/Status/Cxx20Issues.csv
    libcxx/docs/Status/Cxx20Papers.csv
    libcxx/docs/Status/RangesIssues.csv
    libcxx/include/__iterator/move_iterator.h
    libcxx/include/__ranges/size.h
    libcxx/include/algorithm
    libcxx/include/functional
    libcxx/include/iterator
    libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index e332267e5ffa..e62ba746eac7 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -205,7 +205,7 @@
 "`3238 <https://wg21.link/LWG3238>`__","Insufficiently-defined behavior of ``std::function``\  deduction guides","Prague","",""
 "`3242 <https://wg21.link/LWG3242>`__","``std::format``\ : missing rules for ``arg-id``\  in ``width``\  and ``precision``\ ","Prague","|Complete|","Clang 14","|format|"
 "`3243 <https://wg21.link/LWG3243>`__","``std::format``\  and negative zeroes","Prague","|Complete|","14.0","|format|"
-"`3247 <https://wg21.link/LWG3247>`__","``ranges::iter_move``\  should perform ADL-only lookup of ``iter_move``\ ","Prague","","","|ranges|"
+"`3247 <https://wg21.link/LWG3247>`__","``ranges::iter_move``\  should perform ADL-only lookup of ``iter_move``\ ","Prague","|Complete|","15.0","|ranges|"
 "`3248 <https://wg21.link/LWG3248>`__","``std::format``\  ``#b``\ , ``#B``\ , ``#o``\ , ``#x``\ , and ``#X``\   presentation types misformat negative numbers","Prague","|Complete|","14.0","|format|"
 "`3250 <https://wg21.link/LWG3250>`__","``std::format``\ : ``#``\  (alternate form) for NaN and inf","Prague","|Complete|","14.0","|format|"
 "`3251 <https://wg21.link/LWG3251>`__","Are ``std::format``\  alignment specifiers applied to string arguments?","Prague","|Complete|","14.0","|format|"
@@ -228,7 +228,7 @@
 "`3294 <https://wg21.link/LWG3294>`__","``zoned_time``\  deduction guides misinterprets ``string``\ /``char*``\ ","Prague","","","|chrono|"
 "`3296 <https://wg21.link/LWG3296>`__","Inconsistent default argument for ``basic_regex<>::assign``\ ","Prague","|Complete|",""
 "`3299 <https://wg21.link/LWG3299>`__","Pointers don't need customized iterator behavior","Prague","|Complete|","15.0","|ranges|"
-"`3300 <https://wg21.link/LWG3300>`__","Non-array ``ssize``\  overload is underconstrained","Prague","",""
+"`3300 <https://wg21.link/LWG3300>`__","Non-array ``ssize``\  overload is underconstrained","Prague","|Nothing To Do|",""
 "`3301 <https://wg21.link/LWG3301>`__","``transform_view::iterator``\  has incorrect ``iterator_category``\ ","Prague","|Complete|","15.0","|ranges|"
 "`3302 <https://wg21.link/LWG3302>`__","Range adaptor objects ``keys``\  and ``values``\  are unspecified","Prague","","","|ranges|"
 "`3303 <https://wg21.link/LWG3303>`__","Bad ""``constexpr``\ "" marker for ``destroy/destroy_n``\ ","Prague","",""
@@ -255,7 +255,7 @@
 "`3331 <https://wg21.link/LWG3331>`__","Define ``totally_ordered/_with``\  in terms of ``partially-ordered-with``\ ","Prague","|Complete|","13.0"
 "`3332 <https://wg21.link/LWG3332>`__","Issue in |sect|\ [time.format]","Prague","","","|chrono| |format|"
 "`3334 <https://wg21.link/LWG3334>`__","``basic_osyncstream``\  move assignment and destruction calls ``basic_syncbuf::emit()``\  twice","Prague","",""
-"`3335 <https://wg21.link/LWG3335>`__","Resolve C++20 NB comments US 273 and GB 274","Prague","","","|ranges|"
+"`3335 <https://wg21.link/LWG3335>`__","Resolve C++20 NB comments US 273 and GB 274","Prague","|Complete|","15.0","|ranges|"
 "`3338 <https://wg21.link/LWG3338>`__","Rename ``default_constructible``\  to ``default_initializable``\ ","Prague","|Complete|","13.0"
 "`3340 <https://wg21.link/LWG3340>`__","Formatting functions should throw on argument/format string mismatch in |sect|\ [format.functions]","Prague","|Complete|","14.0","|format|"
 "`3346 <https://wg21.link/LWG3346>`__","``pair``\  and ``tuple``\  copy and move constructor have backwards specification","Prague","",""
@@ -266,7 +266,7 @@
 "`3351 <https://wg21.link/LWG3351>`__","``ranges::enable_safe_range``\  should not be constrained","Prague","|Complete|","15.0","|ranges|"
 "`3352 <https://wg21.link/LWG3352>`__","``strong_equality``\  isn't a thing","Prague","|Nothing To Do|","","|spaceship|"
 "`3354 <https://wg21.link/LWG3354>`__","``has_strong_structural_equality``\  has a meaningless definition","Prague","|Nothing To Do|","","|spaceship|"
-"`3355 <https://wg21.link/LWG3355>`__","The memory algorithms should support move-only input iterators introduced by P1207","Prague","","","|ranges|"
+"`3355 <https://wg21.link/LWG3355>`__","The memory algorithms should support move-only input iterators introduced by P1207","Prague","|Complete|","15.0","|ranges|"
 "`3356 <https://wg21.link/LWG3356>`__","``__cpp_lib_nothrow_convertible``\  should be ``__cpp_lib_is_nothrow_convertible``\ ","Prague","|Complete|","12.0"
 "`3358 <https://wg21.link/LWG3358>`__","|sect|\ [span.cons] is mistaken that ``to_address``\  can throw","Prague","",""
 "`3359 <https://wg21.link/LWG3359>`__","``<chrono>``\  leap second support should allow for negative leap seconds","Prague","","","|chrono|"

diff  --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 7ed0c7b825c4..d00be2aefb94 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -187,7 +187,7 @@
 "`P2002R1 <https://wg21.link/P2002R1>`__","CWG","Defaulted comparison specification cleanups","Prague","* *",""
 "`P2045R1 <https://wg21.link/P2045R1>`__","LWG","Missing Mandates for the standard library","Prague","* *",""
 "`P2085R0 <https://wg21.link/P2085R0>`__","CWG","Consistent defaulted comparisons","Prague","* *",""
-"`P2091R0 <https://wg21.link/P2091R0>`__","LWG","Issues with range access CPOs","Prague","* *",""
+"`P2091R0 <https://wg21.link/P2091R0>`__","LWG","Issues with range access CPOs","Prague","|Complete|","15.0"
 "`P2101R0 <https://wg21.link/P2101R0>`__","LWG","'Models' subsumes 'satisfies' (Wording for US298 and US300)","Prague","* *",""
 "`P2102R0 <https://wg21.link/P2102R0>`__","LWG","Make 'implicit expression variations' more explicit (Wording for US185)","Prague","* *",""
 "`P2106R0 <https://wg21.link/P2106R0>`__","LWG","Alternative wording for GB315 and GB316","Prague","* *",""

diff  --git a/libcxx/docs/Status/RangesIssues.csv b/libcxx/docs/Status/RangesIssues.csv
index 928dbf23a729..0629255ba493 100644
--- a/libcxx/docs/Status/RangesIssues.csv
+++ b/libcxx/docs/Status/RangesIssues.csv
@@ -20,7 +20,7 @@
 `P1970R2 <https://wg21.link/P1970R2>`__,Consistency for size() functions: Add ranges::ssize,|Complete|,15.0
 `P1983R0 <https://wg21.link/P1983R0>`__,"Wording for GB301, US296, US292, US291, and US283",|Complete|,15.0
 `P1994R1 <https://wg21.link/P1994R1>`__,elements_view Needs Its Own sentinel,,
-`P2091R0 <https://wg21.link/P2091R0>`__,Fixing Issues With Range Access CPOs,,
+`P2091R0 <https://wg21.link/P2091R0>`__,Fixing Issues With Range Access CPOs,|Complete|,15.0
 `P2106R0 <https://wg21.link/P2106R0>`__,Range Algorithm Result Types,,
 
 `P2325R3 <https://wg21.link/P2325R3>`__,Views should not be required to be default constructible ,,

diff  --git a/libcxx/include/__iterator/move_iterator.h b/libcxx/include/__iterator/move_iterator.h
index e0c7e73a9cc3..6be9f216dbb4 100644
--- a/libcxx/include/__iterator/move_iterator.h
+++ b/libcxx/include/__iterator/move_iterator.h
@@ -93,6 +93,9 @@ class _LIBCPP_TEMPLATE_VIS move_iterator
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
     move_iterator& operator++() { ++__current_; return *this; }
 
+    _LIBCPP_DEPRECATED_IN_CXX20 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
+    pointer operator->() const { return __current_; }
+
 #if _LIBCPP_STD_VER > 17
     _LIBCPP_HIDE_FROM_ABI constexpr
     move_iterator() requires is_constructible_v<_Iter> : __current_() {}
@@ -156,8 +159,6 @@ class _LIBCPP_TEMPLATE_VIS move_iterator
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
     reference operator*() const { return static_cast<reference>(*__current_); }
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
-    pointer operator->() const { return __current_; }
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
     reference operator[](
diff erence_type __n) const { return static_cast<reference>(__current_[__n]); }
 
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14

diff  --git a/libcxx/include/__ranges/size.h b/libcxx/include/__ranges/size.h
index b83266831396..32ca4b854bc3 100644
--- a/libcxx/include/__ranges/size.h
+++ b/libcxx/include/__ranges/size.h
@@ -35,68 +35,76 @@ namespace ranges {
 
 namespace ranges {
 namespace __size {
-  void size(auto&) = delete;
-  void size(const auto&) = delete;
-
-  template <class _Tp>
-  concept __size_enabled = !disable_sized_range<remove_cvref_t<_Tp>>;
-
-  template <class _Tp>
-  concept __member_size =
-    __size_enabled<_Tp> &&
-    __workaround_52970<_Tp> &&
-    requires(_Tp&& __t) {
-      { _LIBCPP_AUTO_CAST(__t.size()) } -> __integer_like;
-    };
-
-  template <class _Tp>
-  concept __unqualified_size =
-    __size_enabled<_Tp> &&
-    !__member_size<_Tp> &&
-    __class_or_enum<remove_cvref_t<_Tp>> &&
-    requires(_Tp&& __t) {
-      { _LIBCPP_AUTO_CAST(size(__t)) } -> __integer_like;
-    };
-
-  template <class _Tp>
-  concept __
diff erence =
-    !__member_size<_Tp> &&
-    !__unqualified_size<_Tp> &&
-    __class_or_enum<remove_cvref_t<_Tp>> &&
-    requires(_Tp&& __t) {
-      { ranges::begin(__t) } -> forward_iterator;
-      { ranges::end(__t) } -> sized_sentinel_for<decltype(ranges::begin(declval<_Tp>()))>;
-    };
-
-  struct __fn {
-    template <class _Tp, size_t _Sz>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_t operator()(_Tp (&&)[_Sz]) const noexcept {
-      return _Sz;
-    }
-
-    template <class _Tp, size_t _Sz>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_t operator()(_Tp (&)[_Sz]) const noexcept {
-      return _Sz;
-    }
-
-    template <__member_size _Tp>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-        noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.size()))) {
-      return _LIBCPP_AUTO_CAST(__t.size());
-    }
-
-    template <__unqualified_size _Tp>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-        noexcept(noexcept(_LIBCPP_AUTO_CAST(size(__t)))) {
-      return _LIBCPP_AUTO_CAST(size(__t));
-    }
-
-    template<__
diff erence _Tp>
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
-        noexcept(noexcept(ranges::end(__t) - ranges::begin(__t))) {
-      return std::__to_unsigned_like(ranges::end(__t) - ranges::begin(__t));
-    }
+void size(auto&) = delete;
+void size(const auto&) = delete;
+
+template <class _Tp>
+concept __size_enabled = !disable_sized_range<remove_cvref_t<_Tp>>;
+
+template <class _Tp>
+concept __member_size =
+  __size_enabled<_Tp> &&
+  __workaround_52970<_Tp> &&
+  requires(_Tp&& __t) {
+    { _LIBCPP_AUTO_CAST(__t.size()) } -> __integer_like;
   };
+
+template <class _Tp>
+concept __unqualified_size =
+  __size_enabled<_Tp> &&
+  !__member_size<_Tp> &&
+  __class_or_enum<remove_cvref_t<_Tp>> &&
+  requires(_Tp&& __t) {
+    { _LIBCPP_AUTO_CAST(size(__t)) } -> __integer_like;
+  };
+
+template <class _Tp>
+concept __
diff erence =
+  !__member_size<_Tp> &&
+  !__unqualified_size<_Tp> &&
+  __class_or_enum<remove_cvref_t<_Tp>> &&
+  requires(_Tp&& __t) {
+    { ranges::begin(__t) } -> forward_iterator;
+    { ranges::end(__t) } -> sized_sentinel_for<decltype(ranges::begin(declval<_Tp>()))>;
+  };
+
+struct __fn {
+
+  // `[range.prim.size]`: the array case (for rvalues).
+  template <class _Tp, size_t _Sz>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_t operator()(_Tp (&&)[_Sz]) const noexcept {
+    return _Sz;
+  }
+
+  // `[range.prim.size]`: the array case (for lvalues).
+  template <class _Tp, size_t _Sz>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_t operator()(_Tp (&)[_Sz]) const noexcept {
+    return _Sz;
+  }
+
+  // `[range.prim.size]`: `auto(t.size())` is a valid expression.
+  template <__member_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.size()))) {
+    return _LIBCPP_AUTO_CAST(__t.size());
+  }
+
+  // `[range.prim.size]`: `auto(size(t))` is a valid expression.
+  template <__unqualified_size _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const
+      noexcept(noexcept(_LIBCPP_AUTO_CAST(size(__t)))) {
+    return _LIBCPP_AUTO_CAST(size(__t));
+  }
+
+  // [range.prim.size]: the `to-unsigned-like` case.
+  template <__
diff erence _Tp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+    noexcept(noexcept(std::__to_unsigned_like(ranges::end(__t) - ranges::begin(__t))))
+    -> decltype(      std::__to_unsigned_like(ranges::end(__t) - ranges::begin(__t)))
+    { return          std::__to_unsigned_like(ranges::end(__t) - ranges::begin(__t));
+  }
+};
+
 } // namespace __size
 
 inline namespace __cpo {
@@ -108,19 +116,18 @@ inline namespace __cpo {
 
 namespace ranges {
 namespace __ssize {
-  struct __fn {
-    template<class _Tp>
-      requires requires (_Tp&& __t) { ranges::size(__t); }
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr integral auto operator()(_Tp&& __t) const
-      noexcept(noexcept(ranges::size(__t)))
-    {
-      using _Signed = make_signed_t<decltype(ranges::size(__t))>;
-      if constexpr (sizeof(ptr
diff _t) > sizeof(_Signed))
-        return static_cast<ptr
diff _t>(ranges::size(__t));
-      else
-        return static_cast<_Signed>(ranges::size(__t));
-    }
-  };
+struct __fn {
+  template<class _Tp>
+    requires requires (_Tp&& __t) { ranges::size(__t); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr integral auto operator()(_Tp&& __t) const
+    noexcept(noexcept(ranges::size(__t))) {
+    using _Signed = make_signed_t<decltype(ranges::size(__t))>;
+    if constexpr (sizeof(ptr
diff _t) > sizeof(_Signed))
+      return static_cast<ptr
diff _t>(ranges::size(__t));
+    else
+      return static_cast<_Signed>(ranges::size(__t));
+  }
+};
 } // namespace __ssize
 
 inline namespace __cpo {

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 9ae647181c5f..62b67c39d9a8 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -19,12 +19,17 @@ namespace std
 {
 
 namespace ranges {
+
+  // [algorithms.results], algorithm result types
   template <class I, class F>
     struct in_fun_result;     // since C++20
 
   template <class I1, class I2>
     struct in_in_result;      // since C++20
 
+  template <class I, class O>
+    struct in_out_result;  // since C++20
+
   template <class I1, class I2, class O>
     struct in_in_out_result;  // since C++20
 
@@ -53,6 +58,9 @@ namespace ranges {
     indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
   constexpr borrowed_iterator_t<R> ranges::max_element(R&& r, Comp comp = {}, Proj proj = {});             // since C++20
 
+  template<class I1, class I2>
+    using mismatch_result = in_in_result<I1, I2>;
+
   template <input_iterator I1, sentinel_for<_I1> S1, input_iterator I2, sentinel_for<_I2> S2,
           class Pred = ranges::equal_to, class Proj1 = identity, class Proj2 = identity>
     requires indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
@@ -174,6 +182,9 @@ namespace ranges {
     constexpr range_
diff erence_t<R>
       count_if(R&& r, Pred pred, Proj proj = {});                                   // since C++20
 
+  template<class T>
+  using minmax_result = min_max_result<T>;
+
   template<class T, class Proj = identity,
            indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
     constexpr ranges::minmax_result<const T&>
@@ -190,6 +201,9 @@ namespace ranges {
     constexpr ranges::minmax_result<range_value_t<R>>
       minmax(R&& r, Comp comp = {}, Proj proj = {});                                      // since C++20
 
+  template<class I>
+  using minmax_element_result = min_max_result<I>;
+
   template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
            indirect_strict_weak_order<projected<I, Proj>> Comp = ranges::less>
     constexpr ranges::minmax_element_result<I>
@@ -201,10 +215,10 @@ namespace ranges {
       minmax_element(R&& r, Comp comp = {}, Proj proj = {});                              // since C++20
 
   template<class I, class O>
-      using copy_result = in_out_result<I, O>;                                              // since C++20
+    using copy_result = in_out_result<I, O>;                                              // since C++20
 
-    template<class I, class O>
-      using copy_n_result = in_out_result<I, O>;                                            // since C++20
+  template<class I, class O>
+    using copy_n_result = in_out_result<I, O>;                                            // since C++20
 
   template<class I, class O>
     using copy_if_result = in_out_result<I, O>;                                             // since C++20
@@ -638,19 +652,34 @@ template <class BidirectionalIterator1, class BidirectionalIterator2>
     copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last,
                   BidirectionalIterator2 result);
 
+// [alg.move], move
+template<class InputIterator, class OutputIterator>
+    constexpr OutputIterator move(InputIterator first, InputIterator last,
+                                OutputIterator result);
+
+template<class BidirectionalIterator1, class BidirectionalIterator2>
+    constexpr BidirectionalIterator2
+    move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last,
+                  BidirectionalIterator2 result);
+
 template <class ForwardIterator1, class ForwardIterator2>
     constexpr ForwardIterator2    // constexpr in C++20
     swap_ranges(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2);
 
+namespace ranges {
+    template<class I1, class I2>
+    using swap_ranges_result = in_in_result<I1, I2>;
+
 template<input_iterator I1, sentinel_for<I1> S1, input_iterator I2, sentinel_for<I2> S2>
         requires indirectly_swappable<I1, I2>
     constexpr ranges::swap_ranges_result<I1, I2>
-        ranges::swap_ranges(I1 first1, S1 last1, I2 first2, S2 last2);
+        swap_ranges(I1 first1, S1 last1, I2 first2, S2 last2);
 
 template<input_range R1, input_range R2>
         requires indirectly_swappable<iterator_t<R1>, iterator_t<R2>>
     constexpr ranges::swap_ranges_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>>
-        ranges::swap_ranges(R1&& r1, R2&& r2);
+        swap_ranges(R1&& r1, R2&& r2);
+}
 
 template <class ForwardIterator1, class ForwardIterator2>
     constexpr void                // constexpr in C++20

diff  --git a/libcxx/include/functional b/libcxx/include/functional
index ca17dd4ed3de..de02059f642a 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -482,6 +482,16 @@ template <> struct hash<long double>;
 template<class T> struct hash<T*>;
 template <> struct hash<nullptr_t>;  // C++17
 
+namespace ranges {
+  // [range.cmp], concept-constrained comparisons
+  struct equal_to;
+  struct not_equal_to;
+  struct greater;
+  struct less;
+  struct greater_equal;
+  struct less_equal;
+}
+
 }  // std
 
 POLICY:  For non-variadic implementations, the number of arguments is limited

diff  --git a/libcxx/include/iterator b/libcxx/include/iterator
index d2f5ac2c0e93..225ae815363c 100644
--- a/libcxx/include/iterator
+++ b/libcxx/include/iterator
@@ -406,7 +406,7 @@ public:
     constexpr Iterator base() &&; // From C++20
 
     constexpr reference operator*() const;
-    constexpr pointer operator->() const; // Removed in C++20
+    constexpr pointer operator->() const; // Deprecated in C++20
     constexpr move_iterator& operator++();
     constexpr auto operator++(int); // Return type was move_iterator until C++20
     constexpr move_iterator& operator--();

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/deprecated.verify.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/deprecated.verify.cpp
new file mode 100644
index 000000000000..8c1684f0319d
--- /dev/null
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/deprecated.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// move_iterator
+
+#include <iterator>
+
+int main(int, char**) {
+  (void)std::move_iterator<int*>().operator->();
+  // expected-warning at -1{{'operator->' is deprecated}}
+
+  return 0;
+}

diff  --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp
index 8be60bc349df..cbb4a167c506 100644
--- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp
+++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.ref/op_arrow.pass.cpp
@@ -6,6 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
 // <iterator>
 
 // move_iterator
@@ -13,32 +15,21 @@
 // pointer operator->() const;
 //
 //  constexpr in C++17
-//  removed in C++20
+//  deprecated in C++20
 
 #include <iterator>
 #include <cassert>
 
 #include "test_macros.h"
 
-#if TEST_STD_VER > 17
-template <class T>
-concept HasArrow = requires (T t) {
-    t.operator->();
-};
-static_assert(!HasArrow<std::move_iterator<int*>>);
-static_assert(!HasArrow<std::move_iterator<int*>&>);
-static_assert(!HasArrow<std::move_iterator<int*>&&>);
-#endif // TEST_STD_VER > 17
-
 TEST_CONSTEXPR_CXX17 bool test()
 {
-#if TEST_STD_VER <= 17
     char a[] = "123456789";
     std::move_iterator<char *> it1 = std::make_move_iterator(a);
     std::move_iterator<char *> it2 = std::make_move_iterator(a + 1);
     assert(it1.operator->() == a);
     assert(it2.operator->() == a + 1);
-#endif
+
     return true;
 }
 

diff  --git a/libcxx/test/std/ranges/range.access/begin.verify.cpp b/libcxx/test/std/ranges/range.access/begin.verify.cpp
new file mode 100644
index 000000000000..3a170353f41f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/begin.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::begin
+
+#include <ranges>
+
+struct NonBorrowedRange {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
+
+// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::begin` is ill-formed.
+void test() {
+  std::ranges::begin(NonBorrowedRange());
+  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__begin::__fn'}}}}
+  // expected-error at -2  {{attempt to use a deleted function}}
+}

diff  --git a/libcxx/test/std/ranges/range.access/data.verify.cpp b/libcxx/test/std/ranges/range.access/data.verify.cpp
new file mode 100644
index 000000000000..a70ffca467dc
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/data.verify.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::data
+
+#include <ranges>
+
+struct NonBorrowedRange {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
+
+// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::data` is ill-formed.
+void test() {
+  std::ranges::data(NonBorrowedRange());
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__data::__fn'}}}}
+}

diff  --git a/libcxx/test/std/ranges/range.access/empty.verify.cpp b/libcxx/test/std/ranges/range.access/empty.verify.cpp
new file mode 100644
index 000000000000..21bbcaa93d12
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/empty.verify.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::empty
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::empty` is ill-formed.
+void test() {
+  std::ranges::empty(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__empty::__fn'}}}}
+}

diff  --git a/libcxx/test/std/ranges/range.access/end.verify.cpp b/libcxx/test/std/ranges/range.access/end.verify.cpp
new file mode 100644
index 000000000000..800f2d61634e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/end.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::end
+
+#include <ranges>
+
+struct NonBorrowedRange {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
+
+// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::end` is ill-formed.
+void test() {
+  std::ranges::end(NonBorrowedRange());
+  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__end::__fn'}}}}
+  // expected-error at -2  {{attempt to use a deleted function}}
+}

diff  --git a/libcxx/test/std/ranges/range.access/rbegin.verify.cpp b/libcxx/test/std/ranges/range.access/rbegin.verify.cpp
new file mode 100644
index 000000000000..42ed5cef1e85
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/rbegin.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::rbegin
+
+#include <ranges>
+
+struct NonBorrowedRange {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
+
+// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::rbegin` is ill-formed.
+void test() {
+  std::ranges::rbegin(NonBorrowedRange());
+  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__rbegin::__fn'}}}}
+  // expected-error at -2  {{attempt to use a deleted function}}
+}

diff  --git a/libcxx/test/std/ranges/range.access/rend.verify.cpp b/libcxx/test/std/ranges/range.access/rend.verify.cpp
new file mode 100644
index 000000000000..5c5a33529ef9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/rend.verify.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::rend
+
+#include <ranges>
+
+struct NonBorrowedRange {
+  int* begin() const;
+  int* end() const;
+};
+static_assert(!std::ranges::enable_borrowed_range<NonBorrowedRange>);
+
+// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::rend` is ill-formed.
+void test() {
+  std::ranges::rend(NonBorrowedRange());
+  // expected-error-re at -1 {{{{call to deleted function call operator in type 'const (std::ranges::)?__rend::__fn'}}}}
+  // expected-error at -2  {{attempt to use a deleted function}}
+}

diff  --git a/libcxx/test/std/ranges/range.access/size.verify.cpp b/libcxx/test/std/ranges/range.access/size.verify.cpp
new file mode 100644
index 000000000000..c14f87180106
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/size.verify.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::size
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::size` is ill-formed.
+void test() {
+  std::ranges::size(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__size::__fn'}}}}
+}

diff  --git a/libcxx/test/std/ranges/range.access/ssize.verify.cpp b/libcxx/test/std/ranges/range.access/ssize.verify.cpp
new file mode 100644
index 000000000000..b0a4ff6d8d92
--- /dev/null
+++ b/libcxx/test/std/ranges/range.access/ssize.verify.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// std::ranges::size
+
+#include <ranges>
+
+extern int arr[];
+
+// Verify that for an array of unknown bound `ranges::ssize` is ill-formed.
+void test() {
+  std::ranges::ssize(arr);
+  // expected-error-re at -1 {{{{no matching function for call to object of type 'const (std::ranges::)?__ssize::__fn'}}}}
+}

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
index f1b65c5e052e..b6bf4fe9306d 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp
@@ -370,5 +370,32 @@ int main(int, char**) {
     assert(result.out == out.end());
   }
 
+  // Move-only iterators are supported.
+  {
+    using MoveOnlyIter = cpp20_input_iterator<const int*>;
+    static_assert(!std::is_copy_constructible_v<MoveOnlyIter>);
+
+    constexpr int N = 3;
+    struct MoveOnlyRange {
+      int buffer[N] = {1, 2, 3};
+      auto begin() const { return MoveOnlyIter(buffer); }
+      auto end() const { return sentinel_wrapper<MoveOnlyIter>(MoveOnlyIter(buffer)); }
+    };
+    static_assert(std::ranges::input_range<MoveOnlyRange>);
+    MoveOnlyRange in;
+
+    // (iter, sentinel) overload.
+    {
+      Buffer<int, N> out;
+      std::ranges::uninitialized_copy(in.begin(), in.end(), out.begin(), out.end());
+    }
+
+    // (range) overload.
+    {
+      Buffer<int, N> out;
+      std::ranges::uninitialized_copy(in, out);
+    }
+  }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
index c1d9cf09a1cb..f0bd16bfb498 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp
@@ -21,6 +21,7 @@
 #include <memory>
 #include <ranges>
 #include <type_traits>
+#include <utility>
 
 #include "../buffer.h"
 #include "../counted.h"
@@ -148,5 +149,18 @@ int main(int, char**) {
     assert(result.out == out.end());
   }
 
+  // Move-only iterators are supported.
+  {
+    using MoveOnlyIter = cpp20_input_iterator<const int*>;
+    static_assert(!std::is_copy_constructible_v<MoveOnlyIter>);
+
+    constexpr int N = 3;
+    int buffer[N] = {1, 2, 3};
+
+    MoveOnlyIter in(buffer);
+    Buffer<int, N> out;
+    std::ranges::uninitialized_copy_n(std::move(in), N, out.begin(), out.end());
+  }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
index c317efca64b2..4d635613d599 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp
@@ -391,5 +391,32 @@ int main(int, char**) {
     iter_moves = 0;
   }
 
+  // Move-only iterators are supported.
+  {
+    using MoveOnlyIter = cpp20_input_iterator<const int*>;
+    static_assert(!std::is_copy_constructible_v<MoveOnlyIter>);
+
+    constexpr int N = 3;
+    struct MoveOnlyRange {
+      int buffer[N] = {1, 2, 3};
+      auto begin() const { return MoveOnlyIter(buffer); }
+      auto end() const { return sentinel_wrapper<MoveOnlyIter>(MoveOnlyIter(buffer)); }
+    };
+    static_assert(std::ranges::input_range<MoveOnlyRange>);
+    MoveOnlyRange in;
+
+    // (iter, sentinel) overload.
+    {
+      Buffer<int, N> out;
+      std::ranges::uninitialized_move(in.begin(), in.end(), out.begin(), out.end());
+    }
+
+    // (range) overload.
+    {
+      Buffer<int, N> out;
+      std::ranges::uninitialized_move(in, out);
+    }
+  }
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp
index 70a68880e0f4..a0ac5dc2daff 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp
@@ -167,5 +167,18 @@ int main(int, char**) {
     iter_moves = 0;
   }
 
+  // Move-only iterators are supported.
+  {
+    using MoveOnlyIter = cpp20_input_iterator<const int*>;
+    static_assert(!std::is_copy_constructible_v<MoveOnlyIter>);
+
+    constexpr int N = 3;
+    int buffer[N] = {1, 2, 3};
+
+    MoveOnlyIter in(buffer);
+    Buffer<int, N> out;
+    std::ranges::uninitialized_move_n(std::move(in), N, out.begin(), out.end());
+  }
+
   return 0;
 }


        


More information about the libcxx-commits mailing list