diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index bbcc76b52f0a9..b50a200e3fb10 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -362,7 +362,7 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges`` ``202406L`` ---------------------------------------------------------- ----------------- - ``__cpp_lib_ranges_as_const`` *unimplemented* + ``__cpp_lib_ranges_as_const`` ``202311L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_as_rvalue`` ``202207L`` ---------------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index f67a5fced6fc5..f9f22146144dd 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -44,6 +44,8 @@ Implemented Papers - P2255R2: A type trait to detect reference binding to temporary (implemented the type traits only) (`Github `__) - P2562R1: ``constexpr`` Stable Sorting (`Github `__) - P1222R4: A Standard ``flat_set`` is partially implemented and ``flat_set`` is provided (`Github `__) +- P2278R4: ``cbegin`` should always return a constant iterator (`Github `__) +- P2836R1: ``std::basic_const_iterator`` should follow its underlying type's convertibility (`Github `__) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv index 8cfc25099c034..d80e35dc39b04 100644 --- a/libcxx/docs/Status/Cxx23Issues.csv +++ b/libcxx/docs/Status/Cxx23Issues.csv @@ -219,9 +219,9 @@ "`LWG3761 `__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","" "`LWG3762 `__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","","","" "`LWG3764 `__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","" -"`LWG3765 `__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","" -"`LWG3766 `__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","" -"`LWG3770 `__","``const_sentinel_t`` is missing","2022-11 (Kona)","","","" +"`LWG3765 `__","``const_sentinel`` should be constrained","2022-11 (Kona)","|Complete|","21","" +"`LWG3766 `__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","|Complete|","21","" +"`LWG3770 `__","``const_sentinel_t`` is missing","2022-11 (Kona)","|Complete|","21","" "`LWG3773 `__","``views::zip_transform`` still requires ``F`` to be ``copy_constructible`` when empty pack","2022-11 (Kona)","","","" "`LWG3774 `__","```` should include ````","2022-11 (Kona)","","","" "`LWG3775 `__","Broken dependencies in the ``Cpp17Allocator`` requirements","2022-11 (Kona)","","","" @@ -252,9 +252,9 @@ "`LWG3664 `__","`LWG 3392 `__ ``broke std::ranges::distance(a, a+3)``","2023-02 (Issaquah)","","","" "`LWG3720 `__","Restrict the valid types of ``arg-id`` for width and precision in ``std-format-spec``","2023-02 (Issaquah)","|Complete|","17","" "`LWG3756 `__","Is the ``std::atomic_flag`` class signal-safe?","2023-02 (Issaquah)","","","" -"`LWG3769 `__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","","","" +"`LWG3769 `__","``basic_const_iterator::operator==`` causes infinite constraint recursion","2023-02 (Issaquah)","|Complete|","21","" "`LWG3807 `__","The feature test macro for ``ranges::find_last`` should be renamed","2023-02 (Issaquah)","","","" -"`LWG3811 `__","``views::as_const`` on ``ref_view`` should return ``ref_view``","2023-02 (Issaquah)","","","" +"`LWG3811 `__","``views::as_const`` on ``ref_view`` should return ``ref_view``","2023-02 (Issaquah)","|Complete|","21","" "`LWG3820 `__","``cartesian_product_view::iterator::prev`` is not quite right","2023-02 (Issaquah)","","","" "`LWG3825 `__","Missing compile-time argument ``id`` check in ``basic_format_parse_context::next_arg_id``","2023-02 (Issaquah)","|Complete|","17","" "`LWG3204 `__","``sub_match::swap`` only swaps the base class","2023-02 (Issaquah)","|Complete|","17","" @@ -269,9 +269,9 @@ "`LWG3842 `__","Unclear wording for ``precision`` in ``chrono-format-spec``","2023-02 (Issaquah)","|Complete|","16","" "`LWG3848 `__","``adjacent_view``, ``adjacent_transform_view`` and ``slide_view`` missing ``base`` accessor","2023-02 (Issaquah)","","","" "`LWG3849 `__","``cartesian_product_view::iterator``'s default constructor is overconstrained","2023-02 (Issaquah)","","","" -"`LWG3850 `__","``views::as_const`` on ``empty_view`` should return ``empty_view``","2023-02 (Issaquah)","","","" +"`LWG3850 `__","``views::as_const`` on ``empty_view`` should return ``empty_view``","2023-02 (Issaquah)","|Complete|","21","" "`LWG3851 `__","``chunk_view::inner-iterator`` missing custom ``iter_move`` and ``iter_swap``","2023-02 (Issaquah)","","","" -"`LWG3853 `__","``basic_const_iterator::operator->`` is ill-formed","2023-02 (Issaquah)","","","" +"`LWG3853 `__","``basic_const_iterator::operator->`` is ill-formed","2023-02 (Issaquah)","|Complete|","21","" "`LWG3857 `__","``basic_string_view`` should allow explicit conversion when only traits vary","2023-02 (Issaquah)","|Complete|","17","" "`LWG3860 `__","``range_common_reference_t`` is missing","2023-02 (Issaquah)","|Complete|","17","" "`LWG3866 `__","Bad Mandates for ``expected::transform_error`` overloads","2023-02 (Issaquah)","|Complete|","17","" @@ -293,12 +293,12 @@ "`LWG3836 `__","``std::expected`` conversion constructor ``expected(const expected&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","2023-02 (Issaquah)","|Complete|","18","" "`LWG3843 `__","``std::expected::value() &`` assumes ``E`` is copy constructible","2023-02 (Issaquah)","|Complete|","17","" "`LWG3847 `__","``ranges::to`` can still return views","2023-02 (Issaquah)","|Complete|","17","" -"`LWG3862 `__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","2023-02 (Issaquah)","","","" +"`LWG3862 `__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","2023-02 (Issaquah)","|Complete|","21","" "`LWG3865 `__","Sorting a range of ``pairs``","2023-02 (Issaquah)","|Complete|","17","" "`LWG3869 `__","Deprecate ``std::errc`` constants related to UNIX STREAMS","2023-02 (Issaquah)","|Complete|","19","" "`LWG3870 `__","Remove ``voidify``","2023-02 (Issaquah)","|Complete|","20","" "`LWG3871 `__","Adjust note about ``terminate``","2023-02 (Issaquah)","","","" -"`LWG3872 `__","``basic_const_iterator`` should have custom ``iter_move``","2023-02 (Issaquah)","","","" +"`LWG3872 `__","``basic_const_iterator`` should have custom ``iter_move``","2023-02 (Issaquah)","|Complete|","21","" "`LWG3875 `__","``std::ranges::repeat_view::iterator`` may be ill-formed","2023-02 (Issaquah)","|Complete|","17","" "`LWG3876 `__","Default constructor of ``std::layout_XX::mapping`` misses precondition","2023-02 (Issaquah)","","","" "`LWG3877 `__","Incorrect constraints on ``const``-qualified monadic overloads for ``std::expected``","2023-02 (Issaquah)","|Complete|","17","" diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index 4e45debd419ef..1b9b8ef5f277c 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -61,7 +61,7 @@ "`P1899R3 `__","``stride_view``","2022-07 (Virtual)","","","" "`P2093R14 `__","Formatted output","2022-07 (Virtual)","|Complete|","18","" "`P2165R4 `__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","|Partial|","","Only the part for ``zip_view`` is implemented." -"`P2278R4 `__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","","","" +"`P2278R4 `__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","|Complete|","21","" "`P2286R8 `__","Formatting Ranges","2022-07 (Virtual)","|Complete|","16","" "`P2291R3 `__","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ```` Header","2022-07 (Virtual)","|Complete|","16","" "`P2302R4 `__","``std::ranges::contains``","2022-07 (Virtual)","|Complete|","19","" diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index 9bf31c417f3c9..2033f7fe8d27f 100644 --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -25,9 +25,9 @@ "`LWG3809 `__","Is ``std::subtract_with_carry_engine`` supposed to work","2023-11 (Kona)","","","" "`LWG3892 `__","Incorrect formatting of nested ranges and tuples","2023-11 (Kona)","|Complete|","17","" "`LWG3897 `__","``inout_ptr`` will not update raw pointer to 0","2023-11 (Kona)","|Complete|","19","" -"`LWG3946 `__","The definition of ``const_iterator_t`` should be reworked","2023-11 (Kona)","","","" +"`LWG3946 `__","The definition of ``const_iterator_t`` should be reworked","2023-11 (Kona)","|Complete|","21","" "`LWG3947 `__","Unexpected constraints on ``adjacent_transform_view::base()``","2023-11 (Kona)","","","" -"`LWG3948 `__","``possibly-const-range and as-const-pointer`` should be ``noexcept``","2023-11 (Kona)","","","" +"`LWG3948 `__","``possibly-const-range and as-const-pointer`` should be ``noexcept``","2023-11 (Kona)","|Complete|","21","" "`LWG3949 `__","``std::atomic``'s trivial destructor dropped in C++17 spec wording","2023-11 (Kona)","","","" "`LWG3951 `__","[expected.object.swap]: Using ``value()`` instead of ``has_value()``","2023-11 (Kona)","|Complete|","16","" "`LWG3953 `__","``iter_move`` for ``common_iterator`` and ``counted_iterator`` should return ``decltype(auto)``","2023-11 (Kona)","|Complete|","20","" @@ -84,7 +84,7 @@ "`LWG3918 `__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","" "`LWG4014 `__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","" "`LWG4024 `__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","","16","" -"`LWG4027 `__","``possibly-const-range`` should prefer returning ``const R&``","2024-11 (Wrocław)","","","" +"`LWG4027 `__","``possibly-const-range`` should prefer returning ``const R&``","2024-11 (Wrocław)","|Complete|","21","" "`LWG4044 `__","Confusing requirements for ``std::print`` on POSIX platforms","2024-11 (Wrocław)","","","" "`LWG4064 `__","Clarify that ``std::launder`` is not needed when using the result of ``std::memcpy``","2024-11 (Wrocław)","","","" "`LWG4072 `__","``std::optional`` comparisons: constrain harder","2024-11 (Wrocław)","","","" diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 9a4cd3e57e842..7803fd47e36ca 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -42,7 +42,7 @@ "`P2819R2 `__","Add tuple protocol to complex","2023-11 (Kona)","|Complete|","19","" "`P2937R0 `__","Freestanding: Remove ``strtok``","2023-11 (Kona)","","","" "`P2833R2 `__","Freestanding Library: inout expected span","2023-11 (Kona)","","","" -"`P2836R1 `__","``std::basic_const_iterator`` should follow its underlying type's convertibility","2023-11 (Kona)","","","" +"`P2836R1 `__","``std::basic_const_iterator`` should follow its underlying type's convertibility","2023-11 (Kona)","|Complete|","21","Implemented as a DR against C++23. (MSVC STL and libstdc++ do the same.)" "`P2264R7 `__","Make ``assert()`` macro user friendly for C and C++","2023-11 (Kona)","","","" "`P1673R13 `__","A free function linear algebra interface based on the BLAS","2023-11 (Kona)","","","" "","","","","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index a021b9bb44d67..a0ce4fdc3c195 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -468,6 +468,7 @@ set(files __iterator/bounded_iter.h __iterator/common_iterator.h __iterator/concepts.h + __iterator/const_iterator.h __iterator/counted_iterator.h __iterator/cpp17_iterator_concepts.h __iterator/data.h @@ -679,14 +680,15 @@ set(files __random/weibull_distribution.h __ranges/access.h __ranges/all.h + __ranges/as_const_view.h __ranges/as_rvalue_view.h __ranges/chunk_by_view.h __ranges/common_view.h __ranges/concepts.h + __ranges/const_access.h __ranges/container_compatible_range.h __ranges/counted.h __ranges/dangling.h - __ranges/data.h __ranges/drop_view.h __ranges/drop_while_view.h __ranges/elements_view.h diff --git a/libcxx/include/__algorithm/ranges_reverse_copy.h b/libcxx/include/__algorithm/ranges_reverse_copy.h index e5ca5cf652dc4..c290fd24946fb 100644 --- a/libcxx/include/__algorithm/ranges_reverse_copy.h +++ b/libcxx/include/__algorithm/ranges_reverse_copy.h @@ -34,6 +34,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD namespace ranges { +template +_LIBCPP_HIDE_FROM_ABI constexpr ranges::subrange>, + reverse_iterator>> +__reverse_range(_Range&& __range) { + auto __first = ranges::begin(__range); + return {std::make_reverse_iterator(ranges::next(__first, ranges::end(__range))), std::make_reverse_iterator(__first)}; +} + template using reverse_copy_result = in_out_result<_InIter, _OutIter>; @@ -49,7 +57,7 @@ struct __reverse_copy { requires indirectly_copyable, _OutIter> _LIBCPP_HIDE_FROM_ABI constexpr reverse_copy_result, _OutIter> operator()(_Range&& __range, _OutIter __result) const { - auto __ret = ranges::copy(std::__reverse_range(__range), std::move(__result)); + auto __ret = ranges::copy(ranges::__reverse_range(__range), std::move(__result)); return {ranges::next(ranges::begin(__range), ranges::end(__range)), std::move(__ret.out)}; } }; diff --git a/libcxx/include/__format/range_default_formatter.h b/libcxx/include/__format/range_default_formatter.h index bb4c520f5ea11..87c8c7b8ca113 100644 --- a/libcxx/include/__format/range_default_formatter.h +++ b/libcxx/include/__format/range_default_formatter.h @@ -22,8 +22,8 @@ #include <__format/formatter.h> #include <__format/range_formatter.h> #include <__iterator/back_insert_iterator.h> +#include <__ranges/access.h> #include <__ranges/concepts.h> -#include <__ranges/data.h> #include <__ranges/from_range.h> #include <__ranges/size.h> #include <__type_traits/conditional.h> diff --git a/libcxx/include/__format/range_formatter.h b/libcxx/include/__format/range_formatter.h index def55c86ce51c..c7a07727dee2e 100644 --- a/libcxx/include/__format/range_formatter.h +++ b/libcxx/include/__format/range_formatter.h @@ -26,8 +26,8 @@ #include <__format/formatter_output.h> #include <__format/parser_std_format_spec.h> #include <__iterator/back_insert_iterator.h> +#include <__ranges/access.h> #include <__ranges/concepts.h> -#include <__ranges/data.h> #include <__ranges/from_range.h> #include <__ranges/size.h> #include <__type_traits/remove_cvref.h> diff --git a/libcxx/include/__iterator/const_iterator.h b/libcxx/include/__iterator/const_iterator.h new file mode 100644 index 0000000000000..048cc838d5218 --- /dev/null +++ b/libcxx/include/__iterator/const_iterator.h @@ -0,0 +1,350 @@ +// -*- 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___ITERATOR_CONST_ITERATOR_H +#define _LIBCPP___ITERATOR_CONST_ITERATOR_H + +#include <__compare/three_way_comparable.h> +#include <__concepts/common_with.h> +#include <__concepts/constructible.h> +#include <__concepts/convertible_to.h> +#include <__concepts/different_from.h> +#include <__concepts/same_as.h> +#include <__concepts/semiregular.h> +#include <__concepts/totally_ordered.h> +#include <__iterator/concepts.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/iter_move.h> +#include <__iterator/iterator_traits.h> +#include <__memory/addressof.h> +#include <__memory/pointer_traits.h> +#include <__type_traits/common_reference.h> +#include <__type_traits/common_type.h> +#include <__type_traits/conditional.h> +#include <__type_traits/integral_constant.h> +#include <__type_traits/is_reference.h> +#include <__type_traits/is_specialization.h> +#include <__type_traits/remove_cvref.h> +#include <__utility/forward.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +template +using iter_const_reference_t _LIBCPP_NODEBUG = common_reference_t&&, iter_reference_t<_Iter>>; + +template +concept __constant_iterator = input_iterator<_Iter> && same_as, iter_reference_t<_Iter>>; + +template +class basic_const_iterator; + +template +using const_iterator = conditional_t<__constant_iterator<_Iter>, _Iter, basic_const_iterator<_Iter>>; + +// This doesn't use `conditional_t` to avoid instantiating const_iterator<_Sent> when _Sent is not an input_iterator. +template +struct __const_sentinel_impl { + using type _LIBCPP_NODEBUG = _Sent; +}; +template + requires input_iterator<_Sent> +struct __const_sentinel_impl<_Sent> { + using type _LIBCPP_NODEBUG = const_iterator<_Sent>; +}; +template +using const_sentinel = __const_sentinel_impl<_Sent>::type; + +template +concept __not_a_const_iterator = !__is_specialization_v<_Iter, basic_const_iterator>; + +template +using __iter_const_rvalue_reference_t _LIBCPP_NODEBUG = + common_reference_t&&, iter_rvalue_reference_t<_Iter>>; + +template +struct __basic_const_iterator_category {}; +template +struct __basic_const_iterator_category<_Iter> { + using iterator_category = std::iterator_traits<_Iter>::iterator_category; +}; + +template +class _LIBCPP_TEMPLATE_VIS basic_const_iterator : public __basic_const_iterator_category<_Iter> { + _Iter __current_ = _Iter(); + + using __reference _LIBCPP_NODEBUG = iter_const_reference_t<_Iter>; + using __rvalue_reference _LIBCPP_NODEBUG = __iter_const_rvalue_reference_t<_Iter>; + +public: + using value_type = iter_value_t<_Iter>; + using difference_type = iter_difference_t<_Iter>; + // clang-format off + using iterator_concept = + conditional_t, + contiguous_iterator_tag, + conditional_t, + random_access_iterator_tag, + conditional_t, + bidirectional_iterator_tag, + conditional_t, + forward_iterator_tag, + // else + input_iterator_tag>>>>; + // clang-format on + + _LIBCPP_HIDE_FROM_ABI basic_const_iterator() + requires default_initializable<_Iter> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(_Iter __cur) : __current_(std::move(__cur)) {} + template _Type> + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(basic_const_iterator<_Type> __cur) + : __current_(std::move(__cur.__current_)) {} + template <__different_from _Type> + requires convertible_to<_Type, _Iter> + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator(_Type&& __cur) : __current_(std::forward<_Type>(__cur)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr const _Iter& base() const& noexcept { return __current_; } + _LIBCPP_HIDE_FROM_ABI constexpr _Iter base() && { return std::move(__current_); } + + _LIBCPP_HIDE_FROM_ABI constexpr __reference operator*() const { return static_cast<__reference>(*__current_); } + _LIBCPP_HIDE_FROM_ABI constexpr const auto* operator->() const + requires is_lvalue_reference_v> && + same_as>, value_type> + { + if constexpr (contiguous_iterator<_Iter>) { + return std::to_address(__current_); + } else { + return std::addressof(*__current_); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator++() { + ++__current_; + return *this; + } + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++__current_; } + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator operator++(int) + requires forward_iterator<_Iter> + { + auto __tmp = *this; + ++__current_; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator--() + requires bidirectional_iterator<_Iter> + { + --__current_; + return *this; + } + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator operator--(int) + requires bidirectional_iterator<_Iter> + { + auto __tmp = *this; + --__current_; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator+=(difference_type __n) + requires random_access_iterator<_Iter> + { + __current_ += __n; + return *this; + } + _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator& operator-=(difference_type __n) + requires random_access_iterator<_Iter> + { + __current_ -= __n; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __reference operator[](difference_type __n) const + requires random_access_iterator<_Iter> + { + return static_cast<__reference>(__current_[__n]); + } + + template _Sent> + _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Sent& __sent) const { + return __current_ == __sent; + } + + template <__not_a_const_iterator _ConstIt> + requires __constant_iterator<_ConstIt> && convertible_to<_Iter const&, _ConstIt> + _LIBCPP_HIDE_FROM_ABI constexpr operator _ConstIt() const& { + return __current_; + } + template <__not_a_const_iterator _ConstIt> + requires __constant_iterator<_ConstIt> && convertible_to<_Iter, _ConstIt> + _LIBCPP_HIDE_FROM_ABI constexpr operator _ConstIt() && { + return std::move(__current_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const basic_const_iterator& __rhs) const + requires random_access_iterator<_Iter> + { + return __current_ < __rhs.__current_; + } + _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const basic_const_iterator& __rhs) const + requires random_access_iterator<_Iter> + { + return __current_ > __rhs.__current_; + } + _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const basic_const_iterator& __rhs) const + requires random_access_iterator<_Iter> + { + return __current_ <= __rhs.__current_; + } + _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const basic_const_iterator& __rhs) const + requires random_access_iterator<_Iter> + { + return __current_ >= __rhs.__current_; + } + _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const basic_const_iterator& __rhs) const + requires random_access_iterator<_Iter> && three_way_comparable<_Iter> + { + return __current_ <=> __rhs.__current_; + } + + template <__different_from _Iter2> + _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Iter2& __rhs) const + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> + { + return __current_ < __rhs; + } + template <__different_from _Iter2> + _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Iter2& __rhs) const + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> + { + return __current_ > __rhs; + } + template <__different_from _Iter2> + _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Iter2& __rhs) const + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> + { + return __current_ <= __rhs; + } + template <__different_from _Iter2> + _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Iter2& __rhs) const + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> + { + return __current_ >= __rhs; + } + template <__different_from _Iter2> + _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const _Iter2& __rhs) const + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> && + three_way_comparable_with<_Iter, _Iter2> + { + return __current_ <=> __rhs; + } + + template <__not_a_const_iterator _Iter2> + friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Iter2& __lhs, const basic_const_iterator& __rhs) + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> + { + return __lhs < __rhs.__current_; + } + template <__not_a_const_iterator _Iter2> + friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Iter2& __lhs, const basic_const_iterator& __rhs) + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> + { + return __lhs > __rhs.__current_; + } + template <__not_a_const_iterator _Iter2> + friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Iter2& __lhs, const basic_const_iterator& __rhs) + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> + { + return __lhs <= __rhs.__current_; + } + template <__not_a_const_iterator _Iter2> + friend _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Iter2& __lhs, const basic_const_iterator& __rhs) + requires random_access_iterator<_Iter> && totally_ordered_with<_Iter, _Iter2> + { + return __lhs >= __rhs.__current_; + } + + friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator + operator+(const basic_const_iterator& __it, difference_type __n) + requires random_access_iterator<_Iter> + { + return basic_const_iterator(__it.__current_ + __n); + } + friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator + operator+(difference_type __n, const basic_const_iterator& __it) + requires random_access_iterator<_Iter> + { + return basic_const_iterator(__it.__current_ + __n); + } + + friend _LIBCPP_HIDE_FROM_ABI constexpr basic_const_iterator + operator-(const basic_const_iterator& __it, difference_type __n) + requires random_access_iterator<_Iter> + { + return basic_const_iterator(__it.__current_ - __n); + } + template _Sent> + _LIBCPP_HIDE_FROM_ABI constexpr difference_type operator-(const _Sent& __rhs) const { + return __current_ - __rhs; + } + template <__not_a_const_iterator _Sent> + requires sized_sentinel_for<_Sent, _Iter> + friend _LIBCPP_HIDE_FROM_ABI constexpr difference_type + operator-(const _Sent& __lhs, const basic_const_iterator& __rhs) { + return __lhs - __rhs; + } + + friend _LIBCPP_HIDE_FROM_ABI constexpr __rvalue_reference iter_move(const basic_const_iterator& __it) noexcept( + noexcept(static_cast<__rvalue_reference>(ranges::iter_move(__it.__current_)))) { + return static_cast<__rvalue_reference>(ranges::iter_move(__it.__current_)); + } +}; + +template _Type2> + requires input_iterator> +struct common_type, _Type2> { + using type _LIBCPP_NODEBUG = basic_const_iterator>; +}; +template _Type2> + requires input_iterator> +struct common_type<_Type2, basic_const_iterator<_Type1>> { + using type _LIBCPP_NODEBUG = basic_const_iterator>; +}; +template _Type2> + requires input_iterator> +struct common_type, basic_const_iterator<_Type2>> { + using type _LIBCPP_NODEBUG = basic_const_iterator>; +}; + +template +_LIBCPP_HIDE_FROM_ABI constexpr const_iterator<_Iter> make_const_iterator(_Iter __it) { + return __it; +} +template +_LIBCPP_HIDE_FROM_ABI constexpr const_sentinel<_Sent> make_const_sentinel(_Sent __sent) { + return __sent; +} + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___ITERATOR_CONST_ITERATOR_H diff --git a/libcxx/include/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h index 5bd1f868d3ff3..aa9c3903a8ee3 100644 --- a/libcxx/include/__iterator/reverse_iterator.h +++ b/libcxx/include/__iterator/reverse_iterator.h @@ -27,9 +27,6 @@ #include <__iterator/readable_traits.h> #include <__iterator/segmented_iterator.h> #include <__memory/addressof.h> -#include <__ranges/access.h> -#include <__ranges/concepts.h> -#include <__ranges/subrange.h> #include <__type_traits/conditional.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_assignable.h> @@ -317,16 +314,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Ite } #endif -#if _LIBCPP_STD_VER >= 20 -template -_LIBCPP_HIDE_FROM_ABI constexpr ranges::subrange>, - reverse_iterator>> -__reverse_range(_Range&& __range) { - auto __first = ranges::begin(__range); - return {std::make_reverse_iterator(ranges::next(__first, ranges::end(__range))), std::make_reverse_iterator(__first)}; -} -#endif - template struct __unwrap_iter_impl >, __b> { using _UnwrappedIter _LIBCPP_NODEBUG = decltype(__unwrap_iter_impl<_Iter>::__unwrap(std::declval<_Iter>())); diff --git a/libcxx/include/__ranges/access.h b/libcxx/include/__ranges/access.h index bbacef3eae6be..777db55d38f17 100644 --- a/libcxx/include/__ranges/access.h +++ b/libcxx/include/__ranges/access.h @@ -15,10 +15,14 @@ #include <__cstddef/size_t.h> #include <__iterator/concepts.h> #include <__iterator/readable_traits.h> +#include <__memory/pointer_traits.h> #include <__ranges/enable_borrowed_range.h> #include <__type_traits/decay.h> +#include <__type_traits/is_object.h> +#include <__type_traits/is_pointer.h> #include <__type_traits/is_reference.h> #include <__type_traits/remove_cvref.h> +#include <__type_traits/remove_pointer.h> #include <__type_traits/remove_reference.h> #include <__utility/auto_cast.h> #include <__utility/declval.h> @@ -148,58 +152,39 @@ inline constexpr auto end = __end::__fn{}; } // namespace __cpo } // namespace ranges -// [range.access.cbegin] +// [range.prim.data] namespace ranges { -namespace __cbegin { -struct __fn { - template - requires is_lvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::begin(static_cast&>(__t)))) - -> decltype(ranges::begin(static_cast&>(__t))) { - return ranges::begin(static_cast&>(__t)); - } +namespace __data { +template +concept __ptr_to_object = is_pointer_v<_Tp> && is_object_v>; - template - requires is_rvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::begin(static_cast(__t)))) - -> decltype(ranges::begin(static_cast(__t))) { - return ranges::begin(static_cast(__t)); - } +template +concept __member_data = __can_borrow<_Tp> && requires(_Tp&& __t) { + { _LIBCPP_AUTO_CAST(__t.data()) } -> __ptr_to_object; }; -} // namespace __cbegin -inline namespace __cpo { -inline constexpr auto cbegin = __cbegin::__fn{}; -} // namespace __cpo -} // namespace ranges - -// [range.access.cend] +template +concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && requires(_Tp&& __t) { + { ranges::begin(__t) } -> contiguous_iterator; +}; -namespace ranges { -namespace __cend { struct __fn { - template - requires is_lvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::end(static_cast&>(__t)))) - -> decltype(ranges::end(static_cast&>(__t))) { - return ranges::end(static_cast&>(__t)); + template <__member_data _Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) { + return __t.data(); } - template - requires is_rvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept( - noexcept(ranges::end(static_cast(__t)))) -> decltype(ranges::end(static_cast(__t))) { - return ranges::end(static_cast(__t)); + template <__ranges_begin_invocable _Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(std::to_address(ranges::begin(__t)))) { + return std::to_address(ranges::begin(__t)); } }; -} // namespace __cend +} // namespace __data inline namespace __cpo { -inline constexpr auto cend = __cend::__fn{}; +inline constexpr auto data = __data::__fn{}; } // namespace __cpo } // namespace ranges diff --git a/libcxx/include/__ranges/as_const_view.h b/libcxx/include/__ranges/as_const_view.h new file mode 100644 index 0000000000000..1fe6eaa6b8d13 --- /dev/null +++ b/libcxx/include/__ranges/as_const_view.h @@ -0,0 +1,210 @@ +// -*- 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_AS_CONST_VIEW_H +#define _LIBCPP___RANGES_AS_CONST_VIEW_H + +#include <__concepts/constructible.h> +#include <__cstddef/size_t.h> +#include <__fwd/span.h> +#include <__iterator/concepts.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/const_access.h> +#include <__ranges/empty_view.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/ref_view.h> +#include <__ranges/size.h> +#include <__ranges/view_interface.h> +#include <__type_traits/is_reference.h> +#include <__type_traits/is_specialization.h> +#include <__type_traits/remove_cvref.h> +#include <__utility/auto_cast.h> +#include <__utility/declval.h> +#include <__utility/forward.h> +#include <__utility/move.h> +#include <__utility/pair.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { +template + requires view<_View> +class as_const_view : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + +public: + _LIBCPP_HIDE_FROM_ABI as_const_view() + requires default_initializable<_View> + = default; + _LIBCPP_HIDE_FROM_ABI constexpr explicit as_const_view(_View __base) : __base_(std::move(__base)) {} + + _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() + requires(!__simple_view<_View>) + { + return ranges::cbegin(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range + { + return ranges::cbegin(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + return ranges::cend(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range + { + return ranges::cend(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return ranges::size(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return ranges::size(__base_); + } +}; + +template +as_const_view(_Range&&) -> as_const_view>; + +template +inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Tp>; + +namespace views { +namespace __as_const { + +template +inline constexpr bool __is_span_v = false; // true if and only if _Tp is a specialization of span +template +inline constexpr bool __is_span_v> = true; + +template +struct __xtype { + using type _LIBCPP_NODEBUG = void; +}; +template +struct __xtype> { + using type _LIBCPP_NODEBUG = _XType; +}; +template +struct __xtype> { + using type _LIBCPP_NODEBUG = _XType; + constexpr static size_t __extent = _Extent; +}; +template +struct __xtype> { + using type _LIBCPP_NODEBUG = _XType; +}; + +struct __fn : __range_adaptor_closure<__fn> { + // implementation strategy taken from Microsoft's STL + enum class __strategy { + __already_const, + __empty_view, + __span, + __ref_view, + __const_is_constant_range, + __otherwise, + __none, + }; + + template + static consteval pair<__strategy, bool> __choose_strategy() { + using _UType = remove_cvref_t<_Type>; + using _XType = __xtype<_UType>::type; + + if constexpr (requires { requires constant_range>; }) { + return {__strategy::__already_const, noexcept(views::all(std::declval<_Type>()))}; + } else if constexpr (__is_specialization_v<_UType, empty_view>) { + return {__strategy::__empty_view, true}; + } else if constexpr (__is_span_v<_UType>) { + return {__strategy::__span, true}; + } else if constexpr (__is_specialization_v<_UType, ref_view> && constant_range) { + return {__strategy::__ref_view, noexcept(ref_view(static_cast(std::declval<_Type>().base())))}; + } else if constexpr (is_lvalue_reference_v<_Type> && constant_range && !view<_UType>) { + return {__strategy::__const_is_constant_range, + noexcept(ref_view(static_cast(std::declval<_Type>())))}; + } else if constexpr (requires { as_const_view(std::declval<_Type>()); }) { + return {__strategy::__otherwise, noexcept(as_const_view(std::declval<_Type>()))}; + } else { + return {__strategy::__none, false}; + } + } + + template + requires(__choose_strategy<_Type>().first != __strategy::__none) + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto + operator()(_Type&& __range) noexcept(__choose_strategy<_Type>().second) { + using _UType = remove_cvref_t<_Type>; + using _XType = __xtype<_UType>::type; + + constexpr auto __st = __choose_strategy<_Type>().first; + + if constexpr (__st == __strategy::__already_const) { + return views::all(std::forward<_Type>(__range)); + } else if constexpr (__st == __strategy::__empty_view) { + return auto(views::empty); + } else if constexpr (__st == __strategy::__span) { + return span::__extent>(std::forward<_Type>(__range)); + } else if constexpr (__st == __strategy::__ref_view) { + return ref_view(static_cast(std::forward<_Type>(__range).base())); + } else if constexpr (__st == __strategy::__const_is_constant_range) { + return ref_view(static_cast(std::forward<_Type>(__range))); + } else if constexpr (__st == __strategy::__otherwise) { + return as_const_view(std::forward<_Type>(__range)); + } + } +}; + +} // namespace __as_const + +inline namespace __cpo { +inline constexpr auto as_const = __as_const::__fn{}; +} // namespace __cpo +} // namespace views + +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_AS_CONST_VIEW_H diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h index 674a3f359ff99..22ff67a73bce1 100644 --- a/libcxx/include/__ranges/concepts.h +++ b/libcxx/include/__ranges/concepts.h @@ -15,12 +15,12 @@ #include <__concepts/same_as.h> #include <__config> #include <__iterator/concepts.h> +#include <__iterator/const_iterator.h> #include <__iterator/incrementable_traits.h> #include <__iterator/iter_move.h> #include <__iterator/iterator_traits.h> #include <__iterator/readable_traits.h> #include <__ranges/access.h> -#include <__ranges/data.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/enable_view.h> #include <__ranges/size.h> @@ -61,6 +61,8 @@ concept borrowed_range = template using sentinel_t = decltype(ranges::end(std::declval<_Rp&>())); +// `const_iterator_t` and `const_sentinel_t` defined in <__ranges/const_access.h> + template using range_difference_t = iter_difference_t>; @@ -70,6 +72,11 @@ using range_value_t = iter_value_t>; template using range_reference_t = iter_reference_t>; +# if _LIBCPP_STD_VER >= 23 +template +using range_const_reference_t = iter_const_reference_t>; +# endif + template using range_rvalue_reference_t = iter_rvalue_reference_t>; @@ -133,6 +140,11 @@ concept viewable_range = (is_lvalue_reference_v<_Tp> || (movable> && !__is_std_initializer_list>)))); +# if _LIBCPP_STD_VER >= 23 +template +concept constant_range = input_range<_Tp> && __constant_iterator>; +# endif // _LIBCPP_STD_VER >= 23 + } // namespace ranges #endif // _LIBCPP_STD_VER >= 20 diff --git a/libcxx/include/__ranges/const_access.h b/libcxx/include/__ranges/const_access.h new file mode 100644 index 0000000000000..bba63f3d12979 --- /dev/null +++ b/libcxx/include/__ranges/const_access.h @@ -0,0 +1,253 @@ +// -*- 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_CONST_ACCESS_H +#define _LIBCPP___RANGES_CONST_ACCESS_H + +#include <__iterator/const_iterator.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/rbegin.h> +#include <__ranges/rend.h> +#include <__type_traits/is_reference.h> +#include <__type_traits/remove_cv.h> +#include <__type_traits/remove_reference.h> +#include <__utility/declval.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 20 + +namespace ranges { +// [range.const] +# if _LIBCPP_STD_VER >= 23 +template +_LIBCPP_HIDE_FROM_ABI constexpr auto& __possibly_const_range(_Rp& __rng) noexcept { + if constexpr (input_range) { + return const_cast(__rng); + } else { + return __rng; + } +} +# endif // _LIBCPP_STD_VER >= 23 + +// [range.access.cbegin] + +namespace __cbegin { +struct __fn { +# if _LIBCPP_STD_VER >= 23 + template + using _UType _LIBCPP_NODEBUG = decltype(ranges::begin(ranges::__possibly_const_range(std::declval<_Rng&>()))); + + template <__can_borrow _Rng> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto operator()(_Rng&& __rng) noexcept( + noexcept(const_iterator<_UType<_Rng>>(ranges::begin(ranges::__possibly_const_range(__rng))))) + -> const_iterator<_UType<_Rng>> { + return const_iterator<_UType<_Rng>>(ranges::begin(ranges::__possibly_const_range(__rng))); + } +# else // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv + template + requires is_lvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::begin(static_cast&>(__t)))) + -> decltype(ranges::begin(static_cast&>(__t))) { + return ranges::begin(static_cast&>(__t)); + } + + template + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::begin(static_cast(__t)))) + -> decltype(ranges::begin(static_cast(__t))) { + return ranges::begin(static_cast(__t)); + } +# endif // ^^^ _LIBCPP_STD_VER < 23 +}; +} // namespace __cbegin + +inline namespace __cpo { +inline constexpr auto cbegin = __cbegin::__fn{}; +} // namespace __cpo + +# if _LIBCPP_STD_VER >= 23 +// [range.range] +template +using const_iterator_t = decltype(ranges::cbegin(std::declval<_Rp&>())); +# endif // _LIBCPP_STD_VER >= 23 + +// [range.access.cend] + +namespace __cend { +struct __fn { +# if _LIBCPP_STD_VER >= 23 + template + using _UType _LIBCPP_NODEBUG = decltype(ranges::end(ranges::__possibly_const_range(std::declval<_Rng&>()))); + + template <__can_borrow _Rng> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto operator()(_Rng&& __rng) noexcept( + noexcept(const_sentinel<_UType<_Rng>>(ranges::end(ranges::__possibly_const_range(__rng))))) + -> const_sentinel<_UType<_Rng>> { + return const_sentinel<_UType<_Rng>>(ranges::end(ranges::__possibly_const_range(__rng))); + } +# else // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv + template + requires is_lvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::end(static_cast&>(__t)))) + -> decltype(ranges::end(static_cast&>(__t))) { + return ranges::end(static_cast&>(__t)); + } + + template + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::end(static_cast(__t)))) + -> decltype(ranges::end(static_cast(__t))) { + return ranges::end(static_cast(__t)); + } +# endif +}; +} // namespace __cend + +inline namespace __cpo { +inline constexpr auto cend = __cend::__fn{}; +} // namespace __cpo + +# if _LIBCPP_STD_VER >= 23 +// [range.range] +template +using const_sentinel_t = decltype(ranges::cend(std::declval<_Rp&>())); +# endif + +// [range.access.crbegin] +namespace __crbegin { +struct __fn { +# if _LIBCPP_STD_VER >= 23 + template + using _UType _LIBCPP_NODEBUG = decltype(ranges::rbegin(ranges::__possibly_const_range(std::declval<_Rng&>()))); + + template <__can_borrow _Rng> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto operator()(_Rng&& __rng) noexcept( + noexcept(const_iterator<_UType<_Rng>>(ranges::rbegin(ranges::__possibly_const_range(__rng))))) + -> const_iterator<_UType<_Rng>> { + return const_iterator<_UType<_Rng>>(ranges::rbegin(ranges::__possibly_const_range(__rng))); + } +# else // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 + template + requires is_lvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::rbegin(static_cast&>(__t)))) + -> decltype(ranges::rbegin(static_cast&>(__t))) { + return ranges::rbegin(static_cast&>(__t)); + } + + template + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::rbegin(static_cast(__t)))) + -> decltype(ranges::rbegin(static_cast(__t))) { + return ranges::rbegin(static_cast(__t)); + } +# endif // ^^^ _LIBCPP_STD_VER < 23 +}; +} // namespace __crbegin + +inline namespace __cpo { +inline constexpr auto crbegin = __crbegin::__fn{}; +} // namespace __cpo + +// [range.access.crend] +namespace __crend { +struct __fn { +# if _LIBCPP_STD_VER >= 23 + template + using _UType _LIBCPP_NODEBUG = decltype(ranges::rend(ranges::__possibly_const_range(std::declval<_Rng&>()))); + + template <__can_borrow _Rng> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto operator()(_Rng&& __rng) noexcept( + noexcept(const_sentinel<_UType<_Rng>>(ranges::rend(ranges::__possibly_const_range(__rng))))) + -> const_sentinel<_UType<_Rng>> { + return const_sentinel<_UType<_Rng>>(ranges::rend(ranges::__possibly_const_range(__rng))); + } +# else // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv + template + requires is_lvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::rend(static_cast&>(__t)))) + -> decltype(ranges::rend(static_cast&>(__t))) { + return ranges::rend(static_cast&>(__t)); + } + + template + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::rend(static_cast(__t)))) + -> decltype(ranges::rend(static_cast(__t))) { + return ranges::rend(static_cast(__t)); + } +# endif // ^^^ _LIBCPP_STD_VER < 23 +}; +} // namespace __crend + +inline namespace __cpo { +inline constexpr auto crend = __crend::__fn{}; +} // namespace __cpo + +// [range.prim.cdata] + +namespace __cdata { +struct __fn { +# if _LIBCPP_STD_VER >= 23 + template + _LIBCPP_HIDE_FROM_ABI constexpr static auto __as_const_pointer(const _Tp* __ptr) noexcept { + return __ptr; + } + + template <__can_borrow _Rng> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static auto + operator()(_Rng&& __rng) noexcept(noexcept(__as_const_pointer(ranges::data(ranges::__possibly_const_range(__rng))))) + -> decltype(__as_const_pointer(ranges::data(ranges::__possibly_const_range(__rng)))) { + return __as_const_pointer(ranges::data(ranges::__possibly_const_range(__rng))); + } + +# else // ^^^ _LIBCPP_STD_VER >= 23 / _LIBCPP_STD_VER < 23 vvv + template + requires is_lvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::data(static_cast&>(__t)))) + -> decltype(ranges::data(static_cast&>(__t))) { + return ranges::data(static_cast&>(__t)); + } + + template + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::data(static_cast(__t)))) + -> decltype(ranges::data(static_cast(__t))) { + return ranges::data(static_cast(__t)); + } +# endif // ^^^ _LIBCPP_STD_VER < 23 +}; +} // namespace __cdata + +inline namespace __cpo { +inline constexpr auto cdata = __cdata::__fn{}; +} // namespace __cpo +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_CONST_ACCESS_H diff --git a/libcxx/include/__ranges/data.h b/libcxx/include/__ranges/data.h deleted file mode 100644 index 50db3cffeeed8..0000000000000 --- a/libcxx/include/__ranges/data.h +++ /dev/null @@ -1,102 +0,0 @@ -// -*- 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_DATA_H -#define _LIBCPP___RANGES_DATA_H - -#include <__concepts/class_or_enum.h> -#include <__config> -#include <__iterator/concepts.h> -#include <__iterator/iterator_traits.h> -#include <__memory/pointer_traits.h> -#include <__ranges/access.h> -#include <__type_traits/decay.h> -#include <__type_traits/is_object.h> -#include <__type_traits/is_pointer.h> -#include <__type_traits/is_reference.h> -#include <__type_traits/remove_pointer.h> -#include <__type_traits/remove_reference.h> -#include <__utility/auto_cast.h> - -#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -# pragma GCC system_header -#endif - -_LIBCPP_BEGIN_NAMESPACE_STD - -#if _LIBCPP_STD_VER >= 20 - -// [range.prim.data] - -namespace ranges { -namespace __data { -template -concept __ptr_to_object = is_pointer_v<_Tp> && is_object_v>; - -template -concept __member_data = __can_borrow<_Tp> && requires(_Tp&& __t) { - { _LIBCPP_AUTO_CAST(__t.data()) } -> __ptr_to_object; -}; - -template -concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && requires(_Tp&& __t) { - { ranges::begin(__t) } -> contiguous_iterator; -}; - -struct __fn { - template <__member_data _Tp> - _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) { - return __t.data(); - } - - template <__ranges_begin_invocable _Tp> - _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(std::to_address(ranges::begin(__t)))) { - return std::to_address(ranges::begin(__t)); - } -}; -} // namespace __data - -inline namespace __cpo { -inline constexpr auto data = __data::__fn{}; -} // namespace __cpo -} // namespace ranges - -// [range.prim.cdata] - -namespace ranges { -namespace __cdata { -struct __fn { - template - requires is_lvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::data(static_cast&>(__t)))) - -> decltype(ranges::data(static_cast&>(__t))) { - return ranges::data(static_cast&>(__t)); - } - - template - requires is_rvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept( - noexcept(ranges::data(static_cast(__t)))) -> decltype(ranges::data(static_cast(__t))) { - return ranges::data(static_cast(__t)); - } -}; -} // namespace __cdata - -inline namespace __cpo { -inline constexpr auto cdata = __cdata::__fn{}; -} // namespace __cpo -} // namespace ranges - -#endif // _LIBCPP_STD_VER >= 20 - -_LIBCPP_END_NAMESPACE_STD - -#endif // _LIBCPP___RANGES_DATA_H diff --git a/libcxx/include/__ranges/owning_view.h b/libcxx/include/__ranges/owning_view.h index 254bdb4329119..7e1b8318a2a4d 100644 --- a/libcxx/include/__ranges/owning_view.h +++ b/libcxx/include/__ranges/owning_view.h @@ -15,7 +15,6 @@ #include <__config> #include <__ranges/access.h> #include <__ranges/concepts.h> -#include <__ranges/data.h> #include <__ranges/empty.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/size.h> diff --git a/libcxx/include/__ranges/rbegin.h b/libcxx/include/__ranges/rbegin.h index 12e739e1a2b85..1e27ccc664826 100644 --- a/libcxx/include/__ranges/rbegin.h +++ b/libcxx/include/__ranges/rbegin.h @@ -20,7 +20,6 @@ #include <__type_traits/decay.h> #include <__type_traits/is_reference.h> #include <__type_traits/remove_cvref.h> -#include <__type_traits/remove_reference.h> #include <__utility/auto_cast.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -85,34 +84,6 @@ inline constexpr auto rbegin = __rbegin::__fn{}; } // namespace __cpo } // namespace ranges -// [range.access.crbegin] - -namespace ranges { -namespace __crbegin { -struct __fn { - template - requires is_lvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::rbegin(static_cast&>(__t)))) - -> decltype(ranges::rbegin(static_cast&>(__t))) { - return ranges::rbegin(static_cast&>(__t)); - } - - template - requires is_rvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::rbegin(static_cast(__t)))) - -> decltype(ranges::rbegin(static_cast(__t))) { - return ranges::rbegin(static_cast(__t)); - } -}; -} // namespace __crbegin - -inline namespace __cpo { -inline constexpr auto crbegin = __crbegin::__fn{}; -} // namespace __cpo -} // namespace ranges - #endif // _LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__ranges/ref_view.h b/libcxx/include/__ranges/ref_view.h index 5329d778dd30d..db858b8e79490 100644 --- a/libcxx/include/__ranges/ref_view.h +++ b/libcxx/include/__ranges/ref_view.h @@ -19,7 +19,6 @@ #include <__memory/addressof.h> #include <__ranges/access.h> #include <__ranges/concepts.h> -#include <__ranges/data.h> #include <__ranges/empty.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/size.h> diff --git a/libcxx/include/__ranges/rend.h b/libcxx/include/__ranges/rend.h index 02b4c5999a7eb..f0194bf192ca2 100644 --- a/libcxx/include/__ranges/rend.h +++ b/libcxx/include/__ranges/rend.h @@ -21,7 +21,6 @@ #include <__type_traits/decay.h> #include <__type_traits/is_reference.h> #include <__type_traits/remove_cvref.h> -#include <__type_traits/remove_reference.h> #include <__utility/auto_cast.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -89,33 +88,6 @@ inline constexpr auto rend = __rend::__fn{}; } // namespace __cpo } // namespace ranges -// [range.access.crend] - -namespace ranges { -namespace __crend { -struct __fn { - template - requires is_lvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::rend(static_cast&>(__t)))) - -> decltype(ranges::rend(static_cast&>(__t))) { - return ranges::rend(static_cast&>(__t)); - } - - template - requires is_rvalue_reference_v<_Tp&&> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept( - noexcept(ranges::rend(static_cast(__t)))) -> decltype(ranges::rend(static_cast(__t))) { - return ranges::rend(static_cast(__t)); - } -}; -} // namespace __crend - -inline namespace __cpo { -inline constexpr auto crend = __crend::__fn{}; -} // namespace __cpo -} // namespace ranges - #endif // _LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__ranges/view_interface.h b/libcxx/include/__ranges/view_interface.h index 3bcfbaf3a2f9e..f825941dcba4c 100644 --- a/libcxx/include/__ranges/view_interface.h +++ b/libcxx/include/__ranges/view_interface.h @@ -20,6 +20,7 @@ #include <__memory/pointer_traits.h> #include <__ranges/access.h> #include <__ranges/concepts.h> +#include <__ranges/const_access.h> #include <__ranges/empty.h> #include <__ranges/size.h> #include <__type_traits/is_class.h> @@ -72,6 +73,27 @@ class view_interface { } } + _LIBCPP_HIDE_FROM_ABI constexpr auto cbegin() + requires input_range<_Derived> + { + return ranges::cbegin(__derived()); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto cbegin() const + requires input_range + { + return ranges::cbegin(__derived()); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto cend() + requires input_range<_Derived> + { + return ranges::cend(__derived()); + } + _LIBCPP_HIDE_FROM_ABI constexpr auto cend() const + requires input_range + { + return ranges::cend(__derived()); + } + template _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() requires requires(_D2& __t) { ranges::empty(__t); } diff --git a/libcxx/include/iterator b/libcxx/include/iterator index d25fdfd2a8b33..bf21fa6467403 100644 --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -383,6 +383,31 @@ insert_iterator inserter(Container& x, typename Container::iterator i template constexpr insert_iterator inserter(Container& x, ranges::iterator_t i); // since C++20 +template +using iter_const_reference_t = see below; // since C++23 +template +using const_iterator = see below; // since C++23 +template +using const_sentinel = see below; // since C++23 + +template +class basic_const_iterator; // since C++23 + +template U> + requires input_iterator> +struct common_type, U>; // since C++23 +template U> + requires input_iterator> +struct common_type>; // since C++23 +template U> + requires input_iterator> +struct common_type, basic_const_iterator>; // since C++23 + +template +constexpr const_iterator make_const_iterator(I it); // since C++23 +template +constexpr const_sentinel make_const_sentinel(S s); // since C++23 + template class move_iterator { public: @@ -729,6 +754,10 @@ template constexpr const E* data(initializer_list il) noexcept; # include <__iterator/unreachable_sentinel.h> # endif +# if _LIBCPP_STD_VER >= 23 +# include <__iterator/const_iterator.h> +# endif + # include // standard-mandated includes diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index b42f945a6832c..f5f1b5e2e4796 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -1478,6 +1478,7 @@ module std [system] { header "__iterator/concepts.h" export std_core.type_traits.common_reference } + module const_iterator { header "__iterator/const_iterator.h" } module counted_iterator { header "__iterator/counted_iterator.h" } module cpp17_iterator_concepts { header "__iterator/cpp17_iterator_concepts.h" } module data { header "__iterator/data.h" } @@ -1816,12 +1817,14 @@ module std [system] { module ranges { module access { header "__ranges/access.h" } module all { header "__ranges/all.h" } + module as_const_view { header "__ranges/as_const_view.h" } module as_rvalue_view { header "__ranges/as_rvalue_view.h" } module chunk_by_view { header "__ranges/chunk_by_view.h" export std.functional.bind_back } module common_view { header "__ranges/common_view.h" } + module const_access { header "__ranges/const_access.h" } module concepts { header "__ranges/concepts.h" } module container_compatible_range { header "__ranges/container_compatible_range.h" } module counted { @@ -1832,9 +1835,6 @@ module std [system] { module dangling { header "__ranges/dangling.h" } - module data { - header "__ranges/data.h" - } module drop_view { header "__ranges/drop_view.h" export std.functional.bind_back diff --git a/libcxx/include/ranges b/libcxx/include/ranges index 9ef614d21f525..909923dec9165 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -39,6 +39,10 @@ namespace std::ranges { using iterator_t = decltype(ranges::begin(declval())); template using sentinel_t = decltype(ranges::end(declval())); + template + using const_iterator_t = decltype(ranges::cbegin(declval())); // since C++23 + template + using const_sentinel_t = decltype(ranges::cend(declval())); // since C++23 template using range_difference_t = iter_difference_t>; template @@ -47,6 +51,8 @@ namespace std::ranges { using range_value_t = iter_value_t>; template using range_reference_t = iter_reference_t>; + template + using range_const_reference_t = iter_const_reference_t>; // since C++23 template using range_rvalue_reference_t = iter_rvalue_reference_t>; template @@ -93,6 +99,9 @@ namespace std::ranges { template concept viewable_range = see below; + template + concept constant_range = see below; // since C++23 + // [range.adaptor.object], range adaptor objects template requires is_class_v && same_as> @@ -390,9 +399,9 @@ namespace std { # include <__ranges/all.h> # include <__ranges/common_view.h> # include <__ranges/concepts.h> +# include <__ranges/const_access.h> # include <__ranges/counted.h> # include <__ranges/dangling.h> -# include <__ranges/data.h> # include <__ranges/drop_view.h> # include <__ranges/drop_while_view.h> # include <__ranges/elements_view.h> @@ -424,6 +433,7 @@ namespace std { # endif # if _LIBCPP_STD_VER >= 23 +# include <__ranges/as_const_view.h> # include <__ranges/as_rvalue_view.h> # include <__ranges/chunk_by_view.h> # include <__ranges/from_range.h> diff --git a/libcxx/include/span b/libcxx/include/span index 5bb09ec515cea..72ab8e931c08d 100644 --- a/libcxx/include/span +++ b/libcxx/include/span @@ -65,7 +65,9 @@ public: using reference = element_type&; using const_reference = const element_type&; using iterator = implementation-defined; + using const_iterator = std::const_iterator; // since C++23 using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::const_iterator; // since C++23 static constexpr size_type extent = Extent; // [span.cons], span constructors, copy, assignment, and destructor @@ -115,8 +117,12 @@ public: // [span.iterators], span iterator support constexpr iterator begin() const noexcept; constexpr iterator end() const noexcept; + constexpr const_iterator cbegin() const noexcept; // since C++23 + constexpr const_iterator cend() const noexcept; // since C++23 constexpr reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() const noexcept; + constexpr const_reverse_iterator crbegin() const noexcept; // since C++23 + constexpr const_reverse_iterator crend() const noexcept; // since C++23 private: pointer data_; // exposition only @@ -157,12 +163,13 @@ template # include <__fwd/span.h> # include <__iterator/bounded_iter.h> # include <__iterator/concepts.h> +# include <__iterator/const_iterator.h> # include <__iterator/iterator_traits.h> # include <__iterator/reverse_iterator.h> # include <__iterator/wrap_iter.h> # include <__memory/pointer_traits.h> +# include <__ranges/access.h> # include <__ranges/concepts.h> -# include <__ranges/data.h> # include <__ranges/enable_borrowed_range.h> # include <__ranges/enable_view.h> # include <__ranges/size.h> @@ -172,6 +179,7 @@ template # include <__type_traits/is_convertible.h> # include <__type_traits/is_integral.h> # include <__type_traits/is_same.h> +# include <__type_traits/is_volatile.h> # include <__type_traits/remove_const.h> # include <__type_traits/remove_cv.h> # include <__type_traits/remove_cvref.h> @@ -246,6 +254,10 @@ public: using iterator = __wrap_iter; # endif using reverse_iterator = std::reverse_iterator; +# if _LIBCPP_STD_VER >= 23 + using const_iterator = std::const_iterator; + using const_reverse_iterator = std::const_iterator; +# endif // _LIBCPP_STD_VER >= 23 static constexpr size_type extent = _Extent; @@ -332,8 +344,8 @@ public: } template - _LIBCPP_HIDE_FROM_ABI constexpr auto - subspan() const noexcept -> span { + _LIBCPP_HIDE_FROM_ABI constexpr auto subspan() const noexcept + -> span { static_assert(_Offset <= _Extent, "span::subspan(): Offset out of range"); static_assert(_Count == dynamic_extent || _Count <= _Extent - _Offset, "span::subspan(): Offset + Count out of range"); @@ -396,8 +408,33 @@ public: return iterator(data() + size()); # endif } +# if _LIBCPP_STD_VER >= 23 + _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cbegin() const noexcept + requires(!is_volatile_v<_Tp>) + { + return begin(); + } + _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cend() const noexcept + requires(!is_volatile_v<_Tp>) + { + return end(); + } +# endif // _LIBCPP_STD_VER >= 23 + _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } +# if _LIBCPP_STD_VER >= 23 + _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crbegin() const noexcept + requires(!is_volatile_v<_Tp>) + { + return rbegin(); + } + _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crend() const noexcept + requires(!is_volatile_v<_Tp>) + { + return rend(); + } +# endif // _LIBCPP_STD_VER >= 23 _LIBCPP_HIDE_FROM_ABI span __as_bytes() const noexcept { return span{reinterpret_cast(data()), size_bytes()}; @@ -429,6 +466,10 @@ public: using iterator = __wrap_iter; # endif using reverse_iterator = std::reverse_iterator; +# if _LIBCPP_STD_VER >= 23 + using const_iterator = std::const_iterator; + using const_reverse_iterator = std::const_iterator; +# endif static constexpr size_type extent = dynamic_extent; @@ -561,8 +602,33 @@ public: return iterator(data() + size()); # endif } +# if _LIBCPP_STD_VER >= 23 + _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cbegin() const noexcept + requires(!is_volatile_v<_Tp>) + { + return begin(); + } + _LIBCPP_HIDE_FROM_ABI constexpr const_iterator cend() const noexcept + requires(!is_volatile_v<_Tp>) + { + return end(); + } +# endif + _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } _LIBCPP_HIDE_FROM_ABI constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } +# if _LIBCPP_STD_VER >= 23 + _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crbegin() const noexcept + requires(!is_volatile_v<_Tp>) + { + return rbegin(); + } + _LIBCPP_HIDE_FROM_ABI constexpr const_reverse_iterator crend() const noexcept + requires(!is_volatile_v<_Tp>) + { + return rend(); + } +# endif _LIBCPP_HIDE_FROM_ABI span __as_bytes() const noexcept { return {reinterpret_cast(data()), size_bytes()}; diff --git a/libcxx/include/string_view b/libcxx/include/string_view index c640ae4e79865..01ac0375d4c16 100644 --- a/libcxx/include/string_view +++ b/libcxx/include/string_view @@ -225,8 +225,8 @@ namespace std { # include <__iterator/reverse_iterator.h> # include <__iterator/wrap_iter.h> # include <__memory/pointer_traits.h> +# include <__ranges/access.h> # include <__ranges/concepts.h> -# include <__ranges/data.h> # include <__ranges/enable_borrowed_range.h> # include <__ranges/enable_view.h> # include <__ranges/size.h> diff --git a/libcxx/include/version b/libcxx/include/version index 83ae11dabd2bc..fe56c4f1a9c33 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -191,7 +191,7 @@ __cpp_lib_quoted_string_io 201304L __cpp_lib_ranges 202406L 202110L // C++20 -__cpp_lib_ranges_as_const 202207L +__cpp_lib_ranges_as_const 202311L __cpp_lib_ranges_as_rvalue 202207L __cpp_lib_ranges_chunk 202202L __cpp_lib_ranges_chunk_by 202202L @@ -507,7 +507,7 @@ __cpp_lib_void_t 201411L # endif # undef __cpp_lib_ranges # define __cpp_lib_ranges 202406L -// # define __cpp_lib_ranges_as_const 202207L +# define __cpp_lib_ranges_as_const 202311L # define __cpp_lib_ranges_as_rvalue 202207L // # define __cpp_lib_ranges_chunk 202202L # define __cpp_lib_ranges_chunk_by 202202L diff --git a/libcxx/modules/std/iterator.inc b/libcxx/modules/std/iterator.inc index 10c63d74e6e05..5278ceaf2785b 100644 --- a/libcxx/modules/std/iterator.inc +++ b/libcxx/modules/std/iterator.inc @@ -182,18 +182,21 @@ export namespace std { using std::insert_iterator; using std::inserter; +#if _LIBCPP_STD_VER >= 23 // [const.iterators], constant iterators and sentinels // [const.iterators.alias], alias templates - // using std::const_iterator; - // using std::const_sentinel; - // using std::iter_const_reference_t; + using std::const_iterator; + using std::const_sentinel; + using std::iter_const_reference_t; // [const.iterators.iterator], class template basic_const_iterator - // using std::basic_const_iterator; + using std::basic_const_iterator; - // using std::common_type; + using std::common_type; - // using std::make_const_iterator; + using std::make_const_iterator; + using std::make_const_sentinel; +#endif // [move.iterators], move iterators and sentinels using std::move_iterator; diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index a5e2a2b4583c1..c1fba00c25f7c 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -34,10 +34,14 @@ export namespace std { using std::ranges::borrowed_range; - // using std::ranges::const_iterator_t; - // using std::ranges::const_sentinel_t; +#if _LIBCPP_STD_VER >= 23 + using std::ranges::const_iterator_t; + using std::ranges::const_sentinel_t; +#endif using std::ranges::iterator_t; - // using std::ranges::range_const_reference_t; +#if _LIBCPP_STD_VER >= 23 + using std::ranges::range_const_reference_t; +#endif using std::ranges::range_common_reference_t; using std::ranges::range_difference_t; using std::ranges::range_reference_t; @@ -58,7 +62,9 @@ export namespace std { // [range.refinements], other range refinements using std::ranges::bidirectional_range; using std::ranges::common_range; - // using std::ranges::constant_range; +#if _LIBCPP_STD_VER >= 23 + using std::ranges::constant_range; +#endif using std::ranges::contiguous_range; using std::ranges::forward_range; using std::ranges::input_range; @@ -259,14 +265,15 @@ export namespace std { using std::ranges::views::reverse; } // namespace views +#if _LIBCPP_STD_VER >= 23 // [range.as.const], as const view -#if 0 using std::ranges::as_const_view; namespace views { using std::ranges::views::as_const; } // namespace views -#endif +#endif // _LIBCPP_STD_VER >= 23 + // [range.elements], elements view using std::ranges::elements_view; diff --git a/libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp b/libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp index 52f0a76dfd2ce..3624224a109a4 100644 --- a/libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp @@ -15,6 +15,8 @@ #include #include +#include "test_macros.h" + using range = std::span; static_assert(std::same_as, range::iterator>); @@ -34,3 +36,8 @@ static_assert(!std::ranges::view && !std::ranges::enable_view); static_assert(std::ranges::borrowed_range); static_assert(std::ranges::viewable_range); + +#if TEST_STD_VER >= 23 +static_assert(std::same_as, range::const_iterator>); +static_assert(std::same_as, range::const_iterator>); +#endif diff --git a/libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp index d3990fd60a459..3780e239a2207 100644 --- a/libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.cons/copy.pass.cpp @@ -87,14 +87,17 @@ constexpr bool test_all() { test(); test(); - // Note: Can't test non-fundamental types with volatile because we require `T*` to be indirectly_readable, - // which isn't the case when T is volatile. + // Note: Can't test class types with volatile because we require `T*` to be indirectly_readable, + // which isn't the case when T is volatile. See also LWG3813. test(); test(); test(); test(); +#if defined(_LIBCPP_VERSION) && TEST_STD_VER < 23 + // libc++ supports span as an extension in C++20 mode, + // but the extension is incompatible with changes of span in C++23. // Regression test for https://github.com/llvm/llvm-project/issues/104496 { struct Incomplete; @@ -103,6 +106,7 @@ constexpr bool test_all() { assert(copy.data() == x.data()); assert(copy.size() == x.size()); } +#endif return true; } diff --git a/libcxx/test/std/containers/views/views.span/span.cons/span.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/span.pass.cpp index a1ac0dd11b2d1..526b738483514 100644 --- a/libcxx/test/std/containers/views/views.span/span.cons/span.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.cons/span.pass.cpp @@ -87,11 +87,16 @@ TEST_CONSTEXPR_CXX20 void check() { } template -TEST_CONSTEXPR_CXX20 void check_cvs() { +constexpr void check_non_volatile() { check(); check(); check(); +} + +template +constexpr void check_cvs() { + check_non_volatile(); check(); check(); @@ -104,12 +109,17 @@ TEST_CONSTEXPR_CXX20 void check_cvs() { struct A {}; -TEST_CONSTEXPR_CXX20 bool test() { +constexpr bool test() { check_cvs(); check_cvs(); check_cvs(); +#if TEST_STD_VER >= 23 // LWG3813: span is generally unsupported since C++23. + check_non_volatile(); + check_non_volatile(); +#else check_cvs(); check_cvs(); +#endif return true; } diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp index 4792b756e42dc..6b0dac9dcf29c 100644 --- a/libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.iterators/begin.pass.cpp @@ -10,7 +10,7 @@ // // constexpr iterator begin() const noexcept; -// constexpr const_iterator cbegin() const noexcept; +// constexpr const_iterator cbegin() const noexcept; // since C++23 #include #include @@ -18,30 +18,30 @@ #include "test_macros.h" -template -constexpr bool testConstexprSpan(Span s) { - bool ret = true; - typename Span::iterator b = s.begin(); - +template +constexpr void testSpanImpl(Span s, Iter first) { if (s.empty()) { - ret = ret && (b == s.end()); + assert(first == s.end()); } else { - ret = ret && (*b == s[0]); - ret = ret && (&*b == &s[0]); + assert(*first == s[0]); + assert(&*first == &s[0]); } - return ret; } -template -void testRuntimeSpan(Span s) { - typename Span::iterator b = s.begin(); +template +constexpr void testSpan(Args&&... args) { + auto s1 = std::span(std::forward(args)...); - if (s.empty()) { - assert(b == s.end()); - } else { - assert(*b == s[0]); - assert(&*b == &s[0]); - } + testSpanImpl(s1, s1.begin()); +#if TEST_STD_VER >= 23 + testSpanImpl(s1, s1.cbegin()); +#endif + + auto s2 = std::span(std::forward(args)...); + testSpanImpl(s2, s2.begin()); +#if TEST_STD_VER >= 23 + testSpanImpl(s2, s2.cbegin()); +#endif } struct A {}; @@ -50,46 +50,39 @@ bool operator==(A, A) { return true; } constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; +constexpr bool test_runtime_and_constexpr() { + testSpan(); + testSpan(); + testSpan(); + testSpan(); + testSpan(); + + testSpan(iArr1, 1); + testSpan(iArr1, 2); + testSpan(iArr1, 3); + testSpan(iArr1, 4); + testSpan(iArr1, 5); + + const std::string s2; + testSpan(&s2, static_cast(0)); + testSpan(&s2, 1); + + return true; +} + int main(int, char**) { - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - - static_assert(testConstexprSpan(std::span(iArr1, 1)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 2)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 3)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 4)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 5)), ""); - - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - - testRuntimeSpan(std::span(iArr2, 1)); - testRuntimeSpan(std::span(iArr2, 2)); - testRuntimeSpan(std::span(iArr2, 3)); - testRuntimeSpan(std::span(iArr2, 4)); - testRuntimeSpan(std::span(iArr2, 5)); - - std::string s; - testRuntimeSpan(std::span(&s, (std::size_t)0)); - testRuntimeSpan(std::span(&s, 1)); + test_runtime_and_constexpr(); + static_assert(test_runtime_and_constexpr()); + + testSpan(iArr2, 1); + testSpan(iArr2, 2); + testSpan(iArr2, 3); + testSpan(iArr2, 4); + testSpan(iArr2, 5); + + std::string s1; + testSpan(&s1, static_cast(0)); + testSpan(&s1, 1); return 0; } diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp index 0daa55bf0542c..c54d5b5c58a6f 100644 --- a/libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.iterators/end.pass.cpp @@ -10,7 +10,7 @@ // // constexpr iterator end() const noexcept; -// constexpr const_iterator cend() const noexcept; +// constexpr const_iterator cend() const noexcept; // since C++23 #include #include @@ -18,34 +18,31 @@ #include "test_macros.h" -template -constexpr bool testConstexprSpan(Span s) { - bool ret = true; - typename Span::iterator e = s.end(); +template +constexpr void testSpanImpl(Span s, Iter last) { if (s.empty()) { - ret = ret && (e == s.begin()); + assert(last == s.begin()); } else { - typename Span::const_pointer last = &*(s.begin() + s.size() - 1); - ret = ret && (e != s.begin()); - ret = ret && (&*(e - 1) == last); + assert(last != s.begin()); + assert(&*(last - 1) == s.data() + s.size() - 1); } - - ret = ret && (static_cast(e - s.begin()) == s.size()); - return ret; + assert(static_cast(last - s.begin()) == s.size()); } -template -void testRuntimeSpan(Span s) { - typename Span::iterator e = s.end(); - if (s.empty()) { - assert(e == s.begin()); - } else { - typename Span::const_pointer last = &*(s.begin() + s.size() - 1); - assert(e != s.begin()); - assert(&*(e - 1) == last); - } +template +constexpr void testSpan(Args&&... args) { + auto s1 = std::span(std::forward(args)...); + + testSpanImpl(s1, s1.end()); +#if TEST_STD_VER >= 23 + testSpanImpl(s1, s1.cend()); +#endif - assert(static_cast(e - s.begin()) == s.size()); + auto s2 = std::span(std::forward(args)...); + testSpanImpl(s2, s2.end()); +#if TEST_STD_VER >= 23 + testSpanImpl(s2, s2.cend()); +#endif } struct A {}; @@ -54,46 +51,39 @@ bool operator==(A, A) { return true; } constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; +constexpr bool test_runtime_and_constexpr() { + testSpan(); + testSpan(); + testSpan(); + testSpan(); + testSpan(); + + testSpan(iArr1, 1); + testSpan(iArr1, 2); + testSpan(iArr1, 3); + testSpan(iArr1, 4); + testSpan(iArr1, 5); + + const std::string s2; + testSpan(&s2, static_cast(0)); + testSpan(&s2, 1); + + return true; +} + int main(int, char**) { - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - - static_assert(testConstexprSpan(std::span(iArr1, 1)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 2)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 3)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 4)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 5)), ""); - - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - - testRuntimeSpan(std::span(iArr2, 1)); - testRuntimeSpan(std::span(iArr2, 2)); - testRuntimeSpan(std::span(iArr2, 3)); - testRuntimeSpan(std::span(iArr2, 4)); - testRuntimeSpan(std::span(iArr2, 5)); - - std::string s; - testRuntimeSpan(std::span(&s, (std::size_t)0)); - testRuntimeSpan(std::span(&s, 1)); + test_runtime_and_constexpr(); + static_assert(test_runtime_and_constexpr()); + + testSpan(iArr2, 1); + testSpan(iArr2, 2); + testSpan(iArr2, 3); + testSpan(iArr2, 4); + testSpan(iArr2, 5); + + std::string s1; + testSpan(&s1, static_cast(0)); + testSpan(&s1, 1); return 0; } diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp index 13a7628e6043d..7379ce61436d8 100644 --- a/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.iterators/iterator.pass.cpp @@ -26,20 +26,17 @@ constexpr void test_type() { using C = std::span; typename C::iterator ii1{}, ii2{}; typename C::iterator ii4 = ii1; - // TODO Test against C++23 after implementing - // P2278R4 cbegin should always return a constant iterator - // The means adjusting the #ifdef to guard against C++23. -#ifdef __cpp_lib_ranges_as_const +#if TEST_STD_VER >= 23 typename C::const_iterator cii{}; #endif assert(ii1 == ii2); assert(ii1 == ii4); -#ifdef __cpp_lib_ranges_as_const +#if TEST_STD_VER >= 23 assert(ii1 == cii); #endif assert(!(ii1 != ii2)); -#ifdef __cpp_lib_ranges_as_const +#if TEST_STD_VER >= 23 assert(!(ii1 != cii)); #endif @@ -47,21 +44,21 @@ constexpr void test_type() { C c{&v, 1}; assert(c.begin() == std::begin(c)); assert(c.rbegin() == std::rbegin(c)); -#ifdef __cpp_lib_ranges_as_const +#if TEST_STD_VER >= 23 assert(c.cbegin() == std::cbegin(c)); assert(c.crbegin() == std::crbegin(c)); #endif assert(c.end() == std::end(c)); assert(c.rend() == std::rend(c)); -#ifdef __cpp_lib_ranges_as_const +#if TEST_STD_VER >= 23 assert(c.cend() == std::cend(c)); assert(c.crend() == std::crend(c)); #endif assert(std::begin(c) != std::end(c)); assert(std::rbegin(c) != std::rend(c)); -#ifdef __cpp_lib_ranges_as_const +#if TEST_STD_VER >= 23 assert(std::cbegin(c) != std::cend(c)); assert(std::crbegin(c) != std::crend(c)); #endif @@ -70,7 +67,7 @@ constexpr void test_type() { std::same_as decltype(auto) r1 = ii1 <=> ii2; assert(r1 == std::strong_ordering::equal); -#ifdef __cpp_lib_ranges_as_const +#if TEST_STD_VER >= 23 std::same_as decltype(auto) r2 = cii <=> ii2; assert(r2 == std::strong_ordering::equal); #endif diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/iterator_concept_conformance.compile.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/iterator_concept_conformance.compile.pass.cpp index e07be3efb4a36..b8631375c782c 100644 --- a/libcxx/test/std/containers/views/views.span/span.iterators/iterator_concept_conformance.compile.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.iterators/iterator_concept_conformance.compile.pass.cpp @@ -19,6 +19,11 @@ using iterator = std::span::iterator; using reverse_iterator = std::span::reverse_iterator; using value_type = int; +#if TEST_STD_VER >= 23 +using const_iterator = std::span::const_iterator; +using const_reverse_iterator = std::span::const_reverse_iterator; +#endif + static_assert(std::contiguous_iterator); LIBCPP_STATIC_ASSERT(std::__has_random_access_iterator_category::value); static_assert(std::indirectly_writable); @@ -31,3 +36,18 @@ static_assert(std::indirectly_movable_storable); static_assert(std::indirectly_copyable); static_assert(std::indirectly_copyable_storable); static_assert(std::indirectly_swappable); + +#if TEST_STD_VER >= 23 +static_assert(std::contiguous_iterator); +LIBCPP_STATIC_ASSERT(std::__has_random_access_iterator_category::value); +static_assert(std::sentinel_for); +static_assert(std::sentinel_for); +static_assert(std::sentinel_for); +static_assert(!std::sentinel_for); +static_assert(!std::sentinel_for); +static_assert(std::sized_sentinel_for); +static_assert(std::sized_sentinel_for); +static_assert(std::sized_sentinel_for); +static_assert(!std::sized_sentinel_for); +static_assert(!std::sized_sentinel_for); +#endif diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp index 1030dec7f6e40..359190b154da2 100644 --- a/libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.iterators/rbegin.pass.cpp @@ -9,8 +9,8 @@ // -// constexpr reverse_iterator rbegin() const noexcept; -// constexpr const_reverse_iterator crbegin() const noexcept; +// constexpr neverse_iterator rbegin() const noexcept; +// constexpr const_reverse_iterator crbegin() const noexcept; // since C++23 #include #include @@ -18,30 +18,32 @@ #include "test_macros.h" -template -constexpr bool testConstexprSpan(Span s) { - bool ret = true; - typename Span::reverse_iterator b = s.rbegin(); +template +constexpr void testSpanImpl(Span s, Iter rfirst) { if (s.empty()) { - ret = ret && (b == s.rend()); + assert(rfirst == s.rend()); } else { const typename Span::size_type last = s.size() - 1; - ret = ret && (*b == s[last]); - ret = ret && (&*b == &s[last]); + + assert(*rfirst == s[last]); + assert(&*rfirst == &s[last]); } - return ret; } -template -void testRuntimeSpan(Span s) { - typename Span::reverse_iterator b = s.rbegin(); - if (s.empty()) { - assert(b == s.rend()); - } else { - const typename Span::size_type last = s.size() - 1; - assert(*b == s[last]); - assert(&*b == &s[last]); - } +template +constexpr void testSpan(Args&&... args) { + auto s1 = std::span(std::forward(args)...); + + testSpanImpl(s1, s1.rbegin()); +#if TEST_STD_VER >= 23 + testSpanImpl(s1, s1.crbegin()); +#endif + + auto s2 = std::span(std::forward(args)...); + testSpanImpl(s2, s2.rbegin()); +#if TEST_STD_VER >= 23 + testSpanImpl(s2, s2.crbegin()); +#endif } struct A {}; @@ -50,46 +52,39 @@ bool operator==(A, A) { return true; } constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; +constexpr bool test_runtime_and_constexpr() { + testSpan(); + testSpan(); + testSpan(); + testSpan(); + testSpan(); + + testSpan(iArr1, 1); + testSpan(iArr1, 2); + testSpan(iArr1, 3); + testSpan(iArr1, 4); + testSpan(iArr1, 5); + + const std::string s2; + testSpan(&s2, static_cast(0)); + testSpan(&s2, 1); + + return true; +} + int main(int, char**) { - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - - static_assert(testConstexprSpan(std::span(iArr1, 1)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 2)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 3)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 4)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 5)), ""); - - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - - testRuntimeSpan(std::span(iArr2, 1)); - testRuntimeSpan(std::span(iArr2, 2)); - testRuntimeSpan(std::span(iArr2, 3)); - testRuntimeSpan(std::span(iArr2, 4)); - testRuntimeSpan(std::span(iArr2, 5)); - - std::string s; - testRuntimeSpan(std::span(&s, static_cast(0))); - testRuntimeSpan(std::span(&s, 1)); + test_runtime_and_constexpr(); + static_assert(test_runtime_and_constexpr()); + + testSpan(iArr2, 1); + testSpan(iArr2, 2); + testSpan(iArr2, 3); + testSpan(iArr2, 4); + testSpan(iArr2, 5); + + std::string s1; + testSpan(&s1, static_cast(0)); + testSpan(&s1, 1); return 0; } diff --git a/libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp b/libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp index ca2b781aaf614..d592de230c2d0 100644 --- a/libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.iterators/rend.pass.cpp @@ -10,7 +10,7 @@ // // constexpr reverse_iterator rend() const noexcept; -// constexpr const_reverse_iterator crend() const noexcept; +// constexpr const_reverse_iterator crend() const noexcept; // since C++23 #include #include @@ -18,30 +18,32 @@ #include "test_macros.h" -template -constexpr bool testConstexprSpan(Span s) { - bool ret = true; - typename Span::reverse_iterator e = s.rend(); +template +constexpr void testSpanImpl(Span s, Iter rlast) { if (s.empty()) { - ret = ret && (e == s.rbegin()); + assert(rlast == s.rbegin()); } else { - ret = ret && (e != s.rbegin()); + assert(rlast != s.rbegin()); + assert(rlast == std::make_reverse_iterator(s.begin())); } - ret = ret && (static_cast(e - s.rbegin()) == s.size()); - return ret; + assert(static_cast(rlast - s.rbegin()) == s.size()); } -template -void testRuntimeSpan(Span s) { - typename Span::reverse_iterator e = s.rend(); - if (s.empty()) { - assert(e == s.rbegin()); - } else { - assert(e != s.rbegin()); - } +template +constexpr void testSpan(Args&&... args) { + auto s1 = std::span(std::forward(args)...); - assert(static_cast(e - s.rbegin()) == s.size()); + testSpanImpl(s1, s1.rend()); +#if TEST_STD_VER >= 23 + testSpanImpl(s1, s1.crend()); +#endif + + auto s2 = std::span(std::forward(args)...); + testSpanImpl(s2, s2.rend()); +#if TEST_STD_VER >= 23 + testSpanImpl(s2, s2.crend()); +#endif } struct A {}; @@ -50,46 +52,39 @@ bool operator==(A, A) { return true; } constexpr int iArr1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int iArr2[] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; +constexpr bool test_runtime_and_constexpr() { + testSpan(); + testSpan(); + testSpan(); + testSpan(); + testSpan(); + + testSpan(iArr1, 1); + testSpan(iArr1, 2); + testSpan(iArr1, 3); + testSpan(iArr1, 4); + testSpan(iArr1, 5); + + const std::string s2; + testSpan(&s2, static_cast(0)); + testSpan(&s2, 1); + + return true; +} + int main(int, char**) { - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - static_assert(testConstexprSpan(std::span()), ""); - - static_assert(testConstexprSpan(std::span(iArr1, 1)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 2)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 3)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 4)), ""); - static_assert(testConstexprSpan(std::span(iArr1, 5)), ""); - - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - testRuntimeSpan(std::span()); - - testRuntimeSpan(std::span(iArr2, 1)); - testRuntimeSpan(std::span(iArr2, 2)); - testRuntimeSpan(std::span(iArr2, 3)); - testRuntimeSpan(std::span(iArr2, 4)); - testRuntimeSpan(std::span(iArr2, 5)); - - std::string s; - testRuntimeSpan(std::span(&s, (std::size_t)0)); - testRuntimeSpan(std::span(&s, 1)); + test_runtime_and_constexpr(); + static_assert(test_runtime_and_constexpr()); + + testSpan(iArr2, 1); + testSpan(iArr2, 2); + testSpan(iArr2, 3); + testSpan(iArr2, 4); + testSpan(iArr2, 5); + + std::string s1; + testSpan(&s1, static_cast(0)); + testSpan(&s1, 1); return 0; } diff --git a/libcxx/test/std/containers/views/views.span/types.pass.cpp b/libcxx/test/std/containers/views/views.span/types.pass.cpp index 14f5de823f353..a5fe1acffcd79 100644 --- a/libcxx/test/std/containers/views/views.span/types.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/types.pass.cpp @@ -65,15 +65,22 @@ void testSpan() { } template -void test() { - testSpan, T, std::dynamic_extent>(); +void test_non_volatile() { + testSpan, T, std::dynamic_extent>(); testSpan, const T, std::dynamic_extent>(); - testSpan, volatile T, std::dynamic_extent>(); - testSpan, const volatile T, std::dynamic_extent>(); - testSpan, T, 5>(); + testSpan, T, 5>(); testSpan, const T, 5>(); - testSpan, volatile T, 5>(); +} + +template +void test() { + test_non_volatile(); + + testSpan, volatile T, std::dynamic_extent>(); + testSpan, const volatile T, std::dynamic_extent>(); + + testSpan, volatile T, 5>(); testSpan, const volatile T, 5>(); } @@ -83,8 +90,13 @@ int main(int, char**) { test(); test(); test(); +#if TEST_STD_VER >= 23 // LWG3813: span is generally unsupported since C++23. + test_non_volatile(); + test_non_volatile(); +#else test(); test(); +#endif return 0; } diff --git a/libcxx/test/std/iterators/const.iterators/alias.compile.pass.cpp b/libcxx/test/std/iterators/const.iterators/alias.compile.pass.cpp new file mode 100644 index 0000000000000..c915ebec0e066 --- /dev/null +++ b/libcxx/test/std/iterators/const.iterators/alias.compile.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// template +// using const_iterator = see below; +// template +// using const_sentinel = see below; + +#include +#include +#include +#include "test_macros.h" + +template +concept UsableForConstIterator = requires { typename std::const_iterator; }; + +static_assert(UsableForConstIterator); +static_assert(UsableForConstIterator); +static_assert(!UsableForConstIterator); +static_assert(!UsableForConstIterator); +static_assert(!UsableForConstIterator); + +template +concept UsableForConstSentinel = requires { typename std::const_sentinel; }; + +static_assert(UsableForConstSentinel); +static_assert(UsableForConstSentinel); +static_assert(UsableForConstSentinel); +static_assert(!UsableForConstSentinel); +static_assert(!UsableForConstSentinel); + +ASSERT_SAME_TYPE(std::const_iterator, std::basic_const_iterator); +ASSERT_SAME_TYPE(std::const_iterator, const int*); +ASSERT_SAME_TYPE(std::const_sentinel, std::basic_const_iterator); +ASSERT_SAME_TYPE(std::const_sentinel, const int*); +ASSERT_SAME_TYPE(std::const_sentinel, std::default_sentinel_t); + +using list_iterator = std::list::iterator; +using list_const_iterator = std::list::const_iterator; + +ASSERT_SAME_TYPE(std::const_iterator, std::basic_const_iterator); +ASSERT_SAME_TYPE(std::const_iterator, list_const_iterator); +ASSERT_SAME_TYPE(std::const_sentinel, std::basic_const_iterator); +ASSERT_SAME_TYPE(std::const_sentinel, list_const_iterator); diff --git a/libcxx/test/std/iterators/const.iterators/iterator.pass.cpp b/libcxx/test/std/iterators/const.iterators/iterator.pass.cpp new file mode 100644 index 0000000000000..4fc85ca467703 --- /dev/null +++ b/libcxx/test/std/iterators/const.iterators/iterator.pass.cpp @@ -0,0 +1,187 @@ +//===----------------------------------------------------------------------===// +// +// 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: std-at-least-c++23 + +// std::basic_const_iterator + +#include +#include +#include +#include +#include +#include +#include "test_macros.h" +#include "test_iterators.h" +#include "type_algorithms.h" + +template +concept has_iterator_category = requires { typename T::iterator_category; }; + +template +constexpr bool check_category_and_concept() { + using ConstIt = std::basic_const_iterator; + + if constexpr (std::contiguous_iterator) { + ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::contiguous_iterator_tag); + static_assert(std::contiguous_iterator); + } else if constexpr (std::random_access_iterator) { + ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::random_access_iterator_tag); + static_assert(!std::contiguous_iterator); + static_assert(std::random_access_iterator); + } else if constexpr (std::bidirectional_iterator) { + ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::bidirectional_iterator_tag); + static_assert(!std::random_access_iterator); + static_assert(std::bidirectional_iterator); + } else if constexpr (std::forward_iterator) { + ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::forward_iterator_tag); + static_assert(!std::bidirectional_iterator); + static_assert(std::forward_iterator); + } else { + ASSERT_SAME_TYPE(typename ConstIt::iterator_concept, std::input_iterator_tag); + static_assert(!std::forward_iterator); + static_assert(std::input_iterator); + } + + if constexpr (std::forward_iterator) { + ASSERT_SAME_TYPE(typename ConstIt::iterator_category, typename std::iterator_traits::iterator_category); + } else { + static_assert(!has_iterator_category); + } + + return true; +} + +constexpr bool test_p2836r1() { + auto f = [](std::vector::const_iterator) {}; + + auto v = std::vector(); + { + auto i1 = std::ranges::cbegin(v); + f(i1); + } + + auto t = v | std::views::take_while([](int const x) { return x < 100; }); + { + auto i2 = std::ranges::cbegin(t); + f(i2); + } + + return true; +} + +struct S { + int x; +}; + +template +constexpr void test_basic_operations() { + S arr[10]{}; + + std::basic_const_iterator first = It{arr}; + std::basic_const_iterator last = It{arr + 10}; + + ASSERT_NOEXCEPT(first.base()); + assert(first.base() == It{arr}); + assert(last.base() == It{arr + 10}); + + static_assert(noexcept(iter_move(first)) == noexcept(std::ranges::iter_move(first.base()))); + static_assert(noexcept(std::ranges::iter_move(first)) == noexcept(std::ranges::iter_move(first.base()))); + + for (auto it = first; it.base() != last.base(); ++it) { + (void)*it; + (void)it->x; + (void)iter_move(it); + + std::same_as auto& it_ref = ++it; + assert(std::addressof(it_ref) == std::addressof(it)); + } + static_assert(!std::is_invocable_v); + + if constexpr (std::bidirectional_iterator) { + { + std::same_as auto& it_ref = ++first; + assert(std::addressof(it_ref) == std::addressof(first)); + assert(it_ref == It{arr + 1}); + } + { + std::same_as auto& it_ref = --first; + assert(std::addressof(it_ref) == std::addressof(first)); + assert(it_ref == It{arr + 0}); + } + assert(first++ == It{arr + 0}); + assert(first-- == It{arr + 1}); + } + + if constexpr (std::random_access_iterator) { + assert(first + 3 == It{arr + 3}); + assert(last - 1 == It{arr + 9}); + + { + std::same_as auto& it_ref = first += 3; + assert(std::addressof(it_ref) == std::addressof(first)); + assert(first == It{arr + 3}); + } + { + std::same_as auto& it_ref = first -= 2; + assert(std::addressof(it_ref) == std::addressof(first)); + assert(first == It{arr + 1}); + } + --first; + + assert(first < last); + assert(last > first); + assert(first <= last); + assert(last >= first); + + assert(first < It{arr + 1}); + assert(It{arr + 1} > first); + assert(first <= It{arr + 1}); + assert(It{arr + 1} >= first); + + if constexpr (std::three_way_comparable) { + assert((first <=> last) < 0); + assert((first <=> It{arr + 1}) < 0); + assert((It{arr + 1} <=> first) > 0); + } + } +} + +constexpr bool test_basic_operations() { + test_basic_operations(); + test_basic_operations>(); + test_basic_operations>(); + test_basic_operations>(); + test_basic_operations>(); + + return true; +} + +int main() { + types::for_each(types::cpp20_input_iterator_list{}, []() { + using ConstIt = std::basic_const_iterator; + ASSERT_SAME_TYPE(typename ConstIt::value_type, int); + static_assert(check_category_and_concept()); + + ASSERT_SAME_TYPE(std::iter_reference_t, const int&); + ASSERT_SAME_TYPE(std::iter_rvalue_reference_t, const int&&); + + static_assert(std::is_constructible_v); + static_assert(std::is_convertible_v); + static_assert(std::is_constructible_v == std::is_copy_constructible_v); + static_assert(std::is_convertible_v == std::is_copy_constructible_v); + }); + + test_p2836r1(); + static_assert(test_p2836r1()); + + test_basic_operations(); + static_assert(test_basic_operations()); + + return 0; +} diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp index 8493505ef0236..df8f1c96960d2 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp @@ -19,7 +19,7 @@ __cpp_lib_default_template_type_for_algorithm_values 202403L [C++26] __cpp_lib_ranges 202110L [C++20] 202406L [C++23] - __cpp_lib_ranges_as_const 202207L [C++23] + __cpp_lib_ranges_as_const 202311L [C++23] __cpp_lib_ranges_as_rvalue 202207L [C++23] __cpp_lib_ranges_chunk 202202L [C++23] __cpp_lib_ranges_chunk_by 202202L [C++23] @@ -250,17 +250,11 @@ # error "__cpp_lib_ranges should have the value 202406L in c++23" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_as_const -# error "__cpp_lib_ranges_as_const should be defined in c++23" -# endif -# if __cpp_lib_ranges_as_const != 202207L -# error "__cpp_lib_ranges_as_const should have the value 202207L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_as_const -# error "__cpp_lib_ranges_as_const should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_as_const +# error "__cpp_lib_ranges_as_const should be defined in c++23" +# endif +# if __cpp_lib_ranges_as_const != 202311L +# error "__cpp_lib_ranges_as_const should have the value 202311L in c++23" # endif # ifndef __cpp_lib_ranges_as_rvalue @@ -369,17 +363,11 @@ # error "__cpp_lib_ranges should have the value 202406L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_as_const -# error "__cpp_lib_ranges_as_const should be defined in c++26" -# endif -# if __cpp_lib_ranges_as_const != 202207L -# error "__cpp_lib_ranges_as_const should have the value 202207L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_as_const -# error "__cpp_lib_ranges_as_const should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_as_const +# error "__cpp_lib_ranges_as_const should be defined in c++26" +# endif +# if __cpp_lib_ranges_as_const != 202311L +# error "__cpp_lib_ranges_as_const should have the value 202311L in c++26" # endif # ifndef __cpp_lib_ranges_as_rvalue diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index e5a657207923b..12d8c4f6f5a8c 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -174,7 +174,7 @@ __cpp_lib_quoted_string_io 201304L [C++14] __cpp_lib_ranges 202110L [C++20] 202406L [C++23] - __cpp_lib_ranges_as_const 202207L [C++23] + __cpp_lib_ranges_as_const 202311L [C++23] __cpp_lib_ranges_as_rvalue 202207L [C++23] __cpp_lib_ranges_chunk 202202L [C++23] __cpp_lib_ranges_chunk_by 202202L [C++23] @@ -5713,17 +5713,11 @@ # error "__cpp_lib_ranges should have the value 202406L in c++23" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_as_const -# error "__cpp_lib_ranges_as_const should be defined in c++23" -# endif -# if __cpp_lib_ranges_as_const != 202207L -# error "__cpp_lib_ranges_as_const should have the value 202207L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_as_const -# error "__cpp_lib_ranges_as_const should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_as_const +# error "__cpp_lib_ranges_as_const should be defined in c++23" +# endif +# if __cpp_lib_ranges_as_const != 202311L +# error "__cpp_lib_ranges_as_const should have the value 202311L in c++23" # endif # ifndef __cpp_lib_ranges_as_rvalue @@ -7597,17 +7591,11 @@ # error "__cpp_lib_ranges should have the value 202406L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_as_const -# error "__cpp_lib_ranges_as_const should be defined in c++26" -# endif -# if __cpp_lib_ranges_as_const != 202207L -# error "__cpp_lib_ranges_as_const should have the value 202207L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_as_const -# error "__cpp_lib_ranges_as_const should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_as_const +# error "__cpp_lib_ranges_as_const should be defined in c++26" +# endif +# if __cpp_lib_ranges_as_const != 202311L +# error "__cpp_lib_ranges_as_const should have the value 202311L in c++26" # endif # ifndef __cpp_lib_ranges_as_rvalue diff --git a/libcxx/test/std/ranges/range.access/begin.pass.cpp b/libcxx/test/std/ranges/range.access/begin.pass.cpp index 5ca3d59abb140..7f2357b319a8a 100644 --- a/libcxx/test/std/ranges/range.access/begin.pass.cpp +++ b/libcxx/test/std/ranges/range.access/begin.pass.cpp @@ -9,7 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // std::ranges::begin -// std::ranges::cbegin +// std::ranges::cbegin // until C++23 #include @@ -19,7 +19,9 @@ #include "test_iterators.h" using RangeBeginT = decltype(std::ranges::begin); +#if TEST_STD_VER < 23 using RangeCBeginT = decltype(std::ranges::cbegin); +#endif // TEST_STD_VER < 23 static int globalBuff[8]; @@ -27,33 +29,43 @@ static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct Incomplete; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 // This case is IFNDR; we handle it SFINAE-friendly. LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +#if TEST_STD_VER < 23 LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 // This case is IFNDR; we handle it SFINAE-friendly. LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +#if TEST_STD_VER < 23 LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct BeginMember { int x; @@ -65,45 +77,48 @@ static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 constexpr bool testReturnTypes() { - { - int *x[2]; - ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int**); - ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), int* const*); - } - { - int x[2][2]; - ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int(*)[2]); - ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), const int(*)[2]); - } - { - struct Different { - char*& begin(); - short*& begin() const; - } x; - ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), char*); - ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), short*); - } + int* a[2]; + int b[2][2]; + struct Different { + char*& begin(); + short*& begin() const; + } c; + + ASSERT_SAME_TYPE(decltype(std::ranges::begin(a)), int**); + ASSERT_SAME_TYPE(decltype(std::ranges::begin(b)), int(*)[2]); + ASSERT_SAME_TYPE(decltype(std::ranges::begin(c)), char*); + +#if TEST_STD_VER < 23 + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(a)), int* const*); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(b)), const int(*)[2]); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(c)), short*); +#endif // TEST_STD_VER < 23 + return true; } constexpr bool testArray() { int a[2]; - assert(std::ranges::begin(a) == a); - assert(std::ranges::cbegin(a) == a); - int b[2][2]; - assert(std::ranges::begin(b) == b); - assert(std::ranges::cbegin(b) == b); - BeginMember c[2]; + + assert(std::ranges::begin(a) == a); + assert(std::ranges::begin(b) == b); assert(std::ranges::begin(c) == c); + +#if TEST_STD_VER < 23 + assert(std::ranges::cbegin(a) == a); + assert(std::ranges::cbegin(b) == b); assert(std::ranges::cbegin(c) == c); +#endif // TEST_STD_VER < 23 return true; } @@ -136,8 +151,10 @@ struct NonConstBeginMember { }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct EnabledBorrowingBeginMember { constexpr int *begin() const { return &globalBuff[0]; } @@ -159,28 +176,28 @@ struct EmptyPtrBeginMember { constexpr bool testBeginMember() { BeginMember a; + NonConstBeginMember b; + EnabledBorrowingBeginMember c; + BeginMemberFunction d; + EmptyPtrBeginMember e; + assert(std::ranges::begin(a) == &a.x); - assert(std::ranges::cbegin(a) == &a.x); static_assert(!std::is_invocable_v); - static_assert(!std::is_invocable_v); - - NonConstBeginMember b; assert(std::ranges::begin(b) == &b.x); - static_assert(!std::is_invocable_v); - - EnabledBorrowingBeginMember c; assert(std::ranges::begin(c) == &globalBuff[0]); - assert(std::ranges::cbegin(c) == &globalBuff[0]); assert(std::ranges::begin(std::move(c)) == &globalBuff[0]); - assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]); - - BeginMemberFunction d; assert(std::ranges::begin(d) == &d.x); - assert(std::ranges::cbegin(d) == &d.x); - - EmptyPtrBeginMember e; assert(std::ranges::begin(e) == &e.x); + +#if TEST_STD_VER < 23 + assert(std::ranges::cbegin(a) == &a.x); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + assert(std::ranges::cbegin(c) == &globalBuff[0]); + assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]); + assert(std::ranges::cbegin(d) == &d.x); assert(std::ranges::cbegin(e) == &e.x); +#endif // TEST_STD_VER < 23 return true; } @@ -193,8 +210,10 @@ struct BeginFunction { static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(std::is_invocable_v); // Ill-formed before P2602R2 Poison Pills are Too Toxic +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct BeginFunctionReturnsInt { friend int begin(BeginFunctionReturnsInt const&); @@ -215,7 +234,9 @@ static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct BeginFunctionEnabledBorrowing { friend constexpr int *begin(BeginFunctionEnabledBorrowing) { return &globalBuff[2]; } @@ -245,85 +266,96 @@ struct BeginFunctionWithPrivateBeginMember { constexpr bool testBeginFunction() { BeginFunction a{}; const BeginFunction aa{}; - assert(std::ranges::begin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::cbegin(a) == &a.x); - assert(std::ranges::begin(aa) == &aa.x); - assert(std::ranges::cbegin(aa) == &aa.x); - BeginFunctionByValue b{}; const BeginFunctionByValue bb{}; - assert(std::ranges::begin(b) == &globalBuff[1]); - assert(std::ranges::cbegin(b) == &globalBuff[1]); - assert(std::ranges::begin(bb) == &globalBuff[1]); - assert(std::ranges::cbegin(bb) == &globalBuff[1]); - BeginFunctionEnabledBorrowing c{}; const BeginFunctionEnabledBorrowing cc{}; - assert(std::ranges::begin(std::move(c)) == &globalBuff[2]); - assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]); - assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]); - assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]); - BeginFunctionReturnsEmptyPtr d{}; const BeginFunctionReturnsEmptyPtr dd{}; - assert(std::ranges::begin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::cbegin(d) == &d.x); - assert(std::ranges::begin(dd) == &dd.x); - assert(std::ranges::cbegin(dd) == &dd.x); - BeginFunctionWithDataMember e{}; const BeginFunctionWithDataMember ee{}; + BeginFunctionWithPrivateBeginMember f{}; + const BeginFunctionWithPrivateBeginMember ff{}; + + assert(std::ranges::begin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::begin(aa) == &aa.x); + assert(std::ranges::begin(b) == &globalBuff[1]); + assert(std::ranges::begin(bb) == &globalBuff[1]); + assert(std::ranges::begin(std::move(c)) == &globalBuff[2]); + assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]); + assert(std::ranges::begin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::begin(dd) == &dd.x); assert(std::ranges::begin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic assert(std::ranges::begin(ee) == &ee.x); + assert(std::ranges::begin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::begin(ff) == &ff.y); + +#if TEST_STD_VER < 23 + assert(std::ranges::cbegin(a) == &a.x); + assert(std::ranges::cbegin(aa) == &aa.x); + assert(std::ranges::cbegin(b) == &globalBuff[1]); + assert(std::ranges::cbegin(bb) == &globalBuff[1]); + assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]); + assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]); + assert(std::ranges::cbegin(d) == &d.x); + assert(std::ranges::cbegin(dd) == &dd.x); assert(std::ranges::cbegin(e) == &e.x); assert(std::ranges::cbegin(ee) == &ee.x); - - BeginFunctionWithPrivateBeginMember f{}; - const BeginFunctionWithPrivateBeginMember ff{}; - assert(std::ranges::begin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic assert(std::ranges::cbegin(f) == &f.y); - assert(std::ranges::begin(ff) == &ff.y); assert(std::ranges::cbegin(ff) == &ff.y); +#endif // TEST_STD_VER < 23 return true; } ASSERT_NOEXCEPT(std::ranges::begin(std::declval())); +#if TEST_STD_VER < 23 ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval())); +#endif // TEST_STD_VER < 23 struct NoThrowMemberBegin { ThrowingIterator begin() const noexcept; // auto(t.begin()) doesn't throw } ntmb; static_assert(noexcept(std::ranges::begin(ntmb))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::cbegin(ntmb))); +#endif // TEST_STD_VER < 23 struct NoThrowADLBegin { friend ThrowingIterator begin(NoThrowADLBegin&) noexcept; // auto(begin(t)) doesn't throw friend ThrowingIterator begin(const NoThrowADLBegin&) noexcept; } ntab; static_assert(noexcept(std::ranges::begin(ntab))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::cbegin(ntab))); +#endif // TEST_STD_VER < 23 struct NoThrowMemberBeginReturnsRef { ThrowingIterator& begin() const noexcept; // auto(t.begin()) may throw } ntmbrr; static_assert(!noexcept(std::ranges::begin(ntmbrr))); +#if TEST_STD_VER < 23 static_assert(!noexcept(std::ranges::cbegin(ntmbrr))); +#endif // TEST_STD_VER < 23 struct BeginReturnsArrayRef { auto begin() const noexcept -> int(&)[10]; } brar; static_assert(noexcept(std::ranges::begin(brar))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::cbegin(brar))); +#endif // TEST_STD_VER < 23 // Test ADL-proofing. struct Incomplete; template struct Holder { T t; }; static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); +#endif // TEST_STD_VER < 23 int main(int, char**) { static_assert(testReturnTypes()); diff --git a/libcxx/test/std/ranges/range.access/cbegin.pass.cpp b/libcxx/test/std/ranges/range.access/cbegin.pass.cpp new file mode 100644 index 0000000000000..a04036fe8e0c5 --- /dev/null +++ b/libcxx/test/std/ranges/range.access/cbegin.pass.cpp @@ -0,0 +1,207 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// std::ranges::cbegin +// std::ranges::crbegin + +#include + +#include +#include +#include "almost_satisfies_types.h" +#include "test_macros.h" +#include "test_iterators.h" + +using RangeCBeginT = decltype(std::ranges::cbegin); +using RangeCRBeginT = decltype(std::ranges::crbegin); + +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct Incomplete; + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +// This case is IFNDR; we handle it SFINAE-friendly. +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); + +// This case is IFNDR; we handle it SFINAE-friendly. +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); + +struct NonborrowingRange { + int x; + constexpr const int* begin() const { return &x; } + constexpr const int* rbegin() const { return &x; } + constexpr const int* end() const { return &x; } + constexpr const int* rend() const { return &x; } +}; + +// Ensure that we can't call with rvalues with borrowing disabled. +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); + +constexpr bool testReturnTypes() { + int* a[2]; + int b[2][2]; + struct PossiblyConstRange { + char*& begin(); + char*& end(); + const short*& begin() const; + const short*& end() const; + int*& rbegin(); + int*& rend(); + const long*& rbegin() const; + const long*& rend() const; + } c; + struct AlwaysConstRange { + const char*& begin(); + const char*& end(); + const short*& begin() const; + const short*& end() const; + const int*& rbegin(); + const int*& rend(); + const long*& rbegin() const; + const long*& rend() const; + } d; + struct NeverConstRange { + char*& begin(); + char*& end(); + short*& begin() const; + short& end() const; + int*& rbegin(); + int*& rend(); + long*& rbegin() const; + long*& rend() const; + } e; + + static_assert(!std::ranges::constant_range); + static_assert(std::ranges::constant_range); + static_assert(std::ranges::constant_range); + static_assert(std::ranges::constant_range); + static_assert(!std::ranges::constant_range); + static_assert(!std::ranges::constant_range); + + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(a)), int* const*); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(b)), const int(*)[2]); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(c)), const short*); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(d)), const short*); + ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(e)), std::basic_const_iterator); + + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(a)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(b)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(c)), const long*); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(d)), const long*); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(e)), std::basic_const_iterator); + + return true; +} + +constexpr bool testArray() { + int a[2]; + int b[2][2]; + NonborrowingRange c[2]; + + assert(std::ranges::cbegin(a) == a); + assert(std::ranges::cbegin(b) == b); + assert(std::ranges::cbegin(c) == c); + + assert(std::ranges::crbegin(a).base() == a + 2); + assert(std::ranges::crbegin(b).base() == b + 2); + assert(std::ranges::crbegin(c).base() == c + 2); + + return true; +} + +struct BorrowingRange { + int* begin() const; + int* end() const; +}; +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); + +struct NoThrowBeginThrowingEnd { + const int* begin() const noexcept; + const int* end() const; +} ntbte; +static_assert(noexcept(std::ranges::cbegin(ntbte))); +static_assert(!noexcept(std::ranges::crbegin(ntbte))); + +struct ThrowingBeginNoThrowEnd { + const int* begin() const; + const int* end() const noexcept; +} tbnte; +static_assert(!noexcept(std::ranges::cbegin(tbnte))); +static_assert(noexcept(std::ranges::crbegin(tbnte))); + +// Test ADL-proofing. +struct Incomplete; +template +struct Holder { + T t; +}; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*&>); +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*&>); + +int main(int, char**) { + static_assert(testReturnTypes()); + + testArray(); + static_assert(testArray()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.access/cbegin.verify.cpp b/libcxx/test/std/ranges/range.access/cbegin.verify.cpp new file mode 100644 index 0000000000000..18ac8ae65e338 --- /dev/null +++ b/libcxx/test/std/ranges/range.access/cbegin.verify.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// std::ranges::cbegin + +#include + +struct NonBorrowedRange { + int* begin() const; + int* end() const; +}; +static_assert(!std::ranges::enable_borrowed_range); + +// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::cbegin` is ill-formed. +void test() { + std::ranges::cbegin(NonBorrowedRange()); + // expected-error-re@-1 {{{{no matching function for call to object of type 'const (std::ranges::)?__cbegin::__fn'}}}} +} diff --git a/libcxx/test/std/ranges/range.access/cend.pass.cpp b/libcxx/test/std/ranges/range.access/cend.pass.cpp new file mode 100644 index 0000000000000..3c1f4967f0fcc --- /dev/null +++ b/libcxx/test/std/ranges/range.access/cend.pass.cpp @@ -0,0 +1,207 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// std::ranges::cend +// std::ranges::crend + +#include + +#include +#include +#include "almost_satisfies_types.h" +#include "test_macros.h" +#include "test_iterators.h" + +using RangeCEndT = decltype(std::ranges::cend); +using RangeCREndT = decltype(std::ranges::crend); + +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +struct Incomplete; + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); + +// This case is IFNDR; we handle it SFINAE-friendly. +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); + +// This case is IFNDR; we handle it SFINAE-friendly. +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +LIBCPP_STATIC_ASSERT(!std::is_invocable_v); + +struct NonborrowingRange { + int x; + constexpr const int* begin() const { return &x; } + constexpr const int* rbegin() const { return &x; } + constexpr const int* end() const { return &x; } + constexpr const int* rend() const { return &x; } +}; + +// Ensure that we can't call with rvalues with borrowing disabled. +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); + +constexpr bool testReturnTypes() { + int* a[2]; + int b[2][2]; + struct PossiblyConstRange { + char*& begin(); + char*& end(); + const short*& begin() const; + const short*& end() const; + int*& rbegin(); + int*& rend(); + const long*& rbegin() const; + const long*& rend() const; + } c; + struct AlwaysConstRange { + const char*& begin(); + const char*& end(); + const short*& begin() const; + const short*& end() const; + const int*& rbegin(); + const int*& rend(); + const long*& rbegin() const; + const long*& rend() const; + } d; + struct NeverConstRange { + char*& begin(); + char*& end(); + short*& begin() const; + short& end() const; + int*& rbegin(); + int*& rend(); + long*& rbegin() const; + long*& rend() const; + } e; + + static_assert(!std::ranges::constant_range); + static_assert(std::ranges::constant_range); + static_assert(std::ranges::constant_range); + static_assert(std::ranges::constant_range); + static_assert(!std::ranges::constant_range); + static_assert(!std::ranges::constant_range); + + ASSERT_SAME_TYPE(decltype(std::ranges::cend(a)), int* const*); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(b)), const int(*)[2]); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(c)), const short*); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(d)), const short*); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(e)), std::basic_const_iterator); + + ASSERT_SAME_TYPE(decltype(std::ranges::crend(a)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(b)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(c)), const long*); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(d)), const long*); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(e)), std::basic_const_iterator); + + return true; +} + +constexpr bool testArray() { + int a[2]; + int b[2][2]; + NonborrowingRange c[2]; + + assert(std::ranges::cend(a) == a + 2); + assert(std::ranges::cend(b) == b + 2); + assert(std::ranges::cend(c) == c + 2); + + assert(std::ranges::crend(a).base() == a); + assert(std::ranges::crend(b).base() == b); + assert(std::ranges::crend(c).base() == c); + + return true; +} + +struct BorrowingRange { + int* begin() const; + int* end() const; +}; +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); + +struct NoThrowEndThrowingEnd { + const int* begin() const noexcept; + const int* end() const; +} ntbte; +static_assert(!noexcept(std::ranges::cend(ntbte))); +static_assert(noexcept(std::ranges::crend(ntbte))); + +struct ThrowingEndNoThrowEnd { + const int* begin() const; + const int* end() const noexcept; +} tbnte; +static_assert(noexcept(std::ranges::cend(tbnte))); +static_assert(!noexcept(std::ranges::crend(tbnte))); + +// Test ADL-proofing. +struct Incomplete; +template +struct Holder { + T t; +}; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*&>); +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*&>); + +int main(int, char**) { + static_assert(testReturnTypes()); + + testArray(); + static_assert(testArray()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.access/cend.verify.cpp b/libcxx/test/std/ranges/range.access/cend.verify.cpp new file mode 100644 index 0000000000000..e50b90b4fbd49 --- /dev/null +++ b/libcxx/test/std/ranges/range.access/cend.verify.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// std::ranges::cend + +#include + +struct NonBorrowedRange { + int* begin() const; + int* end() const; +}; +static_assert(!std::ranges::enable_borrowed_range); + +// Verify that if the expression is an rvalue and `enable_borrowed_range` is false, `ranges::cend` is ill-formed. +void test() { + std::ranges::cend(NonBorrowedRange()); + // expected-error-re@-1 {{{{no matching function for call to object of type 'const (std::ranges::)?__cend::__fn'}}}} +} diff --git a/libcxx/test/std/ranges/range.access/data.pass.cpp b/libcxx/test/std/ranges/range.access/data.pass.cpp index 357e52a146275..2722d2d980713 100644 --- a/libcxx/test/std/ranges/range.access/data.pass.cpp +++ b/libcxx/test/std/ranges/range.access/data.pass.cpp @@ -9,6 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // std::ranges::data +// std::ranges::cdata #include @@ -20,6 +21,14 @@ using RangeDataT = decltype(std::ranges::data); using RangeCDataT = decltype(std::ranges::cdata); +#if TEST_STD_VER < 23 +# define STD_VER_20(...) __VA_ARGS__ +# define STD_VER_23(...) static_assert(true) +#else +# define STD_VER_20(...) static_assert(true) +# define STD_VER_23(...) __VA_ARGS__ +#endif + static int globalBuff[2]; struct Incomplete; @@ -38,9 +47,26 @@ static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +struct DataMemberNonRange { + int x; + constexpr const int* data() const { return &x; } +}; +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +#endif + struct DataMember { int x; constexpr const int *data() const { return &x; } + const int* begin() const; + const int* end() const; }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); @@ -66,16 +92,51 @@ constexpr bool testReturnTypes() { struct D { char*& data(); short*& data() const; + int* begin(); + int* end(); }; ASSERT_SAME_TYPE(decltype(std::ranges::data(std::declval())), char*); static_assert(!std::is_invocable_v); ASSERT_SAME_TYPE(decltype(std::ranges::data(std::declval())), short*); static_assert(!std::is_invocable_v); - ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), short*); + + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + STD_VER_20(ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), short*)); + STD_VER_23(ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), const char*)); + STD_VER_20(ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), short*)); + STD_VER_23(static_assert(!std::is_invocable_v)); + } +#if TEST_STD_VER >= 23 + { + struct D { + char*& data(); + short*& data() const; + int* begin(); + int* end(); + int* begin() const; + int* end() const; + }; + ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), const short*); + static_assert(!std::is_invocable_v); + ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), const short*); + static_assert(!std::is_invocable_v); + } + { + struct D { + char*& data(); + short*& data() const; + int* begin(); + int* end(); + const int* begin() const; + const int* end() const; + }; + ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), const short*); static_assert(!std::is_invocable_v); - ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), short*); + ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), const short*); static_assert(!std::is_invocable_v); } +#endif // TEST_STD_VER >= 23 { struct NC { char *begin() const; @@ -88,10 +149,16 @@ constexpr bool testReturnTypes() { static_assert(!std::is_invocable_v); ASSERT_SAME_TYPE(decltype(std::ranges::data(std::declval())), char*); static_assert(!std::is_invocable_v); - ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), char*); + static_assert(!std::is_invocable_v); - ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), char*); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 + ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), char*); + ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), char*); +#else + ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), const char*); + ASSERT_SAME_TYPE(decltype(std::ranges::cdata(std::declval())), const char*); +#endif } return true; } @@ -121,10 +188,14 @@ static_assert(!std::is_invocable_v struct NonConstDataMember { int x; constexpr int *data() { return &x; } + int* begin(); + int* end(); }; struct EnabledBorrowingDataMember { constexpr int *data() { return &globalBuff[0]; } + int* begin(); + int* end(); }; template<> inline constexpr bool std::ranges::enable_borrowed_range = true; @@ -133,6 +204,7 @@ struct DataMemberAndBegin { int x; constexpr const int *data() const { return &x; } const int *begin() const; + const int* end() const; }; constexpr bool testDataMember() { @@ -142,15 +214,13 @@ constexpr bool testDataMember() { NonConstDataMember b; assert(std::ranges::data(b) == &b.x); - static_assert(!std::is_invocable_v); + STD_VER_20(static_assert(!std::is_invocable_v)); + STD_VER_23(assert(std::ranges::cdata(b) == &b.x)); EnabledBorrowingDataMember c; assert(std::ranges::data(std::move(c)) == &globalBuff[0]); - static_assert(!std::is_invocable_v); - - DataMemberAndBegin d; - assert(std::ranges::data(d) == &d.x); - assert(std::ranges::cdata(d) == &d.x); + STD_VER_20(static_assert(!std::is_invocable_v)); + STD_VER_23(assert(std::ranges::data(c) == &globalBuff[0])); return true; } @@ -161,6 +231,7 @@ struct BeginMemberContiguousIterator { int buff[8]; constexpr ContiguousIter begin() const { return ContiguousIter(buff); } + constexpr ContiguousIter end() const { return ContiguousIter(buff); } }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); @@ -175,6 +246,7 @@ struct BeginMemberRandomAccess { int buff[8]; random_access_iterator begin() const; + random_access_iterator end() const; }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); @@ -191,6 +263,7 @@ struct BeginFriendContiguousIterator { friend constexpr ContiguousIter begin(const BeginFriendContiguousIterator &iter) { return ContiguousIter(iter.buff); } + friend constexpr ContiguousIter end(const BeginFriendContiguousIterator& iter) { return ContiguousIter(iter.buff); } }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); @@ -202,7 +275,8 @@ static_assert( std::is_invocable_v); struct BeginFriendRandomAccess { - friend random_access_iterator begin(const BeginFriendRandomAccess iter); + friend random_access_iterator begin(const BeginFriendRandomAccess& iter); + friend random_access_iterator end(const BeginFriendRandomAccess& iter); }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); @@ -217,6 +291,7 @@ struct BeginMemberRvalue { int buff[8]; ContiguousIter begin() &&; + ContiguousIter end() &&; }; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); @@ -229,6 +304,7 @@ static_assert(!std::is_invocable_v); struct BeginMemberBorrowingEnabled { constexpr contiguous_iterator begin() { return contiguous_iterator{&globalBuff[1]}; } + constexpr contiguous_iterator end() { return contiguous_iterator{&globalBuff[2]}; } }; template<> inline constexpr bool std::ranges::enable_borrowed_range = true; @@ -236,11 +312,28 @@ static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); +STD_VER_20(static_assert(!std::is_invocable_v)); +STD_VER_23(static_assert(std::is_invocable_v)); +STD_VER_20(static_assert(!std::is_invocable_v)); +STD_VER_23(static_assert(std::is_invocable_v)); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +struct ConstBeginMemberBorrowingEnabled { + constexpr contiguous_iterator begin() const { return contiguous_iterator{&globalBuff[1]}; } + constexpr contiguous_iterator end() const { return contiguous_iterator{&globalBuff[2]}; } +}; +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); +static_assert(std::is_invocable_v); + constexpr bool testViaRangesBegin() { int arr[2]; assert(std::ranges::data(arr) == arr + 0); @@ -256,7 +349,12 @@ constexpr bool testViaRangesBegin() { BeginMemberBorrowingEnabled c; assert(std::ranges::data(std::move(c)) == &globalBuff[1]); - static_assert(!std::is_invocable_v); + STD_VER_20(static_assert(!std::is_invocable_v)); + STD_VER_23(static_assert(std::is_invocable_v)); + + ConstBeginMemberBorrowingEnabled d; + assert(std::ranges::data(std::move(d)) == &globalBuff[1]); + assert(std::ranges::cdata(std::move(d)) == &globalBuff[1]); return true; } diff --git a/libcxx/test/std/ranges/range.access/end.pass.cpp b/libcxx/test/std/ranges/range.access/end.pass.cpp index 3e465b357e985..3197634c2d97f 100644 --- a/libcxx/test/std/ranges/range.access/end.pass.cpp +++ b/libcxx/test/std/ranges/range.access/end.pass.cpp @@ -9,7 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // std::ranges::end -// std::ranges::cend +// std::ranges::cend // until C++23 #include @@ -19,7 +19,9 @@ #include "test_iterators.h" using RangeEndT = decltype(std::ranges::end); +#if TEST_STD_VER < 23 using RangeCEndT = decltype(std::ranges::cend); +#endif // TEST_STD_VER < 23 static int globalBuff[8]; @@ -27,16 +29,20 @@ static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct Incomplete; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct EndMember { int x; @@ -49,47 +55,50 @@ static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 constexpr bool testReturnTypes() { - { - int *x[2]; - ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int**); - ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), int* const*); - } - { - int x[2][2]; - ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int(*)[2]); - ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), const int(*)[2]); - } - { - struct Different { - char *begin(); - sentinel_wrapper& end(); - short *begin() const; - sentinel_wrapper& end() const; - } x; - ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), sentinel_wrapper); - ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), sentinel_wrapper); - } + int* a[2]; + int b[2][2]; + struct Different { + char* begin(); + sentinel_wrapper& end(); + short* begin() const; + sentinel_wrapper& end() const; + } c; + + ASSERT_SAME_TYPE(decltype(std::ranges::end(a)), int**); + ASSERT_SAME_TYPE(decltype(std::ranges::end(b)), int(*)[2]); + ASSERT_SAME_TYPE(decltype(std::ranges::end(c)), sentinel_wrapper); + +#if TEST_STD_VER < 23 + ASSERT_SAME_TYPE(decltype(std::ranges::cend(a)), int* const*); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(b)), const int(*)[2]); + ASSERT_SAME_TYPE(decltype(std::ranges::cend(c)), sentinel_wrapper); +#endif // TEST_STD_VER < 23 + return true; } constexpr bool testArray() { int a[2]; - assert(std::ranges::end(a) == a + 2); - assert(std::ranges::cend(a) == a + 2); - int b[2][2]; - assert(std::ranges::end(b) == b + 2); - assert(std::ranges::cend(b) == b + 2); - EndMember c[2]; + + assert(std::ranges::end(a) == a + 2); + assert(std::ranges::end(b) == b + 2); assert(std::ranges::end(c) == c + 2); + +#if TEST_STD_VER < 23 + assert(std::ranges::cend(a) == a + 2); + assert(std::ranges::cend(b) == b + 2); assert(std::ranges::cend(c) == c + 2); +#endif // TEST_STD_VER < 23 return true; } @@ -127,8 +136,10 @@ struct NonConstEndMember { }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct EnabledBorrowingEndMember { constexpr int *begin() const { return nullptr; } @@ -160,24 +171,24 @@ struct EmptyPtrEndMember { constexpr bool testEndMember() { EndMember a; - assert(std::ranges::end(a) == &a.x); - assert(std::ranges::cend(a) == &a.x); - NonConstEndMember b; - assert(std::ranges::end(b) == &b.x); - static_assert(!std::is_invocable_v); - EnabledBorrowingEndMember c; - assert(std::ranges::end(std::move(c)) == &globalBuff[0]); - assert(std::ranges::cend(std::move(c)) == &globalBuff[0]); - EndMemberFunction d; - assert(std::ranges::end(d) == &d.x); - assert(std::ranges::cend(d) == &d.x); - EmptyPtrEndMember e; + + assert(std::ranges::end(a) == &a.x); + assert(std::ranges::end(b) == &b.x); + assert(std::ranges::end(std::move(c)) == &globalBuff[0]); + assert(std::ranges::end(d) == &d.x); assert(std::ranges::end(e) == &e.x); + +#if TEST_STD_VER < 23 + assert(std::ranges::cend(a) == &a.x); + static_assert(!std::is_invocable_v); + assert(std::ranges::cend(std::move(c)) == &globalBuff[0]); + assert(std::ranges::cend(d) == &d.x); assert(std::ranges::cend(e) == &e.x); +#endif // TEST_STD_VER < 23 return true; } @@ -194,8 +205,10 @@ static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(std::is_invocable_v); // Ill-formed before P2602R2 Poison Pills are Too Toxic +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct EndFunctionReturnsInt { friend constexpr int begin(EndFunctionReturnsInt const&); @@ -230,7 +243,9 @@ struct EndFunctionByValue { friend constexpr int *begin(EndFunctionByValue) { return nullptr; } friend constexpr int *end(EndFunctionByValue) { return &globalBuff[1]; } }; +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct EndFunctionEnabledBorrowing { friend constexpr int *begin(EndFunctionEnabledBorrowing) { return nullptr; } @@ -268,61 +283,63 @@ struct BeginMemberEndFunction { constexpr bool testEndFunction() { const EndFunction a{}; - assert(std::ranges::end(a) == &a.x); - assert(std::ranges::cend(a) == &a.x); EndFunction aa{}; - assert(std::ranges::end(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::cend(aa) == &aa.x); - EndFunctionByValue b; - assert(std::ranges::end(b) == &globalBuff[1]); - assert(std::ranges::cend(b) == &globalBuff[1]); - EndFunctionEnabledBorrowing c; - assert(std::ranges::end(std::move(c)) == &globalBuff[2]); - assert(std::ranges::cend(std::move(c)) == &globalBuff[2]); - const EndFunctionReturnsEmptyPtr d{}; - assert(std::ranges::end(d) == &d.x); - assert(std::ranges::cend(d) == &d.x); EndFunctionReturnsEmptyPtr dd{}; - assert(std::ranges::end(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::cend(dd) == &dd.x); - const EndFunctionWithDataMember e{}; - assert(std::ranges::end(e) == &e.x); - assert(std::ranges::cend(e) == &e.x); EndFunctionWithDataMember ee{}; - assert(std::ranges::end(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::cend(ee) == &ee.x); - const EndFunctionWithPrivateEndMember f{}; - assert(std::ranges::end(f) == &f.y); - assert(std::ranges::cend(f) == &f.y); EndFunctionWithPrivateEndMember ff{}; - assert(std::ranges::end(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::cend(ff) == &ff.y); - const BeginMemberEndFunction g{}; - assert(std::ranges::end(g) == &g.x); - assert(std::ranges::cend(g) == &g.x); BeginMemberEndFunction gg{}; + + assert(std::ranges::end(a) == &a.x); + assert(std::ranges::end(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::end(b) == &globalBuff[1]); + assert(std::ranges::end(std::move(c)) == &globalBuff[2]); + assert(std::ranges::end(d) == &d.x); + assert(std::ranges::end(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::end(e) == &e.x); + assert(std::ranges::end(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::end(f) == &f.y); + assert(std::ranges::end(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::end(g) == &g.x); assert(std::ranges::end(gg) == &gg.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + +#if TEST_STD_VER < 23 + assert(std::ranges::cend(a) == &a.x); + assert(std::ranges::cend(aa) == &aa.x); + assert(std::ranges::cend(b) == &globalBuff[1]); + assert(std::ranges::cend(std::move(c)) == &globalBuff[2]); + assert(std::ranges::cend(d) == &d.x); + assert(std::ranges::cend(dd) == &dd.x); + assert(std::ranges::cend(e) == &e.x); + assert(std::ranges::cend(ee) == &ee.x); + assert(std::ranges::cend(f) == &f.y); + assert(std::ranges::cend(ff) == &ff.y); + assert(std::ranges::cend(g) == &g.x); assert(std::ranges::cend(gg) == &gg.x); +#endif // TEST_STD_VER < 23 return true; } ASSERT_NOEXCEPT(std::ranges::end(std::declval())); +#if TEST_STD_VER < 23 ASSERT_NOEXCEPT(std::ranges::cend(std::declval())); +#endif // TEST_STD_VER < 23 struct NoThrowMemberEnd { ThrowingIterator begin() const; ThrowingIterator end() const noexcept; // auto(t.end()) doesn't throw } ntme; static_assert(noexcept(std::ranges::end(ntme))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::cend(ntme))); +#endif // TEST_STD_VER < 23 struct NoThrowADLEnd { ThrowingIterator begin() const; @@ -330,29 +347,37 @@ struct NoThrowADLEnd { friend ThrowingIterator end(const NoThrowADLEnd&) noexcept; } ntae; static_assert(noexcept(std::ranges::end(ntae))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::cend(ntae))); +#endif // TEST_STD_VER < 23 struct NoThrowMemberEndReturnsRef { ThrowingIterator begin() const; ThrowingIterator& end() const noexcept; // auto(t.end()) may throw } ntmerr; static_assert(!noexcept(std::ranges::end(ntmerr))); +#if TEST_STD_VER < 23 static_assert(!noexcept(std::ranges::cend(ntmerr))); +#endif // TEST_STD_VER < 23 struct EndReturnsArrayRef { auto begin() const noexcept -> int(&)[10]; auto end() const noexcept -> int(&)[10]; } erar; static_assert(noexcept(std::ranges::end(erar))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::cend(erar))); +#endif // TEST_STD_VER < 23 // Test ADL-proofing. struct Incomplete; template struct Holder { T t; }; static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); +#endif // TEST_STD_VER < 23 int main(int, char**) { static_assert(testReturnTypes()); diff --git a/libcxx/test/std/ranges/range.access/rbegin.pass.cpp b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp index 3997f38efd029..7919b84d2b35a 100644 --- a/libcxx/test/std/ranges/range.access/rbegin.pass.cpp +++ b/libcxx/test/std/ranges/range.access/rbegin.pass.cpp @@ -9,7 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // std::ranges::rbegin -// std::ranges::crbegin +// std::ranges::crbegin // until C++23 #include @@ -19,7 +19,9 @@ #include "test_iterators.h" using RangeRBeginT = decltype(std::ranges::rbegin); +#if TEST_STD_VER < 23 using RangeCRBeginT = decltype(std::ranges::crbegin); +#endif // TEST_STD_VER < 23 static int globalBuff[8]; @@ -27,34 +29,42 @@ static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct Incomplete; static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); - +static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); + +#if TEST_STD_VER < 23 +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 // This case is IFNDR; we handle it SFINAE-friendly. LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +#if TEST_STD_VER < 23 LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 // This case is IFNDR; we handle it SFINAE-friendly. LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +#if TEST_STD_VER < 23 LIBCPP_STATIC_ASSERT(!std::is_invocable_v); LIBCPP_STATIC_ASSERT(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct RBeginMember { int x; @@ -66,45 +76,48 @@ static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 constexpr bool testReturnTypes() { - { - int *x[2]; - ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator); - ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator); - } - { - int x[2][2]; - ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), std::reverse_iterator); - ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), std::reverse_iterator); - } - { - struct Different { - char*& rbegin(); - short*& rbegin() const; - } x; - ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(x)), char*); - ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(x)), short*); - } + int* a[2]; + int b[2][2]; + struct Different { + char*& rbegin(); + short*& rbegin() const; + } c; + + ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(a)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(b)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::rbegin(c)), char*); + +#if TEST_STD_VER < 23 + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(a)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(b)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crbegin(c)), short*); +#endif // TEST_STD_VER < 23 + return true; } constexpr bool testArray() { int a[2]; - assert(std::ranges::rbegin(a).base() == a + 2); - assert(std::ranges::crbegin(a).base() == a + 2); - int b[2][2]; - assert(std::ranges::rbegin(b).base() == b + 2); - assert(std::ranges::crbegin(b).base() == b + 2); - RBeginMember c[2]; + + assert(std::ranges::rbegin(a).base() == a + 2); + assert(std::ranges::rbegin(b).base() == b + 2); assert(std::ranges::rbegin(c).base() == c + 2); + +#if TEST_STD_VER < 23 + assert(std::ranges::crbegin(a).base() == a + 2); + assert(std::ranges::crbegin(b).base() == b + 2); assert(std::ranges::crbegin(c).base() == c + 2); +#endif // TEST_STD_VER < 23 return true; } @@ -131,8 +144,10 @@ struct NonConstRBeginMember { }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct EnabledBorrowingRBeginMember { constexpr int *rbegin() const { return globalBuff; } @@ -154,28 +169,28 @@ struct EmptyPtrRBeginMember { constexpr bool testRBeginMember() { RBeginMember a; + NonConstRBeginMember b; + EnabledBorrowingRBeginMember c; + RBeginMemberFunction d; + EmptyPtrRBeginMember e; + assert(std::ranges::rbegin(a) == &a.x); - assert(std::ranges::crbegin(a) == &a.x); static_assert(!std::is_invocable_v); - static_assert(!std::is_invocable_v); - - NonConstRBeginMember b; assert(std::ranges::rbegin(b) == &b.x); - static_assert(!std::is_invocable_v); - - EnabledBorrowingRBeginMember c; assert(std::ranges::rbegin(c) == globalBuff); - assert(std::ranges::crbegin(c) == globalBuff); assert(std::ranges::rbegin(std::move(c)) == globalBuff); - assert(std::ranges::crbegin(std::move(c)) == globalBuff); - - RBeginMemberFunction d; assert(std::ranges::rbegin(d) == &d.x); - assert(std::ranges::crbegin(d) == &d.x); - - EmptyPtrRBeginMember e; assert(std::ranges::rbegin(e) == &e.x); + +#if TEST_STD_VER < 23 + assert(std::ranges::crbegin(a) == &a.x); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + assert(std::ranges::crbegin(c) == globalBuff); + assert(std::ranges::crbegin(std::move(c)) == globalBuff); + assert(std::ranges::crbegin(d) == &d.x); assert(std::ranges::crbegin(e) == &e.x); +#endif // TEST_STD_VER < 23 return true; } @@ -189,8 +204,10 @@ static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); // Ill-formed before P2602R2 Poison Pills are Too Toxic +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct RBeginFunctionReturnsInt { friend int rbegin(RBeginFunctionReturnsInt const&); @@ -217,7 +234,9 @@ static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct RBeginFunctionEnabledBorrowing { friend constexpr int *rbegin(RBeginFunctionEnabledBorrowing) { return globalBuff + 2; } @@ -247,45 +266,44 @@ struct RBeginFunctionWithPrivateBeginMember { constexpr bool testRBeginFunction() { RBeginFunction a{}; const RBeginFunction aa{}; - assert(std::ranges::rbegin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::crbegin(a) == &a.x); - assert(std::ranges::rbegin(aa) == &aa.x); - assert(std::ranges::crbegin(aa) == &aa.x); - RBeginFunctionByValue b{}; const RBeginFunctionByValue bb{}; - assert(std::ranges::rbegin(b) == globalBuff + 1); - assert(std::ranges::crbegin(b) == globalBuff + 1); - assert(std::ranges::rbegin(bb) == globalBuff + 1); - assert(std::ranges::crbegin(bb) == globalBuff + 1); - RBeginFunctionEnabledBorrowing c{}; const RBeginFunctionEnabledBorrowing cc{}; - assert(std::ranges::rbegin(std::move(c)) == globalBuff + 2); - assert(std::ranges::crbegin(std::move(c)) == globalBuff + 2); - assert(std::ranges::rbegin(std::move(cc)) == globalBuff + 2); - assert(std::ranges::crbegin(std::move(cc)) == globalBuff + 2); - RBeginFunctionReturnsEmptyPtr d{}; const RBeginFunctionReturnsEmptyPtr dd{}; - assert(std::ranges::rbegin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::crbegin(d) == &d.x); - assert(std::ranges::rbegin(dd) == &dd.x); - assert(std::ranges::crbegin(dd) == &dd.x); - RBeginFunctionWithDataMember e{}; const RBeginFunctionWithDataMember ee{}; + RBeginFunctionWithPrivateBeginMember f{}; + const RBeginFunctionWithPrivateBeginMember ff{}; + + assert(std::ranges::rbegin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::rbegin(aa) == &aa.x); + assert(std::ranges::rbegin(b) == globalBuff + 1); + assert(std::ranges::rbegin(bb) == globalBuff + 1); + assert(std::ranges::rbegin(std::move(c)) == globalBuff + 2); + assert(std::ranges::rbegin(std::move(cc)) == globalBuff + 2); + assert(std::ranges::rbegin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::rbegin(dd) == &dd.x); assert(std::ranges::rbegin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic assert(std::ranges::rbegin(ee) == &ee.x); + assert(std::ranges::rbegin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::rbegin(ff) == &ff.y); + +#if TEST_STD_VER < 23 + assert(std::ranges::crbegin(a) == &a.x); + assert(std::ranges::crbegin(aa) == &aa.x); + assert(std::ranges::crbegin(b) == globalBuff + 1); + assert(std::ranges::crbegin(bb) == globalBuff + 1); + assert(std::ranges::crbegin(std::move(c)) == globalBuff + 2); + assert(std::ranges::crbegin(std::move(cc)) == globalBuff + 2); + assert(std::ranges::crbegin(d) == &d.x); + assert(std::ranges::crbegin(dd) == &dd.x); assert(std::ranges::crbegin(e) == &e.x); assert(std::ranges::crbegin(ee) == &ee.x); - - RBeginFunctionWithPrivateBeginMember f{}; - const RBeginFunctionWithPrivateBeginMember ff{}; - assert(std::ranges::rbegin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic assert(std::ranges::crbegin(f) == &f.y); - assert(std::ranges::rbegin(ff) == &ff.y); assert(std::ranges::crbegin(ff) == &ff.y); +#endif // TEST_STD_VER < 23 return true; } @@ -301,7 +319,9 @@ struct MemberBeginEnd { }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginEnd { int b, e; @@ -319,7 +339,9 @@ struct FunctionBeginEnd { }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberBeginFunctionEnd { int b, e; @@ -335,7 +357,9 @@ struct MemberBeginFunctionEnd { }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginMemberEnd { int b, e; @@ -351,59 +375,77 @@ struct FunctionBeginMemberEnd { }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberBeginEndDifferentTypes { bidirectional_iterator begin(); bidirectional_iterator end(); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginEndDifferentTypes { friend bidirectional_iterator begin(FunctionBeginEndDifferentTypes&); friend bidirectional_iterator end(FunctionBeginEndDifferentTypes&); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberBeginEndForwardIterators { forward_iterator begin(); forward_iterator end(); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginEndForwardIterators { friend forward_iterator begin(FunctionBeginEndForwardIterators&); friend forward_iterator end(FunctionBeginEndForwardIterators&); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberBeginOnly { bidirectional_iterator begin() const; }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginOnly { friend bidirectional_iterator begin(FunctionBeginOnly&); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberEndOnly { bidirectional_iterator end() const; }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionEndOnly { friend bidirectional_iterator end(FunctionEndOnly&); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 // Make sure there is no clash between the following cases: // - the case that handles classes defining member `rbegin` and `rend` functions; @@ -414,93 +456,112 @@ struct MemberBeginAndRBegin { int* rbegin() const; int* rend() const; }; -static_assert( std::is_invocable_v); -static_assert( std::is_invocable_v); +static_assert(std::is_invocable_v); static_assert( std::same_as, int*>); +#if TEST_STD_VER < 23 +static_assert(std::is_invocable_v); static_assert( std::same_as, int*>); +#endif // TEST_STD_VER < 23 constexpr bool testBeginEnd() { MemberBeginEnd a{}; const MemberBeginEnd aa{}; - assert(base(std::ranges::rbegin(a).base()) == &a.e); - assert(base(std::ranges::crbegin(a).base()) == &a.ce); - assert(base(std::ranges::rbegin(aa).base()) == &aa.ce); - assert(base(std::ranges::crbegin(aa).base()) == &aa.ce); - FunctionBeginEnd b{}; const FunctionBeginEnd bb{}; - assert(base(std::ranges::rbegin(b).base()) == &b.e); - assert(base(std::ranges::crbegin(b).base()) == &b.ce); - assert(base(std::ranges::rbegin(bb).base()) == &bb.ce); - assert(base(std::ranges::crbegin(bb).base()) == &bb.ce); - MemberBeginFunctionEnd c{}; const MemberBeginFunctionEnd cc{}; - assert(base(std::ranges::rbegin(c).base()) == &c.e); - assert(base(std::ranges::crbegin(c).base()) == &c.ce); - assert(base(std::ranges::rbegin(cc).base()) == &cc.ce); - assert(base(std::ranges::crbegin(cc).base()) == &cc.ce); - FunctionBeginMemberEnd d{}; const FunctionBeginMemberEnd dd{}; + + assert(base(std::ranges::rbegin(a).base()) == &a.e); + assert(base(std::ranges::rbegin(aa).base()) == &aa.ce); + assert(base(std::ranges::rbegin(b).base()) == &b.e); + assert(base(std::ranges::rbegin(bb).base()) == &bb.ce); + assert(base(std::ranges::rbegin(c).base()) == &c.e); + assert(base(std::ranges::rbegin(cc).base()) == &cc.ce); assert(base(std::ranges::rbegin(d).base()) == &d.e); - assert(base(std::ranges::crbegin(d).base()) == &d.ce); assert(base(std::ranges::rbegin(dd).base()) == &dd.ce); + +#if TEST_STD_VER < 23 + assert(base(std::ranges::crbegin(a).base()) == &a.ce); + assert(base(std::ranges::crbegin(aa).base()) == &aa.ce); + assert(base(std::ranges::crbegin(b).base()) == &b.ce); + assert(base(std::ranges::crbegin(bb).base()) == &bb.ce); + assert(base(std::ranges::crbegin(c).base()) == &c.ce); + assert(base(std::ranges::crbegin(cc).base()) == &cc.ce); + assert(base(std::ranges::crbegin(d).base()) == &d.ce); assert(base(std::ranges::crbegin(dd).base()) == &dd.ce); +#endif // TEST_STD_VER < 23 return true; } ASSERT_NOEXCEPT(std::ranges::rbegin(std::declval())); +#if TEST_STD_VER < 23 ASSERT_NOEXCEPT(std::ranges::crbegin(std::declval())); +#endif // TEST_STD_VER < 23 struct NoThrowMemberRBegin { ThrowingIterator rbegin() const noexcept; // auto(t.rbegin()) doesn't throw } ntmb; static_assert(noexcept(std::ranges::rbegin(ntmb))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::crbegin(ntmb))); +#endif // TEST_STD_VER < 23 struct NoThrowADLRBegin { friend ThrowingIterator rbegin(NoThrowADLRBegin&) noexcept; // auto(rbegin(t)) doesn't throw friend ThrowingIterator rbegin(const NoThrowADLRBegin&) noexcept; } ntab; static_assert(noexcept(std::ranges::rbegin(ntab))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::crbegin(ntab))); +#endif // TEST_STD_VER < 23 struct NoThrowMemberRBeginReturnsRef { ThrowingIterator& rbegin() const noexcept; // auto(t.rbegin()) may throw } ntmbrr; static_assert(!noexcept(std::ranges::rbegin(ntmbrr))); +#if TEST_STD_VER < 23 static_assert(!noexcept(std::ranges::crbegin(ntmbrr))); +#endif // TEST_STD_VER < 23 struct RBeginReturnsArrayRef { auto rbegin() const noexcept -> int(&)[10]; } brar; static_assert(noexcept(std::ranges::rbegin(brar))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::crbegin(brar))); +#endif // TEST_STD_VER < 23 struct NoThrowBeginThrowingEnd { int* begin() const noexcept; int* end() const; } ntbte; static_assert(!noexcept(std::ranges::rbegin(ntbte))); +#if TEST_STD_VER < 23 static_assert(!noexcept(std::ranges::crbegin(ntbte))); +#endif // TEST_STD_VER < 23 struct NoThrowEndThrowingBegin { int* begin() const; int* end() const noexcept; } ntetb; static_assert(noexcept(std::ranges::rbegin(ntetb))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::crbegin(ntetb))); +#endif // TEST_STD_VER < 23 // Test ADL-proofing. struct Incomplete; template struct Holder { T t; }; static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); +#endif // TEST_STD_VER < 23 int main(int, char**) { static_assert(testReturnTypes()); diff --git a/libcxx/test/std/ranges/range.access/rend.pass.cpp b/libcxx/test/std/ranges/range.access/rend.pass.cpp index f5f59edf19393..adb150461e044 100644 --- a/libcxx/test/std/ranges/range.access/rend.pass.cpp +++ b/libcxx/test/std/ranges/range.access/rend.pass.cpp @@ -9,7 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 // std::ranges::rend -// std::ranges::crend +// std::ranges::crend // before C++23 #include @@ -19,7 +19,9 @@ #include "test_iterators.h" using RangeREndT = decltype(std::ranges::rend); +#if TEST_STD_VER < 23 using RangeCREndT = decltype(std::ranges::crend); +#endif // TEST_STD_VER < 23 static int globalBuff[8]; @@ -27,16 +29,20 @@ static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct Incomplete; static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct REndMember { int x; @@ -49,50 +55,50 @@ static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 constexpr bool testReturnTypes() { - { - int *x[2]; - ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator); - ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator); - } - - { - int x[2][2]; - ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator); - ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator); - } - - { - struct Different { - char* rbegin(); - sentinel_wrapper& rend(); - short* rbegin() const; - sentinel_wrapper& rend() const; - } x; - ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), sentinel_wrapper); - ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper); - } + int* a[2]; + int b[2][2]; + struct Different { + char* rbegin(); + sentinel_wrapper& rend(); + short* rbegin() const; + sentinel_wrapper& rend() const; + } c; + + ASSERT_SAME_TYPE(decltype(std::ranges::rend(a)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::rend(b)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::rend(c)), sentinel_wrapper); + +#if TEST_STD_VER < 23 + ASSERT_SAME_TYPE(decltype(std::ranges::crend(a)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(b)), std::reverse_iterator); + ASSERT_SAME_TYPE(decltype(std::ranges::crend(c)), sentinel_wrapper); +#endif // TEST_STD_VER < 23 return true; } constexpr bool testArray() { int a[2]; - assert(std::ranges::rend(a).base() == a); - assert(std::ranges::crend(a).base() == a); - int b[2][2]; - assert(std::ranges::rend(b).base() == b); - assert(std::ranges::crend(b).base() == b); - REndMember c[2]; + + assert(std::ranges::rend(a).base() == a); + assert(std::ranges::rend(b).base() == b); assert(std::ranges::rend(c).base() == c); + +#if TEST_STD_VER < 23 + assert(std::ranges::crend(b).base() == b); + assert(std::ranges::crend(a).base() == a); assert(std::ranges::crend(c).base() == c); +#endif // TEST_STD_VER < 23 return true; } @@ -130,8 +136,10 @@ struct NonConstREndMember { }; static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct EnabledBorrowingREndMember { constexpr int* rbegin() const { return nullptr; } @@ -163,24 +171,24 @@ struct EmptyPtrREndMember { constexpr bool testREndMember() { REndMember a; - assert(std::ranges::rend(a) == &a.x); - assert(std::ranges::crend(a) == &a.x); - NonConstREndMember b; - assert(std::ranges::rend(b) == &b.x); - static_assert(!std::is_invocable_v); - EnabledBorrowingREndMember c; - assert(std::ranges::rend(std::move(c)) == &globalBuff[0]); - assert(std::ranges::crend(std::move(c)) == &globalBuff[0]); - REndMemberFunction d; - assert(std::ranges::rend(d) == &d.x); - assert(std::ranges::crend(d) == &d.x); - EmptyPtrREndMember e; + + assert(std::ranges::rend(a) == &a.x); + assert(std::ranges::rend(b) == &b.x); + assert(std::ranges::rend(std::move(c)) == &globalBuff[0]); + assert(std::ranges::rend(d) == &d.x); assert(std::ranges::rend(e) == &e.x); + +#if TEST_STD_VER < 23 + assert(std::ranges::crend(a) == &a.x); + static_assert(!std::is_invocable_v); + assert(std::ranges::crend(std::move(c)) == &globalBuff[0]); + assert(std::ranges::crend(d) == &d.x); assert(std::ranges::crend(e) == &e.x); +#endif // TEST_STD_VER < 23 return true; } @@ -197,8 +205,10 @@ static_assert(!std::is_invocable_v); static_assert( std::is_invocable_v); static_assert(!std::is_invocable_v); static_assert(std::is_invocable_v); // Ill-formed before P2602R2 Poison Pills are Too Toxic +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct REndFunctionReturnsInt { friend constexpr int rbegin(REndFunctionReturnsInt const&); @@ -233,7 +243,9 @@ struct REndFunctionByValue { friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; } friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; } }; +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct REndFunctionEnabledBorrowing { friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; } @@ -269,47 +281,45 @@ struct RBeginMemberEndFunction { constexpr bool testREndFunction() { const REndFunction a{}; - assert(std::ranges::rend(a) == &a.x); - assert(std::ranges::crend(a) == &a.x); REndFunction aa{}; - assert(std::ranges::rend(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::crend(aa) == &aa.x); - REndFunctionByValue b; - assert(std::ranges::rend(b) == &globalBuff[1]); - assert(std::ranges::crend(b) == &globalBuff[1]); - REndFunctionEnabledBorrowing c; - assert(std::ranges::rend(std::move(c)) == &globalBuff[2]); - assert(std::ranges::crend(std::move(c)) == &globalBuff[2]); - const REndFunctionReturnsEmptyPtr d{}; - assert(std::ranges::rend(d) == &d.x); - assert(std::ranges::crend(d) == &d.x); REndFunctionReturnsEmptyPtr dd{}; - assert(std::ranges::rend(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::crend(dd) == &dd.x); - const REndFunctionWithDataMember e{}; - assert(std::ranges::rend(e) == &e.x); - assert(std::ranges::crend(e) == &e.x); REndFunctionWithDataMember ee{}; - assert(std::ranges::rend(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::crend(ee) == &ee.x); - const REndFunctionWithPrivateEndMember f{}; - assert(std::ranges::rend(f) == &f.y); - assert(std::ranges::crend(f) == &f.y); REndFunctionWithPrivateEndMember ff{}; - assert(std::ranges::rend(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic - assert(std::ranges::crend(ff) == &ff.y); - const RBeginMemberEndFunction g{}; - assert(std::ranges::rend(g) == &g.x); - assert(std::ranges::crend(g) == &g.x); RBeginMemberEndFunction gg{}; + + assert(std::ranges::rend(a) == &a.x); + assert(std::ranges::rend(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::rend(b) == &globalBuff[1]); + assert(std::ranges::rend(std::move(c)) == &globalBuff[2]); + assert(std::ranges::rend(d) == &d.x); + assert(std::ranges::rend(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::rend(e) == &e.x); + assert(std::ranges::rend(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::rend(f) == &f.y); + assert(std::ranges::rend(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic + assert(std::ranges::rend(g) == &g.x); assert(std::ranges::rend(gg) == &gg.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic + +#if TEST_STD_VER < 23 + assert(std::ranges::crend(a) == &a.x); + assert(std::ranges::crend(aa) == &aa.x); + assert(std::ranges::crend(b) == &globalBuff[1]); + assert(std::ranges::crend(std::move(c)) == &globalBuff[2]); + assert(std::ranges::crend(d) == &d.x); + assert(std::ranges::crend(dd) == &dd.x); + assert(std::ranges::crend(e) == &e.x); + assert(std::ranges::crend(ee) == &ee.x); + assert(std::ranges::crend(f) == &f.y); + assert(std::ranges::crend(ff) == &ff.y); + assert(std::ranges::crend(g) == &g.x); assert(std::ranges::crend(gg) == &gg.x); +#endif // TEST_STD_VER < 23 return true; } @@ -325,7 +335,9 @@ struct MemberBeginEnd { }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginEnd { int b, e; @@ -343,7 +355,9 @@ struct FunctionBeginEnd { }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberBeginFunctionEnd { int b, e; @@ -359,7 +373,9 @@ struct MemberBeginFunctionEnd { }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginMemberEnd { int b, e; @@ -375,59 +391,77 @@ struct FunctionBeginMemberEnd { }; static_assert( std::is_invocable_v); static_assert( std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert( std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberBeginEndDifferentTypes { bidirectional_iterator begin(); bidirectional_iterator end(); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginEndDifferentTypes { friend bidirectional_iterator begin(FunctionBeginEndDifferentTypes&); friend bidirectional_iterator end(FunctionBeginEndDifferentTypes&); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberBeginEndForwardIterators { forward_iterator begin(); forward_iterator end(); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginEndForwardIterators { friend forward_iterator begin(FunctionBeginEndForwardIterators&); friend forward_iterator end(FunctionBeginEndForwardIterators&); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberBeginOnly { bidirectional_iterator begin() const; }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionBeginOnly { friend bidirectional_iterator begin(FunctionBeginOnly&); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct MemberEndOnly { bidirectional_iterator end() const; }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 struct FunctionEndOnly { friend bidirectional_iterator end(FunctionEndOnly&); }; static_assert(!std::is_invocable_v); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v); +#endif // TEST_STD_VER < 23 // Make sure there is no clash between the following cases: // - the case that handles classes defining member `rbegin` and `rend` functions; @@ -438,53 +472,60 @@ struct MemberBeginAndRBegin { int* rbegin() const; int* rend() const; }; -static_assert( std::is_invocable_v); -static_assert( std::is_invocable_v); +static_assert(std::is_invocable_v); static_assert( std::same_as, int*>); +#if TEST_STD_VER < 23 +static_assert(std::is_invocable_v); static_assert( std::same_as, int*>); +#endif // TEST_STD_VER < 23 constexpr bool testBeginEnd() { MemberBeginEnd a{}; const MemberBeginEnd aa{}; - assert(base(std::ranges::rend(a).base()) == &a.b); - assert(base(std::ranges::crend(a).base()) == &a.cb); - assert(base(std::ranges::rend(aa).base()) == &aa.cb); - assert(base(std::ranges::crend(aa).base()) == &aa.cb); - FunctionBeginEnd b{}; const FunctionBeginEnd bb{}; - assert(base(std::ranges::rend(b).base()) == &b.b); - assert(base(std::ranges::crend(b).base()) == &b.cb); - assert(base(std::ranges::rend(bb).base()) == &bb.cb); - assert(base(std::ranges::crend(bb).base()) == &bb.cb); - MemberBeginFunctionEnd c{}; const MemberBeginFunctionEnd cc{}; - assert(base(std::ranges::rend(c).base()) == &c.b); - assert(base(std::ranges::crend(c).base()) == &c.cb); - assert(base(std::ranges::rend(cc).base()) == &cc.cb); - assert(base(std::ranges::crend(cc).base()) == &cc.cb); - FunctionBeginMemberEnd d{}; const FunctionBeginMemberEnd dd{}; + + assert(base(std::ranges::rend(a).base()) == &a.b); + assert(base(std::ranges::rend(aa).base()) == &aa.cb); + assert(base(std::ranges::rend(b).base()) == &b.b); + assert(base(std::ranges::rend(bb).base()) == &bb.cb); + assert(base(std::ranges::rend(c).base()) == &c.b); + assert(base(std::ranges::rend(cc).base()) == &cc.cb); assert(base(std::ranges::rend(d).base()) == &d.b); - assert(base(std::ranges::crend(d).base()) == &d.cb); assert(base(std::ranges::rend(dd).base()) == &dd.cb); + +#if TEST_STD_VER < 23 + assert(base(std::ranges::crend(a).base()) == &a.cb); + assert(base(std::ranges::crend(aa).base()) == &aa.cb); + assert(base(std::ranges::crend(b).base()) == &b.cb); + assert(base(std::ranges::crend(bb).base()) == &bb.cb); + assert(base(std::ranges::crend(c).base()) == &c.cb); + assert(base(std::ranges::crend(cc).base()) == &cc.cb); + assert(base(std::ranges::crend(d).base()) == &d.cb); assert(base(std::ranges::crend(dd).base()) == &dd.cb); +#endif // TEST_STD_VER < 23 return true; } ASSERT_NOEXCEPT(std::ranges::rend(std::declval())); +#if TEST_STD_VER < 23 ASSERT_NOEXCEPT(std::ranges::crend(std::declval())); +#endif // TEST_STD_VER < 23 struct NoThrowMemberREnd { ThrowingIterator rbegin() const; ThrowingIterator rend() const noexcept; // auto(t.rend()) doesn't throw } ntmre; static_assert(noexcept(std::ranges::rend(ntmre))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::crend(ntmre))); +#endif // TEST_STD_VER < 23 struct NoThrowADLREnd { ThrowingIterator rbegin() const; @@ -492,43 +533,55 @@ struct NoThrowADLREnd { friend ThrowingIterator rend(const NoThrowADLREnd&) noexcept; } ntare; static_assert(noexcept(std::ranges::rend(ntare))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::crend(ntare))); +#endif // TEST_STD_VER < 23 struct NoThrowMemberREndReturnsRef { ThrowingIterator rbegin() const; ThrowingIterator& rend() const noexcept; // auto(t.rend()) may throw } ntmrerr; static_assert(!noexcept(std::ranges::rend(ntmrerr))); +#if TEST_STD_VER < 23 static_assert(!noexcept(std::ranges::crend(ntmrerr))); +#endif // TEST_STD_VER < 23 struct REndReturnsArrayRef { auto rbegin() const noexcept -> int(&)[10]; auto rend() const noexcept -> int(&)[10]; } rerar; static_assert(noexcept(std::ranges::rend(rerar))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::crend(rerar))); +#endif // TEST_STD_VER < 23 struct NoThrowBeginThrowingEnd { int* begin() const noexcept; int* end() const; } ntbte; static_assert(noexcept(std::ranges::rend(ntbte))); +#if TEST_STD_VER < 23 static_assert(noexcept(std::ranges::crend(ntbte))); +#endif // TEST_STD_VER < 23 struct NoThrowEndThrowingBegin { int* begin() const; int* end() const noexcept; } ntetb; static_assert(!noexcept(std::ranges::rend(ntetb))); +#if TEST_STD_VER < 23 static_assert(!noexcept(std::ranges::crend(ntetb))); +#endif // TEST_STD_VER < 23 // Test ADL-proofing. struct Incomplete; template struct Holder { T t; }; static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); +#if TEST_STD_VER < 23 static_assert(!std::is_invocable_v*>); static_assert(!std::is_invocable_v*&>); +#endif // TEST_STD_VER < 23 int main(int, char**) { static_assert(testReturnTypes()); diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/adaptor.pass.cpp new file mode 100644 index 0000000000000..e1b36cb025317 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/adaptor.pass.cpp @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// std::views::as_const + +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +template +concept HasPipe = requires { + { std::declval() | std::declval() }; +}; + +struct DefaultConstructibleView : std::ranges::view_base { + int i_; + int* begin(); + int* end(); +}; +struct NoView {}; + +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(HasPipe); +static_assert(HasPipe); +static_assert(!HasPipe); +static_assert(!HasPipe); +static_assert(std::is_same_v); + +struct const_iterator_range { + constexpr std::const_iterator begin() const { return {}; } + constexpr std::const_iterator end() const { return {}; } +}; +static_assert(!std::ranges::view); +static_assert(std::ranges::range); + +constexpr bool test() { + // Let E be an expression, let T be decltype((E)), and let U be remove_cvref_t. + // The expression views::as_const(E) is expression-equivalent to: + + // - If views::all_t models constant_range, then views::all(E). + { + [[maybe_unused]] std::same_as> decltype(auto) view = + std::views::as_const(const_iterator_range{}); + } + { + // ambiguous with empty_view case + [[maybe_unused]] std::same_as>> decltype(auto) view = + std::views::empty | std::views::as_const; + } + { + // ambiguous with span case + int a[3] = {}; + [[maybe_unused]] std::same_as>> decltype(auto) view1 = + std::span(a) | std::views::as_const; + [[maybe_unused]] std::same_as>> decltype(auto) view2 = + std::span(a) | std::views::as_const; + } + { + // ambiguous with ref_view case + std::array a = {}; + std::ranges::ref_view> r = a; + [[maybe_unused]] std::same_as>> decltype(auto) view = + r | std::views::as_const; + } + { + // ambiguous with constant_range case + std::array a = {}; + [[maybe_unused]] std::same_as>> decltype(auto) view = + a | std::views::as_const; + } + + // - Otherwise, if U denotes empty_view for some type X, then auto(views::empty). + { + [[maybe_unused]] std::same_as> decltype(auto) view = + std::views::empty | std::views::as_const; + } + + // - Otherwise, if U denotes span for some type X and some extent Extent, then span(E). + { + int a[3] = {}; + std::same_as> decltype(auto) view = std::span(a) | std::views::as_const; + assert(std::to_address(view.begin()) == a); + assert(std::to_address(view.end()) == a + 3); + } + { + int a[3] = {}; + std::same_as> decltype(auto) view = std::span(a) | std::views::as_const; + assert(std::to_address(view.begin()) == a); + assert(std::to_address(view.end()) == a + 3); + } + + // - Otherwise, if U denotes ref_view for some type X and const X models constant_range, then ref_view(static_cast(E.base())). + { + std::array a = {}; + std::ranges::ref_view> r = a; + [[maybe_unused]] std::same_as>> decltype(auto) view = + r | std::views::as_const; + } + + // - Otherwise, if E is an lvalue, const U models constant_range, and U does not model view, then ref_view(static_cast(E)). + { + std::array a = {}; + [[maybe_unused]] std::same_as>> decltype(auto) view = + a | std::views::as_const; + } + + // - Otherwise, as_const_view(E). + { // view | views::as_const + DefaultConstructibleView v{{}, 3}; + std::same_as> decltype(auto) view = v | std::views::as_const; + assert(view.base().i_ == 3); + } + + { // adaptor | views::as_const + DefaultConstructibleView v{{}, 3}; + const auto partial = std::views::transform(std::identity{}) | std::views::as_const; + std::same_as>> decltype(auto) view = partial(v); + assert(view.base().base().i_ == 3); + } + + { // views::as_const | adaptor + DefaultConstructibleView v{{}, 3}; + const auto partial = std::views::as_const | std::views::transform(std::identity{}); + std::same_as, + std::identity>> decltype(auto) view = partial(v); + assert(view.base().base().i_ == 3); + } + + { // range | views::as_const + [[maybe_unused]] std::same_as>>> decltype(auto) view = + std::vector{} | std::views::as_const; + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/base.pass.cpp new file mode 100644 index 0000000000000..cbe5214988d41 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// constexpr V base() const& requires copy_constructible { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include +#include +#include + +#include "MoveOnly.h" + +struct SimpleView : std::ranges::view_base { + int i; + int* begin() const; + int* end() const; +}; + +struct MoveOnlyView : SimpleView { + MoveOnly m; +}; + +template +concept HasBase = requires(T&& t) { std::forward(t).base(); }; + +static_assert(HasBase const&>); +static_assert(HasBase&&>); + +static_assert(!HasBase const&>); +static_assert(HasBase&&>); + +constexpr bool test() { + { // const & + const std::ranges::as_const_view view(SimpleView{{}, 5}); + std::same_as decltype(auto) v = view.base(); + assert(v.i == 5); + } + + { // & + std::ranges::as_const_view view(SimpleView{{}, 5}); + std::same_as decltype(auto) v = view.base(); + assert(v.i == 5); + } + + { // && + std::ranges::as_const_view view(SimpleView{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.i == 5); + } + + { // const && + const std::ranges::as_const_view view(SimpleView{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.i == 5); + } + + { // move only + std::ranges::as_const_view view(MoveOnlyView{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.m.get() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/begin.pass.cpp new file mode 100644 index 0000000000000..73f50a7bd6635 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/begin.pass.cpp @@ -0,0 +1,124 @@ +//===----------------------------------------------------------------------===// +// +// 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: std-at-least-c++23 + +// constexpr auto begin() +// constexpr auto begin() const + +#include +#include +#include +#include + +#include "test_iterators.h" + +struct SimpleView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct NonSimpleView : std::ranges::view_base { + char* begin(); + char* end(); + int* begin() const; + int* end() const; +}; + +struct NonConstView : std::ranges::view_base { + char* begin(); + char* end(); +}; + +template +concept HasBegin = requires(T t) { t.begin(); }; + +static_assert(HasBegin>); +static_assert(HasBegin>); +static_assert(HasBegin>); +static_assert(HasBegin>); +static_assert(HasBegin>); +static_assert(!HasBegin>); + +template +constexpr void test_range() { + int a[] = {1, 2}; + std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a)))); + std::ranges::as_const_view view(std::move(range)); + std::same_as> decltype(auto) iter = view.begin(); + assert(base(iter.base()) == std::begin(a)); +} + +template +class WrapRange { + Iter iter_; + Sent sent_; + +public: + constexpr WrapRange(Iter iter, Sent sent) : iter_(std::move(iter)), sent_(std::move(sent)) {} + + constexpr Iter begin() const { return iter_; } + constexpr Sent end() const { return sent_; } +}; + +template +WrapRange(Iter, Sent) -> WrapRange; + +template +constexpr void test_const_range() { + int a[] = {1, 2}; + auto range = WrapRange{Iter(a), Sent(Iter(a + 2))}; + const std::ranges::as_const_view view(std::views::all(range)); + std::same_as decltype(auto) iter = view.begin(); + assert(base(iter) == std::begin(a)); +} + +struct const_pointer_view : std::ranges::view_base { + constexpr const int* begin() const { return {}; } + constexpr const int* end() const { return {}; } +}; +struct const_iterator_view : std::ranges::view_base { + constexpr std::const_iterator begin() const { return {}; } + constexpr std::const_iterator end() const { return {}; } +}; + +constexpr bool test() { + types::for_each(types::cpp20_input_iterator_list{}, [] { + if constexpr (std::sentinel_for) + test_range(); + test_range>(); + test_range>(); + }); + + types::for_each(types::forward_iterator_list{}, [] { + test_const_range(); + test_const_range>(); + test_const_range>(); + }); + + // check that with a const_iterator begin() doesn't return std::const_iterator + { + std::ranges::as_const_view view{const_pointer_view{}}; + std::same_as decltype(auto) it = view.begin(); + assert(it == nullptr); + } + { + std::ranges::as_const_view view{const_iterator_view{}}; + std::same_as> decltype(auto) it = view.begin(); + assert(it == std::const_iterator{}); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/ctad.compile.pass.cpp new file mode 100644 index 0000000000000..b0f95dac88378 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/ctad.compile.pass.cpp @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +#include +#include +#include + +static_assert(std::is_same_v{})), + std::ranges::as_const_view>>>); + +static_assert(std::is_same_v&>())), + std::ranges::as_const_view&>>>); + +static_assert(std::is_same_v{})), + std::ranges::as_const_view>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/ctor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/ctor.pass.cpp new file mode 100644 index 0000000000000..8a683567af995 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/ctor.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// 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: std-at-least-c++23 + +// std::ranges::as_const_view::as_const_view(...) + +#include +#include +#include +#include +#include + +struct DefaultConstructibleView : std::ranges::view_base { + int* begin() const; + int* end() const; + + int i_ = 23; +}; + +struct NonDefaultConstructibleView : std::ranges::view_base { + NonDefaultConstructibleView(int i) : i_(i) {} + + int* begin() const; + int* end() const; + + int i_ = 23; +}; + +static_assert(!std::is_constructible_v>); +static_assert(std::is_constructible_v, int>); +static_assert(std::is_nothrow_constructible_v>); + +template +concept IsImplicitlyConstructible = requires(void (&fun)(T), Args&&... args) { fun({std::forward(args)...}); }; + +static_assert(IsImplicitlyConstructible>); +static_assert(!IsImplicitlyConstructible, int>); + +static_assert(std::is_constructible_v, DefaultConstructibleView>); +static_assert( + std::is_constructible_v, NonDefaultConstructibleView>); +static_assert(!std::is_convertible_v>); +static_assert( + !std::is_convertible_v>); + +constexpr bool test() { + std::ranges::as_const_view view = {}; + assert(view.base().i_ == 23); + + return true; +} + +int main(int, char**) { + static_assert(test()); + test(); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/enable_borrowed_range.compile.pass.cpp new file mode 100644 index 0000000000000..4e26249260455 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/enable_borrowed_range.compile.pass.cpp @@ -0,0 +1,16 @@ +//===----------------------------------------------------------------------===// +// +// 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: std-at-least-c++23 + +#include +#include + +static_assert(std::ranges::enable_borrowed_range>>); +static_assert(std::ranges::enable_borrowed_range&>>>); +static_assert(!std::ranges::enable_borrowed_range>>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/end.pass.cpp new file mode 100644 index 0000000000000..c9a63e88f4765 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/end.pass.cpp @@ -0,0 +1,129 @@ +//===----------------------------------------------------------------------===// +// +// 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: std-at-least-c++23 + +// constexpr auto end() +// constexpr auto end() const + +#include +#include +#include + +#include "test_iterators.h" + +struct DefaultConstructibleView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct NonConstCommonRange : std::ranges::view_base { + int* begin(); + int* end(); + + int* begin() const; + sentinel_wrapper end() const; +}; + +struct NonConstView : std::ranges::view_base { + int* begin(); + int* end(); +}; + +template +concept HasEnd = requires(T t) { t.end(); }; + +static_assert(HasEnd>); +static_assert(HasEnd>); +static_assert(HasEnd>); +static_assert(!HasEnd>); + +static_assert(std::is_same_v>().end()), + std::const_iterator>); +static_assert(std::is_same_v>().end()), + std::const_sentinel>>); + +template +constexpr void test_range() { + int a[] = {1, 2}; + std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a)))); + std::ranges::as_const_view view(std::move(range)); + std::same_as> decltype(auto) iter = view.end(); + assert(base(base(iter)) == std::end(a)); +} + +template +class WrapRange { + Iter iter_; + Sent sent_; + +public: + constexpr WrapRange(Iter iter, Sent sent) : iter_(std::move(iter)), sent_(std::move(sent)) {} + + constexpr Iter begin() const { return iter_; } + constexpr Sent end() const { return sent_; } +}; + +template +WrapRange(Iter, Sent) -> WrapRange; + +template +constexpr void test_const_range() { + int a[] = {1, 2}; + auto range = WrapRange{Iter(a), Sent(Iter(a + 2))}; + const std::ranges::as_const_view view(std::move(range)); + std::same_as> decltype(auto) iter = view.end(); + assert(base(base(iter)) == std::end(a)); +} + +struct const_pointer_view : std::ranges::view_base { + constexpr const int* begin() const { return {}; } + constexpr const int* end() const { return {}; } +}; +struct const_iterator_view : std::ranges::view_base { + constexpr std::const_iterator begin() const { return {}; } + constexpr std::const_iterator end() const { return {}; } +}; + +constexpr bool test() { + test_range, sentinel_wrapper>>(); + test_range, sized_sentinel>>(); + test_range, sentinel_wrapper>>(); + test_range, sized_sentinel>>(); + + types::for_each(types::forward_iterator_list{}, [] { + test_range(); + test_range>(); + test_range>(); + }); + + // check that with a const_iterator end() doesn't return std::const_iterator + { + std::ranges::as_const_view view{const_pointer_view{}}; + std::same_as decltype(auto) it = view.end(); + assert(it == nullptr); + } + { + std::ranges::as_const_view view{const_iterator_view{}}; + std::same_as> decltype(auto) it = view.end(); + assert(it == std::const_iterator{}); + } + + return true; +} + +int main(int, char**) { + test(); + +// gcc cannot have mutable member in constant expression +#if !defined(TEST_COMPILER_GCC) + static_assert(test()); +#endif + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.const/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.const/size.pass.cpp new file mode 100644 index 0000000000000..81ad72309e5e6 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.const/size.pass.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// constexpr auto size() +// constexpr auto size() const + +// REQUIRES: std-at-least-c++23 + +#include +#include +#include + +struct ConstSizedView : std::ranges::view_base { + bool* size_called; + int* begin() const; + int* end() const; + + constexpr std::size_t size() const { + *size_called = true; + return 3; + } +}; + +struct SizedView : std::ranges::view_base { + bool* size_called; + int* begin() const; + int* end() const; + + constexpr int size() { + *size_called = true; + return 5; + } +}; + +struct UnsizedView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template +concept HasSize = requires(T v) { v.size(); }; + +static_assert(HasSize); +static_assert(HasSize); +static_assert(HasSize); +static_assert(!HasSize); +static_assert(!HasSize); +static_assert(!HasSize); + +constexpr bool test() { + { + bool size_called = false; + std::ranges::as_const_view view(ConstSizedView{{}, &size_called}); + std::same_as auto size = view.size(); + assert(size == 3); + assert(size_called); + } + + { + bool size_called = false; + std::ranges::as_const_view view(SizedView{{}, &size_called}); + std::same_as auto size = view.size(); + assert(size == 5); + assert(size_called); + } + + 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 index 113272703d59f..b5a11a2a96d88 100644 --- 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 @@ -14,6 +14,7 @@ #include #include +#include #include #include "test_iterators.h" #include "test_range.h" @@ -138,6 +139,15 @@ constexpr bool test() { static_assert(ConstBeginDisabled); } + // LWG4027 ensures that the behavior is unchanged in C++23. + { + [[maybe_unused]] auto r = std::views::single(0) | std::views::lazy_split(0); + using R1 = decltype((*std::ranges::cbegin(r)).front()); + using R2 = decltype((*std::cbegin(r)).front()); + static_assert(std::is_same_v); + static_assert(std::is_const_v>); + } + return true; } diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/begin.pass.cpp index a3ddbf8914bf3..4d1e1eff9bf9e 100644 --- a/libcxx/test/std/ranges/range.adaptors/range.transform/begin.pass.cpp +++ b/libcxx/test/std/ranges/range.adaptors/range.transform/begin.pass.cpp @@ -14,6 +14,7 @@ // regular_invocable>; #include +#include #include "test_macros.h" #include "types.h" @@ -47,6 +48,14 @@ constexpr bool test() { assert(*transformView.begin() == 1); } + // LWG4027 ensures that the behavior is unchanged in C++23. + { + [[maybe_unused]] auto r = std::views::single(0) | std::views::transform([](int) { return 0; }); + using CIt1 = decltype(std::ranges::cbegin(r)); + using CIt2 = decltype(std::cbegin(r)); + static_assert(std::is_same_v); + } + static_assert(!BeginInvocable>); return true; diff --git a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp index 3f7c174d3fe48..8c711cd770862 100644 --- a/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp +++ b/libcxx/test/std/ranges/range.utility/view.interface/view.interface.pass.cpp @@ -35,8 +35,15 @@ using InputIter = cpp20_input_iterator; struct InputRange : std::ranges::view_interface { int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + constexpr InputIter begin() const { return InputIter(buff); } - constexpr InputIter end() const { return InputIter(buff + 8); } + constexpr sentinel_wrapper end() const { return sentinel_wrapper(InputIter(buff + 8)); } +}; + +struct NonConstInputRange : std::ranges::view_interface { + int buff[8] = {0, 1, 2, 3, 4, 5, 6, 7}; + constexpr InputIter begin() { return InputIter(buff); } + constexpr sentinel_wrapper end() { return sentinel_wrapper(InputIter(buff + 8)); } }; struct SizedInputRange : std::ranges::view_interface { @@ -222,6 +229,19 @@ constexpr bool testEmpty() { return true; } +#if TEST_STD_VER >= 23 +template +concept ConstAccessorsInvocable = requires(T& t) { + t.cbegin(); + t.cend(); +}; + +static_assert(ConstAccessorsInvocable); +static_assert(ConstAccessorsInvocable); +static_assert(ConstAccessorsInvocable); +static_assert(!ConstAccessorsInvocable); +#endif // TEST_STD_VER >= 23 + template concept DataInvocable = requires (T const& obj) { obj.data(); }; diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h index ead8a3e8f87d2..5111dbc0a1a0d 100644 --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -511,6 +511,13 @@ template three_way_contiguous_iterator(It) -> three_way_contiguous_iterator; #endif // TEST_STD_VER > 17 +#if TEST_STD_VER >= 23 +template +TEST_CONSTEXPR Iter base(std::basic_const_iterator i) { + return i.base(); +} +#endif + template // ADL base() for everything else (including pointers) TEST_CONSTEXPR Iter base(Iter i) { return i; } diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 53252d5e2d673..2befe4acca85c 100755 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1039,11 +1039,9 @@ def add_version_header(tc): { "name": "__cpp_lib_ranges_as_const", "values": { - "c++23": 202207 # P2278R4 cbegin should always return a constant iterator - # 202311 # DR P2836R1 std::basic_const_iterator should follow its underlying type’s convertibility + "c++23": 202311 # DR P2836R1 std::basic_const_iterator should follow its underlying type’s convertibility }, "headers": ["ranges"], - "unimplemented": True, }, { "name": "__cpp_lib_ranges_as_rvalue",