diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -108,7 +108,7 @@ "`P0784R7 `__","CWG","More constexpr containers","Cologne","|Complete|","12.0" "`P0980R1 `__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0" "`P1004R2 `__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0" -"`P1035R7 `__","LWG","Input Range Adaptors","Cologne","|In Progress|","" +"`P1035R7 `__","LWG","Input Range Adaptors, Todo: elements_view and drop_while_view","Cologne","|In Progress|","" "`P1065R2 `__","LWG","Constexpr INVOKE","Cologne","|Complete|","12.0" "`P1135R6 `__","LWG","The C++20 Synchronization Library","Cologne","|Complete|","11.0" "`P1207R4 `__","LWG","Movability of Single-pass Iterators","Cologne","|Complete|","15.0" diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -9,7 +9,7 @@ "`3435 `__","``three_way_comparable_with, reverse_iterator>``","November 2020","|Complete|","13.0" "`3432 `__","Missing requirement for ``comparison_category``","November 2020","|Complete|","16.0","|spaceship|" "`3447 `__","Deduction guides for ``take_view`` and ``drop_view`` have different constraints","November 2020","|Complete|","14.0" -"`3450 `__","The const overloads of ``take_while_view::begin/end`` are underconstrained","November 2020","","","|ranges|" +"`3450 `__","The const overloads of ``take_while_view::begin/end`` are underconstrained","November 2020","|Complete|","16.0","|ranges|" "`3464 `__","``istream::gcount()`` can overflow","November 2020","","" "`2731 `__","Existence of ``lock_guard::mutex_type`` typedef unclear","November 2020","|Complete|","5.0" "`2743 `__","P0083R3 ``node_handle`` private members missing ""exposition only"" comment","November 2020","|Nothing To Do|","" @@ -34,7 +34,7 @@ "`3437 `__","``__cpp_lib_polymorphic_allocator`` is in the wrong header","November 2020","|Complete|","14.0" "`3446 `__","``indirectly_readable_traits`` ambiguity for types with both ``value_type`` and ``element_type``","November 2020","|Complete|","14.0","|ranges|" "`3448 `__","``transform_view``'s ``sentinel`` not comparable with ``iterator``","November 2020","","","|ranges|" -"`3449 `__","``take_view`` and ``take_while_view``'s ``sentinel`` not comparable with their ``const iterator``","November 2020","","","|ranges|" +"`3449 `__","``take_view`` and ``take_while_view``'s ``sentinel`` not comparable with their ``const iterator``","November 2020","Complete","16.0","|ranges|" "`3453 `__","Generic code cannot call ``ranges::advance(i, s)``","November 2020","|Nothing To Do|","","|ranges|" "`3455 `__","Incorrect Postconditions on ``unique_ptr`` move assignment","November 2020","|Nothing To Do|","" "`3460 `__","Unimplementable ``noop_coroutine_handle`` guarantees","November 2020","|Complete|","14.0" @@ -175,7 +175,7 @@ "`3704 `__","LWG 2059 added overloads that might be ill-formed for sets","July 2022","","" "`3705 `__","Hashability shouldn't depend on basic_string's allocator","July 2022","|Complete|","16.0" "`3707 `__","chunk_view::outer-iterator::value_type::size should return unsigned type","July 2022","","","|ranges|" -"`3708 `__","``take_while_view::sentinel``'s conversion constructor should move","July 2022","","","|ranges|" +"`3708 `__","``take_while_view::sentinel``'s conversion constructor should move","July 2022","Complete","16.0","|ranges|" "`3709 `__","LWG-3703 was underly ambitious","July 2022","","" "`3710 `__","The ``end`` of ``chunk_view`` for input ranges can be ``const``","July 2022","","","|ranges|" "`3711 `__","Missing preconditions for slide_view constructor","July 2022","","","|ranges|" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -495,6 +495,7 @@ __ranges/size.h __ranges/subrange.h __ranges/take_view.h + __ranges/take_while_view.h __ranges/transform_view.h __ranges/view_interface.h __ranges/views.h diff --git a/libcxx/include/__ranges/take_while_view.h b/libcxx/include/__ranges/take_while_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/take_while_view.h @@ -0,0 +1,177 @@ +// -*- 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_TAKE_WHILE_VIEW_H +#define _LIBCPP___RANGES_TAKE_WHILE_VIEW_H + +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__config> +#include <__functional/bind_back.h> +#include <__functional/invoke.h> +#include <__iterator/concepts.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/copyable_box.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/view_interface.h> +#include <__type_traits/decay.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/is_object.h> +#include <__type_traits/maybe_const.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_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +namespace ranges { + +// The spec uses the unnamed requirement inside the `begin` and `end` member functions: +// constexpr auto begin() const +// requires range && indirect_unary_predicate> +// However, due to a clang-14 and clang-15 bug, the above produces a hard error when `const V` is not a range. +// The workaround is to create a named concept and use the concept instead. +// As of take_while_view is implemented, the clang-trunk has already fixed the bug. +// It is OK to remove the workaround once our CI no longer uses clang-14, clang-15 based compilers, +// because we don't actually expect a lot of vendors to ship a new libc++ with an old clang. +template +concept __take_while_const_is_range = + range && indirect_unary_predicate>; + +template + requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate> +class take_while_view : public view_interface> { + template + class __sentinel; + + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + _LIBCPP_NO_UNIQUE_ADDRESS __copyable_box<_Pred> __pred_; + +public: + _LIBCPP_HIDE_FROM_ABI take_while_view() + requires default_initializable<_View> && default_initializable<_Pred> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr take_while_view(_View __base, _Pred __pred) + : __base_(std::move(__base)), __pred_(std::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 auto begin() + requires(!__simple_view<_View>) + { + return ranges::begin(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires __take_while_const_is_range<_View, _Pred> + { + return ranges::begin(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + return __sentinel(ranges::end(__base_), std::addressof(*__pred_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires __take_while_const_is_range<_View, _Pred> + { + return __sentinel(ranges::end(__base_), std::addressof(*__pred_)); + } +}; + +template +take_while_view(_Range&&, _Pred) -> take_while_view, _Pred>; + +template + requires input_range<_View> && is_object_v<_Pred> && indirect_unary_predicate> +template +class take_while_view<_View, _Pred>::__sentinel { + using _Base = __maybe_const<_Const, _View>; + + sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + const _Pred* __pred_ = nullptr; + + friend class __sentinel; + +public: + _LIBCPP_HIDE_FROM_ABI __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(sentinel_t<_Base> __end, const _Pred* __pred) + : __end_(std::move(__end)), __pred_(__pred) {} + + _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel __s) + requires _Const && convertible_to, sentinel_t<_Base>> + : __end_(std::move(__s.__end_)), __pred_(__s.__pred_) {} + + _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const iterator_t<_Base>& __x, const __sentinel& __y) { + return __x == __y.__end_ || !std::invoke(*__y.__pred_, *__x); + } + + template + requires sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const iterator_t<__maybe_const<_OtherConst, _View>>& __x, const __sentinel& __y) { + return __x == __y.__end_ || !std::invoke(*__y.__pred_, *__x); + } +}; + +namespace views { +namespace __take_while { + +struct __fn { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pred&& __pred) const + noexcept(noexcept(/**/ take_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)))) + -> decltype(/*--*/ take_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))) { + return /*-------------*/ take_while_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)); + } + + template + requires constructible_from, _Pred> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pred&& __pred) const + noexcept(is_nothrow_constructible_v, _Pred>) { + return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pred>(__pred))); + } +}; + +} // namespace __take_while + +inline namespace __cpo { +inline constexpr auto take_while = __take_while::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_TAKE_WHILE_VIEW_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1031,6 +1031,7 @@ module size { private header "__ranges/size.h" } module subrange { private header "__ranges/subrange.h" } module take_view { private header "__ranges/take_view.h" } + module take_while_view { private header "__ranges/take_while_view.h" } module transform_view { private header "__ranges/transform_view.h" export functional.__functional.bind_back diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -198,6 +198,14 @@ template inline constexpr bool enable_borrowed_range> = enable_borrowed_range; + // [range.take.while], take while view + template + requires input_range && is_object_v && + indirect_unary_predicate> + class take_while_view; + + namespace views { inline constexpr unspecified take_while = unspecified; } + template requires is_object_v class single_view; @@ -311,6 +319,7 @@ #include <__ranges/size.h> #include <__ranges/subrange.h> #include <__ranges/take_view.h> +#include <__ranges/take_while_view.h> #include <__ranges/transform_view.h> #include <__ranges/view_interface.h> #include <__ranges/views.h> diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -526,6 +526,7 @@ #include <__ranges/size.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/size.h'}} #include <__ranges/subrange.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/subrange.h'}} #include <__ranges/take_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/take_view.h'}} +#include <__ranges/take_while_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/take_while_view.h'}} #include <__ranges/transform_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/transform_view.h'}} #include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}} #include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/adaptor.pass.cpp @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::take_while + +#include +#include +#include +#include +#include + +#include "types.h" + +struct Pred { + constexpr bool operator()(int i) const { return i < 3; } +}; + +struct Foo {}; + +struct MoveOnlyView : IntBufferViewBase { + using IntBufferViewBase::IntBufferViewBase; + MoveOnlyView(const MoveOnlyView&) = delete; + MoveOnlyView& operator=(const MoveOnlyView&) = delete; + MoveOnlyView(MoveOnlyView&&) = default; + MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr const int* begin() const { return buffer_; } + constexpr const int* end() const { return buffer_ + size_; } +}; + +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); + +template +concept CanBePiped = + requires(View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; + }; + +static_assert(!CanBePiped); +static_assert(CanBePiped); +static_assert(!CanBePiped); +static_assert(CanBePiped); +static_assert(!CanBePiped); + +constexpr bool test() { + int buff[] = {1, 2, 3, 4, 3, 2, 1}; + + // Test `views::take_while(p)(v)` + { + using Result = std::ranges::take_while_view; + std::same_as decltype(auto) result = std::views::take_while(Pred{})(MoveOnlyView{buff}); + auto expected = {1, 2}; + assert(std::ranges::equal(result, expected)); + } + { + auto const partial = std::views::take_while(Pred{}); + using Result = std::ranges::take_while_view; + std::same_as decltype(auto) result = partial(MoveOnlyView{buff}); + auto expected = {1, 2}; + assert(std::ranges::equal(result, expected)); + } + + // Test `v | views::take_while(p)` + { + using Result = std::ranges::take_while_view; + std::same_as decltype(auto) result = MoveOnlyView{buff} | std::views::take_while(Pred{}); + auto expected = {1, 2}; + assert(std::ranges::equal(result, expected)); + } + { + auto const partial = std::views::take_while(Pred{}); + using Result = std::ranges::take_while_view; + std::same_as decltype(auto) result = MoveOnlyView{buff} | partial; + auto expected = {1, 2}; + assert(std::ranges::equal(result, expected)); + } + + // Test `views::take_while(v, p)` + { + using Result = std::ranges::take_while_view; + std::same_as decltype(auto) result = std::views::take_while(MoveOnlyView{buff}, Pred{}); + auto expected = {1, 2}; + assert(std::ranges::equal(result, expected)); + } + + // Test adaptor | adaptor + { + struct Pred2 { + constexpr bool operator()(int i) const { return i < 2; } + }; + auto const partial = std::views::take_while(Pred{}) | std::views::take_while(Pred2{}); + using Result = std::ranges::take_while_view, Pred2>; + std::same_as decltype(auto) result = MoveOnlyView{buff} | partial; + auto expected = {1}; + assert(std::ranges::equal(result, expected)); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/base.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr V base() const & requires copy_constructible { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include +#include +#include +#include + +#include "MoveOnly.h" +#include "types.h" + +struct View : std::ranges::view_interface { + int i; + int* begin() const; + int* end() const; +}; + +struct MoveOnlyView : View { + MoveOnly mo; +}; + +template +concept HasBase = requires(T&& t) { std::forward(t).base(); }; + +struct Pred { + constexpr bool operator()(int i) const { return i > 5; } +}; + +static_assert(HasBase const&>); +static_assert(HasBase&&>); + +static_assert(!HasBase const&>); +static_assert(HasBase&&>); + +constexpr bool test() { + // const & + { + const std::ranges::take_while_view twv{View{{}, 5}, {}}; + std::same_as decltype(auto) v = twv.base(); + assert(v.i == 5); + } + + // & + { + std::ranges::take_while_view twv{View{{}, 5}, {}}; + std::same_as decltype(auto) v = twv.base(); + assert(v.i == 5); + } + + // && + { + std::ranges::take_while_view twv{View{{}, 5}, {}}; + std::same_as decltype(auto) v = std::move(twv).base(); + assert(v.i == 5); + } + + // const && + { + const std::ranges::take_while_view twv{View{{}, 5}, {}}; + std::same_as decltype(auto) v = std::move(twv).base(); + assert(v.i == 5); + } + + // move only + { + std::ranges::take_while_view twv{MoveOnlyView{{}, 5}, {}}; + std::same_as decltype(auto) v = std::move(twv).base(); + assert(v.mo.get() == 5); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/begin.pass.cpp @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto begin() requires (!simple-view) +// { return ranges::begin(base_); } +// +// constexpr auto begin() const +// requires range && +// indirect_unary_predicate> +// { return ranges::begin(base_); } + +#include +#include +#include +#include + +#include "types.h" + +// Test Constraints +template +concept HasConstBegin = requires(const T& ct) { ct.begin(); }; + +template +concept HasBegin = requires(T& t) { t.begin(); }; + +template +concept HasConstAndNonConstBegin = + HasConstBegin && + requires(T& t, const T& ct) { requires !std::same_as; }; + +template +concept HasOnlyNonConstBegin = HasBegin && !HasConstBegin; + +template +concept HasOnlyConstBegin = HasConstBegin && !HasConstAndNonConstBegin; + +struct Pred { + constexpr bool operator()(int i) const { return i > 5; } +}; + +static_assert(HasOnlyConstBegin>); + +static_assert(HasOnlyNonConstBegin>); + +static_assert(HasConstAndNonConstBegin>); + +struct NotPredForConst { + constexpr bool operator()(int& i) const { return i > 5; } +}; +static_assert(HasOnlyNonConstBegin>); + +constexpr bool test() { + // simple-view + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + SimpleView v{buffer}; + std::ranges::take_while_view twv(v, Pred{}); + std::same_as decltype(auto) it1 = twv.begin(); + assert(it1 == buffer); + std::same_as decltype(auto) it2 = std::as_const(twv).begin(); + assert(it2 == buffer); + } + + // const not range + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + ConstNotRange v{buffer}; + std::ranges::take_while_view twv(v, Pred{}); + std::same_as decltype(auto) it1 = twv.begin(); + assert(it1 == buffer); + } + + // NonSimple + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + NonSimple v{buffer}; + std::ranges::take_while_view twv(v, Pred{}); + std::same_as decltype(auto) it1 = twv.begin(); + assert(it1 == buffer); + std::same_as decltype(auto) it2 = std::as_const(twv).begin(); + assert(it2 == buffer); + } + + // NotPredForConst + // LWG 3450: The const overloads of `take_while_view::begin/end` are underconstrained + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + NonSimple v{buffer}; + std::ranges::take_while_view twv(v, NotPredForConst{}); + std::same_as decltype(auto) it1 = twv.begin(); + assert(it1 == buffer); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctad.compile.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 + +// template +// take_while_view(R&&, Pred) -> take_while_view, Pred>; + +#include +#include +#include + +#include "types.h" + +struct Container { + int* begin() const; + int* end() const; +}; + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct Pred { + bool operator()(int i) const; +}; + +bool pred(int); + +static_assert(std::is_same_v, Pred>>); + +static_assert(std::is_same_v>); + +static_assert(std::is_same_v>); + +void testRef() { + Container c{}; + Pred p{}; + static_assert(std::is_same_v, Pred>>); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.default.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// take_while_view() requires default_initializable && default_initializable = default; + +#include +#include +#include + +template +struct View : std::ranges::view_base { + int i; + constexpr explicit View() + requires defaultInitable + = default; + int* begin() const; + int* end() const; +}; + +template +struct Pred { + int i; + constexpr explicit Pred() + requires defaultInitable + = default; + bool operator()(int) const; +}; + +// clang-format off +static_assert( std::is_default_constructible_v, Pred>>); +static_assert(!std::is_default_constructible_v, Pred>>); +static_assert(!std::is_default_constructible_v, Pred>>); +static_assert(!std::is_default_constructible_v, Pred>>); +// clang-format on + +constexpr bool test() { + { + std::ranges::take_while_view, Pred> twv = {}; + assert(twv.base().i == 0); + assert(twv.pred().i == 0); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/ctor.view.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr take_while_view(V base, Pred pred); + +#include +#include +#include +#include + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + MoveOnly mo; + int* begin() const; + int* end() const; +}; + +struct Pred { + bool copied = false; + bool moved = false; + constexpr Pred() = default; + constexpr Pred(Pred&&) : moved(true) {} + constexpr Pred(const Pred&) : copied(true) {} + bool operator()(int) const; +}; + +constexpr bool test() { + { + std::ranges::take_while_view twv = {View{{}, MoveOnly{5}}, Pred{}}; + assert(twv.pred().moved); + assert(!twv.pred().copied); + assert(std::move(twv).base().mo.get() == 5); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/end.pass.cpp @@ -0,0 +1,110 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr auto end() requires (!simple-view) +// { return sentinel(ranges::end(base_), addressof(*pred_)); } +// constexpr auto end() const +// requires range && +// indirect_unary_predicate> +// { return sentinel(ranges::end(base_), addressof(*pred_)); } + +#include +#include +#include +#include + +#include "types.h" + +// Test Constraints +template +concept HasConstEnd = requires(const T& ct) { ct.end(); }; + +template +concept HasEnd = requires(T& t) { t.end(); }; + +template +concept HasConstAndNonConstEnd = + HasConstEnd && requires(T& t, const T& ct) { requires !std::same_as; }; + +template +concept HasOnlyNonConstEnd = HasEnd && !HasConstEnd; + +template +concept HasOnlyConstEnd = HasConstEnd && !HasConstAndNonConstEnd; + +struct Pred { + constexpr bool operator()(int i) const { return i < 5; } +}; + +static_assert(HasOnlyConstEnd>); + +static_assert(HasOnlyNonConstEnd>); + +static_assert(HasConstAndNonConstEnd>); + +struct NotPredForConst { + constexpr bool operator()(int& i) const { return i > 5; } +}; +static_assert(HasOnlyNonConstEnd>); + +constexpr bool test() { + // simple-view + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + SimpleView v{buffer}; + std::ranges::take_while_view twv(v, Pred{}); + decltype(auto) it1 = twv.end(); + assert(it1 == buffer + 4); + decltype(auto) it2 = std::as_const(twv).end(); + assert(it2 == buffer + 4); + + static_assert(std::same_as); + } + + // const not range + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + ConstNotRange v{buffer}; + std::ranges::take_while_view twv(v, Pred{}); + decltype(auto) it1 = twv.end(); + assert(it1 == buffer + 4); + } + + // NonSimple + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + NonSimple v{buffer}; + std::ranges::take_while_view twv(v, Pred{}); + decltype(auto) it1 = twv.end(); + assert(it1 == buffer + 4); + decltype(auto) it2 = std::as_const(twv).end(); + assert(it2 == buffer + 4); + + static_assert(!std::same_as); + } + + // NotPredForConst + // LWG 3450: The const overloads of `take_while_view::begin/end` are underconstrained + { + int buffer[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1}; + NonSimple v{buffer}; + std::ranges::take_while_view twv(v, NotPredForConst{}); + decltype(auto) it1 = twv.end(); + assert(it1 == buffer); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/general.pass.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Some basic examples of how take_while_view might be used in the wild. This is a general +// collection of sample algorithms and functions that try to mock general usage of +// this view. + +#include +#include +#include + +int main(int, char**) { + { + auto input = {0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 0}; + auto small = [](const auto x) noexcept { return x < 5; }; + auto small_ints = input | std::views::take_while(small); + auto expected = {0, 1, 2, 3, 4}; + assert(std::ranges::equal(small_ints, expected)); + } + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/pred.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/pred.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// constexpr const Pred& pred() const; + +#include +#include +#include +#include + +struct View : std::ranges::view_interface { + int* begin() const; + int* end() const; +}; + +struct Pred { + int i; + bool operator()(int) const; +}; + +constexpr bool test() { + // & + { + std::ranges::take_while_view twv{{}, Pred{5}}; + decltype(auto) x = twv.pred(); + static_assert(std::same_as); + assert(x.i == 5); + } + + // const & + { + const std::ranges::take_while_view twv{{}, Pred{5}}; + decltype(auto) x = twv.pred(); + static_assert(std::same_as); + assert(x.i == 5); + } + + // && + { + std::ranges::take_while_view twv{{}, Pred{5}}; + decltype(auto) x = std::move(twv).pred(); + static_assert(std::same_as); + assert(x.i == 5); + } + + // const && + { + const std::ranges::take_while_view twv{{}, Pred{5}}; + decltype(auto) x = std::move(twv).pred(); + static_assert(std::same_as); + assert(x.i == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/range.concept.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/range.concept.compile.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// concept checking +// template +// requires input_range && is_object_v && +// indirect_unary_predicate> +// class take_while_view; + +#include +#include + +#include "test_iterators.h" + +template +using Range = std::ranges::subrange>; + +template +struct Pred { + bool operator()(const Val&) const; +}; + +template +concept HasTakeWhileView = requires { typename std::ranges::take_while_view; }; + +static_assert(HasTakeWhileView, bool (*)(int)>); +static_assert(HasTakeWhileView, Pred>); + +// !view +static_assert(!HasTakeWhileView, Pred>); + +// !input_range +static_assert(!HasTakeWhileView>, bool (*)(int)>); + +// !is_object_v +static_assert(!HasTakeWhileView, Pred&>); + +// !indirect_unary_predicate> +static_assert(!HasTakeWhileView, int>); +static_assert(!HasTakeWhileView, Pred>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.base.pass.cpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr explicit sentinel(sentinel_t end, const Pred* pred); + +#include +#include +#include + +#include "../types.h" + +struct Sent { + int i; + + friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + Sent end(); +}; + +struct Pred { + bool operator()(int i) const; +}; + +// Test explicit +template +void conversion_test(T); + +template +concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test({std::forward(args)...}); }; +static_assert(ImplicitlyConstructible); + +static_assert(std::is_constructible_v>, + std::ranges::sentinel_t, + const Pred*>); +static_assert(!ImplicitlyConstructible>, + std::ranges::sentinel_t, + const Pred*>); + +constexpr bool test() { + // base is init correctly + { + using R = std::ranges::take_while_view; + using Sentinel = std::ranges::sentinel_t; + + Sentinel s1(Sent{5}, nullptr); + assert(s1.base().i == 5); + } + + // pred is init correctly + { + bool called = false; + auto pred = [&](int) { + called = true; + return false; + }; + + using R = std::ranges::take_while_view; + using Sentinel = std::ranges::sentinel_t; + + int i = 10; + int* iter = &i; + Sentinel s(Sent{0}, &pred); + + bool b = iter == s; + assert(called); + assert(b); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.convert.pass.cpp @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// constexpr sentinel(sentinel s) +// requires Const && convertible_to, sentinel_t>; + +#include +#include + +#include "../types.h" + +struct Sent { + int i; + constexpr Sent() = default; + constexpr Sent(int ii) : i(ii) {} + friend constexpr bool operator==(int* iter, const Sent& s) { return s.i > *iter; } +}; + +struct ConstSent { + int i; + constexpr ConstSent() = default; + constexpr ConstSent(int ii) : i(ii) {} + constexpr ConstSent(const Sent& s) : i(s.i) {} + friend constexpr bool operator==(int* iter, const ConstSent& s) { return s.i > *iter; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + Sent end(); + ConstSent end() const; +}; + +struct Pred { + bool operator()(int i) const; +}; + +struct NonConvertConstSent { + int i; + constexpr NonConvertConstSent() = default; + constexpr NonConvertConstSent(int ii) : i(ii) {} + friend constexpr bool operator==(int* iter, const NonConvertConstSent& s) { return s.i > *iter; } +}; + +struct NonConvertConstSentRange : std::ranges::view_base { + int* begin() const; + Sent end(); + NonConvertConstSent end() const; +}; + +// Test Constraint +static_assert(std::is_constructible_v>, + std::ranges::sentinel_t>>); + +// !Const +static_assert(!std::is_constructible_v>, + std::ranges::sentinel_t>>); + +// !convertible_to, sentinel_t> +static_assert(!std::is_constructible_v< + std::ranges::sentinel_t>, + std::ranges::sentinel_t>>); + +constexpr bool test() { + // base is init correctly + { + using R = std::ranges::take_while_view; + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::same_as); + + Sentinel s1(Sent{5}, nullptr); + ConstSentinel s2 = s1; + assert(s2.base().i == 5); + } + + // pred is init correctly + { + bool called = false; + auto pred = [&](int) { + called = true; + return false; + }; + + using R = std::ranges::take_while_view; + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::same_as); + + int i = 10; + int* iter = &i; + Sentinel s1(Sent{0}, &pred); + ConstSentinel s2 = s1; + + [[maybe_unused]] bool b = iter == s2; + assert(called); + } + + // LWG 3708 `take_while_view::sentinel`'s conversion constructor should move + { + struct MoveOnlyConvert { + int i; + constexpr MoveOnlyConvert() = default; + constexpr MoveOnlyConvert(Sent&& s) : i(s.i) { s.i = 0; } + constexpr bool operator==(int* iter) const { return i > *iter; } + }; + + struct Rng : std::ranges::view_base { + int* begin() const; + Sent end(); + MoveOnlyConvert end() const; + }; + + using R = std::ranges::take_while_view; + using Sentinel = std::ranges::sentinel_t; + using ConstSentinel = std::ranges::sentinel_t; + static_assert(!std::same_as); + + Sentinel s1(Sent{5}, nullptr); + ConstSentinel s2 = s1; + assert(s2.base().i == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/ctor.default.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// sentinel() = default; + +#include +#include + +struct Sent { + bool b; // deliberately uninitialised + + friend constexpr bool operator==(int*, const Sent& s) { return s.b; } +}; + +struct Range : std::ranges::view_base { + int* begin() const; + Sent end(); +}; + +constexpr bool test() { + { + using R = std::ranges::take_while_view; + using Sentinel = std::ranges::sentinel_t; + + Sentinel s1 = {}; + assert(!s1.base().b); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/equality.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/equality.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.take.while/sentinel/equality.pass.cpp @@ -0,0 +1,204 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// friend constexpr bool operator==(const iterator_t& x, const sentinel& y); +// +// template +// requires sentinel_for, iterator_t>> +// friend constexpr bool operator==(const iterator_t>& x, +// const sentinel& y); + +#include +#include +#include + +#include "../types.h" + +template +struct Iter { + int* it_; + + using value_type = int; + using difference_type = intptr_t; + using iterator_concept = std::input_iterator_tag; + + constexpr decltype(auto) operator*() const { return *it_; } + constexpr Iter& operator++() { + ++it_; + return *this; + } + constexpr void operator++(int) { ++it_; } +}; + +template +struct Sent { + int* end_; + + constexpr bool operator==(const Iter& i) const { return i.it_ == end_; } +}; + +template +struct CrossComparableSent { + int* end_; + + template + constexpr bool operator==(const Iter& i) const { + return i.it_ == end_; + } +}; + +template