[libc++][ranges] Implement P2474R2(`views::repeat`).
authoryrong <yronglin777@gmail.com>
Thu, 20 Jul 2023 12:00:10 +0000 (20:00 +0800)
committeryronglin <yronglin777@gmail.com>
Thu, 20 Jul 2023 12:03:01 +0000 (20:03 +0800)
- Implement https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2474r2.html
- Implement LWG3875(https://cplusplus.github.io/LWG/issue3875).

Depends on D151629

Reviewed By: #libc, Mordante, philnik, var-const

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

39 files changed:
libcxx/docs/FeatureTestMacroTable.rst
libcxx/docs/ReleaseNotes/17.rst
libcxx/docs/Status/Cxx23Issues.csv
libcxx/docs/Status/Cxx23Papers.csv
libcxx/docs/Status/RangesViews.csv
libcxx/include/CMakeLists.txt
libcxx/include/__ranges/drop_view.h
libcxx/include/__ranges/repeat_view.h [new file with mode: 0644]
libcxx/include/__ranges/take_view.h
libcxx/include/module.modulemap.in
libcxx/include/ranges
libcxx/include/version
libcxx/modules/std/ranges.cppm
libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp [new file with mode: 0644]
libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp [new file with mode: 0644]
libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.drop/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.take/adaptor.pass.cpp
libcxx/test/std/ranges/range.factories/range.repeat.view/begin.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.default.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/end.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/compare.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/ctor.default.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/decrement.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/increment.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/member_typedefs.compile.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/minus.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/minus_eq.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/plus.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/plus_eq.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/star.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/subscript.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/size.pass.cpp [new file with mode: 0644]
libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp [new file with mode: 0644]
libcxx/utils/generate_feature_test_macro_components.py

index 14a251a..79c14e0 100644 (file)
@@ -352,6 +352,8 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_join_with``                      *unimplemented*
     --------------------------------------------------- -----------------
+    ``__cpp_lib_ranges_repeat``                         ``202207L``
+    --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_slide``                          *unimplemented*
     --------------------------------------------------- -----------------
     ``__cpp_lib_ranges_starts_ends_with``               *unimplemented*
index f5b568f..f59d597 100644 (file)
@@ -67,7 +67,7 @@ Implemented Papers
 - P2494R2 - Relaxing range adaptors to allow for move only types
 - P2585R0 - Improving default container formatting
 - P0408R7 - Efficient Access to ``basic_stringbuf``'s Buffer
-
+- P2474R2 - ``views::repeat``
 
 Improvements and New Features
 -----------------------------
index aa7b69b..0324c0f 100644 (file)
 "`3870 <https://wg21.link/LWG3870>`__","Remove ``voidify``","February 2023","","",""
 "`3871 <https://wg21.link/LWG3871>`__","Adjust note about ``terminate``","February 2023","","",""
 "`3872 <https://wg21.link/LWG3872>`__","``basic_const_iterator`` should have custom ``iter_move``","February 2023","","",""
-"`3875 <https://wg21.link/LWG3875>`__","``std::ranges::repeat_view<T, IntegerClass>::iterator`` may be ill-formed","February 2023","","","|ranges|"
+"`3875 <https://wg21.link/LWG3875>`__","``std::ranges::repeat_view<T, IntegerClass>::iterator`` may be ill-formed","February 2023","|Complete|","17.0","|ranges|"
 "`3876 <https://wg21.link/LWG3876>`__","Default constructor of ``std::layout_XX::mapping`` misses precondition","February 2023","","",""
 "`3877 <https://wg21.link/LWG3877>`__","Incorrect constraints on ``const``-qualified monadic overloads for ``std::expected``","February 2023","|Complete|","17.0",""
 "`3878 <https://wg21.link/LWG3878>`__","import ``std;`` should guarantee initialization of standard iostreams objects","February 2023","","",""
index c8a8c9f..b8ea096 100644 (file)
@@ -77,7 +77,7 @@
 "`P2460R2 <https://wg21.link/P2460R2>`__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","",""
 "`P2465R3 <https://wg21.link/P2465R3>`__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","",""
 "`P2467R1 <https://wg21.link/P2467R1>`__","LWG","Support exclusive mode for ``fstreams``","July 2022","",""
-"`P2474R2 <https://wg21.link/P2474R2>`__","LWG","``views::repeat``","July 2022","","","|ranges|"
+"`P2474R2 <https://wg21.link/P2474R2>`__","LWG","``views::repeat``","July 2022","|Complete|","17.0","|ranges|"
 "`P2494R2 <https://wg21.link/P2494R2>`__","LWG","Relaxing range adaptors to allow for move only types","July 2022","|Complete|","17.0","|ranges|"
 "`P2499R0 <https://wg21.link/P2499R0>`__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","|Complete|","16.0","|ranges|"
 "`P2502R2 <https://wg21.link/P2502R2>`__","LWG","``std::generator``: Synchronous Coroutine Generator for Ranges","July 2022","","","|ranges|"
index b07103e..73769de 100644 (file)
@@ -22,7 +22,7 @@ C++20,`istream <https://wg21.link/P1035R7>`_,Hui Xie,`D133317 <https://llvm.org/
 ,,,,
 ,,,,
 ,,,,
-C++23,`repeat <https://wg21.link/P2474R2>`_,Unassigned,No patch yet,Not started
+C++23,`repeat <https://wg21.link/P2474R2>`_,Yrong,`D141699 <https://llvm.org/D141699>`_,✅
 C++23,`cartesian_product <https://wg21.link/P2374R4>`_,Unassigned,No patch yet,Not started
 C++23,`zip <https://wg21.link/P2321R2>`_,Hui Xie,`D122806 <https://llvm.org/D122806>`_,✅
 C++23,`zip_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
index cfb367e..b7c15a2 100644 (file)
@@ -637,6 +637,7 @@ set(files
   __ranges/rbegin.h
   __ranges/ref_view.h
   __ranges/rend.h
+  __ranges/repeat_view.h
   __ranges/reverse_view.h
   __ranges/single_view.h
   __ranges/size.h
index cdf00ca..f10476f 100644 (file)
@@ -30,6 +30,7 @@
 #include <__ranges/iota_view.h>
 #include <__ranges/non_propagating_cache.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/repeat_view.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
@@ -267,6 +268,32 @@ struct __fn {
                               ranges::end(__rng),
                               std::__to_unsigned_like(__dist - __clamped)
                               );}
+// clang-format off
+#if _LIBCPP_STD_VER >= 23
+  // [range.drop.overview]: the `repeat_view` "_RawRange models sized_range" case.
+  template <class _Range,
+            convertible_to<range_difference_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>,
+            class _Dist     = range_difference_t<_Range>>
+    requires (__is_repeat_specialization<_RawRange> && sized_range<_RawRange>)
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Np&& __n) const
+    noexcept(noexcept(views::repeat(*__range.__value_, ranges::distance(__range) - std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n)))))
+    -> decltype(      views::repeat(*__range.__value_, ranges::distance(__range) - std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n))))
+    { return          views::repeat(*__range.__value_, ranges::distance(__range) - std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n))); }
+
+  // [range.drop.overview]: the `repeat_view` "otherwise" case.
+  template <class _Range,
+            convertible_to<range_difference_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>,
+            class _Dist     = range_difference_t<_Range>>
+    requires (__is_repeat_specialization<_RawRange> && !sized_range<_RawRange>)
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI
+  constexpr auto operator()(_Range&& __range, _Np&&) const
+    noexcept(noexcept(_LIBCPP_AUTO_CAST(std::forward<_Range>(__range))))
+    -> decltype(      _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)))
+    { return          _LIBCPP_AUTO_CAST(std::forward<_Range>(__range)); }
+#endif
+// clang-format on
 
   // [range.drop.overview]: the "otherwise" case.
   template <class _Range, convertible_to<range_difference_t<_Range>> _Np,
@@ -274,6 +301,9 @@ struct __fn {
     // Note: without specifically excluding the other cases, GCC sees this overload as ambiguous with the other
     // overloads.
     requires (!(__is_empty_view<_RawRange> ||
+#if _LIBCPP_STD_VER >= 23
+                __is_repeat_specialization<_RawRange> ||
+#endif
                (__is_subrange_specialization_with_store_size<_RawRange> &&
                sized_range<_RawRange> &&
                 random_access_range<_RawRange>) ||
diff --git a/libcxx/include/__ranges/repeat_view.h b/libcxx/include/__ranges/repeat_view.h
new file mode 100644 (file)
index 0000000..fddf4ba
--- /dev/null
@@ -0,0 +1,260 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_REPEAT_VIEW_H
+#define _LIBCPP___RANGES_REPEAT_VIEW_H
+
+#include <__concepts/constructible.h>
+#include <__concepts/same_as.h>
+#include <__concepts/semiregular.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/unreachable_sentinel.h>
+#include <__memory/addressof.h>
+#include <__ranges/iota_view.h>
+#include <__ranges/movable_box.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/make_unsigned.h>
+#include <__type_traits/remove_cv.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <__utility/piecewise_construct.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Tp>
+concept __integer_like_with_usable_difference_type =
+    __signed_integer_like<_Tp> || (__integer_like<_Tp> && weakly_incrementable<_Tp>);
+
+template <class _Tp>
+struct __repeat_view_iterator_difference {
+  using type = _IotaDiffT<_Tp>;
+};
+
+template <__signed_integer_like _Tp>
+struct __repeat_view_iterator_difference<_Tp> {
+  using type = _Tp;
+};
+
+template <class _Tp>
+using __repeat_view_iterator_difference_t = typename __repeat_view_iterator_difference<_Tp>::type;
+
+namespace views::__drop {
+struct __fn;
+} // namespace views::__drop
+
+namespace views::__take {
+struct __fn;
+} // namespace views::__take
+
+template <move_constructible _Tp, semiregular _Bound = unreachable_sentinel_t>
+  requires(is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>> &&
+           (__integer_like_with_usable_difference_type<_Bound> || same_as<_Bound, unreachable_sentinel_t>))
+class repeat_view : public view_interface<repeat_view<_Tp, _Bound>> {
+  friend struct views::__take::__fn;
+  friend struct views::__drop::__fn;
+  class __iterator;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI repeat_view()
+    requires default_initializable<_Tp>
+  = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit repeat_view(const _Tp& __value, _Bound __bound_sentinel = _Bound())
+    requires copy_constructible<_Tp>
+      : __value_(in_place, __value), __bound_(__bound_sentinel) {
+    if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+      _LIBCPP_ASSERT(__bound_ >= 0, "The value of bound must be greater than or equal to 0");
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit repeat_view(_Tp&& __value, _Bound __bound_sentinel = _Bound())
+      : __value_(in_place, std::move(__value)), __bound_(__bound_sentinel) {
+    if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+      _LIBCPP_ASSERT(__bound_ >= 0, "The value of bound must be greater than or equal to 0");
+  }
+
+  template <class... _TpArgs, class... _BoundArgs>
+    requires(constructible_from<_Tp, _TpArgs...> && constructible_from<_Bound, _BoundArgs...>)
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit repeat_view(
+      piecewise_construct_t, tuple<_TpArgs...> __value_args, tuple<_BoundArgs...> __bound_args = tuple<>{})
+      : __value_(in_place, std::make_from_tuple<_Tp>(std::move(__value_args))),
+        __bound_(std::make_from_tuple<_Bound>(std::move(__bound_args))) {
+    if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+      _LIBCPP_ASSERT(
+          __bound_ >= 0, "The behavior is undefined if Bound is not unreachable_sentinel_t and bound is negative");
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() const { return __iterator(std::addressof(*__value_)); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator end() const
+    requires(!same_as<_Bound, unreachable_sentinel_t>)
+  {
+    return __iterator(std::addressof(*__value_), __bound_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr unreachable_sentinel_t end() const noexcept { return unreachable_sentinel; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+    requires(!same_as<_Bound, unreachable_sentinel_t>)
+  {
+    return std::__to_unsigned_like(__bound_);
+  }
+
+private:
+  __movable_box<_Tp> __value_;
+  _LIBCPP_NO_UNIQUE_ADDRESS _Bound __bound_ = _Bound();
+};
+
+template <class _Tp, class _Bound>
+repeat_view(_Tp, _Bound) -> repeat_view<_Tp, _Bound>;
+
+// [range.repeat.iterator]
+template <move_constructible _Tp, semiregular _Bound>
+  requires(is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>> &&
+           (__integer_like_with_usable_difference_type<_Bound> || same_as<_Bound, unreachable_sentinel_t>))
+class repeat_view<_Tp, _Bound>::__iterator {
+  friend class repeat_view;
+
+  using _IndexT = conditional_t<same_as<_Bound, unreachable_sentinel_t>, ptrdiff_t, _Bound>;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(const _Tp* __value, _IndexT __bound_sentinel = _IndexT())
+      : __value_(__value), __current_(__bound_sentinel) {}
+
+public:
+  using iterator_concept  = random_access_iterator_tag;
+  using iterator_category = random_access_iterator_tag;
+  using value_type        = _Tp;
+  using difference_type   = __repeat_view_iterator_difference_t<_IndexT>;
+
+  _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const noexcept { return *__value_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+    ++__current_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+    auto __tmp = *this;
+    ++*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--() {
+    if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+      _LIBCPP_ASSERT(__current_ > 0, "The value of bound must be greater than or equal to 0");
+    --__current_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) {
+    auto __tmp = *this;
+    --*this;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __n) {
+    if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+      _LIBCPP_ASSERT(__current_ + __n >= 0, "The value of bound must be greater than or equal to 0");
+    __current_ += __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __n) {
+    if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+      _LIBCPP_ASSERT(__current_ - __n >= 0, "The value of bound must be greater than or equal to 0");
+    __current_ -= __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](difference_type __n) const noexcept { return *(*this + __n); }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ == __y.__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) {
+    return __x.__current_ <=> __y.__current_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator __i, difference_type __n) {
+    __i += __n;
+    return __i;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, __iterator __i) {
+    __i += __n;
+    return __i;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(__iterator __i, difference_type __n) {
+    __i -= __n;
+    return __i;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y) {
+    return static_cast<difference_type>(__x.__current_) - static_cast<difference_type>(__y.__current_);
+  }
+
+private:
+  const _Tp* __value_ = nullptr;
+  _IndexT __current_  = _IndexT();
+};
+
+// clang-format off
+namespace views {
+namespace __repeat {
+struct __fn {
+  template <class _Tp>
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __value) const
+    noexcept(noexcept(ranges::repeat_view(std::forward<_Tp>(__value))))
+    -> decltype(      ranges::repeat_view(std::forward<_Tp>(__value)))
+    { return          ranges::repeat_view(std::forward<_Tp>(__value)); }
+
+
+  template <class _Tp, class _Bound>
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __value, _Bound&& __bound_sentinel) const
+    noexcept(noexcept(ranges::repeat_view(std::forward<_Tp>(__value), std::forward<_Bound>(__bound_sentinel))))
+    -> decltype(      ranges::repeat_view(std::forward<_Tp>(__value), std::forward<_Bound>(__bound_sentinel)))
+    { return          ranges::repeat_view(std::forward<_Tp>(__value), std::forward<_Bound>(__bound_sentinel)); }
+};
+} // namespace __repeat
+// clang-format on
+
+inline namespace __cpo {
+inline constexpr auto repeat = __repeat::__fn{};
+} // namespace __cpo
+} // namespace views
+
+template <class _Tp>
+inline constexpr bool __is_repeat_specialization = false;
+
+template <class _Tp, class _Bound>
+inline constexpr bool __is_repeat_specialization<repeat_view<_Tp, _Bound>> = true;
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___RANGES_REPEAT_VIEW_H
index 34978a7..4204017 100644 (file)
@@ -31,6 +31,7 @@
 #include <__ranges/enable_borrowed_range.h>
 #include <__ranges/iota_view.h>
 #include <__ranges/range_adaptor.h>
+#include <__ranges/repeat_view.h>
 #include <__ranges/size.h>
 #include <__ranges/subrange.h>
 #include <__ranges/view_interface.h>
@@ -301,6 +302,31 @@ struct __fn {
                               *ranges::begin(__rng),
                               *ranges::begin(__rng) + std::min<_Dist>(ranges::distance(__rng), std::forward<_Np>(__n))
                               ); }
+// clang-format off
+#if _LIBCPP_STD_VER >= 23
+  // [range.take.overview]: the `repeat_view` "_RawRange models sized_range" case.
+  template <class _Range,
+            convertible_to<range_difference_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>,
+            class _Dist     = range_difference_t<_Range>>
+    requires(__is_repeat_specialization<_RawRange> && sized_range<_RawRange>)
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Np&& __n) const
+    noexcept(noexcept(views::repeat(*__range.__value_, std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n)))))
+    -> decltype(      views::repeat(*__range.__value_, std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n))))
+    { return          views::repeat(*__range.__value_, std::min<_Dist>(ranges::distance(__range), std::forward<_Np>(__n))); }
+
+  // [range.take.overview]: the `repeat_view` "otherwise" case.
+  template <class _Range,
+            convertible_to<range_difference_t<_Range>> _Np,
+            class _RawRange = remove_cvref_t<_Range>,
+            class _Dist     = range_difference_t<_Range>>
+    requires(__is_repeat_specialization<_RawRange> && !sized_range<_RawRange>)
+  _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Np&& __n) const
+    noexcept(noexcept(views::repeat(*__range.__value_, static_cast<_Dist>(__n))))
+    -> decltype(      views::repeat(*__range.__value_, static_cast<_Dist>(__n)))
+    { return          views::repeat(*__range.__value_, static_cast<_Dist>(__n)); }
+#endif
+// clang-format on
 
   // [range.take.overview]: the "otherwise" case.
   template <class _Range, convertible_to<range_difference_t<_Range>> _Np,
@@ -308,6 +334,9 @@ struct __fn {
     // Note: without specifically excluding the other cases, GCC sees this overload as ambiguous with the other
     // overloads.
     requires (!(__is_empty_view<_RawRange> ||
+#if _LIBCPP_STD_VER >= 23
+                __is_repeat_specialization<_RawRange> ||
+#endif
                (__is_iota_specialization<_RawRange> &&
                 sized_range<_RawRange> &&
                 random_access_range<_RawRange>) ||
index 88de198..cfc53e5 100644 (file)
@@ -1481,6 +1481,7 @@ module std [system] {
       module rbegin                     { private header "__ranges/rbegin.h" }
       module ref_view                   { private header "__ranges/ref_view.h" }
       module rend                       { private header "__ranges/rend.h" }
+      module repeat_view                { private header "__ranges/repeat_view.h" }
       module reverse_view               { private header "__ranges/reverse_view.h" }
       module single_view                { private header "__ranges/single_view.h" }
       module size                       {
index bcf05f5..3bf9814 100644 (file)
@@ -252,6 +252,19 @@ namespace std::ranges {
   template<class W, class Bound>
     inline constexpr bool enable_borrowed_range<iota_view<W, Bound>> = true;
 
+  // [range.repeat], repeat view
+  template<class T>
+    concept integer-like-with-usable-difference-type =  // exposition only
+      is-signed-integer-like<T> || (is-integer-like<T> && weakly_incrementable<T>);
+
+  template<move_constructible T, semiregular Bound = unreachable_sentinel_t>
+    requires (is_object_v<T> && same_as<T, remove_cv_t<T>> &&
+              (integer-like-with-usable-difference-type<Bound> ||
+               same_as<Bound, unreachable_sentinel_t>))
+  class repeat_view;
+
+  namespace views { inline constexpr unspecified repeat = unspecified; }
+
   // [range.join], join view
   template<input_range V>
     requires view<V> && input_range<range_reference_t<V>>
@@ -370,6 +383,7 @@ namespace std {
 #include <__ranges/rbegin.h>
 #include <__ranges/ref_view.h>
 #include <__ranges/rend.h>
+#include <__ranges/repeat_view.h>
 #include <__ranges/reverse_view.h>
 #include <__ranges/single_view.h>
 #include <__ranges/size.h>
index 6f588c8..87b7924 100644 (file)
@@ -158,6 +158,7 @@ __cpp_lib_ranges_chunk                                  202202L <ranges>
 __cpp_lib_ranges_chunk_by                               202202L <ranges>
 __cpp_lib_ranges_iota                                   202202L <numeric>
 __cpp_lib_ranges_join_with                              202202L <ranges>
+__cpp_lib_ranges_repeat                                 202207L <ranges>
 __cpp_lib_ranges_slide                                  202202L <ranges>
 __cpp_lib_ranges_starts_ends_with                       202106L <algorithm>
 __cpp_lib_ranges_to_container                           202202L <deque> <forward_list> <list>
@@ -436,6 +437,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 // # define __cpp_lib_ranges_chunk_by                      202202L
 // # define __cpp_lib_ranges_iota                          202202L
 // # define __cpp_lib_ranges_join_with                     202202L
+# define __cpp_lib_ranges_repeat                        202207L
 // # define __cpp_lib_ranges_slide                         202202L
 // # define __cpp_lib_ranges_starts_ends_with              202106L
 // # define __cpp_lib_ranges_to_container                  202202L
index c6b000a..7e4c543 100644 (file)
@@ -119,13 +119,12 @@ export namespace std {
     } // namespace views
 
     // [range.repeat], repeat view
-#if 0
     using std::ranges::repeat_view;
 
     namespace views {
       using std::ranges::views::repeat;
     } // namespace views
-#endif
+
 #ifndef _LIBCPP_HAS_NO_LOCALIZATION
     // [range.istream], istream view
     using std::ranges::basic_istream_view;
diff --git a/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp b/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp
new file mode 100644 (file)
index 0000000..b63a0dc
--- /dev/null
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// REQUIRES: has-unix-headers
+// XFAIL: availability-verbose_abort-missing
+
+// template<class... TArgs, class... BoundArgs>
+//       requires constructible_from<T, TArgs...> &&
+//                constructible_from<Bound, BoundArgs...>
+//     constexpr explicit repeat_view(piecewise_construct_t,
+//       tuple<TArgs...> value_args, tuple<BoundArgs...> bound_args = tuple<>{});
+
+#include <ranges>
+#include <tuple>
+
+#include "check_assertion.h"
+
+// clang-format off
+int main(int, char**) {
+  using Repeat = std::ranges::repeat_view<int, int>;
+  TEST_LIBCPP_ASSERT_FAILURE(Repeat(std::piecewise_construct, std::tuple{1}, std::tuple{-1}), "The behavior is undefined if Bound is not unreachable_sentinel_t and bound is negative");
+
+  return 0;
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp b/libcxx/test/libcxx/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp
new file mode 100644 (file)
index 0000000..7f50bff
--- /dev/null
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode
+// REQUIRES: has-unix-headers
+// XFAIL: availability-verbose_abort-missing
+
+// constexpr explicit repeat_view(W&& value, Bound bound = Bound());
+// constexpr explicit repeat_view(const W& value, Bound bound = Bound());
+
+#include <ranges>
+
+#include "check_assertion.h"
+
+// clang-format off
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::ranges::repeat_view(0, -1), "The value of bound must be greater than or equal to 0");
+  const int val = 0;
+  TEST_LIBCPP_ASSERT_FAILURE(std::ranges::repeat_view(val, -1), "The value of bound must be greater than or equal to 0");
+
+  return 0;
+}
+// clang-format on
index 7a1edd1..3758c53 100644 (file)
@@ -21,6 +21,7 @@
     __cpp_lib_ranges_chunk        202202L [C++23]
     __cpp_lib_ranges_chunk_by     202202L [C++23]
     __cpp_lib_ranges_join_with    202202L [C++23]
+    __cpp_lib_ranges_repeat       202207L [C++23]
     __cpp_lib_ranges_slide        202202L [C++23]
     __cpp_lib_ranges_zip          202110L [C++23]
 */
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_slide
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_slide
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_slide
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_slide
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 #   endif
 # endif
 
+# ifndef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_repeat != 202207L
+#   error "__cpp_lib_ranges_repeat should have the value 202207L in c++23"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_ranges_slide
 #     error "__cpp_lib_ranges_slide should be defined in c++23"
 #   endif
 # endif
 
+# ifndef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_repeat != 202207L
+#   error "__cpp_lib_ranges_repeat should have the value 202207L in c++26"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_ranges_slide
 #     error "__cpp_lib_ranges_slide should be defined in c++26"
index 75991c2..94d2838 100644 (file)
     __cpp_lib_ranges_chunk_by                        202202L [C++23]
     __cpp_lib_ranges_iota                            202202L [C++23]
     __cpp_lib_ranges_join_with                       202202L [C++23]
+    __cpp_lib_ranges_repeat                          202207L [C++23]
     __cpp_lib_ranges_slide                           202202L [C++23]
     __cpp_lib_ranges_starts_ends_with                202106L [C++23]
     __cpp_lib_ranges_to_container                    202202L [C++23]
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_slide
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_slide
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_slide
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 #   error "__cpp_lib_ranges_join_with should not be defined before c++23"
 # endif
 
+# ifdef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should not be defined before c++23"
+# endif
+
 # ifdef __cpp_lib_ranges_slide
 #   error "__cpp_lib_ranges_slide should not be defined before c++23"
 # endif
 #   endif
 # endif
 
+# ifndef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_repeat != 202207L
+#   error "__cpp_lib_ranges_repeat should have the value 202207L in c++23"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_ranges_slide
 #     error "__cpp_lib_ranges_slide should be defined in c++23"
 #   endif
 # endif
 
+# ifndef __cpp_lib_ranges_repeat
+#   error "__cpp_lib_ranges_repeat should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_repeat != 202207L
+#   error "__cpp_lib_ranges_repeat should have the value 202207L in c++26"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_ranges_slide
 #     error "__cpp_lib_ranges_slide should be defined in c++26"
index 542f2cd..457b137 100644 (file)
@@ -220,6 +220,27 @@ constexpr bool test() {
     test_small_range(std::views::iota(1, 8));
   }
 
+#if TEST_STD_VER >= 23
+  // `views::drop(repeat_view, n)` returns a `repeat_view` when `repeat_view` models `sized_range`.
+  {
+    auto repeat                                = std::ranges::repeat_view<int, int>(1, 8);
+    using Result                               = std::ranges::repeat_view<int, int>;
+    std::same_as<Result> decltype(auto) result = repeat | std::views::drop(3);
+    static_assert(std::ranges::sized_range<Result>);
+    assert(result.size() == 5);
+    assert(*result.begin() == 1);
+  }
+
+  // `views::drop(repeat_view, n)` returns a `repeat_view` when `repeat_view` doesn't model `sized_range`.
+  {
+    auto repeat                                = std::ranges::repeat_view<int>(1);
+    using Result                               = std::ranges::repeat_view<int, std::unreachable_sentinel_t>;
+    std::same_as<Result> decltype(auto) result = repeat | std::views::drop(3);
+    static_assert(!std::ranges::sized_range<Result>);
+    static_assert(std::same_as<std::unreachable_sentinel_t, decltype(result.end())>);
+  }
+#endif
+
   // Test that it's possible to call `std::views::drop` with any single argument as long as the resulting closure is
   // never invoked. There is no good use case for it, but it's valid.
   {
index 9f4a563..8ffac8d 100644 (file)
@@ -179,6 +179,27 @@ constexpr bool test() {
     assert(result.size() == 3);
   }
 
+#if TEST_STD_VER >= 23
+  // `views::take(repeat_view, n)` returns a `repeat_view` when `repeat_view` models `sized_range`.
+  {
+    auto repeat                                = std::ranges::repeat_view<int, int>(1, 8);
+    using Result                               = std::ranges::repeat_view<int, int>;
+    std::same_as<Result> decltype(auto) result = repeat | std::views::take(3);
+    static_assert(std::ranges::sized_range<Result>);
+    assert(result.size() == 3);
+    assert(*result.begin() == 1);
+  }
+
+  // `views::take(repeat_view, n)` returns a `repeat_view` when `repeat_view` doesn't model `sized_range`.
+  {
+    auto repeat  = std::ranges::repeat_view<int>(1);
+    using Result = std::ranges::repeat_view<int, std::ranges::range_difference_t<decltype(repeat)>>;
+    std::same_as<Result> decltype(auto) result = repeat | std::views::take(3);
+    assert(result.size() == 3);
+    assert(*result.begin() == 1);
+  }
+#endif
+
   // When the size of the input range `s` is shorter than `n`, only `s` elements are taken.
   {
     test_small_range(std::span(buf));
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/begin.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/begin.pass.cpp
new file mode 100644 (file)
index 0000000..d8d8657
--- /dev/null
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr iterator begin() const;
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+
+constexpr bool test() {
+  // Test unbound && non-const view
+  {
+    std::ranges::repeat_view<int> rv(0);
+    std::same_as<std::ranges::iterator_t<decltype(rv)>> decltype(auto) iter = rv.begin();
+    assert(*iter == 0);
+  }
+
+  // Test unbound && const view
+  {
+    const std::ranges::repeat_view<int> rv(0);
+    std::same_as<std::ranges::iterator_t<decltype(rv)>> decltype(auto) iter = rv.begin();
+    assert(*iter == 0);
+  }
+
+  // Test bound && non-const view
+  {
+    std::ranges::repeat_view<int, int> rv(1024, 10);
+    std::same_as<std::ranges::iterator_t<decltype(rv)>> decltype(auto) iter = rv.begin();
+    assert(*iter == 1024);
+  }
+
+  // Test bound && const view
+  {
+    const std::ranges::repeat_view<int, long long> rv(1024, 10);
+    std::same_as<std::ranges::iterator_t<decltype(rv)>> decltype(auto) iter = rv.begin();
+    assert(*iter == 1024);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctad.compile.pass.cpp
new file mode 100644 (file)
index 0000000..454bac6
--- /dev/null
@@ -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, c++20
+
+// template<class T, class Bound>
+//    repeat_view(T, Bound) -> repeat_view<T, Bound>;
+
+#include <concepts>
+#include <ranges>
+#include <utility>
+
+struct Empty {};
+
+// clang-format off
+static_assert(std::same_as<decltype(std::ranges::repeat_view(Empty())), std::ranges::repeat_view<Empty>>);
+static_assert(std::same_as<decltype(std::ranges::repeat_view(std::declval<Empty&>())), std::ranges::repeat_view<Empty>>);
+static_assert(std::same_as<decltype(std::ranges::repeat_view(std::declval<Empty&&>())), std::ranges::repeat_view<Empty>>);
+static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1)), std::ranges::repeat_view<int, int>>);
+static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1U)), std::ranges::repeat_view<int, unsigned>>);
+static_assert(std::same_as<decltype(std::ranges::repeat_view(10, 1UL)), std::ranges::repeat_view<int, unsigned long>>);
+// clang-format on
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.default.pass.cpp
new file mode 100644 (file)
index 0000000..f123770
--- /dev/null
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// repeat_view() requires default_initializable<T> = default;
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+
+struct DefaultInt42 {
+  int value = 42;
+};
+
+struct Int {
+  Int(int) {}
+};
+
+static_assert(std::default_initializable<std::ranges::repeat_view<DefaultInt42>>);
+static_assert(!std::default_initializable<std::ranges::repeat_view<Int>>);
+
+constexpr bool test() {
+  std::ranges::repeat_view<DefaultInt42> rv;
+  assert((*rv.begin()).value == 42);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.piecewise.pass.cpp
new file mode 100644 (file)
index 0000000..5b7939a
--- /dev/null
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// template<class... TArgs, class... BoundArgs>
+//       requires constructible_from<T, TArgs...> &&
+//                constructible_from<Bound, BoundArgs...>
+//     constexpr explicit repeat_view(piecewise_construct_t,
+//       tuple<TArgs...> value_args, tuple<BoundArgs...> bound_args = tuple<>{});
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+#include <tuple>
+#include <utility>
+
+struct C {};
+
+struct B {
+  int v;
+};
+
+struct A {
+  int x = 111;
+  int y = 222;
+
+  constexpr A() = default;
+  constexpr A(B b) : x(b.v), y(b.v + 1) {}
+  constexpr A(int _x, int _y) : x(_x), y(_y) {}
+};
+
+static_assert(std::constructible_from<std::ranges::repeat_view<A, int>,
+                                      std::piecewise_construct_t,
+                                      std::tuple<int, int>,
+                                      std::tuple<int>>);
+static_assert(std::constructible_from<std::ranges::repeat_view<A, int>,
+                                      std::piecewise_construct_t,
+                                      std::tuple<B>,
+                                      std::tuple<int>>);
+static_assert(std::constructible_from<std::ranges::repeat_view<A, int>,
+                                      std::piecewise_construct_t,
+                                      std::tuple<>,
+                                      std::tuple<int>>);
+static_assert(std::constructible_from<std::ranges::repeat_view<A>,
+                                      std::piecewise_construct_t,
+                                      std::tuple<int, int>,
+                                      std::tuple<std::unreachable_sentinel_t>>);
+static_assert(std::constructible_from<std::ranges::repeat_view<A>,
+                                      std::piecewise_construct_t,
+                                      std::tuple<B>,
+                                      std::tuple<std::unreachable_sentinel_t>>);
+static_assert(std::constructible_from<std::ranges::repeat_view<A>,
+                                      std::piecewise_construct_t,
+                                      std::tuple<>,
+                                      std::tuple<std::unreachable_sentinel_t>>);
+static_assert(!std::constructible_from<std::ranges::repeat_view<A, int>,
+                                       std::piecewise_construct_t,
+                                       std::tuple<C>,
+                                       std::tuple<int>>);
+static_assert(!std::constructible_from<std::ranges::repeat_view<A>,
+                                       std::piecewise_construct_t,
+                                       std::tuple<C>,
+                                       std::tuple<std::unreachable_sentinel_t>>);
+static_assert(!std::constructible_from<std::ranges::repeat_view<A, int>,
+                                       std::piecewise_construct_t,
+                                       std::tuple<int, int, int>,
+                                       std::tuple<int>>);
+static_assert(!std::constructible_from<std::ranges::repeat_view<A>,
+                                       std::piecewise_construct_t,
+                                       std::tuple<int, int, int>,
+                                       std::tuple<std::unreachable_sentinel_t>>);
+static_assert(
+    !std::constructible_from<std::ranges::repeat_view<A>, std::piecewise_construct_t, std::tuple<B>, std::tuple<int>>);
+
+constexpr bool test() {
+  {
+    std::ranges::repeat_view<A, int> rv(std::piecewise_construct, std::tuple{}, std::tuple{3});
+    assert(rv.size() == 3);
+    assert(rv[0].x == 111);
+    assert(rv[0].y == 222);
+    assert(rv.begin() + 3 == rv.end());
+  }
+  {
+    std::ranges::repeat_view<A> rv(std::piecewise_construct, std::tuple{}, std::tuple{std::unreachable_sentinel});
+    assert(rv[0].x == 111);
+    assert(rv[0].y == 222);
+    assert(rv.begin() + 300 != rv.end());
+  }
+  {
+    std::ranges::repeat_view<A, int> rv(std::piecewise_construct, std::tuple{1, 2}, std::tuple{3});
+    assert(rv.size() == 3);
+    assert(rv[0].x == 1);
+    assert(rv[0].y == 2);
+    assert(rv.begin() + 3 == rv.end());
+  }
+  {
+    std::ranges::repeat_view<A> rv(std::piecewise_construct, std::tuple{1, 2}, std::tuple{std::unreachable_sentinel});
+    assert(rv[0].x == 1);
+    assert(rv[0].y == 2);
+    assert(rv.begin() + 300 != rv.end());
+  }
+  {
+    std::ranges::repeat_view<A, int> rv(std::piecewise_construct, std::tuple{B{11}}, std::tuple{3});
+    assert(rv.size() == 3);
+    assert(rv[0].x == 11);
+    assert(rv[0].y == 12);
+    assert(rv.begin() + 3 == rv.end());
+  }
+  {
+    std::ranges::repeat_view<A> rv(std::piecewise_construct, std::tuple{B{10}}, std::tuple{std::unreachable_sentinel});
+    assert(rv[0].x == 10);
+    assert(rv[0].y == 11);
+    assert(rv.begin() + 300 != rv.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/ctor.value.bound.pass.cpp
new file mode 100644 (file)
index 0000000..733729f
--- /dev/null
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr explicit repeat_view(const T& value, Bound bound = Bound()) requires copy_constructible<T>;
+// constexpr explicit repeat_view(T&& value, Bound bound = Bound());
+
+#include <ranges>
+#include <cassert>
+#include <iterator>
+#include <type_traits>
+
+#include "MoveOnly.h"
+
+struct Empty {};
+
+// Test explicit
+static_assert(std::is_constructible_v<std::ranges::repeat_view<Empty>, const Empty&>);
+static_assert(std::is_constructible_v<std::ranges::repeat_view<Empty>, Empty&&>);
+static_assert(std::is_constructible_v<std::ranges::repeat_view<Empty, int>, const Empty&>);
+static_assert(std::is_constructible_v<std::ranges::repeat_view<Empty, int>, Empty&&>);
+
+static_assert(!std::is_convertible_v<const Empty&, std::ranges::repeat_view<Empty>>);
+static_assert(!std::is_convertible_v<Empty&&, std::ranges::repeat_view<Empty>>);
+static_assert(!std::is_convertible_v<const Empty&, std::ranges::repeat_view<Empty, int>>);
+static_assert(!std::is_convertible_v<Empty&&, std::ranges::repeat_view<Empty, int>>);
+
+static_assert(!std::is_constructible_v<std::ranges::repeat_view<MoveOnly>, const MoveOnly&>);
+static_assert(std::is_constructible_v<std::ranges::repeat_view<MoveOnly>, MoveOnly&&>);
+
+constexpr bool test() {
+  // Move && unbound && default argument
+  {
+    std::ranges::repeat_view<Empty> rv(Empty{});
+    assert(rv.begin() + 10 != rv.end());
+  }
+
+  // Move && unbound && user-provided argument
+  {
+    std::ranges::repeat_view<Empty> rv(Empty{}, std::unreachable_sentinel);
+    assert(rv.begin() + 10 != rv.end());
+  }
+
+  // Move && bound && default argument
+  {
+    std::ranges::repeat_view<Empty, int> rv(Empty{});
+    assert(rv.begin() == rv.end());
+  }
+
+  // Move && bound && user-provided argument
+  {
+    std::ranges::repeat_view<Empty, int> rv(Empty{}, 10);
+    assert(rv.begin() + 10 == rv.end());
+  }
+
+  // Copy && unbound && default argument
+  {
+    Empty e;
+    std::ranges::repeat_view<Empty> rv(e);
+    assert(rv.begin() + 10 != rv.end());
+  }
+
+  // Copy && unbound && user-provided argument
+  {
+    Empty e;
+    std::ranges::repeat_view<Empty> rv(e, std::unreachable_sentinel);
+    assert(rv.begin() + 10 != rv.end());
+  }
+
+  // Copy && bound && default argument
+  {
+    Empty e;
+    std::ranges::repeat_view<Empty, int> rv(e);
+    assert(rv.begin() == rv.end());
+  }
+
+  // Copy && bound && user-provided argument
+  {
+    Empty e;
+    std::ranges::repeat_view<Empty, int> rv(e, 10);
+    assert(rv.begin() + 10 == rv.end());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/end.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/end.pass.cpp
new file mode 100644 (file)
index 0000000..32ef9eb
--- /dev/null
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr unreachable_sentinel_t end() const noexcept;
+// constexpr iterator end() const requires (!same_as<Bound, unreachable_sentinel_t>);
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+#include <iterator>
+
+constexpr bool test() {
+  // bound
+  {
+    std::ranges::repeat_view<int, int> rv(0, 10);
+    assert(rv.begin() + 10 == rv.end());
+    std::same_as<std::ranges::iterator_t<decltype(rv)>> decltype(auto) iter = rv.end();
+    static_assert(std::same_as<decltype(*iter), const int&>);
+    for (const auto& i : rv) {
+      assert(i == 0);
+    }
+  }
+
+  // unbound
+  {
+    std::ranges::repeat_view<int> rv(0);
+    assert(rv.begin() + 10 != rv.end());
+    static_assert(std::same_as<decltype(rv.end()), std::unreachable_sentinel_t>);
+    static_assert(noexcept(rv.end()));
+    for (const auto& i : rv | std::views::take(10)) {
+      assert(i == 0);
+    }
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/compare.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/compare.pass.cpp
new file mode 100644 (file)
index 0000000..f7bfca6
--- /dev/null
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// repeat_view::<iterator>::operator{==,<=>}
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+
+constexpr bool test() {
+  // Test unbound
+  {
+    using R = std::ranges::repeat_view<int>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    std::ranges::repeat_view<int> r(42);
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    static_assert(std::same_as<decltype(iter1 == iter2), bool>);
+
+    assert(iter1 == iter1);
+    assert(!(iter1 == iter2));
+    assert(iter2 == iter2);
+
+    assert(!(iter1 < iter1));
+    assert(iter1 < iter2);
+    assert(!(iter2 < iter1));
+    assert(iter1 <= iter1);
+    assert(iter1 <= iter2);
+    assert(!(iter2 <= iter1));
+    assert(!(iter1 > iter1));
+    assert(!(iter1 > iter2));
+    assert(iter2 > iter1);
+    assert(iter1 >= iter1);
+    assert(!(iter1 >= iter2));
+    assert(iter2 >= iter1);
+    assert(iter1 == iter1);
+    assert(!(iter1 == iter2));
+    assert(iter2 == iter2);
+    assert(!(iter1 != iter1));
+    assert(iter1 != iter2);
+    assert(!(iter2 != iter2));
+
+    assert((iter1 <=> iter2) == std::strong_ordering::less);
+    assert((iter1 <=> iter1) == std::strong_ordering::equal);
+    assert((iter2 <=> iter1) == std::strong_ordering::greater);
+
+    static_assert(std::same_as<decltype(iter1 <=> iter2), std::strong_ordering>);
+  }
+
+  // Test bound
+  {
+    using R = std::ranges::repeat_view<int, int>;
+    static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
+
+    std::ranges::repeat_view<int, int> r(42, 10);
+    auto iter1 = r.begin();
+    auto iter2 = iter1 + 1;
+
+    static_assert(std::same_as<decltype(iter1 == iter2), bool>);
+
+    assert(iter1 == iter1);
+    assert(!(iter1 == iter2));
+    assert(iter2 == iter2);
+
+    assert(!(iter1 < iter1));
+    assert(iter1 < iter2);
+    assert(!(iter2 < iter1));
+    assert(iter1 <= iter1);
+    assert(iter1 <= iter2);
+    assert(!(iter2 <= iter1));
+    assert(!(iter1 > iter1));
+    assert(!(iter1 > iter2));
+    assert(iter2 > iter1);
+    assert(iter1 >= iter1);
+    assert(!(iter1 >= iter2));
+    assert(iter2 >= iter1);
+    assert(iter1 == iter1);
+    assert(!(iter1 == iter2));
+    assert(iter2 == iter2);
+    assert(!(iter1 != iter1));
+    assert(iter1 != iter2);
+    assert(!(iter2 != iter2));
+
+    assert((iter1 <=> iter2) == std::strong_ordering::less);
+    assert((iter1 <=> iter1) == std::strong_ordering::equal);
+    assert((iter2 <=> iter1) == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/ctor.default.pass.cpp
new file mode 100644 (file)
index 0000000..fa69145
--- /dev/null
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// iterator() = default;
+
+#include <ranges>
+#include <type_traits>
+
+constexpr bool test() {
+  using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+  static_assert(std::is_default_constructible_v<Iter>);
+  [[maybe_unused]] Iter iter;
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/decrement.pass.cpp
new file mode 100644 (file)
index 0000000..aa90f84
--- /dev/null
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr iterator& operator--();
+// constexpr iterator operator--(int);
+
+#include <ranges>
+#include <cassert>
+
+constexpr bool test() {
+  using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+  std::ranges::repeat_view<int> rv(10);
+  auto iter = rv.begin() + 10;
+
+  assert(iter-- == rv.begin() + 10);
+  assert(--iter == rv.begin() + 8);
+
+  static_assert(std::same_as<decltype(iter--), Iter>);
+  static_assert(std::same_as<decltype(--iter), Iter&>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/increment.pass.cpp
new file mode 100644 (file)
index 0000000..8632300
--- /dev/null
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr iterator& operator++();
+// constexpr void operator++(int);
+
+#include <ranges>
+#include <concepts>
+#include <cassert>
+
+constexpr bool test() {
+  using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+  std::ranges::repeat_view<int> rv(10);
+  using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+  auto iter  = rv.begin();
+
+  assert(iter++ == rv.begin());
+  assert(++iter == rv.begin() + 2);
+
+  static_assert(std::same_as<decltype(iter++), Iter>);
+  static_assert(std::same_as<decltype(++iter), Iter&>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/member_typedefs.compile.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/member_typedefs.compile.pass.cpp
new file mode 100644 (file)
index 0000000..2ef09a6
--- /dev/null
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// Test iterator category and iterator concepts.
+
+// using index-type = conditional_t<same_as<Bound, unreachable_sentinel_t>, ptrdiff_t, Bound>;
+// using iterator_concept = random_access_iterator_tag;
+// using iterator_category = random_access_iterator_tag;
+// using value_type = T;
+// using difference_type = see below:
+// If is-signed-integer-like<index-type> is true, the member typedef-name difference_type denotes
+// index-type. Otherwise, it denotes IOTA-DIFF-T(index-type).
+
+#include <cassert>
+#include <concepts>
+#include <cstdint>
+#include <ranges>
+#include <type_traits>
+
+constexpr bool test() {
+  // unbound
+  {
+    using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+    static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
+    static_assert(std::same_as<Iter::iterator_category, std::random_access_iterator_tag>);
+    static_assert(std::same_as<Iter::value_type, int>);
+    static_assert(std::same_as<Iter::difference_type, ptrdiff_t>);
+    static_assert(std::is_signed_v<Iter::difference_type>);
+  }
+
+  // bound
+  {
+    {
+      using Iter = std::ranges::iterator_t<const std::ranges::repeat_view<int, std::int8_t>>;
+      static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::iterator_category, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::value_type, int>);
+      static_assert(std::is_signed_v<Iter::difference_type>);
+      static_assert(sizeof(Iter::difference_type) == sizeof(std::int8_t));
+    }
+
+    {
+      using Iter = std::ranges::iterator_t<const std::ranges::repeat_view<int, std::uint8_t>>;
+      static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::iterator_category, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::value_type, int>);
+      static_assert(std::is_signed_v<Iter::difference_type>);
+      static_assert(sizeof(Iter::difference_type) > sizeof(std::uint8_t));
+    }
+
+    {
+      using Iter = std::ranges::iterator_t<const std::ranges::repeat_view<int, std::int16_t>>;
+      static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::iterator_category, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::value_type, int>);
+      static_assert(std::is_signed_v<Iter::difference_type>);
+      static_assert(sizeof(Iter::difference_type) == sizeof(std::int16_t));
+    }
+
+    {
+      using Iter = std::ranges::iterator_t<const std::ranges::repeat_view<int, std::uint16_t>>;
+      static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::iterator_category, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::value_type, int>);
+      static_assert(std::is_signed_v<Iter::difference_type>);
+      static_assert(sizeof(Iter::difference_type) > sizeof(std::uint16_t));
+    }
+
+    {
+      using Iter = std::ranges::iterator_t<const std::ranges::repeat_view<int, std::int32_t>>;
+      static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::iterator_category, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::value_type, int>);
+      static_assert(std::is_signed_v<Iter::difference_type>);
+      static_assert(sizeof(Iter::difference_type) == sizeof(std::int32_t));
+    }
+
+    {
+      using Iter = std::ranges::iterator_t<const std::ranges::repeat_view<int, std::uint32_t>>;
+      static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::iterator_category, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::value_type, int>);
+      static_assert(std::is_signed_v<Iter::difference_type>);
+      static_assert(sizeof(Iter::difference_type) > sizeof(std::uint32_t));
+    }
+
+    {
+      using Iter = std::ranges::iterator_t<const std::ranges::repeat_view<int, std::int64_t>>;
+      static_assert(std::same_as<Iter::iterator_concept, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::iterator_category, std::random_access_iterator_tag>);
+      static_assert(std::same_as<Iter::value_type, int>);
+      static_assert(std::is_signed_v<Iter::difference_type>);
+      static_assert(sizeof(Iter::difference_type) == sizeof(std::int64_t));
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/minus.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/minus.pass.cpp
new file mode 100644 (file)
index 0000000..3a37374
--- /dev/null
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// friend constexpr iterator operator-(iterator i, difference_type n);
+// friend constexpr difference_type operator-(const iterator& x, const iterator& y);
+
+#include <ranges>
+#include <cassert>
+#include <cstdint>
+#include <concepts>
+
+constexpr bool test() {
+  // <iterator> - difference_type
+  {
+    using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+    std::ranges::repeat_view<int> v(0);
+    Iter iter = v.begin() + 10;
+    assert(iter - 5 == v.begin() + 5);
+    static_assert(std::same_as<decltype(iter - 5), Iter>);
+  }
+
+  // <iterator> - <iterator>
+  {
+    // unbound
+    {
+      std::ranges::repeat_view<int> v(0);
+      auto iter1 = v.begin() + 10;
+      auto iter2 = v.begin() + 5;
+      assert(iter1 - iter2 == 5);
+      static_assert(std::same_as<decltype(iter1 - iter2), ptrdiff_t>);
+    }
+
+    // bound && signed bound sentinel
+    {
+      std::ranges::repeat_view<int, int> v(0, 20);
+      auto iter1 = v.begin() + 10;
+      auto iter2 = v.begin() + 5;
+      assert(iter1 - iter2 == 5);
+      static_assert(std::same_as<decltype(iter1 - iter2), int>);
+    }
+
+    // bound && unsigned bound sentinel
+    {
+      std::ranges::repeat_view<int, unsigned> v(0, 20);
+      auto iter1 = v.begin() + 10;
+      auto iter2 = v.begin() + 5;
+      assert(iter1 - iter2 == 5);
+      static_assert(sizeof(decltype(iter1 - iter2)) > sizeof(unsigned));
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/minus_eq.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/minus_eq.pass.cpp
new file mode 100644 (file)
index 0000000..84310e4
--- /dev/null
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr iterator& operator-=(difference_type n);
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+
+constexpr bool test() {
+  using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+  std::ranges::repeat_view<int> v(10);
+  auto iter = v.begin() + 10;
+  iter -= 5;
+  assert(iter == v.begin() + 5);
+
+  static_assert(std::same_as<decltype(iter -= 5), Iter&>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/plus.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/plus.pass.cpp
new file mode 100644 (file)
index 0000000..5b484a8
--- /dev/null
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// friend constexpr iterator operator+(iterator i, difference_type n);
+// friend constexpr iterator operator+(difference_type n, iterator i);
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+
+constexpr bool test() {
+  std::ranges::repeat_view<int> v(10);
+  using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+  auto iter  = v.begin();
+  assert(iter + 5 == v.begin() + 5);
+  assert(5 + iter == v.begin() + 5);
+  assert(2 + iter == v.begin() + 2);
+  assert(3 + iter == v.begin() + 3);
+
+  static_assert(std::same_as<decltype(iter + 5), Iter>);
+  static_assert(std::same_as<decltype(5 + iter), Iter>);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/plus_eq.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/plus_eq.pass.cpp
new file mode 100644 (file)
index 0000000..a4696d7
--- /dev/null
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr iterator& operator+=(difference_type n);
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+
+constexpr bool test() {
+  std::ranges::repeat_view<int> v(10);
+  using Iter = std::ranges::iterator_t<std::ranges::repeat_view<int>>;
+  auto iter1 = v.begin() + 10;
+  auto iter2 = v.begin() + 10;
+  assert(iter1 == iter2);
+  iter1 += 5;
+  assert(iter1 != iter2);
+  assert(iter1 == iter2 + 5);
+
+  static_assert(std::same_as<decltype(iter2 += 5), Iter&>);
+  assert(std::addressof(iter2) == std::addressof(iter2 += 5));
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/star.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/star.pass.cpp
new file mode 100644 (file)
index 0000000..214ee62
--- /dev/null
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr const W & operator*() const noexcept;
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+
+constexpr bool test() {
+  // unbound
+  {
+    std::ranges::repeat_view<int> v(31);
+    auto iter = v.begin();
+
+    const int& val = *iter;
+    for (int i = 0; i < 100; ++i, ++iter) {
+      assert(*iter == 31);
+      assert(&*iter == &val);
+    }
+
+    static_assert(noexcept(*iter));
+    static_assert(std::same_as<decltype(*iter), const int&>);
+  }
+
+  // bound && one element
+  {
+    std::ranges::repeat_view<int, int> v(31, 1);
+    auto iter = v.begin();
+    assert(*iter == 31);
+    static_assert(noexcept(*iter));
+    static_assert(std::same_as<decltype(*iter), const int&>);
+  }
+
+  // bound && several elements
+  {
+    std::ranges::repeat_view<int, int> v(31, 100);
+    auto iter = v.begin();
+
+    const int& val = *iter;
+    for (int i = 0; i < 100; ++i, ++iter) {
+      assert(*iter == 31);
+      assert(&*iter == &val);
+    }
+  }
+
+  // bound && foreach
+  {
+    for (const auto& val : std::views::repeat(31, 100))
+      assert(val == 31);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/subscript.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/iterator/subscript.pass.cpp
new file mode 100644 (file)
index 0000000..3075168
--- /dev/null
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr const W & operator[](difference_type n) const noexcept;
+
+#include <ranges>
+#include <cassert>
+#include <concepts>
+#include <algorithm>
+
+constexpr bool test() {
+  // unbound
+  {
+    std::ranges::repeat_view<int> v(31);
+    auto iter = v.begin();
+    assert(std::ranges::all_of(std::views::iota(0, 100), [&v](int i) { return v[i] == 31; }));
+
+    static_assert(noexcept(iter[0]));
+    static_assert(std::same_as<decltype(iter[0]), const int&>);
+  }
+
+  // bound
+  {
+    std::ranges::repeat_view<int, int> v(32);
+    auto iter = v.begin();
+    assert(std::ranges::all_of(v, [](int i) { return i == 32; }));
+    static_assert(noexcept(iter[0]));
+    static_assert(std::same_as<decltype(iter[0]), const int&>);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/size.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/size.pass.cpp
new file mode 100644 (file)
index 0000000..097dc94
--- /dev/null
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// constexpr auto size() const requires (!same_as<Bound, unreachable_sentinel_t>);
+
+#include <ranges>
+#include <numeric>
+#include <concepts>
+#include <cassert>
+#include <iterator>
+
+template <class T>
+concept has_size = requires(T&& view) {
+  { std::forward<T>(view).size() };
+};
+
+static_assert(has_size<std::ranges::repeat_view<int, int>>);
+static_assert(!has_size<std::ranges::repeat_view<int>>);
+static_assert(!has_size<std::ranges::repeat_view<int, std::unreachable_sentinel_t>>);
+
+constexpr bool test() {
+  {
+    std::ranges::repeat_view<int, int> rv(10, 20);
+    assert(rv.size() == 20);
+  }
+
+  {
+    std::ranges::repeat_view<int, int> rv(10, std::numeric_limits<int>::max());
+    assert(rv.size() == std::numeric_limits<int>::max());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp b/libcxx/test/std/ranges/range.factories/range.repeat.view/views_repeat.pass.cpp
new file mode 100644 (file)
index 0000000..c174850
--- /dev/null
@@ -0,0 +1,122 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// template <class T>
+// views::repeat(T &&) requires constructible_from<ranges::repeat_view<T>, T>;
+
+// templaye <class T, class Bound>
+// views::repeat(T &&, Bound &&) requires constructible_from<ranges::repeat_view<T, Bound>, T, Bound>;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+
+#include "MoveOnly.h"
+
+struct NonCopyable {
+  NonCopyable(NonCopyable&) = delete;
+};
+
+struct NonDefaultCtor {
+  NonDefaultCtor(int) {}
+};
+
+struct Empty {};
+
+struct LessThan3 {
+  constexpr bool operator()(int i) const { return i < 3; }
+};
+
+struct EqualTo33 {
+  constexpr bool operator()(int i) const { return i == 33; }
+};
+
+struct Add3 {
+  constexpr int operator()(int i) const { return i + 3; }
+};
+
+// Tp is_object
+static_assert(std::is_invocable_v<decltype(std::views::repeat), int>);
+static_assert(!std::is_invocable_v<decltype(std::views::repeat), void>);
+
+// _Bound is semiregular, integer like or std::unreachable_sentinel_t
+static_assert(!std::is_invocable_v<decltype(std::views::repeat), int, Empty>);
+static_assert(!std::is_invocable_v<decltype(std::views::repeat), int, NonCopyable>);
+static_assert(!std::is_invocable_v<decltype(std::views::repeat), int, NonDefaultCtor>);
+static_assert(std::is_invocable_v<decltype(std::views::repeat), int, std::unreachable_sentinel_t>);
+
+// Tp is copy_constructible
+static_assert(!std::is_invocable_v<decltype(std::views::repeat), NonCopyable>);
+
+// Tp is move_constructible
+static_assert(std::is_invocable_v<decltype(std::views::repeat), MoveOnly>);
+
+constexpr bool test() {
+  assert(*std::views::repeat(33).begin() == 33);
+  assert(*std::views::repeat(33, 10).begin() == 33);
+  static_assert(std::same_as<decltype(std::views::repeat(42)), std::ranges::repeat_view<int>>);
+  static_assert(std::same_as<decltype(std::views::repeat(42, 3)), std::ranges::repeat_view<int, int>>);
+  static_assert(std::same_as<decltype(std::views::repeat), decltype(std::ranges::views::repeat)>);
+
+  // unbound && drop_view
+  {
+    auto r = std::views::repeat(33) | std::views::drop(3);
+    static_assert(!std::ranges::sized_range<decltype(r)>);
+    assert(*r.begin() == 33);
+  }
+
+  // bound && drop_view
+  {
+    auto r = std::views::repeat(33, 8) | std::views::drop(3);
+    static_assert(std::ranges::sized_range<decltype(r)>);
+    assert(*r.begin() == 33);
+    assert(r.size() == 5);
+  }
+
+  // unbound && take_view
+  {
+    auto r = std::views::repeat(33) | std::views::take(3);
+    static_assert(std::ranges::sized_range<decltype(r)>);
+    assert(*r.begin() == 33);
+    assert(r.size() == 3);
+  }
+
+  // bound && take_view
+  {
+    auto r = std::views::repeat(33, 8) | std::views::take(3);
+    static_assert(std::ranges::sized_range<decltype(r)>);
+    assert(*r.begin() == 33);
+    assert(r.size() == 3);
+  }
+
+  // bound && transform_view
+  {
+    auto r = std::views::repeat(33, 8) | std::views::transform(Add3{});
+    assert(*r.begin() == 36);
+    assert(r.size() == 8);
+  }
+
+  // unbound && transform_view
+  {
+    auto r = std::views::repeat(33) | std::views::transform(Add3{});
+    assert(*r.begin() == 36);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
index 67545f2..90a0646 100755 (executable)
@@ -817,6 +817,11 @@ feature_test_macros = [
             "unimplemented": True,
         },
         {
+            "name": "__cpp_lib_ranges_repeat",
+            "values": { "c++23": 202207},
+            "headers": ["ranges"],
+        },
+        {
             "name": "__cpp_lib_ranges_slide",
             "values": {"c++23": 202202},
             "headers": ["ranges"],