[libcxx-commits] [libcxx] 065dc48 - [libc++][ranges] Implement P2443R1: `views::chunk_by`
Konstantin Varlamov via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Sep 5 16:20:01 PDT 2023
Author: Jakub Mazurkiewicz
Date: 2023-09-05T16:19:49-07:00
New Revision: 065dc485bd4b33f6110993cc4b353f1e7c36cac3
URL: https://github.com/llvm/llvm-project/commit/065dc485bd4b33f6110993cc4b353f1e7c36cac3
DIFF: https://github.com/llvm/llvm-project/commit/065dc485bd4b33f6110993cc4b353f1e7c36cac3.diff
LOG: [libc++][ranges] Implement P2443R1: `views::chunk_by`
This patch implements https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2443r1.html (`views::chunk_by`).
Reviewed By: #libc, var-const
Differential Revision: https://reviews.llvm.org/D144767
Added:
libcxx/include/__ranges/chunk_by_view.h
libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/adaptor.nodiscard.verify.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.begin.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-next.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-prev.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.deref.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.increment.pass.cpp
libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/types.h
libcxx/test/std/ranges/range.adaptors/range.chunk.by/adaptor.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/constraints.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctad.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.view_pred.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/pred.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/compare.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/decrement.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/deref.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/increment.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/types.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.chunk.by/types.h
Modified:
libcxx/docs/FeatureTestMacroTable.rst
libcxx/docs/ReleaseNotes/18.rst
libcxx/docs/Status/Cxx23Papers.csv
libcxx/docs/Status/RangesViews.csv
libcxx/include/CMakeLists.txt
libcxx/include/__algorithm/ranges_adjacent_find.h
libcxx/include/__ranges/movable_box.h
libcxx/include/module.modulemap.in
libcxx/include/ranges
libcxx/include/version
libcxx/modules/std/ranges.inc
libcxx/test/libcxx/transitive_includes/cxx03.csv
libcxx/test/libcxx/transitive_includes/cxx11.csv
libcxx/test/libcxx/transitive_includes/cxx14.csv
libcxx/test/libcxx/transitive_includes/cxx17.csv
libcxx/test/libcxx/transitive_includes/cxx20.csv
libcxx/test/libcxx/transitive_includes/cxx23.csv
libcxx/test/libcxx/transitive_includes/cxx26.csv
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/utils/generate_feature_test_macro_components.py
Removed:
################################################################################
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 63abcbb886ab222..e1b4172b22c53da 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -346,7 +346,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_ranges_chunk`` *unimplemented*
--------------------------------------------------- -----------------
- ``__cpp_lib_ranges_chunk_by`` *unimplemented*
+ ``__cpp_lib_ranges_chunk_by`` ``202202L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_iota`` *unimplemented*
--------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 1d08693adb02e8b..b10c8fa78c2270d 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -41,7 +41,7 @@ Implemented Papers
- P2497R0 - Testing for success or failure of ``<charconv>`` functions
- P2697R1 - Interfacing ``bitset`` with ``string_view``
-
+- P2443R1 - ``views::chunk_by``
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 8356a986681e15b..ba26cebbc1e8a67 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -49,7 +49,7 @@
"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","","","|ranges|"
"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
-"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","","","|ranges|"
+"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
"","","","","","",""
"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|In progress| [#note-P0009R18]_|",""
"`P0429R9 <https://wg21.link/P0429R9>`__","LWG","A Standard ``flat_map``","July 2022","",""
diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv
index 0c1bbb8e842c2db..d7ae06932fb0c55 100644
--- a/libcxx/docs/Status/RangesViews.csv
+++ b/libcxx/docs/Status/RangesViews.csv
@@ -31,7 +31,7 @@ C++23,`adjacent_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not
C++23,`join_with <https://wg21.link/P2441R2>`_,Unassigned,No patch yet,Not started
C++23,`slide <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
C++23,`chunk <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
-C++23,`chunk_by <https://wg21.link/P2443R1>`_,Unassigned,No patch yet,Not started
+C++23,`chunk_by <https://wg21.link/P2443R1>`_,Jakub Mazurkiewicz,`D144767 <https://llvm.org/D144767>`,✅
C++23,`as_const <https://wg21.link/P2278R4>`_,Unassigned,No patch yet,Not started
C++23,`as_rvalue <https://wg21.link/P2446R2>`_,Nikolas Klauser,`D137637 <https://llvm.org/D137637>`_,✅
C++23,`stride <https://wg21.link/P1899R3>`_,Hristo Hristov and Will Hawkins,`D156924 <https://llvm.org/D156924>`_,In Progress
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 77a7269121ec142..4fc4d23b510104e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -602,6 +602,7 @@ set(files
__ranges/access.h
__ranges/all.h
__ranges/as_rvalue_view.h
+ __ranges/chunk_by_view.h
__ranges/common_view.h
__ranges/concepts.h
__ranges/container_compatible_range.h
diff --git a/libcxx/include/__algorithm/ranges_adjacent_find.h b/libcxx/include/__algorithm/ranges_adjacent_find.h
index 5f33192666ae907..a10b04167ede697 100644
--- a/libcxx/include/__algorithm/ranges_adjacent_find.h
+++ b/libcxx/include/__algorithm/ranges_adjacent_find.h
@@ -24,6 +24,9 @@
# pragma GCC system_header
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
#if _LIBCPP_STD_VER >= 20
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -75,4 +78,6 @@ _LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_STD_VER >= 20
+_LIBCPP_POP_MACROS
+
#endif // _LIBCPP___ALGORITHM_RANGES_ADJACENT_FIND_H
diff --git a/libcxx/include/__ranges/chunk_by_view.h b/libcxx/include/__ranges/chunk_by_view.h
new file mode 100644
index 000000000000000..cfb149b443571e8
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_by_view.h
@@ -0,0 +1,230 @@
+// -*- 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_CHUNK_BY_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_BY_VIEW_H
+
+#include <__algorithm/ranges_adjacent_find.h>
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__functional/invoke.h>
+#include <__functional/not_fn.h>
+#include <__functional/reference_wrapper.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
+#include <__iterator/prev.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/movable_box.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/reverse_view.h>
+#include <__ranges/subrange.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <forward_range _View, indirect_binary_predicate<iterator_t<_View>, iterator_t<_View>> _Pred>
+ requires view<_View> && is_object_v<_Pred>
+class chunk_by_view : public view_interface<chunk_by_view<_View, _Pred>> {
+ _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+ _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Pred> __pred_;
+
+ // We cache the result of begin() to allow providing an amortized O(1).
+ using _Cache = __non_propagating_cache<iterator_t<_View>>;
+ _Cache __cached_begin_;
+
+ class __iterator;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> __find_next(iterator_t<_View> __current) {
+ _LIBCPP_ASSERT_UNCATEGORIZED(
+ __pred_.__has_value(), "Trying to call __find_next() on a chunk_by_view that does not have a valid predicate.");
+
+ return ranges::next(ranges::adjacent_find(__current, ranges::end(__base_), std::not_fn(std::ref(*__pred_))),
+ 1,
+ ranges::end(__base_));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> __find_prev(iterator_t<_View> __current)
+ requires bidirectional_range<_View>
+ {
+ _LIBCPP_ASSERT_UNCATEGORIZED(
+ __current != ranges::begin(__base_), "Trying to call __find_prev() on a begin iterator.");
+ _LIBCPP_ASSERT_UNCATEGORIZED(
+ __pred_.__has_value(), "Trying to call __find_prev() on a chunk_by_view that does not have a valid predicate.");
+
+ auto __first = ranges::begin(__base_);
+ reverse_view __reversed{subrange{__first, __current}};
+ auto __reversed_pred = [this]<class _Tp, class _Up>(_Tp&& __x, _Up&& __y) {
+ return !std::invoke(*__pred_, std::forward<_Up>(__y), std::forward<_Tp>(__x));
+ };
+ return ranges::prev(ranges::adjacent_find(__reversed, __reversed_pred).base(), 1, std::move(__first));
+ }
+
+public:
+ _LIBCPP_HIDE_FROM_ABI chunk_by_view()
+ requires default_initializable<_View> && default_initializable<_Pred>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_by_view(_View __base, _Pred __pred)
+ : __base_(std::move(__base)), __pred_(in_place, std::move(__pred)) {}
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+ requires copy_constructible<_View>
+ {
+ return __base_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr const _Pred& pred() const { return *__pred_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() {
+ _LIBCPP_ASSERT_UNCATEGORIZED(
+ __pred_.__has_value(), "Trying to call begin() on a chunk_by_view that does not have a valid predicate.");
+
+ auto __first = ranges::begin(__base_);
+ if (!__cached_begin_.__has_value()) {
+ __cached_begin_.__emplace(__find_next(__first));
+ }
+ return {*this, std::move(__first), *__cached_begin_};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+ if constexpr (common_range<_View>) {
+ return __iterator{*this, ranges::end(__base_), ranges::end(__base_)};
+ } else {
+ return default_sentinel;
+ }
+ }
+};
+
+template <class _Range, class _Pred>
+chunk_by_view(_Range&&, _Pred) -> chunk_by_view<views::all_t<_Range>, _Pred>;
+
+template <forward_range _View, indirect_binary_predicate<iterator_t<_View>, iterator_t<_View>> _Pred>
+ requires view<_View> && is_object_v<_Pred>
+class chunk_by_view<_View, _Pred>::__iterator {
+ friend chunk_by_view;
+
+ chunk_by_view* __parent_ = nullptr;
+ _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>();
+ _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __next_ = iterator_t<_View>();
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+ chunk_by_view& __parent, iterator_t<_View> __current, iterator_t<_View> __next)
+ : __parent_(std::addressof(__parent)), __current_(__current), __next_(__next) {}
+
+public:
+ using value_type = subrange<iterator_t<_View>>;
+ using
diff erence_type = range_
diff erence_t<_View>;
+ using iterator_category = input_iterator_tag;
+ using iterator_concept = conditional_t<bidirectional_range<_View>, bidirectional_iterator_tag, forward_iterator_tag>;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__current_ != __next_, "Trying to dereference past-the-end chunk_by_view iterator.");
+ return {__current_, __next_};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__current_ != __next_, "Trying to increment past end chunk_by_view iterator.");
+ __current_ = __next_;
+ __next_ = __parent_->__find_next(__current_);
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+ requires bidirectional_range<_View>
+ {
+ __next_ = __current_;
+ __current_ = __parent_->__find_prev(__next_);
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+ requires bidirectional_range<_View>
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ _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 bool operator==(const __iterator& __x, default_sentinel_t) {
+ return __x.__current_ == __x.__next_;
+ }
+};
+
+namespace views {
+namespace __chunk_by {
+struct __fn {
+ template <class _Range, class _Pred>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pred&& __pred) const
+ noexcept(noexcept(/**/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))))
+ -> decltype(/*--*/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))) {
+ return /*-------------*/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred));
+ }
+
+ template <class _Pred>
+ requires constructible_from<decay_t<_Pred>, _Pred>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pred&& __pred) const
+ noexcept(is_nothrow_constructible_v<decay_t<_Pred>, _Pred>) {
+ return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pred>(__pred)));
+ }
+};
+} // namespace __chunk_by
+
+inline namespace __cpo {
+inline constexpr auto chunk_by = __chunk_by::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_BY_VIEW_H
diff --git a/libcxx/include/__ranges/movable_box.h b/libcxx/include/__ranges/movable_box.h
index 8b3716a06c5b7d6..6615533d374349e 100644
--- a/libcxx/include/__ranges/movable_box.h
+++ b/libcxx/include/__ranges/movable_box.h
@@ -26,6 +26,9 @@
# pragma GCC system_header
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 20
@@ -203,4 +206,6 @@ class __movable_box<_Tp> {
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // _LIBCPP___RANGES_MOVABLE_BOX_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3b85b6a91a21d87..e95f50dcbf05373 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1659,6 +1659,7 @@ module std_private_ranges_all [system] {
export std_private_ranges_owning_view
}
module std_private_ranges_as_rvalue_view [system] { header "__ranges/as_rvalue_view.h" }
+module std_private_ranges_chunk_by_view [system] { header "__ranages/chunk_by_view.h" }
module std_private_ranges_common_view [system] { header "__ranges/common_view.h" }
module std_private_ranges_concepts [system] {
header "__ranges/concepts.h"
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 523f7cdcb360fa3..db592fd5cb360c4 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -320,17 +320,24 @@ namespace std::ranges {
class zip_view; // C++23
template<class... Views>
- inline constexpr bool enable_borrowed_range<zip_view<Views...>> = // C++23
+ inline constexpr bool enable_borrowed_range<zip_view<Views...>> = // C++23
(enable_borrowed_range<Views> && ...);
- namespace views { inline constexpr unspecified zip = unspecified; } // C++23
+ namespace views { inline constexpr unspecified zip = unspecified; } // C++23
// [range.as.rvalue]
template <view V>
requires input_range<V>
- class as_rvalue_view; // since C++23
+ class as_rvalue_view; // C++23
- namespace views { inline constexpr unspecified as_rvalue ) unspecified; } // since C++23
+ namespace views { inline constexpr unspecified as_rvalue ) unspecified; } // C++23
+
+ [range.chunk.by]
+ template<forward_range V, indirect_binary_predicate<iterator_t<V>, iterator_t<V>> Pred>
+ requires view<V> && is_object_v<Pred>
+ class chunk_by_view; // C++23
+
+ namespace views { inline constexpr unspecified chunk_by = unspecified; } // C++23
}
namespace std {
@@ -373,6 +380,7 @@ namespace std {
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/as_rvalue_view.h>
+#include <__ranges/chunk_by_view.h>
#include <__ranges/common_view.h>
#include <__ranges/concepts.h>
#include <__ranges/counted.h>
diff --git a/libcxx/include/version b/libcxx/include/version
index db6da2e0a8bd151..e5a995366a7aa48 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -435,7 +435,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
// # define __cpp_lib_print 202207L
# define __cpp_lib_ranges_as_rvalue 202207L
// # define __cpp_lib_ranges_chunk 202202L
-// # define __cpp_lib_ranges_chunk_by 202202L
+# 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
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 67b0f02ad9d980f..aeba676d3f43adc 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -309,6 +309,7 @@ export namespace std {
namespace views {
using std::ranges::views::slide;
}
+#endif
// [range.chunk.by], chunk by view
using std::ranges::chunk_by_view;
@@ -317,6 +318,7 @@ export namespace std {
using std::ranges::views::chunk_by;
}
+#if 0
// [range.stride], stride view
using std::ranges::stride_view;
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/adaptor.nodiscard.verify.cpp
new file mode 100644
index 000000000000000..14b3385724050de
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/adaptor.nodiscard.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, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::views::chunk_by is marked as [[nodiscard]].
+
+#include <functional>
+#include <ranges>
+
+void test() {
+ int range[] = {1, 2, 3, 0, 1, 2};
+ std::ranges::less_equal pred;
+
+ std::views::chunk_by(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::chunk_by(range, pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ range | std::views::chunk_by(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::all | std::views::chunk_by(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.begin.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.begin.pass.cpp
new file mode 100644
index 000000000000000..0ba4745be082677
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.begin.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-exceptions
+// UNSUPPORTED: !libcpp-hardening-mode=debug
+// XFAIL: availability-verbose_abort-missing
+
+// <ranges>
+
+// Call begin() on chunk_by_view with empty predicate
+
+#include <ranges>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main(int, char**) {
+ int input[] = {1, 2, 3};
+ auto view1 = std::views::chunk_by(input, ThrowOnCopyPred{});
+ auto view2 = std::views::chunk_by(input, ThrowOnCopyPred{});
+ try {
+ view1 = view2;
+ } catch (...) {
+ }
+ TEST_LIBCPP_ASSERT_FAILURE(
+ view1.begin(), "Trying to call begin() on a chunk_by_view that does not have a valid predicate.");
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-next.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-next.pass.cpp
new file mode 100644
index 000000000000000..9e5536bf51be2bb
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-next.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-exceptions
+// UNSUPPORTED: !libcpp-hardening-mode=debug
+// XFAIL: availability-verbose_abort-missing
+
+// <ranges>
+
+// Call find-next() on chunk_by_view with empty predicate
+
+#include <ranges>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main(int, char**) {
+ int input[] = {1, 2, 3};
+ // This is the easiest way to get '__find_next' to fail. If we used default constructed view here,
+ // then begin() would fail instead of __find_next.
+ auto view1 = std::views::chunk_by(input, ThrowOnCopyPred{});
+ auto view2 = std::views::chunk_by(input, ThrowOnCopyPred{});
+ auto it = view1.begin();
+ try {
+ view1 = view2;
+ } catch (...) {
+ }
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ++it, "Trying to call __find_next() on a chunk_by_view that does not have a valid predicate.");
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-prev.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-prev.pass.cpp
new file mode 100644
index 000000000000000..4f746cfec2f1a49
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-prev.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-exceptions
+// UNSUPPORTED: !libcpp-hardening-mode=debug
+// XFAIL: availability-verbose_abort-missing
+
+// <ranges>
+
+// Call find-prev() on chunk_by_view with begin iterator
+// Call find-prev() on chunk_by_view with empty predicate
+
+#include <functional>
+#include <ranges>
+
+#include "check_assertion.h"
+#include "types.h"
+
+int main(int, char**) {
+ int input[] = {1, 1, 2, 2};
+
+ { // Call find-prev() on chunk_by_view with begin iterator
+ auto view = std::views::chunk_by(input, std::equal_to{});
+ auto it = view.begin();
+ TEST_LIBCPP_ASSERT_FAILURE(--it, "Trying to call __find_prev() on a begin iterator.");
+ }
+
+ { // Call find-prev() on chunk_by_view with empty predicate
+ auto view1 = std::views::chunk_by(input, ThrowOnCopyPred{});
+ auto view2 = std::views::chunk_by(input, ThrowOnCopyPred{});
+ auto it = view1.begin();
+ ++it;
+ try {
+ view1 = view2;
+ } catch (...) {
+ }
+ TEST_LIBCPP_ASSERT_FAILURE(
+ --it, "Trying to call __find_prev() on a chunk_by_view that does not have a valid predicate.");
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.deref.pass.cpp
new file mode 100644
index 000000000000000..80c08efc56c05e0
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.deref.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: !libcpp-hardening-mode=debug
+// XFAIL: availability-verbose_abort-missing
+
+// <ranges>
+
+// Dereference past end chunk_by_view iterator
+
+#include <functional>
+#include <ranges>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ int input[] = {1, 2, 3};
+ auto view = std::views::chunk_by(input, std::less{});
+ auto it = view.begin();
+ ++it;
+ TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_by_view iterator.");
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.increment.pass.cpp
new file mode 100644
index 000000000000000..7381ad288629399
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.increment.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: !libcpp-hardening-mode=debug
+// XFAIL: availability-verbose_abort-missing
+
+// <ranges>
+
+// Increment past end chunk_by_view iterator
+
+#include <functional>
+#include <ranges>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ int input[] = {1, 2, 3};
+ auto view = std::views::chunk_by(input, std::less{});
+ auto it = view.begin();
+ ++it;
+ TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past end chunk_by_view iterator.");
+ return 0;
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/types.h
new file mode 100644
index 000000000000000..88fbcefab4d7410
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/types.h
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H
+#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H
+
+struct ThrowOnCopyPred {
+ ThrowOnCopyPred() = default;
+ ThrowOnCopyPred(const ThrowOnCopyPred&) { throw 0; }
+ ThrowOnCopyPred& operator=(const ThrowOnCopyPred&) = delete;
+
+ ThrowOnCopyPred(ThrowOnCopyPred&&) = default;
+ ThrowOnCopyPred& operator=(ThrowOnCopyPred&&) = default;
+
+ bool operator()(int x, int y) const { return x != y; }
+};
+
+#endif // TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 02976e87092e4be..7d7696fcd4f8c03 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -687,11 +687,11 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
+ranges new
ranges optional
ranges span
ranges tuple
ranges type_traits
-ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index e51c9be04a26cd6..63eb7cc18c54c11 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -692,11 +692,11 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
+ranges new
ranges optional
ranges span
ranges tuple
ranges type_traits
-ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 880b97a61deb918..b1e05b70a3a45cb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -694,11 +694,11 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
+ranges new
ranges optional
ranges span
ranges tuple
ranges type_traits
-ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 880b97a61deb918..b1e05b70a3a45cb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -694,11 +694,11 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
+ranges new
ranges optional
ranges span
ranges tuple
ranges type_traits
-ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index b04506293513113..b82629137dc14df 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -700,11 +700,11 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
+ranges new
ranges optional
ranges span
ranges tuple
ranges type_traits
-ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index bd3ab60f5756c67..524eb674f3b5e0f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -499,6 +499,7 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
+ranges new
ranges optional
ranges span
ranges tuple
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index bd3ab60f5756c67..524eb674f3b5e0f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -499,6 +499,7 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
+ranges new
ranges optional
ranges span
ranges tuple
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
index 4a439ae90fcc0f3..582e0c7dfe5e65d 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp
@@ -214,17 +214,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_chunk_by
-# error "__cpp_lib_ranges_chunk_by should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_chunk_by != 202202L
-# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_chunk_by
-# error "__cpp_lib_ranges_chunk_by should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_chunk_by
+# error "__cpp_lib_ranges_chunk_by should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_chunk_by != 202202L
+# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
@@ -309,17 +303,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_chunk_by
-# error "__cpp_lib_ranges_chunk_by should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_chunk_by != 202202L
-# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_chunk_by
-# error "__cpp_lib_ranges_chunk_by should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_chunk_by
+# error "__cpp_lib_ranges_chunk_by should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_chunk_by != 202202L
+# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 8903913027db73f..566c595d8c30823 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -4971,17 +4971,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_chunk_by
-# error "__cpp_lib_ranges_chunk_by should be defined in c++23"
-# endif
-# if __cpp_lib_ranges_chunk_by != 202202L
-# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_chunk_by
-# error "__cpp_lib_ranges_chunk_by should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_chunk_by
+# error "__cpp_lib_ranges_chunk_by should be defined in c++23"
+# endif
+# if __cpp_lib_ranges_chunk_by != 202202L
+# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
@@ -6518,17 +6512,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_ranges_chunk_by
-# error "__cpp_lib_ranges_chunk_by should be defined in c++26"
-# endif
-# if __cpp_lib_ranges_chunk_by != 202202L
-# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_ranges_chunk_by
-# error "__cpp_lib_ranges_chunk_by should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_ranges_chunk_by
+# error "__cpp_lib_ranges_chunk_by should be defined in c++26"
+# endif
+# if __cpp_lib_ranges_chunk_by != 202202L
+# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/adaptor.pass.cpp
new file mode 100644
index 000000000000000..423c481127e20e8
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/adaptor.pass.cpp
@@ -0,0 +1,237 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// std::views::chunk_by
+
+#include <ranges>
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <initializer_list>
+#include <type_traits>
+#include <utility>
+
+#include "test_iterators.h"
+
+template <class View, class T>
+concept CanBePiped = requires(View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(t) };
+};
+
+struct Pred {
+ constexpr bool operator()(int x, int y) const { return x != -y; }
+};
+
+struct NonCopyablePredicate : Pred {
+ NonCopyablePredicate(NonCopyablePredicate const&) = delete;
+};
+
+struct Range : std::ranges::view_base {
+ using Iterator = forward_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+template <typename View>
+constexpr void compareViews(View v, std::initializer_list<std::initializer_list<int>> list) {
+ auto b1 = v.begin();
+ auto e1 = v.end();
+ auto b2 = list.begin();
+ auto e2 = list.end();
+ for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
+ bool eq = std::ranges::equal(*b1, *b2, [](int x, int y) {
+ assert(x == y);
+ return true;
+ });
+ assert(eq);
+ }
+ assert(b1 == e1);
+ assert(b2 == e2);
+}
+
+constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }
+
+template <class T>
+constexpr const T&& asConstRvalue(T&& t) {
+ return static_cast<T const&&>(t);
+}
+
+constexpr bool test() {
+ int buff[] = {-4, -3, -2, -1, 1, 2, 3, 4};
+
+ // Test range adaptor object
+ {
+ using RangeAdaptorObject = decltype(std::views::chunk_by);
+ static_assert(std::is_const_v<RangeAdaptorObject>);
+
+ // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular
+ static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);
+ }
+
+ // Test `views::chunk_by(pred)(v)`
+ {
+ using Result = std::ranges::chunk_by_view<Range, Pred>;
+ Range const range(buff, buff + 8);
+ Pred pred;
+
+ {
+ // 'views::chunk_by(pred)' - &&
+ std::same_as<Result> decltype(auto) result = std::views::chunk_by(pred)(range);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by(pred)' - const&&
+ std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::chunk_by(pred))(range);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by(pred)' - &
+ auto partial = std::views::chunk_by(pred);
+ std::same_as<Result> decltype(auto) result = partial(range);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by(pred)' - const&
+ auto const partial = std::views::chunk_by(pred);
+ std::same_as<Result> decltype(auto) result = partial(range);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ }
+
+ // Test `v | views::chunk_by(pred)`
+ {
+ using Result = std::ranges::chunk_by_view<Range, Pred>;
+ Range const range(buff, buff + 8);
+ Pred pred;
+
+ {
+ // 'views::chunk_by(pred)' - &&
+ std::same_as<Result> decltype(auto) result = range | std::views::chunk_by(pred);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by(pred)' - const&&
+ std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::chunk_by(pred));
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by(pred)' - &
+ auto partial = std::views::chunk_by(pred);
+ std::same_as<Result> decltype(auto) result = range | partial;
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by(pred)' - const&
+ auto const partial = std::views::chunk_by(pred);
+ std::same_as<Result> decltype(auto) result = range | partial;
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ }
+
+ // Test `views::chunk_by(v, pred)` range adaptor object
+ {
+ using Result = std::ranges::chunk_by_view<Range, Pred>;
+ Range const range(buff, buff + 8);
+ Pred pred;
+
+ {
+ // 'views::chunk_by' - &&
+ auto range_adaptor = std::views::chunk_by;
+ std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pred);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by' - const&&
+ auto const range_adaptor = std::views::chunk_by;
+ std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pred);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by' - &
+ auto range_adaptor = std::views::chunk_by;
+ std::same_as<Result> decltype(auto) result = range_adaptor(range, pred);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ {
+ // 'views::chunk_by' - const&
+ auto const range_adaptor = std::views::chunk_by;
+ std::same_as<Result> decltype(auto) result = range_adaptor(range, pred);
+ compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}});
+ }
+ }
+
+ // Test that one can call std::views::chunk_by with arbitrary stuff, as long as we
+ // don't try to actually complete the call by passing it a range.
+ //
+ // That makes no sense and we can't do anything with the result, but it's valid.
+ {
+ int array[3] = {1, 2, 3};
+ [[maybe_unused]] auto partial = std::views::chunk_by(std::move(array));
+ }
+
+ // Test `adaptor | views::chunk_by(pred)`
+ {
+ Range const range(buff, buff + 8);
+
+ {
+ auto pred1 = [](int i) { return absoluteValue(i) < 3; };
+ Pred pred2;
+ using Result = std::ranges::chunk_by_view<std::ranges::filter_view<Range, decltype(pred1)>, Pred>;
+ std::same_as<Result> decltype(auto) result = range | std::views::filter(pred1) | std::views::chunk_by(pred2);
+ compareViews(result, {{-2, -1}, {1, 2}});
+ }
+ {
+ auto pred1 = [](int i) { return absoluteValue(i) < 3; };
+ Pred pred2;
+ using Result = std::ranges::chunk_by_view<std::ranges::filter_view<Range, decltype(pred1)>, Pred>;
+ auto const partial = std::views::filter(pred1) | std::views::chunk_by(pred2);
+ std::same_as<Result> decltype(auto) result = range | partial;
+ compareViews(result, {{-2, -1}, {1, 2}});
+ }
+ }
+
+ // Test SFINAE friendliness
+ {
+ struct NotAView {};
+ struct NotInvocable {};
+
+ static_assert(!CanBePiped<Range, decltype(std::views::chunk_by)>);
+ static_assert(CanBePiped<Range, decltype(std::views::chunk_by(Pred{}))>);
+ static_assert(!CanBePiped<NotAView, decltype(std::views::chunk_by(Pred{}))>);
+ static_assert(!CanBePiped<std::initializer_list<int>, decltype(std::views::chunk_by(Pred{}))>);
+ static_assert(!CanBePiped<Range, decltype(std::views::chunk_by(NotInvocable{}))>);
+
+ static_assert(!std::is_invocable_v<decltype(std::views::chunk_by)>);
+ static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), Pred, Range>);
+ static_assert(std::is_invocable_v<decltype(std::views::chunk_by), Range, Pred>);
+ static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), Range, Pred, Pred>);
+ static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), NonCopyablePredicate>);
+ }
+
+ { static_assert(std::is_same_v<decltype(std::ranges::views::chunk_by), decltype(std::views::chunk_by)>); }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/base.pass.cpp
new file mode 100644
index 000000000000000..cc3fcfd96fbec81
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/base.pass.cpp
@@ -0,0 +1,99 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr View base() const& requires copy_constructible<View>;
+// constexpr View base() &&;
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+struct Range : std::ranges::view_base {
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {}
+ constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {}
+ Range& operator=(Range const&) = default;
+ Range& operator=(Range&&) = default;
+ constexpr int* begin() const { return begin_; }
+ constexpr int* end() const { return end_; }
+
+ int* begin_;
+ int* end_;
+ bool wasCopyInitialized = false;
+ bool wasMoveInitialized = false;
+};
+
+static_assert(std::ranges::view<Range>);
+static_assert(std::ranges::forward_range<Range>);
+
+struct Pred {
+ bool operator()(int, int) const;
+};
+
+struct NonCopyableRange : std::ranges::view_base {
+ explicit NonCopyableRange(int*, int*);
+ NonCopyableRange(NonCopyableRange const&) = delete;
+ NonCopyableRange(NonCopyableRange&&) = default;
+ NonCopyableRange& operator=(NonCopyableRange const&) = default;
+ NonCopyableRange& operator=(NonCopyableRange&&) = default;
+ int* begin() const;
+ int* end() const;
+};
+
+static_assert(!std::copy_constructible<NonCopyableRange>);
+
+template <typename T>
+concept CanCallBaseOn = requires(T t) { std::forward<T>(t).base(); };
+
+constexpr bool test() {
+ int buff[] = {1, 2, 3, 4};
+
+ // Check the const& overload
+ {
+ Range range(buff, buff + 4);
+ std::ranges::chunk_by_view<Range, Pred> const view(range, Pred{});
+ std::same_as<Range> decltype(auto) result = view.base();
+ assert(result.wasCopyInitialized);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 4);
+ }
+
+ // Check the && overload
+ {
+ Range range(buff, buff + 4);
+ std::ranges::chunk_by_view<Range, Pred> view(range, Pred{});
+ std::same_as<Range> decltype(auto) result = std::move(view).base();
+ assert(result.wasMoveInitialized);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 4);
+ }
+
+ // Ensure the const& overload is not considered when the base is not copy-constructible
+ {
+ static_assert(!CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred> const&>);
+ static_assert(!CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred>&>);
+ static_assert(!CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred> const&&>);
+ static_assert(CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred>&&>);
+ static_assert(CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
new file mode 100644
index 000000000000000..d2d1d313cebe939
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr iterator begin();
+
+#include <ranges>
+
+#include <cassert>
+#include <utility>
+
+#include "test_iterators.h"
+#include "types.h"
+
+struct Range : std::ranges::view_base {
+ using Iterator = forward_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+struct TrackingPred : TrackInitialization {
+ using TrackInitialization::TrackInitialization;
+ constexpr bool operator()(int x, int y) { return x != -y; }
+};
+
+template <class T>
+concept HasBegin = requires(T t) { t.begin(); };
+
+static_assert(HasBegin<std::ranges::chunk_by_view<Range, TrackingPred>>);
+static_assert(!HasBegin<const std::ranges::chunk_by_view<Range, TrackingPred>>);
+
+constexpr bool test() {
+ int buff[] = {-4, -3, -2, -1, 1, 2, 3, 4};
+
+ // Check the return type of `begin()`
+ {
+ Range range(buff, buff + 1);
+ auto pred = [](int, int) { return true; };
+ std::ranges::chunk_by_view view(range, pred);
+ using ChunkByIterator = std::ranges::iterator_t<decltype(view)>;
+ ASSERT_SAME_TYPE(ChunkByIterator, decltype(view.begin()));
+ }
+
+ // begin() over an empty range
+ {
+ Range range(buff, buff);
+ auto pred = [](int, int) { return true; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto it = view.begin();
+ assert(it == view.begin());
+ assert(it == view.end());
+ }
+
+ // begin() over a 1-element range
+ {
+ Range range(buff, buff + 1);
+ auto pred = [](int x, int y) { return x == y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto it = view.begin();
+ assert(base((*it).begin()) == buff);
+ assert(base((*it).end()) == buff + 1);
+ }
+
+ // begin() over a 2-element range
+ {
+ Range range(buff, buff + 2);
+ auto pred = [](int x, int y) { return x == y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto it = view.begin();
+ assert(base((*it).begin()) == buff);
+ assert(base((*it).end()) == buff + 1);
+ assert(base((*++it).begin()) == buff + 1);
+ assert(base((*it).end()) == buff + 2);
+ }
+
+ // begin() over a longer range
+ {
+ Range range(buff, buff + 8);
+ auto pred = [](int x, int y) { return x != -y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto it = view.begin();
+ assert(base((*it).end()) == buff + 4);
+ }
+
+ // Make sure we do not make a copy of the predicate when we call begin()
+ // (we should be passing it to ranges::adjacent_find using std::ref)
+ {
+ bool moved = false, copied = false;
+ Range range(buff, buff + 2);
+ std::ranges::chunk_by_view view(range, TrackingPred(&moved, &copied));
+ std::exchange(moved, false);
+ [[maybe_unused]] auto it = view.begin();
+ assert(!moved);
+ assert(!copied);
+ }
+
+ // Test with a non-const predicate
+ {
+ Range range(buff, buff + 8);
+ auto pred = [](int x, int y) mutable { return x != -y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto it = view.begin();
+ assert(base((*it).end()) == buff + 4);
+ }
+
+ // Test with a predicate that takes by non-const reference
+ {
+ Range range(buff, buff + 8);
+ auto pred = [](int& x, int& y) { return x != -y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto it = view.begin();
+ assert(base((*it).end()) == buff + 4);
+ }
+
+ // Test caching
+ {
+ // Make sure that we cache the result of begin() on subsequent calls
+ Range range(buff, buff + 8);
+ int called = 0;
+ auto pred = [&](int x, int y) {
+ ++called;
+ return x != -y;
+ };
+
+ std::ranges::chunk_by_view view(range, pred);
+ assert(called == 0);
+ for (int k = 0; k != 3; ++k) {
+ auto it = view.begin();
+ assert(base((*it).end()) == buff + 4);
+ assert(called == 4); // 4, because the cached iterator is 'buff + 4' (end of the first chunk)
+ }
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/constraints.compile.pass.cpp
new file mode 100644
index 000000000000000..9054fea08c07500
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/constraints.compile.pass.cpp
@@ -0,0 +1,121 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// Check constraints on the type itself.
+//
+// template <forward_range View, indirect_binary_predicate<iterator_t<View>, iterator_t<View>> Pred>
+// requires view<View> && is_object_v<Pred>
+// class chunk_by_view;
+
+#include <ranges>
+
+#include <concepts>
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+template <class View, class Pred>
+concept CanFormChunkByView = requires { typename std::ranges::chunk_by_view<View, Pred>; };
+
+// chunk_by_view is not valid when the view is not a forward_range
+namespace test_when_view_is_not_a_forward_range {
+
+struct View : std::ranges::view_base {
+ ForwardIteratorNotDerivedFrom begin() const;
+ ForwardIteratorNotDerivedFrom end() const;
+};
+struct Pred {
+ bool operator()(int, int) const;
+};
+
+static_assert(!std::ranges::forward_range<View>);
+static_assert(std::indirect_binary_predicate<Pred, int*, int*>);
+static_assert(std::ranges::view<View>);
+static_assert(std::is_object_v<Pred>);
+static_assert(!CanFormChunkByView<View, Pred>);
+
+} // namespace test_when_view_is_not_a_forward_range
+
+// chunk_by_view is not valid when the predicate is not indirect_binary_predicate
+namespace test_when_the_predicate_is_not_indirect_binary_predicate {
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+struct Pred {};
+
+static_assert(std::ranges::forward_range<View>);
+static_assert(!std::indirect_binary_predicate<Pred, int*, int*>);
+static_assert(std::ranges::view<View>);
+static_assert(std::is_object_v<Pred>);
+static_assert(!CanFormChunkByView<View, Pred>);
+
+} // namespace test_when_the_predicate_is_not_indirect_binary_predicate
+
+// chunk_by_view is not valid when the view is not a view
+namespace test_when_the_view_param_is_not_a_view {
+
+struct View {
+ int* begin() const;
+ int* end() const;
+};
+struct Pred {
+ bool operator()(int, int) const;
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::indirect_binary_predicate<Pred, int*, int*>);
+static_assert(!std::ranges::view<View>);
+static_assert(std::is_object_v<Pred>);
+static_assert(!CanFormChunkByView<View, Pred>);
+
+} // namespace test_when_the_view_param_is_not_a_view
+
+// chunk_by_view is not valid when the predicate is not an object type
+namespace test_when_the_predicate_is_not_an_object_type {
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+using Pred = bool (&)(int, int);
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::indirect_binary_predicate<Pred, int*, int*>);
+static_assert(std::ranges::view<View>);
+static_assert(!std::is_object_v<Pred>);
+static_assert(!CanFormChunkByView<View, Pred>);
+
+} // namespace test_when_the_predicate_is_not_an_object_type
+
+// chunk_by_view is valid when all the constraints are satisfied (test the test)
+namespace test_when_all_the_constraints_are_satisfied {
+
+struct View : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+struct Pred {
+ bool operator()(int, int) const;
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::indirect_binary_predicate<Pred, int*, int*>);
+static_assert(std::ranges::view<View>);
+static_assert(std::is_object_v<Pred>);
+static_assert(CanFormChunkByView<View, Pred>);
+
+} // namespace test_when_all_the_constraints_are_satisfied
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctad.pass.cpp
new file mode 100644
index 000000000000000..9df292844f1899d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctad.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// template <class Range, class Pred>
+// chunk_by_view(Range&&, Pred) -> chunk_by_view<views::all_t<Range>, Pred>;
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+ View() = default;
+ forward_iterator<int*> begin() const;
+ sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+static_assert(std::ranges::view<View>);
+
+// A range that is not a view
+struct Range {
+ Range() = default;
+ forward_iterator<int*> begin() const;
+ sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+static_assert(std::ranges::range<Range>);
+static_assert(!std::ranges::view<Range>);
+
+struct Pred {
+ constexpr bool operator()(int x, int y) const { return x <= y; }
+};
+
+constexpr bool test() {
+ {
+ View v;
+ Pred pred;
+ std::ranges::chunk_by_view view(v, pred);
+ static_assert(std::is_same_v<decltype(view), std::ranges::chunk_by_view<View, Pred>>);
+ }
+ {
+ Range r;
+ Pred pred;
+ std::ranges::chunk_by_view view(r, pred);
+ static_assert(std::is_same_v<decltype(view), std::ranges::chunk_by_view<std::ranges::ref_view<Range>, Pred>>);
+ }
+ {
+ Pred pred;
+ std::ranges::chunk_by_view view(Range{}, pred);
+ static_assert(std::is_same_v<decltype(view), std::ranges::chunk_by_view<std::ranges::owning_view<Range>, Pred>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.default.pass.cpp
new file mode 100644
index 000000000000000..98c6cb7af5f5633
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.default.pass.cpp
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// chunk_by_view() requires std::default_initializable<View> &&
+// std::default_initializable<Pred> = default;
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+
+constexpr int buff[] = {-2, 1, -1, 2};
+
+struct DefaultConstructibleView : std::ranges::view_base {
+ DefaultConstructibleView() = default;
+ constexpr int const* begin() const { return buff; }
+ constexpr int const* end() const { return buff + 4; }
+};
+
+struct DefaultConstructiblePredicate {
+ DefaultConstructiblePredicate() = default;
+ constexpr bool operator()(int x, int y) const { return x != -y; }
+};
+
+struct NoDefaultView : std::ranges::view_base {
+ NoDefaultView() = delete;
+ int* begin() const;
+ int* end() const;
+};
+
+struct NoDefaultPredicate {
+ NoDefaultPredicate() = delete;
+ constexpr bool operator()(int, int) const;
+};
+
+struct NoexceptView : std::ranges::view_base {
+ NoexceptView() noexcept;
+ int const* begin() const;
+ int const* end() const;
+};
+
+struct NoexceptPredicate {
+ NoexceptPredicate() noexcept;
+ bool operator()(int, int) const;
+};
+
+struct MayThrowView : std::ranges::view_base {
+ MayThrowView() noexcept(false);
+ int const* begin() const;
+ int const* end() const;
+};
+
+struct MayThrowPredicate {
+ MayThrowPredicate() noexcept(false);
+ bool operator()(int, int) const;
+};
+
+constexpr void compareRanges(std::ranges::subrange<const int*> v, std::initializer_list<int> list) {
+ assert(v.size() == list.size());
+ for (size_t i = 0; i < v.size(); ++i) {
+ assert(v[i] == list.begin()[i]);
+ }
+}
+
+constexpr bool test() {
+ // Check default constructor with default initialization
+ {
+ using View = std::ranges::chunk_by_view<DefaultConstructibleView, DefaultConstructiblePredicate>;
+ View view;
+ auto it = view.begin(), end = view.end();
+ compareRanges(*it++, {-2, 1});
+ compareRanges(*it++, {-1, 2});
+ assert(it == end);
+ }
+
+ // Check default construction with copy-list-initialization
+ {
+ using View = std::ranges::chunk_by_view<DefaultConstructibleView, DefaultConstructiblePredicate>;
+ View view = {};
+ auto it = view.begin(), end = view.end();
+ compareRanges(*it++, {-2, 1});
+ compareRanges(*it++, {-1, 2});
+ assert(it == end);
+ }
+
+ // Check cases where the default constructor isn't provided
+ {
+ static_assert(
+ !std::is_default_constructible_v<std::ranges::chunk_by_view<NoDefaultView, DefaultConstructiblePredicate>>);
+ static_assert(
+ !std::is_default_constructible_v<std::ranges::chunk_by_view<DefaultConstructibleView, NoDefaultPredicate>>);
+ static_assert(!std::is_default_constructible_v<std::ranges::chunk_by_view<NoDefaultView, NoDefaultPredicate>>);
+ }
+
+ // Check noexcept-ness
+ {
+ {
+ using View = std::ranges::chunk_by_view<MayThrowView, MayThrowPredicate>;
+ static_assert(!noexcept(View()));
+ }
+ {
+ using View = std::ranges::chunk_by_view<MayThrowView, NoexceptPredicate>;
+ static_assert(!noexcept(View()));
+ }
+ {
+ using View = std::ranges::chunk_by_view<NoexceptView, MayThrowPredicate>;
+ static_assert(!noexcept(View()));
+ }
+ {
+ using View = std::ranges::chunk_by_view<NoexceptView, NoexceptPredicate>;
+ static_assert(noexcept(View()));
+ }
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.view_pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.view_pred.pass.cpp
new file mode 100644
index 000000000000000..9730a851f3dac8c
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.view_pred.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// constexpr chunk_by_view(View, Pred);
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <utility>
+
+#include "types.h"
+
+struct Range : std::ranges::view_base {
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr int* begin() const { return begin_; }
+ constexpr int* end() const { return end_; }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+static_assert(std::ranges::view<Range>);
+static_assert(std::ranges::forward_range<Range>);
+
+struct Pred {
+ constexpr bool operator()(int x, int y) const { return x <= y; }
+};
+
+struct TrackingPred : TrackInitialization {
+ using TrackInitialization::TrackInitialization;
+ constexpr bool operator()(int&, int&) const;
+};
+
+struct TrackingRange : TrackInitialization, std::ranges::view_base {
+ using TrackInitialization::TrackInitialization;
+ int* begin() const;
+ int* end() const;
+};
+
+template <class T>
+void implicitConstructionTest(T);
+
+template <class T, class... Args>
+concept ImplicitConstructibleFrom =
+ requires(Args&&... args) { implicitConstructionTest({std::forward<Args>(args)...}); };
+
+constexpr bool test() {
+ int buff[] = {1, 2, 3, 0, 1, 2, -1, -1, 0};
+
+ // Test explicit syntax
+ {
+ Range range(buff, buff + 9);
+ Pred pred;
+ std::ranges::chunk_by_view<Range, Pred> view(range, pred);
+ auto it = view.begin(), end = view.end();
+ assert(std::ranges::equal(*it++, std::array{1, 2, 3}));
+ assert(std::ranges::equal(*it++, std::array{0, 1, 2}));
+ assert(std::ranges::equal(*it++, std::array{-1, -1, 0}));
+ assert(it == end);
+ }
+
+ // Test implicit syntax
+ {
+ using ChunkByView = std::ranges::chunk_by_view<Range, Pred>;
+ static_assert(!ImplicitConstructibleFrom<ChunkByView, Range, Pred>);
+ static_assert(!ImplicitConstructibleFrom<ChunkByView, const Range&, const Pred&>);
+ }
+
+ // Make sure we move the view
+ {
+ bool moved = false, copied = false;
+ TrackingRange range(&moved, &copied);
+ Pred pred;
+ [[maybe_unused]] std::ranges::chunk_by_view<TrackingRange, Pred> view(std::move(range), pred);
+ assert(moved);
+ assert(!copied);
+ }
+
+ // Make sure we move the predicate
+ {
+ bool moved = false, copied = false;
+ Range range(buff, buff + 9);
+ TrackingPred pred(&moved, &copied);
+ [[maybe_unused]] std::ranges::chunk_by_view<Range, TrackingPred> view(range, std::move(pred));
+ assert(moved);
+ assert(!copied);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/end.pass.cpp
new file mode 100644
index 000000000000000..61ea0c747195347
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/end.pass.cpp
@@ -0,0 +1,121 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr auto end();
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+
+#include "test_iterators.h"
+
+struct NonCommonRange : std::ranges::view_base {
+ using Iterator = forward_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ constexpr explicit NonCommonRange(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+static_assert(std::ranges::forward_range<NonCommonRange>);
+static_assert(!std::ranges::common_range<NonCommonRange>);
+
+struct CommonRange : std::ranges::view_base {
+ using Iterator = bidirectional_iterator<int*>;
+ constexpr explicit CommonRange(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Iterator end() const { return Iterator(end_); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+static_assert(std::ranges::bidirectional_range<CommonRange>);
+static_assert(std::ranges::common_range<CommonRange>);
+
+constexpr bool test() {
+ int buff[] = {1, 0, 3, 1, 2, 3, 4, 5};
+
+ // Check the return type of `end()`
+ {
+ CommonRange range(buff, buff + 1);
+ auto pred = [](int, int) { return true; };
+ std::ranges::chunk_by_view view(range, pred);
+ using ChunkByView = decltype(view);
+ static_assert(std::ranges::common_range<ChunkByView>);
+ ASSERT_SAME_TYPE(std::ranges::sentinel_t<ChunkByView>, decltype(view.end()));
+ }
+
+ // end() on an empty range
+ {
+ CommonRange range(buff, buff);
+ auto pred = [](int x, int y) { return x <= y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto end = view.end();
+ assert(end == std::default_sentinel);
+ }
+
+ // end() on a 1-element range
+ {
+ CommonRange range(buff, buff + 1);
+ auto pred = [](int& x, int& y) { return x <= y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto end = view.end();
+ assert(base((*--end).begin()) == buff);
+ assert(base((*end).end()) == buff + 1);
+ }
+
+ // end() on a 2-element range
+ {
+ CommonRange range(buff, buff + 2);
+ auto pred = [](int const& x, int const& y) { return x <= y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto end = view.end();
+ assert(base((*--end).begin()) == buff + 1);
+ assert(base((*--end).end()) == buff + 1);
+ }
+
+ // end() on a 8-element range
+ {
+ CommonRange range(buff, buff + 8);
+ auto pred = [](const int x, const int y) { return x < y; };
+ std::ranges::chunk_by_view view(range, pred);
+ auto end = view.end();
+ assert(base((*--end).end()) == buff + 8);
+ assert(base((*--end).end()) == buff + 3);
+ }
+
+ // end() on a non-common range
+ {
+ NonCommonRange range(buff, buff + 1);
+ std::ranges::chunk_by_view view(range, std::ranges::less_equal{});
+ auto end = view.end();
+ ASSERT_SAME_TYPE(std::default_sentinel_t, std::ranges::sentinel_t<decltype(view)>);
+ ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(end));
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/pred.pass.cpp
new file mode 100644
index 000000000000000..6d697a14dc73591
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/pred.pass.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr Pred const& pred() const;
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+
+struct Range : std::ranges::view_base {
+ int* begin() const;
+ int* end() const;
+};
+
+struct Pred {
+ bool operator()(int, int) const;
+ int value;
+};
+
+constexpr bool test() {
+ {
+ Pred pred{42};
+ std::ranges::chunk_by_view<Range, Pred> const view(Range{}, pred);
+ std::same_as<Pred const&> decltype(auto) result = view.pred();
+ assert(result.value == 42);
+
+ // Make sure we're really holding a reference to something inside the view
+ assert(&result == &view.pred());
+ }
+
+ // Same, but calling on a non-const view
+ {
+ Pred pred{42};
+ std::ranges::chunk_by_view<Range, Pred> view(Range{}, pred);
+ std::same_as<Pred const&> decltype(auto) result = view.pred();
+ assert(result.value == 42);
+
+ // Make sure we're really holding a reference to something inside the view
+ assert(&result == &view.pred());
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/compare.pass.cpp
new file mode 100644
index 000000000000000..38b6346e0061f79
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/compare.pass.cpp
@@ -0,0 +1,113 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// friend constexpr bool operator==(const iterator& x, const iterator& y);
+// friend constexpr bool operator==(const iterator& x, default_sentinel_t);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+
+#include "../types.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using Underlying = View<Iterator, Sentinel>;
+ using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
+ using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
+
+ auto make_chunk_by_view = [](auto begin, auto end) {
+ View view{Iterator(begin), Sentinel(Iterator(end))};
+ return ChunkByView(std::move(view), std::ranges::less_equal{});
+ };
+
+ // Test operator==
+ {
+ std::array array{0, 1, 2};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator i = view.begin();
+ ChunkByIterator j = view.begin();
+
+ std::same_as<bool> decltype(auto) result = (i == j);
+ assert(result);
+ ++i;
+ assert(!(i == j));
+ }
+
+ // Test synthesized operator!=
+ {
+ std::array array{0, 1, 2};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator i = view.begin();
+ ChunkByIterator j = view.begin();
+
+ std::same_as<bool> decltype(auto) result = (i != j);
+ assert(!result);
+ ++i;
+ assert(i != j);
+ }
+
+ // Test operator== with std::default_sentinel_t
+ {
+ std::array array{0, 1, 2};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator i = view.begin();
+
+ std::same_as<bool> decltype(auto) result = (i == std::default_sentinel);
+ assert(!result);
+ ++i;
+ assert(i == std::default_sentinel);
+ }
+
+ // Test synthesized operator!= with std::default_sentinel_t
+ {
+ std::array array{0, 1, 2};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator i = view.begin();
+
+ std::same_as<bool> decltype(auto) result = (i != std::default_sentinel);
+ assert(result);
+ ++i;
+ assert(!(i != std::default_sentinel));
+ }
+}
+
+struct TestWithPair {
+ template <class Iterator>
+ constexpr void operator()() const {
+ // Test with pair of iterators
+ test<Iterator, Iterator>();
+
+ // Test with iterator-sentinel pair
+ test<Iterator>();
+ }
+};
+
+constexpr bool tests() {
+ TestWithPair tester;
+ types::for_each(types::forward_iterator_list<int*>{}, tester);
+ types::for_each(types::forward_iterator_list<int const*>{}, tester);
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/ctor.default.pass.cpp
new file mode 100644
index 000000000000000..4493060c59bf761
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/ctor.default.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// std::ranges::chunk_by_view<V>::<iterator>() = default;
+
+#include <ranges>
+
+#include <cassert>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class Iterator, bool IsNoexcept>
+constexpr void testDefaultConstructible() {
+ // Make sure the iterator is default constructible.
+ using ChunkByView = std::ranges::chunk_by_view<View<Iterator>, std::ranges::less_equal>;
+ using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
+ ChunkByIterator i{};
+ ChunkByIterator j;
+ assert(i == j);
+ static_assert(noexcept(ChunkByIterator{}) == IsNoexcept);
+}
+
+constexpr bool tests() {
+ testDefaultConstructible<forward_iterator<int*>, /*IsNoexcept=*/false>();
+ testDefaultConstructible<bidirectional_iterator<int*>, /*IsNoexcept=*/false>();
+ testDefaultConstructible<random_access_iterator<int*>, /*IsNoexcept=*/false>();
+ testDefaultConstructible<contiguous_iterator<int*>, /*IsNoexcept=*/false>();
+ testDefaultConstructible<int*, /*IsNoexcept=*/true>();
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/decrement.pass.cpp
new file mode 100644
index 000000000000000..167f3f753bfae73
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/decrement.pass.cpp
@@ -0,0 +1,221 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr iterator& operator--();
+// constexpr iterator operator--(int);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <span>
+#include <type_traits>
+#include <utility>
+
+#include "../types.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasPreDecrement = requires(T t) {
+ { --t };
+};
+
+template <class T>
+concept HasPostDecrement = requires(T t) {
+ { t-- };
+};
+
+struct TrackingPred : TrackInitialization {
+ using TrackInitialization::TrackInitialization;
+ constexpr bool operator()(int x, int y) const { return x <= y; }
+};
+
+template <class Iterator, IsConst Constant, class Sentinel = sentinel_wrapper<Iterator>>
+constexpr void test() {
+ using Underlying = View<Iterator, Sentinel>;
+ using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
+ using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
+
+ static_assert(HasPostDecrement<ChunkByIterator>);
+ static_assert(HasPreDecrement<ChunkByIterator>);
+
+ auto make_chunk_by_view = [](auto begin, auto end) {
+ View view{Iterator{begin}, Sentinel{Iterator{end}}};
+ return ChunkByView{std::move(view), std::ranges::less_equal{}};
+ };
+
+ // Test with a single chunk
+ {
+ std::array array{0, 1, 2, 3, 4};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator it = std::ranges::next(view.begin(), view.end());
+
+ std::same_as<ChunkByIterator&> decltype(auto) result = --it;
+ assert(&result == &it);
+ assert(base((*result).begin()) == array.begin());
+ }
+
+ // Test with two chunks
+ {
+ std::array array{0, 1, 2, 0, 1, 2};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator it = std::ranges::next(view.begin(), view.end());
+
+ std::same_as<ChunkByIterator&> decltype(auto) result = --it;
+ assert(&result == &it);
+ assert(base((*result).begin()) == array.begin() + 3);
+
+ --it;
+ assert(base((*result).begin()) == array.begin());
+ }
+
+ // Test going forward and then backward on the same iterator
+ {
+ std::array array{7, 8, 9, 4, 5, 6, 1, 2, 3, 0};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator it = view.begin();
+
+ ++it;
+ --it;
+ assert(base((*it).begin()) == array.begin());
+ assert(base((*it).end()) == array.begin() + 3);
+
+ ++it;
+ ++it;
+ --it;
+ assert(base((*it).begin()) == array.begin() + 3);
+ assert(base((*it).end()) == array.begin() + 6);
+
+ ++it;
+ ++it;
+ --it;
+ assert(base((*it).begin()) == array.begin() + 6);
+ assert(base((*it).end()) == array.begin() + 9);
+
+ ++it;
+ ++it;
+ --it;
+ assert(base((*it).begin()) == array.begin() + 9);
+ }
+
+ // Decrement an iterator multiple times
+ if constexpr (std::ranges::common_range<Underlying>) {
+ std::array array{1, 2, 1, 2, 1};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+
+ ChunkByIterator it = view.end();
+ --it;
+ --it;
+ --it;
+ assert(base((*it).begin()) == array.begin());
+ }
+
+ // Test with a predicate that takes by non-const reference
+ if constexpr (!std::to_underlying(Constant)) {
+ std::array array{1, 2, 3, -3, -2, -1};
+ View v{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}};
+ auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; });
+
+ auto it = std::ranges::next(view.begin());
+ assert(base((*it).begin()) == array.begin() + 3);
+ --it;
+ assert(base((*it).begin()) == array.begin());
+ }
+
+ // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()')
+ {
+ std::array array = {1, 2, 3, -3, -2, -1};
+ auto v = View{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}}
+ | std::views::transform([](int x) { return IntWrapper{x}; });
+ auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual);
+
+ auto it = std::ranges::next(view.begin());
+ assert(base((*it).begin().base()) == array.begin() + 3);
+ --it;
+ assert(base((*it).begin().base()) == array.begin());
+ }
+
+ // Make sure we do not make a copy of the predicate when we decrement
+ if constexpr (std::ranges::common_range<Underlying>) {
+ bool moved = false, copied = false;
+ std::array array{1, 2, 1, 3};
+ View v{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
+ auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied));
+ assert(std::exchange(moved, false));
+ auto it = view.end();
+ --it;
+ it--;
+ assert(!moved);
+ assert(!copied);
+ }
+
+ // Check post-decrement
+ {
+ std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator it = std::ranges::next(view.begin(), view.end());
+
+ std::same_as<ChunkByIterator> decltype(auto) result = it--;
+ assert(result != it);
+ assert(result == std::default_sentinel);
+ assert(base((*it).begin()) == array.begin() + 6);
+
+ result = it--;
+ assert(base((*it).begin()) == array.begin() + 3);
+ assert(base((*result).begin()) == array.begin() + 6);
+
+ result = it--;
+ assert(base((*it).begin()) == array.begin());
+ assert(base((*result).begin()) == array.begin() + 3);
+ }
+}
+
+template <class Iterator, IsConst Constant>
+constexpr void test_with_pair() {
+ // Test with pair of iterators
+ test<Iterator, Constant>();
+
+ // Test with iterator-sentinel pair
+ test<Iterator, Constant, Iterator>();
+}
+
+constexpr bool tests() {
+ test_with_pair<bidirectional_iterator<int*>, IsConst::no>();
+ test_with_pair<random_access_iterator<int*>, IsConst::no>();
+ test_with_pair<contiguous_iterator<int*>, IsConst::no>();
+ test_with_pair<int*, IsConst::no>();
+
+ test_with_pair<bidirectional_iterator<int const*>, IsConst::yes>();
+ test_with_pair<random_access_iterator<int const*>, IsConst::yes>();
+ test_with_pair<contiguous_iterator<int const*>, IsConst::yes>();
+ test_with_pair<int const*, IsConst::yes>();
+
+ // Make sure `operator--` isn't provided for non bidirectional ranges
+ {
+ using ForwardView = View<forward_iterator<int*>, sentinel_wrapper<forward_iterator<int*>>>;
+ using ChunkByView = std::ranges::chunk_by_view<ForwardView, std::ranges::less_equal>;
+ static_assert(!HasPreDecrement<std::ranges::iterator_t<ChunkByView>>);
+ static_assert(!HasPostDecrement<std::ranges::iterator_t<ChunkByView>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/deref.pass.cpp
new file mode 100644
index 000000000000000..3f8c073e7b3b068
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/deref.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr value_type operator*() const;
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+
+#include "../types.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <class Iter, class Sent = sentinel_wrapper<Iter>>
+constexpr void test() {
+ using Underlying = View<Iter, Sent>;
+ using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
+ using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
+
+ std::array array{0, 1, 2, 3, -1, 0, 1, 2, -2, 3, 4, 5};
+ std::array expected{std::array{0, 1, 2, 3}, std::array{-1, 0, 1, 2}, std::array{-2, 3, 4, 5}};
+ Underlying underlying{Iter{array.begin()}, Sent{Iter{array.end()}}};
+ ChunkByView view{underlying, std::ranges::less_equal{}};
+
+ size_t idx = 0;
+ for (std::same_as<ChunkByIterator> auto iter = view.begin(); iter != view.end(); ++idx, ++iter) {
+ std::same_as<typename ChunkByIterator::value_type> auto chunk = *iter;
+ assert(std::ranges::equal(chunk, expected[idx]));
+ }
+}
+
+constexpr bool tests() {
+ // Check iterator-sentinel pair
+ test<forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>>();
+ test<int*>();
+
+ // Check iterator pair
+ test<forward_iterator<int*>, forward_iterator<int*>>();
+ test<bidirectional_iterator<int*>, bidirectional_iterator<int*>>();
+ test<random_access_iterator<int*>, random_access_iterator<int*>>();
+ test<contiguous_iterator<int*>, contiguous_iterator<int*>>();
+ test<int*, int*>();
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/increment.pass.cpp
new file mode 100644
index 000000000000000..454e9e7503a5b3a
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/increment.pass.cpp
@@ -0,0 +1,179 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// constexpr iterator& operator++();
+// constexpr iterator operator++(int);
+
+#include <ranges>
+
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <span>
+#include <type_traits>
+#include <utility>
+
+#include "../types.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+struct TrackingPred : TrackInitialization {
+ using TrackInitialization::TrackInitialization;
+ constexpr bool operator()(int x, int y) const { return x <= y; }
+};
+
+template <class Iterator, IsConst Constant>
+constexpr void test() {
+ using Sentinel = sentinel_wrapper<Iterator>;
+ using Underlying = View<Iterator, Sentinel>;
+ using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
+ using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
+
+ auto make_chunk_by_view = [](auto begin, auto end) {
+ View view{Iterator{begin}, Sentinel{Iterator{end}}};
+ return ChunkByView{std::move(view), std::ranges::less_equal{}};
+ };
+
+ // Increment the iterator when it won't find another satisfied value after begin()
+ {
+ std::array array{0, 1, 2, 3, 4};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator it = view.begin();
+
+ std::same_as<ChunkByIterator&> decltype(auto) result = ++it;
+ assert(&result == &it);
+ assert(result == view.end());
+ assert(result == std::default_sentinel);
+ }
+
+ // Increment the iterator and it finds another value after begin()
+ {
+ std::array array{1, 2, 3, -1, -2, -3};
+ int const* second_chunk = array.begin() + 3;
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+
+ ChunkByIterator it = view.begin();
+ ++it;
+ assert(base((*it).begin()) == second_chunk);
+ }
+
+ // Increment advances all the way to the end of the range
+ {
+ std::array array{1, 2, 3, 4, 1};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+
+ ChunkByIterator it = view.begin();
+ ++it;
+ assert(base((*it).begin()) == array.begin() + 4);
+ }
+
+ // Increment an iterator multiple times
+ {
+ std::array array{0, 1, 0, 2, 0, 3, 0, 4};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+
+ ChunkByIterator it = view.begin();
+ assert(base((*it).begin()) == array.begin());
+ ++it;
+ assert(base((*it).begin()) == array.begin() + 2);
+ ++it;
+ assert(base((*it).begin()) == array.begin() + 4);
+ ++it;
+ assert(base((*it).begin()) == array.begin() + 6);
+ ++it;
+ assert(it == std::default_sentinel);
+ }
+
+ // Test with a predicate that takes by non-const reference
+ if constexpr (!std::to_underlying(Constant)) {
+ std::array array{1, 2, 3, -3, -2, -1};
+ View v{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}};
+ auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; });
+
+ auto it = view.begin();
+ assert(base((*it).begin()) == array.begin());
+ ++it;
+ assert(base((*it).begin()) == array.begin() + 3);
+ }
+
+ // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()')
+ {
+ std::array array = {1, 2, 3, -3, -2, -1};
+ auto v = View{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}}
+ | std::views::transform([](int x) { return IntWrapper{x}; });
+ auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual);
+
+ auto it = view.begin();
+ assert(base((*it).begin().base()) == array.begin());
+ ++it;
+ assert(base((*it).begin().base()) == array.begin() + 3);
+ }
+
+ // Make sure we do not make a copy of the predicate when we increment
+ // (we should be passing it to ranges::adjacent_find using std::ref)
+ {
+ bool moved = false, copied = false;
+ std::array array{1, 2, 1, 3};
+ View v{Iterator(array.begin()), Sentinel(Iterator(array.end()))};
+ auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied));
+ assert(std::exchange(moved, false));
+ auto it = view.begin();
+ ++it;
+ it++;
+ assert(!moved);
+ assert(!copied);
+ }
+
+ // Check post-increment
+ {
+ std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4};
+ ChunkByView view = make_chunk_by_view(array.begin(), array.end());
+ ChunkByIterator it = view.begin();
+
+ std::same_as<ChunkByIterator> decltype(auto) result = it++;
+ assert(result != it);
+ assert(base((*result).begin()) == array.begin());
+ assert(base((*it).begin()) == array.begin() + 3);
+
+ result = it++;
+ assert(base((*result).begin()) == array.begin() + 3);
+ assert(base((*it).begin()) == array.begin() + 6);
+
+ result = it++;
+ assert(base((*result).begin()) == array.begin() + 6);
+ assert(it == std::default_sentinel);
+ }
+}
+
+constexpr bool tests() {
+ test<forward_iterator<int*>, IsConst::no>();
+ test<bidirectional_iterator<int*>, IsConst::no>();
+ test<random_access_iterator<int*>, IsConst::no>();
+ test<contiguous_iterator<int*>, IsConst::no>();
+ test<int*, IsConst::no>();
+
+ test<forward_iterator<int const*>, IsConst::yes>();
+ test<bidirectional_iterator<int const*>, IsConst::yes>();
+ test<random_access_iterator<int const*>, IsConst::yes>();
+ test<contiguous_iterator<int const*>, IsConst::yes>();
+ test<int const*, IsConst::yes>();
+
+ return true;
+}
+
+int main(int, char**) {
+ tests();
+ static_assert(tests());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/types.compile.pass.cpp
new file mode 100644
index 000000000000000..2093936ccf79909
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/types.compile.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <ranges>
+
+// std::ranges::chunk_by_view::<iterator>::value_type
+// std::ranges::chunk_by_view::<iterator>::
diff erence_type
+// std::ranges::chunk_by_view::<iterator>::iterator_category
+// std::ranges::chunk_by_view::<iterator>::iterator_concept
+
+#include <ranges>
+
+#include <functional>
+#include <type_traits>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+struct TestValueTypeAndDifferenceType {
+ template <class Iter>
+ constexpr void operator()() const {
+ using Underlying = View<Iter>;
+ using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
+ using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
+ static_assert(std::same_as<typename ChunkByIterator::value_type, std::ranges::range_value_t<ChunkByView>>);
+ static_assert(std::same_as<typename ChunkByIterator::value_type, std::ranges::subrange<Iter>>);
+ static_assert(
+ std::same_as<typename ChunkByIterator::
diff erence_type, std::ranges::range_
diff erence_t<ChunkByView>>);
+ static_assert(std::same_as<typename ChunkByIterator::
diff erence_type, std::ranges::range_
diff erence_t<Underlying>>);
+ }
+};
+
+template <class Iter>
+using ChunkByIteratorFor = std::ranges::iterator_t<std::ranges::chunk_by_view<View<Iter>, std::ranges::less_equal>>;
+
+constexpr void test() {
+ // Check that value_type is range_value_t and
diff erence_type is range_
diff erence_t
+ types::for_each(types::forward_iterator_list<int*>{}, TestValueTypeAndDifferenceType{});
+
+ // Check iterator_concept for various categories of ranges
+ {
+ static_assert(
+ std::same_as<ChunkByIteratorFor<forward_iterator<int*>>::iterator_concept, std::forward_iterator_tag>);
+ static_assert(std::same_as<ChunkByIteratorFor<bidirectional_iterator<int*>>::iterator_concept,
+ std::bidirectional_iterator_tag>);
+ static_assert(std::same_as<ChunkByIteratorFor<random_access_iterator<int*>>::iterator_concept,
+ std::bidirectional_iterator_tag>);
+ static_assert(
+ std::same_as<ChunkByIteratorFor<contiguous_iterator<int*>>::iterator_concept, std::bidirectional_iterator_tag>);
+ static_assert(std::same_as<ChunkByIteratorFor<int*>::iterator_concept, std::bidirectional_iterator_tag>);
+ }
+
+ // Check iterator_category for various categories of ranges
+ {
+ static_assert(std::same_as<ChunkByIteratorFor<forward_iterator<int*>>::iterator_category, std::input_iterator_tag>);
+ static_assert(
+ std::same_as<ChunkByIteratorFor<bidirectional_iterator<int*>>::iterator_category, std::input_iterator_tag>);
+ static_assert(
+ std::same_as<ChunkByIteratorFor<random_access_iterator<int*>>::iterator_category, std::input_iterator_tag>);
+ static_assert(
+ std::same_as<ChunkByIteratorFor<contiguous_iterator<int*>>::iterator_category, std::input_iterator_tag>);
+ static_assert(std::same_as<ChunkByIteratorFor<int*>::iterator_category, std::input_iterator_tag>);
+ }
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/types.h
new file mode 100644
index 000000000000000..ecbf0c73acf4072
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/types.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H
+
+#include <ranges>
+#include <utility>
+
+#include "test_iterators.h"
+
+struct TrackInitialization {
+ constexpr explicit TrackInitialization(bool* moved, bool* copied) : moved_(moved), copied_(copied) {}
+ constexpr TrackInitialization(TrackInitialization const& other) : moved_(other.moved_), copied_(other.copied_) {
+ *copied_ = true;
+ }
+ constexpr TrackInitialization(TrackInitialization&& other) : moved_(other.moved_), copied_(other.copied_) {
+ *moved_ = true;
+ }
+ TrackInitialization& operator=(TrackInitialization const&) = default;
+ TrackInitialization& operator=(TrackInitialization&&) = default;
+ bool* moved_;
+ bool* copied_;
+};
+
+enum class IsConst : bool { no, yes };
+
+template <std::forward_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>>
+struct View : std::ranges::view_base {
+ constexpr explicit View(Iter b, Sent e) : begin_(b), end_(e) {}
+ constexpr Iter begin() { return begin_; }
+ constexpr Sent end() { return end_; }
+
+private:
+ Iter begin_;
+ Sent end_;
+};
+
+template <class I, class S>
+View(I b, S e) -> View<I, S>;
+
+struct IntWrapper {
+ constexpr IntWrapper(int v) : value_(v) {}
+
+ int value_;
+ constexpr bool lessEqual(IntWrapper other) const { return value_ <= other.value_; }
+};
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index c2251c8d817c1ce..ac342aff0beb701 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -801,7 +801,6 @@ def add_version_header(tc):
"name": "__cpp_lib_ranges_chunk_by",
"values": {"c++23": 202202},
"headers": ["ranges"],
- "unimplemented": True,
},
{
"name": "__cpp_lib_ranges_iota",
More information about the libcxx-commits
mailing list