diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -155,7 +155,7 @@ `[range.transform] `_,`transform_view `_,[range.all],Zoe Carver,✅ `[range.take] `_,`take_view `_,[range.all],Zoe Carver,✅ `[range.join] `_,`join_view `_,[range.all],Zoe Carver,✅ -`[range.split] `_,`split_view (renamed to lazy_split_view by P2210) `_,[range.all],Zoe Carver,In progress +`[range.split] `_,`split_view (renamed to lazy_split_view by P2210) `_,[range.all],Zoe Carver and Konstantin Varlamov,✅ `[range.counted] `_,`view::counted `_,[range.subrange],Zoe Carver,✅ `[range.common] `_,`common_view `_,[range.all],Zoe Carver,✅ `[range.reverse] `_,`reverse_view `_,[range.all],Zoe Carver,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -351,6 +351,7 @@ __ranges/enable_view.h __ranges/iota_view.h __ranges/join_view.h + __ranges/lazy_split_view.h __ranges/non_propagating_cache.h __ranges/owning_view.h __ranges/range_adaptor.h diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/lazy_split_view.h @@ -0,0 +1,428 @@ +// -*- 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_LAZY_SPLIT_VIEW_H +#define _LIBCPP___RANGES_LAZY_SPLIT_VIEW_H + +#include <__algorithm/find.h> +#include <__algorithm/ranges_mismatch.h> +#include <__concepts/constructible.h> +#include <__config> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/default_sentinel.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/indirectly_comparable.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/iter_swap.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/single_view.h> +#include <__ranges/view_interface.h> +#include <__utility/move.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_RANGES) + +namespace ranges { + +template _Sp, class _Tp> + requires indirect_binary_predicate +_LIBCPP_HIDE_FROM_ABI constexpr +_Ip __lazy_split_view_find(_Ip __first, _Sp __last, const _Tp& __value) { + for (; __first != __last; ++__first) { + if (*__first == __value) { + break; + } + } + + return __first; +} + +template struct __require_constant; + +template +concept __tiny_range = + sized_range<_Range> && + requires { typename __require_constant::size()>; } && + (remove_reference_t<_Range>::size() <= 1); + +template + requires view<_View> && view<_Pattern> && + indirectly_comparable, iterator_t<_Pattern>, ranges::equal_to> && + (forward_range<_View> || __tiny_range<_Pattern>) +class lazy_split_view : public view_interface> { + + _View __base_ = _View(); + _Pattern __pattern_ = _Pattern(); + + using _MaybeCurrent = _If, __non_propagating_cache>, __empty_cache>; + [[no_unique_address]] _MaybeCurrent __current_ = _MaybeCurrent(); + + template struct __outer_iterator; + template struct __inner_iterator; + +public: + _LIBCPP_HIDE_FROM_ABI + lazy_split_view() + requires default_initializable<_View> && default_initializable<_Pattern> = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr lazy_split_view(_View __base, _Pattern __pattern) + : __base_(std::move(__base)), __pattern_(std::move(__pattern)) {} + + template + requires constructible_from<_View, views::all_t<_Range>> && + constructible_from<_Pattern, single_view>> + _LIBCPP_HIDE_FROM_ABI + constexpr lazy_split_view(_Range&& __r, range_value_t<_Range> __e) + : __base_(views::all(std::forward<_Range>(__r))) + , __pattern_(ranges::single_view(std::move(__e))) {} + + _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 auto begin() { + if constexpr (forward_range<_View>) { + return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::begin(__base_)}; + } else { + __current_.__emplace(ranges::begin(__base_)); + return __outer_iterator{*this}; + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() const requires forward_range<_View> && forward_range { + return __outer_iterator{*this, ranges::begin(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() requires forward_range<_View> && common_range<_View> { + return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() const { + if constexpr (forward_range<_View> && forward_range && common_range) { + return __outer_iterator{*this, ranges::end(__base_)}; + } else { + return default_sentinel; + } + } + +private: + + template + struct __outer_iterator_category {}; + + template + struct __outer_iterator_category<_Tp> { + using iterator_category = input_iterator_tag; + }; + + template struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> { + private: + template + friend class __inner_iterator; + friend __outer_iterator; + + using _Parent = __maybe_const<_Const, lazy_split_view>; + using _Base = __maybe_const<_Const, _View>; + + _Parent* __parent_ = nullptr; + using _MaybeCurrent = _If, iterator_t<_Base>, __empty_cache>; + [[no_unique_address]] _MaybeCurrent __current_ = _MaybeCurrent(); + bool __trailing_empty_ = false; + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto& __current() noexcept { + if constexpr (forward_range<_View>) { + return __current_; + } else { + return *__parent_->__current_; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto& __current() const noexcept { + if constexpr (forward_range<_View>) { + return __current_; + } else { + return *__parent_->__current_; + } + } + + public: + // using iterator_category = inherited; + using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; + using difference_type = range_difference_t<_Base>; + + struct value_type : view_interface { + private: + __outer_iterator __i_ = __outer_iterator(); + + public: + _LIBCPP_HIDE_FROM_ABI + value_type() = default; + _LIBCPP_HIDE_FROM_ABI + constexpr explicit value_type(__outer_iterator __i) + : __i_(std::move(__i)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; } + _LIBCPP_HIDE_FROM_ABI + constexpr default_sentinel_t end() const { return default_sentinel; } + }; + + _LIBCPP_HIDE_FROM_ABI + __outer_iterator() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __outer_iterator(_Parent& __parent) + requires (!forward_range<_Base>) + : __parent_(std::addressof(__parent)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current) + requires forward_range<_Base> + : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __outer_iterator(__outer_iterator __i) + requires _Const && convertible_to, iterator_t<_Base>> + : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr value_type operator*() const { return value_type{*this}; } + + _LIBCPP_HIDE_FROM_ABI + constexpr __outer_iterator& operator++() { + const auto __end = ranges::end(__parent_->__base_); + if (__current() == __end) { + __trailing_empty_ = false; + return *this; + } + + const auto [__pbegin, __pend] = ranges::subrange{__parent_->__pattern_}; + if (__pbegin == __pend) { + ++__current(); + + } else if constexpr (__tiny_range<_Pattern>) { + // TODO(varconst): use the actual `ranges::find` once it lands. + __current() = ranges::__lazy_split_view_find(std::move(__current()), __end, *__pbegin); + if (__current() != __end) { + ++__current(); + if (__current() == __end) + __trailing_empty_ = true; + } + + } else { + do { + const auto [__b, __p] = std::ranges::mismatch(__current(), __end, __pbegin, __pend); + if (__p == __pend) { + __current() = __b; + if (__current() == __end) { + __trailing_empty_ = true; + } + break; // The pattern matched; skip it. + } + } while (++__current() != __end); + } + + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Base>) { + auto __tmp = *this; + ++*this; + return __tmp; + + } else { + ++*this; + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __outer_iterator& __x, const __outer_iterator& __y) + requires forward_range<_Base> { + return __x.__current_ == __y.__current_ && __x.__trailing_empty_ == __y.__trailing_empty_; + } + + friend constexpr bool operator==(const __outer_iterator& __x, default_sentinel_t) { + return __x.__current() == ranges::end(__x.__parent_->__base_) && !__x.__trailing_empty_; + } + }; + + template + struct __inner_iterator_category {}; + + template + struct __inner_iterator_category<_Tp> { + using iterator_category = _If< + derived_from>::iterator_category, forward_iterator_tag>, + forward_iterator_tag, + typename iterator_traits>::iterator_category + >; + }; + + template struct __inner_iterator : __inner_iterator_category<__maybe_const<_Const, _View>> { + private: + using _Base = __maybe_const<_Const, _View>; + __outer_iterator<_Const> __i_ = __outer_iterator<_Const>(); + bool __incremented_ = false; + + public: + // using iterator_category = inherited; + using iterator_concept = typename __outer_iterator<_Const>::iterator_concept; + using value_type = range_value_t<_Base>; + using difference_type = range_difference_t<_Base>; + + _LIBCPP_HIDE_FROM_ABI + constexpr __inner_iterator() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) + : __i_(std::move(__i)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); } + _LIBCPP_HIDE_FROM_ABI + constexpr iterator_t<_Base> base() && + requires forward_range<_View> { return std::move(__i_.__current()); } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator*() const { return *__i_.__current(); } + + _LIBCPP_HIDE_FROM_ABI + constexpr __inner_iterator& operator++() { + __incremented_ = true; + + if constexpr (!forward_range<_Base>) { + if constexpr (_Pattern::size() == 0) { + return *this; + } + } + + ++__i_.__current(); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Base>) { + auto __tmp = *this; + ++*this; + return __tmp; + + } else { + ++*this; + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __inner_iterator& __x, const __inner_iterator& __y) + requires forward_range<_Base> { + return __x.__i_.__current() == __y.__i_.__current(); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __inner_iterator& __x, default_sentinel_t) { + auto [__pcur, __pend] = ranges::subrange{__x.__i_.__parent_->__pattern_}; + auto __end = ranges::end(__x.__i_.__parent_->__base_); + + if constexpr (__tiny_range<_Pattern>) { + const auto& __cur = __x.__i_.__current(); + if (__cur == __end) return true; + if (__pcur == __pend) return __x.__incremented_; + + return *__cur == *__pcur; + + } else { + auto __cur = __x.__i_.__current(); + if (__cur == __end) return true; + if (__pcur == __pend) return __x.__incremented_; + + do { + if (*__cur != *__pcur) return false; + if (++__pcur == __pend) return true; + } while (++__cur != __end); + + return false; + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr decltype(auto) iter_move(const __inner_iterator& __i) + noexcept(noexcept(ranges::iter_move(__i.__i_.__current()))) { + return ranges::iter_move(__i.__i_.__current()); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr void iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) + noexcept(noexcept(ranges::iter_swap(__x.__i_.__current(), __y.__i_.__current()))) + requires indirectly_swappable> { + ranges::iter_swap(__x.__i_.__current(), __y.__i_.__current()); + } + }; + +}; + +template +lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view, views::all_t<_Pattern>>; + +template +lazy_split_view(_Range&&, range_value_t<_Range>) + -> lazy_split_view, single_view>>; + +namespace views { +namespace __lazy_split_view { +struct __fn : __range_adaptor_closure<__fn> { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, _Pattern&& __pattern) const + noexcept(noexcept(lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)))) + -> decltype( lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))) + { return lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)); } + + template + requires constructible_from, _Pattern> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Pattern&& __pattern) const + noexcept(is_nothrow_constructible_v, _Pattern>) { + return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pattern>(__pattern))); + } +}; +} // namespace __lazy_split_view + +inline namespace __cpo { + inline constexpr auto lazy_split = __lazy_split_view::__fn{}; +} // namespace __cpo +} // namespace views + +} // namespace ranges + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_LAZY_SPLIT_VIEW_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -825,6 +825,7 @@ module enable_view { private header "__ranges/enable_view.h" } module iota_view { private header "__ranges/iota_view.h" } module join_view { private header "__ranges/join_view.h" } + module lazy_split_view { private header "__ranges/lazy_split_view.h" } module non_propagating_cache { private header "__ranges/non_propagating_cache.h" } module owning_view { private header "__ranges/owning_view.h" } module range_adaptor { private header "__ranges/range_adaptor.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -196,6 +196,20 @@ template requires view && input_range> class join_view; + + // [range.lazy.split], lazy split view + template + concept tiny-range = see below; // exposition only + + template + requires view && view && + indirectly_comparable, iterator_t, ranges::equal_to> && + (forward_range || tiny-range) + class lazy_split_view; + + namespace views { + inline constexpr unspecified lazy_split = unspecified; + } } namespace std { @@ -245,6 +259,7 @@ #include <__ranges/enable_view.h> #include <__ranges/iota_view.h> #include <__ranges/join_view.h> +#include <__ranges/lazy_split_view.h> #include <__ranges/rbegin.h> #include <__ranges/ref_view.h> #include <__ranges/rend.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__ranges/lazy_split_view.h'}} +#include <__ranges/lazy_split_view.h> diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp @@ -0,0 +1,134 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::views::lazy_split + +#include + +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "types.h" + +template +concept CanBePiped = requires (View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; +}; + +static_assert(std::same_as); + +struct SomeView : std::ranges::view_base { + const std::string_view* v_; + constexpr SomeView(const std::string_view& v) : v_(&v) {} + constexpr auto begin() const { return v_->begin(); } + constexpr auto end() const { return v_->end(); } +}; + +constexpr bool test() { + using namespace std::string_view_literals; + + constexpr std::string_view input = "abc"; + constexpr std::string_view sep = "a"; + + // Test that std::views::lazy_split is a range adaptor. + + // Test `views::lazy_split(input, sep)`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view; + std::same_as auto result = std::views::lazy_split(view, sep); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `views::lazy_split(sep)(input)`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view; + std::same_as auto result = std::views::lazy_split(sep)(view); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `view | views::lazy_split`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view; + std::same_as auto result = view | std::views::lazy_split(sep); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `adaptor | views::lazy_split`. + { + SomeView view(input); + auto f = [](char c) { return c; }; + auto partial = std::views::transform(f) | std::views::lazy_split(sep); + + using Result = std::ranges::lazy_split_view, std::string_view>; + std::same_as auto result = partial(view); + assert(result.base().base().begin() == input.begin()); + assert(result.base().base().end() == input.end()); + } + + // Test `views::lazy_split | adaptor`. + { + SomeView view(input); + auto f = [](auto v) { return v; }; + auto partial = std::views::lazy_split(sep) | std::views::transform(f); + + using Result = std::ranges::transform_view, decltype(f)>; + std::same_as auto result = partial(view); + assert(result.base().base().begin() == input.begin()); + assert(result.base().base().end() == input.end()); + } + + // Test that one can call std::views::filter 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. + { + struct X { }; + auto partial = std::views::lazy_split(X{}); + (void)partial; + } + + // Check SFINAE friendliness. + { + struct NotAView { }; + + 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( CanBePiped); + static_assert( CanBePiped); + static_assert(!CanBePiped); + static_assert(!CanBePiped); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr View base() const& requires copy_constructible<_View>; +// constexpr View base() &&; + +#include + +#include +#include +#include +#include "types.h" + +struct MoveOnlyView : std::ranges::view_base { + std::string_view view_; + constexpr MoveOnlyView() = default; + constexpr MoveOnlyView(const char* ptr) : view_(ptr) {} + constexpr MoveOnlyView(std::string_view v) : view_(v) {} + constexpr MoveOnlyView(MoveOnlyView&&) = default; + constexpr MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr const char* begin() const { return view_.begin(); } + constexpr const char* end() const { return view_.end(); } + constexpr bool operator==(MoveOnlyView rhs) const { return view_ == rhs.view_; } +}; +static_assert( std::ranges::view); +static_assert( std::ranges::contiguous_range); +static_assert(!std::copyable); + +template +concept CanCallBase = requires(View v) { std::forward(v).base(); }; + +static_assert( CanCallBase&>); +static_assert( CanCallBase const &>); +static_assert( CanCallBase&&>); +static_assert( CanCallBase const &&>); +static_assert(!CanCallBase&>); +static_assert(!CanCallBase const &>); +static_assert( CanCallBase&&>); +static_assert(!CanCallBase const &&>); + +struct View : std::ranges::view_base { + enum class InitializedBy { + Copy, + Move, + Invalid + }; + + std::string_view v_; + InitializedBy initialized_by = InitializedBy::Invalid; + constexpr View(std::string_view v) : v_(v) {} + + constexpr auto begin() const { return v_.begin(); } + constexpr auto end() const { return v_.end(); } + + constexpr View(const View& rhs) : v_(rhs.v_) { initialized_by = InitializedBy::Copy; } + constexpr View(View&& rhs) : v_(rhs.v_) { initialized_by = InitializedBy::Move; } + constexpr View& operator=(const View& rhs) = default; + constexpr View& operator=(View&& rhs) = default; + constexpr bool operator==(const View& rhs) const { return v_ == rhs.v_; } +}; + +constexpr bool test() { + // Copyable input -- both lvalue and rvalue overloads of `base` are available. + { + { + constexpr View str("abc def"); + std::ranges::lazy_split_view v(str, " "); + + std::same_as auto result = v.base(); + assert(result == str); + assert(result.initialized_by == View::InitializedBy::Copy); + } + + { + constexpr View str("abc def"); + std::ranges::lazy_split_view v(str, " "); + + std::same_as auto result = std::move(v).base(); + assert(result == str); + assert(result.initialized_by == View::InitializedBy::Move); + } + } + + // Move-only input -- only the rvalue overload of `base` is available. + { + std::ranges::lazy_split_view v; + assert(std::move(v).base() == MoveOnlyView()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.pass.cpp @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto begin(); +// constexpr auto begin() const requires forward_range && forward_range; + +#include + +#include +#include +#include "test_iterators.h" +#include "types.h" + +template +concept OuterIterConceptSameAs = + std::same_as().begin())::iterator_concept, Expected>; + +template +concept InnerIterValueTypeSameAs = requires (View v) { + { *(*v.begin()).begin() } -> std::same_as; +}; + +template +concept ConstBeginDisabled = !requires (const View v) { + { (*v.begin()) }; +}; + +constexpr bool test() { + // non-const: forward_range && simple-view -> outer-iterator + // const: forward_range && forward_range -> outer-iterator + { + using V = ForwardView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !simple-view -> outer-iterator + // const: forward_range && forward_range -> outer-iterator + { + using V = ForwardDiffView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !simple-view -> outer-iterator + // const: forward_range && !forward_range -> disabled + { + using V = ForwardOnlyIfNonConstView; + using P = V; + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + static_assert(ConstBeginDisabled); + } + + // non-const: !forward_range && tiny-range -> outer-iterator + // const: !forward_range -> disabled + { + using V = InputView; + using P = ForwardTinyView; + + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::forward_range

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + static_assert(ConstBeginDisabled); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/constraints.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/constraints.compile.pass.cpp @@ -0,0 +1,164 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// requires view && view && +// indirectly_comparable, iterator_t, ranges::equal_to> && +// (forward_range || tiny-range) +// class lazy_split_view; + +#include + +#include "test_iterators.h" +#include "types.h" + +struct ForwardRange { + forward_iterator begin() const; + forward_iterator end() const; +}; +static_assert( std::ranges::forward_range); + +template +concept CanInstantiate = requires { + typename std::ranges::lazy_split_view; +}; + +// All constraints satisfied (`View` and `Pattern` are forward views). +namespace test1 { + + using View = ForwardView; + using Pattern = ForwardView; + static_assert( std::ranges::forward_range); + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert( CanInstantiate); + +} // namespace test1 + +// All constraints satisfied (`View` is an input view and `Pattern` is a tiny view). +namespace test2 { + + using View = InputView; + using Pattern = ForwardTinyView; + static_assert( std::ranges::input_range); + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert( CanInstantiate); + +} // namespace test2 + +// `View` is not an input range. +namespace test3 { + + struct AlmostInputIterator { + using value_type = char; + using difference_type = ptrdiff_t; + using iterator_concept = int; + + constexpr const char& operator*() const; + constexpr AlmostInputIterator& operator++(); + constexpr void operator++(int); + constexpr bool operator==(const AlmostInputIterator&) const; + }; + + static_assert( std::input_or_output_iterator); + static_assert(!std::input_iterator); + + struct NonInputView : std::ranges::view_base { + AlmostInputIterator begin() const; + AlmostInputIterator end() const; + }; + + using View = NonInputView; + using Pattern = ForwardTinyView; + static_assert(!std::ranges::input_range); + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test3 + +// `View` is not a view. +namespace test4 { + + using View = ForwardRange; + using Pattern = ForwardView; + static_assert( std::ranges::input_range); + static_assert( std::ranges::forward_range); + static_assert(!std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test4 + +// `Pattern` is not a forward range. +namespace test5 { + + using View = ForwardView; + using Pattern = InputView; + static_assert( std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test5 + +// Not indirectly comparable. +namespace test6 { + + struct Empty{}; + struct IntForwardView : std::ranges::view_base { + constexpr forward_iterator begin() const { return {}; } + constexpr forward_iterator end() const { return {}; } + }; + + using View = ForwardView; + using Pattern = IntForwardView; + static_assert( std::ranges::input_range); + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert(!std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test6 + +// `View` is not a forward range and `Pattern` is not a tiny range. +namespace test7 { + + using View = InputView; + using Pattern = ForwardView; + static_assert( std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert( std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__tiny_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test7 diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/copy.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view; + +#include + +#include +#include +#include +#include "small_string.h" +#include "types.h" + +constexpr bool operator==(const InputView& lhs, const InputView& rhs) { + return SmallString(lhs) == SmallString(rhs); +} + +constexpr bool test() { + // Can copy `lazy_split_view`. + { + // Forward range. + { + std::ranges::lazy_split_view v1("abc def", " "); + auto v2 = v1; + assert(v2.base() == v1.base()); + } + + // Input range. + { + SplitViewInput v1("abc def", ' '); + auto v2 = v1; + assert(v2.base() == v1.base()); + } + } + + // Can move `lazy_split_view`. + { + // Forward range. + { + std::string_view base = "abc def"; + std::ranges::lazy_split_view v1(base, " "); + auto v2 = std::move(v1); + assert(v2.base() == base); + } + + // Input range. + { + InputView base("abc def"); + SplitViewInput v1(base, ' '); + auto v2 = std::move(v1); + assert(v2.base() == base); + } + } + + // `non-propagating-cache` is not copied. + { + SplitViewInput v1("abc def ghi", ' '); + auto outer_iter1 = v1.begin(); + ++outer_iter1; + auto val1 = *outer_iter1; + auto i1 = val1.begin(); + assert(*i1 == 'd'); + ++i1; + assert(*i1 == 'e'); + + auto v2 = v1; + auto val2 = *v2.begin(); + auto i2 = val2.begin(); + assert(*i2 == 'a'); + ++i2; + assert(*i2 == 'b'); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.compile.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// lazy_split_view(R&&, P&&) -> lazy_split_view, views::all_t

>; +// +// template +// lazy_split_view(R&&, range_value_t) -> lazy_split_view, single_view>>; + +#include + +#include +#include +#include +#include "types.h" + +struct ForwardRange { + forward_iterator begin() const; + forward_iterator end() const; +}; +static_assert( std::ranges::forward_range); + +struct InputRange { + cpp20_input_iterator begin() const; + std::default_sentinel_t end() const; +}; +bool operator==(const cpp20_input_iterator&, std::default_sentinel_t); +static_assert(std::ranges::input_range); + +template +constexpr void test() { + I1 i1{}; + I2 i2{}; + + std::ranges::lazy_split_view v(std::move(i1), std::move(i2)); + using O = decltype(std::move(v).base()); + static_assert(std::same_as); +} + +constexpr void testCtad() { + // (Range, Pattern) + test(); + test>(); + + // (Range, RangeElement) + test>(); + test>(); + + // (Range, RangeElement) with implicit conversion. + test>(); + test>(); + + // Note: CTAD from (InputRange, TinyForwardRange) doesn't work -- the deduction guide wraps the pattern in + // `views::all_t`, resulting in `views::owning_view`. That type would never satisfy `tiny-range` + // because `views::owning_view` contains a member function `size()` that shadows the static `size()` in + // `TinyForwardRange`. +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// lazy_split_view() requires default_initializable && default_initializable

= default; + +#include + +#include +#include "types.h" + +struct ThrowingDefaultCtorForwardView : std::ranges::view_base { + ThrowingDefaultCtorForwardView() noexcept(false); + forward_iterator begin() const; + forward_iterator end() const; +}; + +struct NoDefaultCtorForwardView : std::ranges::view_base { + NoDefaultCtorForwardView() = delete; + forward_iterator begin() const; + forward_iterator end() const; +}; + +static_assert( std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); + +static_assert( std::is_nothrow_default_constructible_v>); +static_assert(!std::is_nothrow_default_constructible_v); +static_assert(!std::is_nothrow_default_constructible_v< + std::ranges::lazy_split_view>); + +constexpr bool test() { + { + std::ranges::lazy_split_view v; + assert(v.base() == CopyableView()); + } + + { + std::ranges::lazy_split_view v = {}; + assert(v.base() == CopyableView()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.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 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// requires constructible_from> && +// constructible_from>> +// constexpr lazy_split_view(Range&& r, range_value_t e); + +#include + +#include +#include +#include +#include +#include "types.h" + +constexpr bool test() { + decltype(auto) str = "abc def"; + std::ranges::lazy_split_view v(str, ' '); + assert(v.base() == std::string_view(str, sizeof(str))); + + { + using V = std::ranges::lazy_split_view; + static_assert( std::is_constructible_v); + static_assert( std::is_constructible_v); + struct Empty {}; + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr lazy_split_view(View base, Pattern pattern); + +#include + +#include +#include + +constexpr bool test() { + constexpr std::string_view str = "abc def"; + std::ranges::lazy_split_view v(str, " "); + assert(v.base() == str); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp @@ -0,0 +1,165 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto end() requires forward_range && common_range; +// constexpr auto end() const; + +#include + +#include +#include +#include "test_iterators.h" +#include "types.h" + +template +concept OuterIterConceptSameAs = + std::same_as().end())::iterator_concept, Expected>; + +template +concept InnerIterValueTypeSameAs = requires (View v) { + { *(*v.end()).begin() } -> std::same_as; +}; + +struct ForwardNonSimpleView : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardNonSimpleView() = default; + constexpr ForwardNonSimpleView(const char* ptr) : view_(ptr) {} + constexpr ForwardNonSimpleView(std::string_view v) : view_(v) {} + constexpr ForwardNonSimpleView(ForwardNonSimpleView&&) = default; + constexpr ForwardNonSimpleView& operator=(ForwardNonSimpleView&&) = default; + constexpr forward_iterator begin() { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() { return forward_iterator(view_.end()); } + constexpr cpp20_input_iterator begin() const { return cpp20_input_iterator(view_.begin()); } + constexpr cpp20_input_iterator end() const { return cpp20_input_iterator(view_.end()); } +}; + +struct ForwardViewCommonIfConst : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardViewCommonIfConst() = default; + constexpr ForwardViewCommonIfConst(const char* ptr) : view_(ptr) {} + constexpr ForwardViewCommonIfConst(std::string_view v) : view_(v) {} + constexpr ForwardViewCommonIfConst(ForwardViewCommonIfConst&&) = default; + constexpr ForwardViewCommonIfConst& operator=(ForwardViewCommonIfConst&&) = default; + constexpr ForwardViewCommonIfConst(const ForwardViewCommonIfConst&) = default; + constexpr ForwardViewCommonIfConst& operator=(const ForwardViewCommonIfConst&) = default; + constexpr forward_iterator begin() { return forward_iterator(nullptr); } + constexpr std::default_sentinel_t end() { return std::default_sentinel; } + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } +}; +bool operator==(forward_iterator, std::default_sentinel_t) { return false; } + +struct ForwardViewNonCommonRange : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardViewNonCommonRange() = default; + constexpr ForwardViewNonCommonRange(const char* ptr) : view_(ptr) {} + constexpr ForwardViewNonCommonRange(std::string_view v) : view_(v) {} + constexpr ForwardViewNonCommonRange(ForwardViewNonCommonRange&&) = default; + constexpr ForwardViewNonCommonRange& operator=(ForwardViewNonCommonRange&&) = default; + constexpr ForwardViewNonCommonRange(const ForwardViewNonCommonRange&) = default; + constexpr ForwardViewNonCommonRange& operator=(const ForwardViewNonCommonRange&) = default; + constexpr forward_iterator begin() { return forward_iterator(nullptr); } + constexpr std::default_sentinel_t end() { return std::default_sentinel; } + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr std::default_sentinel_t end() const { return std::default_sentinel; } +}; +bool operator==(forward_iterator, std::default_sentinel_t) { return false; } + +constexpr bool test() { + // non-const: forward_range && simple_view && simple_view

-> outer-iterator + // const: forward_range && common_range -> outer-iterator + { + using V = ForwardView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && common_range && simple_view && !simple_view

-> outer-iterator + // const: forward_range && forward_range && common_range -> outer-iterator + { + using V = ForwardView; + using P = ForwardNonSimpleView; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !common_range -> disabled + // const: forward_range && forward_range && common_range -> outer-iterator + { + using V = ForwardViewCommonIfConst; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !common_range -> disabled + // const: forward_range && forward_range && !common_range -> outer-iterator + { + using V = ForwardViewNonCommonRange; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(std::same_as); + + const std::ranges::lazy_split_view cv; + static_assert(std::same_as); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp @@ -0,0 +1,380 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view; + +#include + +#include +#include +#include +#include +#include +#include "small_string.h" + +template +constexpr bool test(const T (&input)[N], Separator separator, std::array expected) { + std::ranges::lazy_split_view v(input, separator); + + return std::equal(v.begin(), v.end(), expected.begin(), expected.end(), + [](const auto& split_value, const auto& expected_value) { + return SmallString(split_value) == expected_value; + }); +} + +template +constexpr bool test_with_piping(const T (&input)[N], Separator separator, std::array expected) { + auto expected_it = expected.begin(); + for (auto e : input | std::ranges::views::lazy_split(separator)) { + if (expected_it == expected.end()) return false; + + if (SmallString(e) != *expected_it) return false; + ++expected_it; + } + + return expected_it == expected.end(); +} + +constexpr bool test_l_r_values() { + using namespace std::string_view_literals; + + // Both lvalues and rvalues can be used as input. + { + // Lvalues. + { + std::string_view input = "abc"; + std::string_view sep = " "; + std::ranges::lazy_split_view v(input, sep); + (void)v; + } + + // Const lvalues. + { + const std::string_view input = "abc"; + const std::string_view sep = " "; + std::ranges::lazy_split_view v(input, sep); + (void)v; + } + + // Rvalues. + { + std::ranges::lazy_split_view v("abc"sv, " "sv); + (void)v; + } + + // Const rvalues. + { + const std::string_view input = "abc"; + const std::string_view sep = " "; + std::ranges::lazy_split_view v(std::move(input), std::move(sep)); + (void)v; + } + } + + return true; +} + +int main(int, char**) { + using namespace std::string_view_literals; + + constexpr char short_sep = ' '; + constexpr std::string_view long_sep = "12"; + + // One separator. + { + { + decltype(auto) input = "abc def"; + constexpr std::array expected = {"abc"sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12def"; + constexpr std::array expected = {"abc"sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Several separators in a row. + { + { + decltype(auto) input = "abc def"; + constexpr std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12121212def"; + constexpr std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Trailing separator. + { + { + decltype(auto) input = "abc def "; + constexpr std::array expected = {"abc"sv, "def"sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12def12"; + constexpr std::array expected = {"abc"sv, "def"sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Leading separator. + { + { + decltype(auto) input = " abc def"; + constexpr std::array expected = {""sv, "abc"sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12abc12def"; + constexpr std::array expected = {""sv, "abc"sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // No separator. + { + { + decltype(auto) input = "abc"; + constexpr std::array expected = {"abc"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc"; + constexpr std::array expected = {"abc"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Empty string. + { + { + decltype(auto) input = ""; + constexpr std::array expected = {""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = ""; + constexpr std::array expected = {""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Input consisting of a single separator. + { + { + decltype(auto) input = " "; + constexpr std::array expected = {""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12"; + constexpr std::array expected = {""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Input consisting of only separators. + { + { + decltype(auto) input = " "; + constexpr std::array expected = {""sv, ""sv, ""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "121212"; + constexpr std::array expected = {""sv, ""sv, ""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Many redundant separators. + { + { + decltype(auto) input = " abc def "; + constexpr std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "1212abc121212def1212"; + constexpr std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Separators after every character. + { + { + decltype(auto) input = " a b c "; + constexpr std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12a12b12c12"; + constexpr std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Empty separator. + { + { + constexpr std::string_view empty_sep = ""; + + decltype(auto) input = "abc"; + constexpr std::array expected = {"a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, empty_sep, expected)); + static_assert(test(input, empty_sep, expected)); + assert(test_with_piping(input, empty_sep, expected)); + static_assert(test_with_piping(input, empty_sep, expected)); + } + } + + // Overlap between the separator and the string. + { + { + constexpr std::string_view overlapping_sep = "ab"; + + decltype(auto) input = "aabaaababb"; + constexpr std::array expected = {"a"sv, "aa"sv, ""sv, "b"sv}; + + assert(test(input, overlapping_sep, expected)); + static_assert(test(input, overlapping_sep, expected)); + assert(test_with_piping(input, overlapping_sep, expected)); + static_assert(test_with_piping(input, overlapping_sep, expected)); + } + } + + // Non-character input. + { + { + constexpr int sep = 0; + constexpr int input[] = {1, 2, 3, 0, 4, 5, 6}; + constexpr std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + + assert(test(input, sep, expected)); + static_assert(test(input, sep, expected)); + assert(test_with_piping(input, sep, expected)); + static_assert(test_with_piping(input, sep, expected)); + } + + { + constexpr std::array sep = {0, 0, 0}; + constexpr int input[] = {1, 2, 3, 0, 0, 0, 4, 5, 6}; + constexpr std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + + assert(test(input, sep, expected)); + static_assert(test(input, sep, expected)); + assert(test_with_piping(input, sep, expected)); + static_assert(test_with_piping(input, sep, expected)); + } + } + + assert(test_l_r_values()); + static_assert(test_l_r_values()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/base.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr const iterator_t& inner-iterator::base() const& noexcept; +// +// constexpr iterator_t inner-iterator::base() && +// requires forward_range; + +#include + +#include +#include +#include "../types.h" + +template +concept CanCallBase = requires(InnerIter i) { + { std::forward(i).base() } -> std::same_as; +}; + +static_assert( CanCallBase); +static_assert( CanCallBase); +static_assert( CanCallBase); +static_assert( CanCallBase); + +static_assert( CanCallBase); +static_assert( CanCallBase); +static_assert( CanCallBase); +static_assert( CanCallBase); + +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); + +constexpr bool test() { + // `base` works with a forward view (two different overloads based on ref-qualification of the `inner-iterator`). + { + using BaseIter = std::ranges::iterator_t; + CopyableView b("abc def"); + std::ranges::lazy_split_view v(b, " "); + + auto i = (*v.begin()).begin(); + static_assert(std::same_as); + assert(i.base() == b.begin()); + static_assert(std::same_as); + assert(std::move(i).base() == b.begin()); + } + + // `base` works with an input view (no overloads). + { + using BaseIter = std::ranges::iterator_t; + InputView b("abc def"); + std::ranges::lazy_split_view v(b, ' '); + + auto i = (*v.begin()).begin(); + static_assert(std::same_as); + static_assert(std::same_as); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.default.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr inner-iterator::inner-iterator() = default; + +#include + +#include "../types.h" + +static_assert(std::is_default_constructible_v); +static_assert(std::is_default_constructible_v); + +constexpr bool test() { + { + InnerIterForward i; + (void)i; + } + + { + InnerIterInput i; + (void)i; + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr explicit inner-iterator::inner-iterator(outer-iterator i); + +#include + +#include "../types.h" + +static_assert( std::is_constructible_v); +static_assert( std::is_constructible_v); +// Is only constructible if both the outer and the inner iterators have the same constness. +static_assert( std::is_constructible_v); +// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`). +static_assert( std::is_constructible_v); +static_assert( std::is_constructible_v); +static_assert(!std::is_constructible_v); + +template +void CheckCopyInitialization(Inner); + +template +concept CannotCopyInitialize = + requires(Inner i) { CheckCopyInitialization(i); } && + !requires(Outer o) { CheckCopyInitialization(o); }; + +template +constexpr void test_impl() { + Inner i(Outer{}); + (void)i; + + // Verify that the constructor is `explicit`. + static_assert(CannotCopyInitialize); +} + +constexpr bool test() { + test_impl(); + test_impl(); + test_impl(); + test_impl(); + test_impl(); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/deref.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr decltype(auto) inner-iterator::operator*() const; + +#include + +#include "../types.h" + +constexpr bool test() { + // Forward view. + { + SplitViewDiff v("abc def", " "); + auto val = *v.begin(); + + // Non-const iterator. + { + auto i = val.begin(); + static_assert(std::same_as); + assert(*i == 'a'); + assert(*(++i) == 'b'); + assert(*(++i) == 'c'); + } + + // Const iterator. + { + const auto ci = val.begin(); + static_assert(std::same_as); + assert(*ci == 'a'); + } + } + + // Input view. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + // Non-const iterator. + { + auto i = val.begin(); + static_assert(std::same_as); + assert(*i == 'a'); + assert(*(++i) == 'b'); + assert(*(++i) == 'c'); + } + + // Const iterator. + { + const auto ci = val.begin(); + static_assert(std::same_as); + // Note: this can be surprising. When the underlying range is an input range, `current` is stored in the + // `lazy_split_view` itself and shared between `inner-iterator`s. Consequently, incrementing one iterator + // effectively increments all of them. + assert(*ci == 'c'); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/equal.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/equal.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr bool operator==(const inner-iterator& x, const inner-iterator& y); +// requires forward_range; +// +// friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t); + +#include + +#include +#include +#include "../types.h" + +template +concept CanCallEquals = requires(const Iter& i) { + i == i; + i != i; +}; + +constexpr bool test() { + // Forward range supports both overloads of `operator==`. + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + auto b = val.begin(); + std::same_as auto e = val.end(); + + // inner-iterator == inner-iterator + + assert(b == b); + assert(!(b != b)); + + // inner-iterator == default_sentinel + + assert(!(b == e)); + assert(b != e); + + assert(!(b == std::default_sentinel)); + assert(b != std::default_sentinel); + } + + // Input range only supports comparing an `inner-iterator` to the default sentinel. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + auto b = val.begin(); + std::same_as auto e = val.end(); + + static_assert(!CanCallEquals); + + assert(!(b == std::default_sentinel)); + assert(b != std::default_sentinel); + assert(!(b == e)); + assert(b != e); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/increment.pass.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr inner-iterator& inner-iterator::operator++(); +// +// constexpr decltype(auto) inner-iterator::operator++(int); + +#include + +#include "../types.h" + +constexpr bool test() { + // Forward range. + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + + // ++i + { + auto i = val.begin(); + assert(*i == 'a'); + + decltype(auto) i2 = ++i; + static_assert(std::is_lvalue_reference_v); + assert(*i2 == 'b'); + } + + // i++ + { + auto i = val.begin(); + assert(*i == 'a'); + + decltype(auto) i2 = i++; + static_assert(!std::is_reference_v); + assert(*i2 == 'a'); + assert(*i == 'b'); + } + } + + // Input range. + { + // ++i + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + auto i = val.begin(); + assert(*i == 'a'); + + decltype(auto) i2 = ++i; + static_assert(std::is_lvalue_reference_v); + assert(*i2 == 'b'); + } + + // i++ + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + auto i = val.begin(); + assert(*i == 'a'); + + static_assert(std::is_void_v); + i++; + assert(*i == 'b'); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr decltype(auto) iter_move(const inner-iterator& i) +// noexcept(noexcept(ranges::iter_move(i.i_.))); + +#include + +#include +#include +#include +#include "../types.h" + +namespace adl { + +template +struct Iterator { + using value_type = int; + using difference_type = ptrdiff_t; + + value_type* ptr_ = nullptr; + int* iter_move_invocations_ = nullptr; + + constexpr Iterator() = default; + constexpr explicit Iterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {} + + constexpr value_type& operator*() const { return *ptr_; } + + Iterator& operator++() { ++ptr_; return *this; } + Iterator operator++(int) { + Iterator prev = *this; + ++ptr_; + return prev; + } + + constexpr Iterator& operator--() { --ptr_; return *this; } + constexpr Iterator operator--(int) { + Iterator prev = *this; + --ptr_; + return prev; + } + + constexpr friend value_type&& iter_move(Iterator iter) noexcept(IsNoexcept) { + if (iter.iter_move_invocations_) { + ++(*iter.iter_move_invocations_); + } + return std::move(*iter); + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } +}; + +template +struct View : std::ranges::view_base { + static constexpr int N = 3; + int a[N] = {0, 1, 2}; + int* iter_moves = nullptr; + + constexpr View() = default; + constexpr View(int& iter_move_invocations) : iter_moves(&iter_move_invocations) { + } + + constexpr adl::Iterator begin() { return adl::Iterator(a, *iter_moves); } + constexpr adl::Iterator end() { return adl::Iterator(a + N, *iter_moves); } +}; + +} // namespace adl + +constexpr bool test() { + // Can use `iter_move` with `inner-iterator`, `View` is a forward range. + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + + // Non-const iterator. + { + auto i = val.begin(); + static_assert(std::same_as); + assert(iter_move(i) == 'a'); + } + + // Const iterator. + { + const auto i = val.begin(); + static_assert(std::same_as); + assert(iter_move(i) == 'a'); + } + } + + // Can use `iter_move` with `inner-iterator`, `View` is an input range. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + // Non-const iterator. + { + auto i = val.begin(); + static_assert(std::same_as); + assert(iter_move(i) == 'a'); + } + + // Const iterator. + { + const auto i = val.begin(); + static_assert(std::same_as); + assert(iter_move(i) == 'a'); + } + } + + // Ensure the `iter_move` customization point is being used. + { + int iter_move_invocations = 0; + adl::View<> input(iter_move_invocations); + std::ranges::lazy_split_view, adl::View<>> v(input, adl::View<>()); + + auto val = *v.begin(); + auto i = val.begin(); + int x = iter_move(i); + assert(x == 0); + assert(iter_move_invocations == 1); + } + + // Check the `noexcept` specification. + { + { + using ThrowingSplitView = std::ranges::lazy_split_view, adl::View>; + using ThrowingValueType = decltype(*std::declval().begin()); + using ThrowingIter = std::ranges::iterator_t; + ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval>())); + ASSERT_NOT_NOEXCEPT(iter_move(std::declval())); + } + + { + using NoexceptSplitView = std::ranges::lazy_split_view, adl::View>; + using NoexceptValueType = decltype(*std::declval().begin()); + using NoexceptIter = std::ranges::iterator_t; + ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval>())); + ASSERT_NOEXCEPT(iter_move(std::declval())); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp @@ -0,0 +1,193 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y) +// noexcept(noexcept(ranges::iter_swap(x.i_., y.i_.))) +// requires indirectly_swappable>; + +#include + +#include +#include +#include +#include "../types.h" + +#include + +namespace adl { + +template +struct Iterator { + using value_type = int; + using difference_type = ptrdiff_t; + + value_type* ptr_ = nullptr; + int* iter_swap_invocations_ = nullptr; + + constexpr Iterator() = default; + constexpr explicit Iterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {} + + value_type& operator*() const { return *ptr_; } + + Iterator& operator++() { ++ptr_; return *this; } + Iterator operator++(int) { + Iterator prev = *this; + ++ptr_; + return prev; + } + + Iterator& operator--() { --ptr_; return *this; } + Iterator operator--(int) { + Iterator prev = *this; + --ptr_; + return prev; + } + + constexpr friend void iter_swap(Iterator a, Iterator) noexcept(IsNoexcept) { + if (a.iter_swap_invocations_) { + ++(*a.iter_swap_invocations_); + } + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } +}; + +template +struct View : std::ranges::view_base { + int* iter_swaps = nullptr; + + constexpr View() = default; + constexpr View(int& iter_swap_invocations) : iter_swaps(&iter_swap_invocations) { + } + + constexpr adl::Iterator begin() { return adl::Iterator(*iter_swaps); } + constexpr adl::Iterator end() { return adl::Iterator(*iter_swaps); } +}; + +} // namespace adl + +constexpr bool test() { + // Can use `iter_swap` with `inner-iterator`, `View` is a forward range. + { + // Non-const iterator. + { + SplitViewDiff v("abc def", " "); + auto val = *v.begin(); + + auto i1 = val.begin(); + auto i2 = i1++; + static_assert(std::is_void_v); + assert(*i1 == 'b'); + assert(*i2 == 'a'); + + iter_swap(i1, i2); + assert(*i1 == 'a'); + assert(*i2 == 'b'); + } + + // Const iterator. + { + SplitViewDiff v("abc def", " "); + auto val = *v.begin(); + + auto i1 = val.begin(); + const auto i2 = i1++; + static_assert(std::is_void_v); + static_assert(std::is_void_v); + assert(*i1 == 'b'); + assert(*i2 == 'a'); + + iter_swap(i1, i2); + assert(*i1 == 'a'); + assert(*i2 == 'b'); + } + } + + // Can use `iter_swap` with `inner-iterator`, `View` is an input range. + { + + // Non-const iterator. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + auto i1 = val.begin(); + auto i2 = i1; + ++i1; + static_assert(std::is_void_v); + assert(*i1 == 'b'); + // For an input view, all inner iterators are essentially thin proxies to the same underlying iterator. + assert(*i2 == 'b'); + + iter_swap(i1, i2); + assert(*i1 == 'b'); + assert(*i2 == 'b'); + } + + // Const iterator. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + const auto i1 = val.begin(); + const auto i2 = i1; + static_assert(std::is_void_v); + assert(*i1 == 'a'); + assert(*i2 == 'a'); + + iter_swap(i1, i2); + assert(*i1 == 'a'); + assert(*i2 == 'a'); + } + } + + // Ensure the `iter_swap` customization point is being used. + { + int iter_swap_invocations = 0; + adl::View<> input(iter_swap_invocations); + std::ranges::lazy_split_view, adl::View<>> v(input, adl::View<>()); + + auto val = *v.begin(); + auto i = val.begin(); + iter_swap(i, i); + assert(iter_swap_invocations == 1); + } + + // Check the `noexcept` specification. + { + { + using ThrowingSplitView = std::ranges::lazy_split_view, adl::View>; + using ThrowingValueType = decltype(*std::declval().begin()); + using ThrowingIter = std::ranges::iterator_t; + ASSERT_NOT_NOEXCEPT( + std::ranges::iter_swap(std::declval>(), std::declval>())); + ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + } + + { + using NoexceptSplitView = std::ranges::lazy_split_view, adl::View>; + using NoexceptValueType = decltype(*std::declval().begin()); + using NoexceptIter = std::ranges::iterator_t; + ASSERT_NOEXCEPT( + std::ranges::iter_swap(std::declval>(), std::declval>())); + ASSERT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/types.compile.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view::outer-iterator; + +#include + +#include +#include +#include "../types.h" + +template +using OuterIter = std::ranges::iterator_t>; +template +using InnerIter = std::ranges::iterator_t())>; + +// iterator_concept + +static_assert(std::same_as::iterator_concept, + typename OuterIter::iterator_concept>); +static_assert(std::same_as::iterator_concept, + typename OuterIter::iterator_concept>); + +// iterator_category + +static_assert(std::same_as::iterator_category, std::forward_iterator_tag>); + +template +concept NoIteratorCategory = !requires { typename InnerIter::iterator_category; }; +static_assert(NoIteratorCategory); + +// value_type + +static_assert(std::same_as::value_type, + std::ranges::range_value_t>); + +// difference_type + +static_assert(std::same_as::difference_type, + std::ranges::range_difference_t>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.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 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::begin() + +#include + +#include +#include +#include +#include + +constexpr bool test() { + using View = std::ranges::lazy_split_view; + std::string_view input = "a"; + + // Non-const. + { + View v(input, "b"); + auto val = *v.begin(); + assert(val.begin().base() == input); + } + + // Const. + { + View v(input, "b"); + const auto val = *v.begin(); + assert(val.begin().base() == input); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::value_type() + +#include + +#include +#include +#include +#include + +using View = std::ranges::lazy_split_view; +using ValueType = decltype(*std::declval().begin()); +static_assert(std::is_default_constructible_v); + +int main(int, char**) { + ValueType val; + (void)val; + + constexpr ValueType val2; + (void)val2; + // Note: attempting a check like `val.begin() == val.end()` leads to null pointer dereference. + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// explicit outer-iterator::value_type::value_type(outer-iterator i) + +#include + +#include +#include +#include +#include + +using View = std::ranges::lazy_split_view; +using ValueType = decltype(*std::declval().begin()); +using OuterIter = std::ranges::iterator_t; +static_assert(std::is_constructible_v); + +template +void CheckCopyInitialization(Inner); + +template +concept CannotCopyInitialize = + requires(Value v) { CheckCopyInitialization(v); } && + !requires(ConvertibleToValue c) { CheckCopyInitialization(c); }; + +// Verify that the constructor is `explicit`. +static_assert(CannotCopyInitialize); + +constexpr bool test() { + std::string_view input = "a"; + View v(input, "b"); + ValueType val(v.begin()); + assert(val.begin().base() == input); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.pass.cpp @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::end() + +#include + +#include +#include +#include + +constexpr bool test() { + using View = std::ranges::lazy_split_view; + std::string_view input = "a"; + + // Non-const. + { + View v(input, "b"); + auto val = *v.begin(); + static_assert(std::same_as); + } + + // Const. + { + View v(input, "b"); + const auto val = *v.begin(); + static_assert(std::same_as); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/view_interface.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/view_interface.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/view_interface.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 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view::outer-iterator::value_type; + +#include + +#include +#include +#include "../types.h" + +using V = decltype(*std::declval().begin()); +static_assert(std::ranges::forward_range); +static_assert(std::ranges::view); + +// empty() +template +concept CanCallEmpty = requires (View& v) { v.empty(); }; +static_assert(CanCallEmpty); + +// operator bool() +template +concept CanCallOperatorBool = requires (View& v) { static_cast(v); }; +static_assert(CanCallOperatorBool); + +// front() +template +concept CanCallFront = requires (View& v) { v.front(); }; +static_assert(CanCallFront); + +// size() is unavailable. +static_assert(!std::sized_sentinel_for, std::ranges::iterator_t>); +template +concept CanCallSize = requires (View& v) { v.size(); }; +static_assert(!CanCallSize); + +// back() is unavailable. +static_assert(!std::ranges::bidirectional_range); +template +concept CanCallBack = requires (View& v) { v.back(); }; +static_assert(!CanCallBack); + +// data() is unavailable. +static_assert(!std::contiguous_iterator>); +template +concept CanCallData = requires (View& v) { v.data(); }; +static_assert(!CanCallData); + +// operator[] is unavailable. +static_assert(!std::ranges::random_access_range); +template +concept CanCallOperatorSubscript = requires (View& v) { v[0]; }; +static_assert(!CanCallOperatorSubscript); + +constexpr bool test() { + // empty() + { + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + assert(!val.empty()); + } + + { + SplitViewForward v; + auto val = *v.begin(); + assert(val.empty()); + } + } + + // operator bool() + { + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + assert(val); + } + + { + SplitViewForward v; + auto val = *v.begin(); + assert(!val); + } + } + + // front() + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + assert(val.front() == 'a'); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.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 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr outer-iterator(outer-iterator i) +// requires Const && convertible_to, iterator_t> + +#include + +#include +#include +#include +#include "../types.h" + +// outer-iterator + +template +concept IsConstOuterIter = requires (Iter i) { + { *(*i).begin() } -> std::same_as; +}; +static_assert( IsConstOuterIter); + +static_assert( std::convertible_to< + std::ranges::iterator_t, std::ranges::iterator_t>); + +// outer-iterator + +template +concept IsNonConstOuterIter = requires (Iter i) { + { *(*i).begin() } -> std::same_as; +}; +static_assert( IsNonConstOuterIter); + +static_assert( std::is_constructible_v); +static_assert(!std::is_constructible_v); + +constexpr bool test() { + OuterIterConst i(OuterIterNonConst{}); + (void)i; + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.default.pass.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::outer-iterator() + +#include + +#include +#include +#include +#include + +using View = std::ranges::lazy_split_view; +using OuterIter = decltype(std::declval().begin()); +static_assert(std::is_default_constructible_v); + +int main(int, char**) { + OuterIter i; + (void)i; + constexpr OuterIter i2; + (void)i2; + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.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 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent) +// requires (!forward_range) + +#include + +#include +#include +#include +#include "../types.h" + +using SplitView = std::ranges::lazy_split_view; +using OuterIter = decltype(std::declval().begin()); +static_assert(std::ranges::forward_range); +static_assert(!std::is_constructible_v); + +using SplitViewInput = std::ranges::lazy_split_view; +using OuterIterInput = decltype(std::declval().begin()); +static_assert(!std::ranges::forward_range); +static_assert( std::is_constructible_v); + +constexpr bool test() { + InputView input; + SplitViewInput v(input, ForwardTinyView()); + + OuterIterInput i(v); + (void)v; + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.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 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr outer-iterator(Parent& parent, iterator_t current); +// requires forward_range + +#include + +#include +#include +#include +#include "../types.h" + +static_assert( std::ranges::forward_range); +static_assert( std::is_constructible_v>); + +static_assert(!std::ranges::forward_range); +static_assert(!std::is_constructible_v>); + +constexpr bool test() { + ForwardView input("abc"); + SplitViewForward v(std::move(input), " "); + + OuterIterForward i(v, input.begin()); + (void)v; + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/deref.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 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr outer-iterator::value-type outer-iterator::operator*() const; + +#include + +#include +#include +#include "../small_string.h" +#include "../types.h" + +constexpr bool test() { + // Forward view. + { + SplitViewDiff v("abc def ghi", " "); + + // Non-const iterator. + { + auto i = v.begin(); + static_assert(!std::is_reference_v); + assert(SmallString(*i) == "abc"_str); + assert(SmallString(*(++i)) == "def"_str); + assert(SmallString(*(++i)) == "ghi"_str); + } + + // Const iterator. + { + const auto ci = v.begin(); + static_assert(!std::is_reference_v); + assert(SmallString(*ci) == "abc"_str); + } + } + + { + SplitViewInput v("abc def ghi", ' '); + + // Non-const iterator. + { + auto i = v.begin(); + static_assert(!std::is_reference_v); + assert(SmallString(*i) == "abc"_str); + assert(SmallString(*(++i)) == "def"_str); + assert(SmallString(*(++i)) == "ghi"_str); + } + + // Const iterator. + { + const auto ci = v.begin(); + static_assert(!std::is_reference_v); + assert(SmallString(*ci) == "abc"_str); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr bool operator==(const outer-iterator& x, const outer-iterator& y) +// requires forward_range; +// +// friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t); + +#include + +#include +#include +#include "../types.h" + +template +concept CanCallEquals = requires(const Iter& i) { + i == i; + i != i; +}; + +constexpr bool test() { + // Forward range supports both overloads of `operator==`. + { + // outer-iterator == outer-iterator + { + SplitViewForward v("abc def", " "); + auto b = v.begin(), e = v.end(); + + assert(b == b); + assert(!(b != b)); + + assert(e == e); + assert(!(e != e)); + + assert(!(b == e)); + assert(b != e); + } + + // outer-iterator == default_sentinel + { + SplitViewForward v("abc def", " "); + auto b = v.begin(), e = v.end(); + + assert(!(b == std::default_sentinel)); + assert(b != std::default_sentinel); + assert(e == std::default_sentinel); + assert(!(e != std::default_sentinel)); + } + } + + // Input range only supports comparing an `outer-iterator` to the default sentinel. + { + using namespace std::string_view_literals; + SplitViewInput v("abc def"sv, ' '); + auto b = v.begin(); + std::same_as auto e = v.end(); + + static_assert(!CanCallEquals); + + assert(!(b == std::default_sentinel)); + assert(b != std::default_sentinel); + assert(!(b == e)); + assert(b != e); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr outer-iterator& outer-iterator::operator++(); +// constexpr decltype(auto) outer-iterator::operator++(int); + +#include + +#include +#include +#include "../small_string.h" +#include "../types.h" + +constexpr bool test() { + // Forward range. + { + SplitViewForward v("abc def ghi", " "); + + // ++i + { + auto i = v.begin(); + assert(*i == "abc"_str); + + decltype(auto) i2 = ++i; + static_assert(std::is_lvalue_reference_v); + assert(*i2 == "def"_str); + } + + // i++ + { + auto i = v.begin(); + assert(*i == "abc"_str); + + decltype(auto) i2 = i++; + static_assert(!std::is_reference_v); + assert(*i2 == "abc"_str); + assert(*i == "def"_str); + } + } + + // Input range. + { + // ++i + { + SplitViewInput v("abc def ghi", ' '); + + auto i = v.begin(); + assert(*i == "abc"_str); + + decltype(auto) i2 = ++i; + static_assert(std::is_lvalue_reference_v); + assert(*i2 == "def"_str); + } + + // i++ + { + SplitViewInput v("abc def ghi", ' '); + + auto i = v.begin(); + assert(*i == "abc"_str); + + static_assert(std::is_void_v); + i++; + assert(*i == "def"_str); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view::outer-iterator; + +#include + +#include +#include +#include "../types.h" + +template +using OuterIter = decltype(std::declval>().begin()); + +// iterator_concept + +static_assert(std::same_as::iterator_concept, std::forward_iterator_tag>); +static_assert(std::same_as::iterator_concept, std::input_iterator_tag>); + +// iterator_category + +static_assert(std::same_as::iterator_category, std::input_iterator_tag>); + +template +concept NoIteratorCategory = !requires { typename OuterIter::iterator_category; }; +static_assert(NoIteratorCategory); + +// difference_type + +static_assert(std::same_as::difference_type, + std::ranges::range_difference_t>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/small_string.h b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/small_string.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/small_string.h @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// 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_LAZY_SPLIT_SMALL_STRING_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_SMALL_STRING_H + +#include +#include +#include +#include + +// A constexpr-friendly lightweight string, primarily useful for comparisons. +// Unlike `std::string`, all functions are `constexpr`. Unlike `std::string_view`, it copies the given string into an +// internal buffer and can work with non-contiguous inputs. +class SmallString { + constexpr static int N = 32; + char buffer_[N] = {}; + size_t size_ = 0; + + constexpr void omit_terminating_null() { + if (size_ > 0 && buffer_[size_ - 1] == '\0') { + --size_; + } + } + +public: + // Main constructors. + + constexpr SmallString() = default; + + constexpr SmallString(std::string_view v) : size_(v.size()) { + assert(size_ < N); + if (size_ == 0) return; + + std::copy(v.begin(), v.end(), buffer_); + omit_terminating_null(); + } + + template + constexpr SmallString(I b, const S& e) { + for (; b != e; ++b) { + buffer_[size_++] = *b; + assert(size_ < N); + } + + omit_terminating_null(); + } + + // Delegating constructors. + + constexpr SmallString(const char* ptr, size_t size) : SmallString(std::string_view(ptr, size)) { + } + + template + constexpr SmallString(R&& from) : SmallString(from.begin(), from.end()) { + } + + // Iterators. + + constexpr char* begin() { return buffer_; } + constexpr char* end() { return buffer_ + size_; } + constexpr const char* begin() const { return buffer_; } + constexpr const char* end() const { return buffer_ + size_; } + + friend constexpr bool operator==(const SmallString& lhs, const SmallString& rhs) { + return lhs.size_ == rhs.size_ && std::equal(lhs.buffer_, lhs.buffer_ + lhs.size_, rhs.buffer_); + } + friend constexpr bool operator==(const SmallString& lhs, std::string_view rhs) { + return lhs == SmallString(rhs); + } +}; + +constexpr SmallString operator "" _str(const char* ptr, size_t size) { + return SmallString(ptr, size); +} + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_SMALL_STRING_H diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h @@ -0,0 +1,194 @@ +//===----------------------------------------------------------------------===// +// +// 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_LAZY_SPLIT_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_TYPES_H + +#include +#include +#include +#include +#include "small_string.h" +#include "test_macros.h" +#include "test_iterators.h" + +// CopyableView + +struct CopyableView : std::ranges::view_base { + std::string_view view_; + constexpr explicit CopyableView() = default; + constexpr CopyableView(const char* ptr) : view_(ptr) {} + constexpr CopyableView(std::string_view v) : view_(v) {} + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } + constexpr bool operator==(const CopyableView& rhs) const { return view_ == rhs.view_; } +}; +static_assert( std::ranges::forward_range); +static_assert( std::ranges::forward_range); +static_assert( std::ranges::view); +static_assert( std::is_copy_constructible_v); + +// ForwardView + +struct ForwardView : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardView() = default; + constexpr ForwardView(const char* ptr) : view_(ptr) {} + constexpr ForwardView(std::string_view v) : view_(v) {} + constexpr ForwardView(ForwardView&&) = default; + constexpr ForwardView& operator=(ForwardView&&) = default; + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } +}; +static_assert( std::ranges::forward_range); +static_assert( std::ranges::forward_range); +static_assert( std::ranges::view); +static_assert(!std::is_copy_constructible_v); +static_assert( std::is_move_constructible_v); + +// ForwardDiffView + +// Iterator types differ based on constness of this class. +struct ForwardDiffView : std::ranges::view_base { + SmallString buffer_; + constexpr explicit ForwardDiffView() = default; + constexpr ForwardDiffView(const char* ptr) : ForwardDiffView(std::string_view(ptr)) {} + constexpr ForwardDiffView(std::string_view v) : buffer_(v) {} + constexpr ForwardDiffView(ForwardDiffView&&) = default; + constexpr ForwardDiffView& operator=(ForwardDiffView&&) = default; + constexpr ForwardDiffView(const ForwardDiffView&) = default; + constexpr ForwardDiffView& operator=(const ForwardDiffView&) = default; + constexpr forward_iterator begin() { return forward_iterator(buffer_.begin()); } + constexpr forward_iterator end() { return forward_iterator(buffer_.end()); } + constexpr forward_iterator begin() const { return forward_iterator(buffer_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(buffer_.end()); } +}; +static_assert( std::ranges::forward_range); +static_assert( std::ranges::forward_range); +static_assert( std::ranges::view); +static_assert(!std::same_as, std::ranges::iterator_t>); + +// ForwardOnlyIfNonConstView + +template +class almost_forward_iterator { + It it_; + + template friend class almost_forward_iterator; + +public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + using pointer = It; + using reference = typename std::iterator_traits::reference; + + constexpr almost_forward_iterator() : it_() {} + constexpr explicit almost_forward_iterator(It it) : it_(it) {} + template + constexpr almost_forward_iterator(const almost_forward_iterator& u) : it_(u.it_) {} + + constexpr reference operator*() const { return *it_; } + constexpr pointer operator->() const { return it_; } + + constexpr almost_forward_iterator& operator++() { ++it_; return *this; } + // Notice the slightly different return type. + constexpr const almost_forward_iterator operator++(int) { return almost_forward_iterator(it_); } + + friend constexpr bool operator==(const almost_forward_iterator& x, const almost_forward_iterator& y) { + return x.it_ == y.it_; + } + friend constexpr bool operator!=(const almost_forward_iterator& x, const almost_forward_iterator& y) { + return x.it_ != y.it_; + } +}; +static_assert(!std::forward_iterator>); +static_assert( std::input_iterator>); + +struct ForwardOnlyIfNonConstView : std::ranges::view_base { + std::string_view view_; + + constexpr explicit ForwardOnlyIfNonConstView() = default; + constexpr ForwardOnlyIfNonConstView(const char* ptr) : view_(ptr) {} + constexpr ForwardOnlyIfNonConstView(std::string_view v) : view_(v) {} + constexpr ForwardOnlyIfNonConstView(ForwardOnlyIfNonConstView&&) = default; + constexpr ForwardOnlyIfNonConstView& operator=(ForwardOnlyIfNonConstView&&) = default; + + constexpr forward_iterator begin() { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() { return forward_iterator(view_.end()); } + constexpr almost_forward_iterator begin() const { + return almost_forward_iterator(view_.begin()); + } + constexpr almost_forward_iterator end() const { + return almost_forward_iterator(view_.end()); + } +}; +static_assert( std::ranges::forward_range); +static_assert(!std::ranges::forward_range); +static_assert( std::ranges::view); + +// InputView + +struct InputView : std::ranges::view_base { + SmallString buffer_; + + constexpr InputView() = default; + constexpr InputView(const char* s) : InputView(std::string_view(s)) {} + constexpr InputView(std::string_view v) : buffer_(v) {} + + constexpr cpp20_input_iterator begin() { return cpp20_input_iterator(buffer_.begin()); } + constexpr char* end() { return buffer_.end(); } + constexpr cpp20_input_iterator begin() const { + return cpp20_input_iterator(buffer_.begin()); + } + constexpr const char* end() const { return buffer_.end(); } +}; +constexpr bool operator==(const cpp20_input_iterator& lhs, char* rhs) { return base(lhs) == rhs; } +constexpr bool operator==(char* lhs, const cpp20_input_iterator& rhs) { return base(rhs) == lhs; } +constexpr bool operator==(const cpp20_input_iterator& lhs, const char* rhs) { return base(lhs) == rhs; } +constexpr bool operator==(const char* lhs, const cpp20_input_iterator& rhs) { return base(rhs) == lhs; } + +static_assert(std::ranges::input_range); +static_assert(std::ranges::input_range); +static_assert(std::ranges::view); + +// ForwardTinyView + +struct ForwardTinyView : std::ranges::view_base { + char c_[1] = {}; + constexpr ForwardTinyView() = default; + constexpr ForwardTinyView(char c) { *c_ = c; } + constexpr forward_iterator begin() const { return forward_iterator(c_); } + constexpr forward_iterator end() const { return forward_iterator(c_ + 1); } + constexpr static size_t size() { return 1; } +}; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::view); +LIBCPP_STATIC_ASSERT(std::ranges::__tiny_range); + +// Aliases + +using SplitViewForward = std::ranges::lazy_split_view; +using OuterIterForward = std::ranges::iterator_t; +using InnerIterForward = std::ranges::iterator_t())>; +using BaseIterForward = std::ranges::iterator_t; + +using SplitViewInput = std::ranges::lazy_split_view; +using OuterIterInput = std::ranges::iterator_t; +using InnerIterInput = std::ranges::iterator_t())>; +using BaseIterInput = std::ranges::iterator_t; + +using SplitViewDiff = std::ranges::lazy_split_view; +using OuterIterConst = decltype(std::declval().begin()); +using OuterIterNonConst = decltype(std::declval().begin()); +static_assert(!std::same_as); +using InnerIterConst = decltype((*std::declval()).begin()); +using InnerIterNonConst = decltype((*std::declval()).begin()); +static_assert(!std::same_as); + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_TYPES_H diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view; + +#include + +#include +#include + +using V = decltype(std::ranges::lazy_split_view("abc", "d")); +static_assert(std::ranges::forward_range); + +// empty() +template +concept CanCallEmpty = requires (View& v) { v.empty(); }; +static_assert(CanCallEmpty); + +// operator bool() +template +concept CanCallOperatorBool = requires (View& v) { static_cast(v); }; +static_assert(CanCallOperatorBool); + +// front() +template +concept CanCallFront = requires (View& v) { v.front(); }; +static_assert(CanCallFront); + +// size() is unavailable. +static_assert(!std::sized_sentinel_for, std::ranges::iterator_t>); +template +concept CanCallSize = requires (View& v) { v.size(); }; +static_assert(!CanCallSize); + +// back() is unavailable. +static_assert(!std::ranges::bidirectional_range); +template +concept CanCallBack = requires (View& v) { v.back(); }; +static_assert(!CanCallBack); + +// data() is unavailable. +static_assert(!std::contiguous_iterator>); +template +concept CanCallData = requires (View& v) { v.data(); }; +static_assert(!CanCallData); + +// operator[] is unavailable. +static_assert(!std::ranges::random_access_range); +template +concept CanCallOperatorSubscript = requires (View& v) { v[0]; }; +static_assert(!CanCallOperatorSubscript); + +constexpr bool test() { + // empty() + { + std::ranges::lazy_split_view v("abc", ""); + assert(!v.empty()); + // Note: a `lazy_split_view` is never empty. + } + + // operator bool() + { + std::ranges::lazy_split_view v("abc", ""); + assert(v); + // Note: a `lazy_split_view` is never empty. + } + + // front() + { + std::ranges::lazy_split_view v("abc", ""); + assert(*(v.front()).begin() == 'a'); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -510,6 +510,9 @@ template void operator,(T const &) = delete; }; +#ifndef _LIBCPP_HAS_NO_CONCEPTS + static_assert(std::input_iterator>); +#endif template struct iter_value_or_void { using type = void; };