diff --git a/stl/inc/ranges b/stl/inc/ranges index 7feab0c5678..1f050170e0c 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1008,6 +1008,489 @@ namespace ranges { inline constexpr _Filter_fn filter; } // namespace views + // CLASS TEMPLATE ranges::transform_view + // clang-format off + template // TRANSITION, LLVM-47414 + concept _Can_const_transform = range && regular_invocable>; + +#if _ITERATOR_DEBUG_LEVEL == 0 +#define _NOEXCEPT_IDL0(...) noexcept(__VA_ARGS__) +#else +#define _NOEXCEPT_IDL0(...) +#endif // _ITERATOR_DEBUG_LEVEL == 0 + + template + requires view<_Vw> && is_object_v<_Fn> + && regular_invocable<_Fn&, range_reference_t<_Vw>> + && _Can_reference>> + class transform_view : public view_interface> { + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Semiregular_box<_Fn> _Fun{}; + + template + class _Sentinel; + + template // TRANSITION, LWG-3289 + struct _Iterator_base {}; + // clang-format off + template + requires _Has_member_iterator_category<_Traits> + struct _Iterator_base<_Traits, _Base> { + // clang-format on + using iterator_category = + conditional_t>>, + conditional_t, + random_access_iterator_tag, typename _Traits::iterator_category>, + input_iterator_tag>; + }; + + template + class _Iterator : public _Iterator_base>, _Maybe_const<_Const, _Vw>> { + private: + template + friend class _Iterator; + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, transform_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + iterator_t<_Base> _Current{}; + _Parent_t* _Parent{}; + +#if _ITERATOR_DEBUG_LEVEL != 0 + constexpr void _Check_dereference() const { + _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized transform_view iterator"); + _STL_VERIFY(_Current != _RANGES end(_Parent->_Range), "cannot dereference end transform_view iterator"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + +#if _ITERATOR_DEBUG_LEVEL != 0 + constexpr void _Same_range(const _Iterator& _Right) const noexcept { + _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible transform_view iterators"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + public: + using iterator_concept = conditional_t, random_access_iterator_tag, + conditional_t, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>; + using value_type = remove_cvref_t>>; + using difference_type = range_difference_t<_Base>; + + _Iterator() = default; + + constexpr _Iterator(_Parent_t& _Parent_, iterator_t<_Base> _Current_) noexcept( + noexcept(is_nothrow_move_constructible_v>)) // strengthened + : _Current{_STD move(_Current_)}, _Parent{_STD addressof(_Parent_)} { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Adl_verify_range(_Current, _RANGES end(_Parent_._Range)); + if constexpr (forward_range<_Base>) { + _Adl_verify_range(_RANGES begin(_Parent_._Range), _Current); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + } + + // clang-format off + constexpr _Iterator(_Iterator _It) + noexcept(is_nothrow_constructible_v, iterator_t<_Vw>>) // strengthened + requires _Const && convertible_to, iterator_t<_Base>> + : _Current{_STD move(_It._Current)}, _Parent{_It._Parent} {} + // clang-format on + + _NODISCARD constexpr iterator_t<_Base> base() const& noexcept(is_nothrow_copy_constructible_v< + iterator_t<_Base>>) /* strengthened */ requires copyable> { + return _Current; + } + _NODISCARD constexpr iterator_t<_Base> base() && noexcept( + is_nothrow_move_constructible_v>) /* strengthened */ { + return _STD move(_Current); + } + + _NODISCARD constexpr decltype(auto) operator*() const + _NOEXCEPT_IDL0(noexcept(_STD invoke(*_Parent->_Fun, *_Current))) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Check_dereference(); + _STL_VERIFY( + _Parent->_Fun, "Cannot dereference iterator into transform_view with no transformation function"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _STD invoke(*_Parent->_Fun, *_Current); + } + + constexpr _Iterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Parent != nullptr, "Cannot increment value-initialized transform_view iterator"); + _STL_VERIFY( + _Current != _RANGES end(_Parent->_Range), "Cannot increment transform_view iterator past end"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + ++_Current; + return *this; + } + + constexpr decltype(auto) operator++(int) noexcept( + noexcept(++_Current) + && (!forward_range<_Base> || is_nothrow_copy_constructible_v>) ) /* strengthened */ { + if constexpr (forward_range<_Base>) { + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } + } + + constexpr _Iterator& operator--() noexcept( + noexcept(--_Current)) /* strengthened */ requires bidirectional_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(_Parent != nullptr, "Cannot decrement value-initialized transform_view iterator"); + if constexpr (forward_range<_Vw>) { + _STL_VERIFY(_Current != _RANGES begin(_Parent->_Range), + "Cannot decrement transform_view iterator before begin"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + --_Current; + return *this; + } + constexpr _Iterator operator--(int) noexcept( + noexcept(--_Current) && is_nothrow_copy_constructible_v>) /* strengthened */ + requires bidirectional_range<_Base> { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr void _Verify_offset(const difference_type _Off) const requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL == 0 + (void) _Off; +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 0 / _ITERATOR_DEBUG_LEVEL != 0 vvv + _STL_VERIFY(_Off == 0 || _Parent, "cannot seek value-initialized transform_view iterator"); + + if constexpr (_Offset_verifiable_v>) { + _Current._Verify_offset(_Off); + } else { + if (_Off < 0) { + if constexpr (sized_sentinel_for, iterator_t<_Base>>) { + _STL_VERIFY(_Off >= _RANGES begin(_Parent->_Range) - _Current, + "cannot seek transform_view iterator before begin"); + } + } else if (_Off > 0) { + if constexpr (sized_sentinel_for, iterator_t<_Base>>) { + _STL_VERIFY(_Off <= _RANGES end(_Parent->_Range) - _Current, + "cannot seek transform_view iterator after end"); + } else if constexpr (sized_sentinel_for, + iterator_t<_Base>> && sized_range<_Base>) { + const auto _Size = _RANGES distance(_Parent->_Range); + _STL_VERIFY(_Off <= _Size - (_Current - _RANGES begin(_Parent->_Range)), + "cannot seek transform_view iterator after end"); + } + } + } +#endif // _ITERATOR_DEBUG_LEVEL == 0 + } + + constexpr _Iterator& operator+=(const difference_type _Off) + _NOEXCEPT_IDL0(noexcept(_Current += _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Verify_offset(_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Current += _Off; + return *this; + } + constexpr _Iterator& operator-=(const difference_type _Off) + _NOEXCEPT_IDL0(noexcept(_Current -= _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Verify_offset(-_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Current -= _Off; + return *this; + } + + _NODISCARD constexpr decltype(auto) operator[](const difference_type _Idx) const + _NOEXCEPT_IDL0(noexcept(_STD invoke(*_Parent->_Fun, _Current[_Idx]))) /* strengthened */ + requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Verify_offset(_Idx); + _STL_VERIFY( + _Parent->_Fun, "Cannot dereference iterator into transform_view with no transformation function"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _STD invoke(*_Parent->_Fun, _Current[_Idx]); + } + + _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current + == _Right._Current)) /* strengthened */ requires equality_comparable> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current == _Right._Current; + } + + _NODISCARD friend constexpr bool operator<(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current < _Right._Current; + } + _NODISCARD friend constexpr bool operator>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires random_access_range<_Base> { + return _Right < _Left; + } + _NODISCARD friend constexpr bool operator<=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires random_access_range<_Base> { + return !(_Right < _Left); + } + _NODISCARD friend constexpr bool operator>=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires random_access_range<_Base> { + return !(_Left < _Right); + } + // clang-format off + _NODISCARD friend constexpr auto operator<=>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Current <=> _Right._Current)) /* strengthened */ + requires random_access_range<_Base> && three_way_comparable> { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current <=> _Right._Current; + } + + _NODISCARD friend constexpr _Iterator operator+(_Iterator _It, difference_type _Off) + _NOEXCEPT_IDL0(noexcept(_It._Current += _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Verify_offset(_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _It._Current += _Off; + return _It; + } + _NODISCARD friend constexpr _Iterator operator+(difference_type _Off, _Iterator _It) + _NOEXCEPT_IDL0(noexcept(_It._Current += _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Verify_offset(_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _It._Current += _Off; + return _It; + } + + _NODISCARD friend constexpr _Iterator operator-(_Iterator _It, difference_type _Off) + _NOEXCEPT_IDL0(noexcept(_It._Current -= _Off)) /* strengthened */ requires random_access_range<_Base> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Verify_offset(-_Off); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _It._Current -= _Off; + return _It; + } + + _NODISCARD friend constexpr difference_type operator-(const _Iterator& _Left, + const _Iterator& _Right) noexcept(noexcept(_Left._Current - _Right._Current)) /* strengthened */ + requires sized_sentinel_for, iterator_t<_Base>> { // constraints per LWG issue + // unnumbered as of 2020-09-03 +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Current - _Right._Current; + } + + _NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept(noexcept(*_It)) { + if constexpr (is_lvalue_reference_v) { + return _STD move(*_It); + } else { + return *_It; + } + } + + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) _NOEXCEPT_IDL0(noexcept( + ranges::iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Check_dereference(); + _Right._Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _RANGES iter_swap(_Left._Current, _Right._Current); + } + }; + + template + class _Sentinel { + private: + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, transform_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + sentinel_t<_Base> _Last{}; + + _NODISCARD constexpr bool _Equal(const _Iterator<_Const>& _It) const + noexcept(noexcept(_It._Current == _Last)) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _It._Parent != nullptr, "cannot compare transform_view sentinel with value-initialized iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _It._Current == _Last; + } + + _NODISCARD constexpr range_difference_t<_Base> _Distance_to(const _Iterator<_Const>& _It) const + noexcept(noexcept(_It._Current - _Last)) { + _STL_INTERNAL_STATIC_ASSERT(sized_sentinel_for, iterator_t<_Base>>); +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY( + _It._Parent != nullptr, "cannot compare transform_view sentinel with value-initialized iterator"); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _It._Current - _Last; + } + + public: + _Sentinel() = default; + constexpr explicit _Sentinel(sentinel_t<_Base> _Last_) noexcept( + is_nothrow_move_constructible_v>) // strengthened + : _Last(_STD move(_Last_)) {} + + // clang-format off + constexpr _Sentinel(_Sentinel _Se) + noexcept(is_nothrow_constructible_v, sentinel_t<_Vw>>) // strengthened + requires _Const && convertible_to, sentinel_t<_Base>> + : _Last(_STD move(_Se._Last)) {} + // clang-format on + + _NODISCARD constexpr sentinel_t<_Base> base() const + noexcept(is_nothrow_copy_constructible_v>) /* strengthened */ { + return _Last; + } + + _NODISCARD friend constexpr bool operator==(const _Iterator<_Const>& _Left, + const _Sentinel& _Right) noexcept(noexcept(_Right._Equal(_Left))) /* strengthened */ { + return _Right._Equal(_Left); + } + + _NODISCARD friend constexpr range_difference_t<_Base> + operator-(const _Iterator<_Const>& _Left, const _Sentinel& _Right) noexcept( + noexcept(_Right._Distance_to( + _Left))) /* strengthened */ requires sized_sentinel_for, iterator_t<_Base>> { + return _Right._Distance_to(_Left); + } + _NODISCARD friend constexpr range_difference_t<_Base> + operator-(const _Sentinel& _Left, const _Iterator<_Const>& _Right) noexcept(noexcept(_Left._Distance_to( + _Right))) /* strengthened */ requires sized_sentinel_for, iterator_t<_Base>> { + return -_Left._Distance_to(_Right); + } + }; + + public: + transform_view() = default; + constexpr transform_view(_Vw _Range_, _Fn _Fun_) noexcept( + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Fn>) // strengthened + : _Range(_STD move(_Range_)), _Fun{in_place, _STD move(_Fun_)} {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr _Iterator begin() noexcept( + noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ { + return _Iterator{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr _Iterator begin() const noexcept(noexcept( + _RANGES begin(_Range)) && is_nothrow_move_constructible_v>) /* strengthened */ +#ifdef __clang__ // TRANSITION, LLVM-47414 + requires _Can_const_transform<_Vw, _Fn> +#else // ^^^ workaround / no workaround vvv + requires range && regular_invocable> +#endif // TRANSITION, LLVM-47414 + { + // clang-format on + return _Iterator{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr auto end() noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v) /* strengthened */ { + // clang-format on + if constexpr (common_range<_Vw>) { + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + // clang-format off + _NODISCARD constexpr auto end() const noexcept(noexcept( + _RANGES end(_Range)) && is_nothrow_move_constructible_v) /* strengthened */ +#ifdef __clang__ // TRANSITION, LLVM-47414 + requires _Can_const_transform<_Vw, _Fn> +#else // ^^^ workaround / no workaround vvv + requires range && regular_invocable> +#endif // TRANSITION, LLVM-47414 + { + // clang-format on + if constexpr (common_range<_Vw>) { + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + _NODISCARD constexpr auto size() noexcept( + noexcept(_RANGES size(_Range))) /* strengthened */ requires sized_range<_Vw> { + return _RANGES size(_Range); + } + _NODISCARD constexpr auto size() const + noexcept(noexcept(_RANGES size(_Range))) /* strengthened */ requires sized_range { + return _RANGES size(_Range); + } + }; + +#undef _NOEXCEPT_IDL0 + + template + transform_view(_Rng&&, _Fn) -> transform_view, _Fn>; + + namespace views { + // VARIABLE views::transform + class _Transform_fn { + private: + template + struct _Partial : _Pipe::_Base<_Partial<_Fn>> { + /* [[no_unique_address]] */ _Semiregular_box<_Fn> _Fun; + + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( + transform_view{_STD forward<_Rng>(_Range), _STD move(*_Fun)})) requires requires { + transform_view{static_cast<_Rng&&>(_Range), _STD move(*_Fun)}; + } { + // clang-format on + return transform_view{_STD forward<_Rng>(_Range), _STD move(*_Fun)}; + } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Fun) const noexcept(noexcept( + transform_view{_STD forward<_Rng>(_Range), _STD move(_Fun)})) requires requires { + transform_view{static_cast<_Rng&&>(_Range), _STD move(_Fun)}; + } { + // clang-format on + return transform_view{_STD forward<_Rng>(_Range), _STD move(_Fun)}; + } + + template <_Copy_constructible_object _Fn> + _NODISCARD constexpr auto operator()(_Fn _Fun) const noexcept(is_nothrow_move_constructible_v<_Fn>) { + return _Partial<_Fn>{._Fun = {in_place, _STD move(_Fun)}}; + } + }; + + inline constexpr _Transform_fn transform; + } // namespace views + // CLASS TEMPLATE ranges::take_view template class take_view : public view_interface> { diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 9ddd8fd4ea8..ac70301e98f 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -415,7 +415,7 @@ namespace test { STATIC_ASSERT(always_false); } - friend void iter_swap(iterator const&, iterator const&) { + friend void iter_swap(iterator const&, iterator const&) requires std::is_same_v { STATIC_ASSERT(always_false); } @@ -441,8 +441,9 @@ namespace test { return std::move(*i.ptr_); } - constexpr friend void iter_swap(iterator const& x, iterator const& y) requires at_least { - ranges::iter_swap(x.ptr_, y.ptr_); + constexpr friend void iter_swap(iterator const& x, iterator const& y) + noexcept(std::is_nothrow_swappable_v) requires at_least && std::swappable { + ranges::swap(*x.ptr_, *y.ptr_); } // forward iterator operations: @@ -492,9 +493,14 @@ namespace test { [[nodiscard]] constexpr boolish operator>=(iterator const& that) const noexcept requires at_least { return !(*this < that); } + [[nodiscard]] constexpr auto operator<=>(iterator const& that) const noexcept requires at_least { + return ptr_ <=> that.ptr_; + } + [[nodiscard]] constexpr ReferenceType operator[](ptrdiff_t const n) const& noexcept requires at_least { return ReferenceType{ptr_[n]}; } + constexpr iterator& operator+=(ptrdiff_t const n) & noexcept requires at_least { ptr_ += n; return *this; @@ -503,6 +509,7 @@ namespace test { ptr_ -= n; return *this; } + [[nodiscard]] constexpr iterator operator+(ptrdiff_t const n) const noexcept requires at_least { return iterator{ptr_ + n}; } @@ -510,6 +517,7 @@ namespace test { requires at_least { return i + n; } + [[nodiscard]] constexpr iterator operator-(ptrdiff_t const n) const noexcept requires at_least { return iterator{ptr_ - n}; } diff --git a/tests/std/test.lst b/tests/std/test.lst index 7e6cc4ff99c..da7a77478ed 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -327,6 +327,8 @@ tests\P0896R4_views_filter_death tests\P0896R4_views_reverse tests\P0896R4_views_single tests\P0896R4_views_take +tests\P0896R4_views_transform +tests\P0896R4_views_transform_death tests\P0898R3_concepts tests\P0898R3_identity tests\P0912R5_coroutine diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 8e5eb5bd9b2..1605cb5ebbe 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -95,6 +95,7 @@ STATIC_ASSERT(test_cpo(ranges::views::filter)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); STATIC_ASSERT(test_cpo(ranges::views::take)); +STATIC_ASSERT(test_cpo(ranges::views::transform)); void test_cpo_ambiguity() { using namespace std::ranges; diff --git a/tests/std/tests/P0896R4_views_transform/env.lst b/tests/std/tests/P0896R4_views_transform/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_transform/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp new file mode 100644 index 00000000000..fb03a17c0f0 --- /dev/null +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -0,0 +1,716 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +#if _ITERATOR_DEBUG_LEVEL == 0 +#define NOEXCEPT_IDL0(...) noexcept(__VA_ARGS__) +#else +#define NOEXCEPT_IDL0(...) true +#endif // _ITERATOR_DEBUG_LEVEL == 0 + +// Test a silly precomposed range adaptor pipeline +constexpr auto add8 = [](const auto& x) noexcept { return x + 8; }; + +using Fun = remove_const_t; +STATIC_ASSERT(is_nothrow_copy_constructible_v&& is_nothrow_move_constructible_v); + +constexpr auto pipeline = + views::transform(add8) | views::transform(add8) | views::transform(add8) | views::transform(add8); + +template > +using pipeline_t = + ranges::transform_view, Fun>, Fun>, + Fun>; + +template +concept CanViewTransform = requires(Rng&& r) { + views::transform(static_cast(r), add8); +}; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::transform_view, ranges::bidirectional_range, ranges::common_range, ranges::contiguous_range, + ranges::enable_borrowed_range, ranges::forward_range, ranges::input_range, ranges::iterator_t, ranges::prev, + ranges::random_access_range, ranges::range, ranges::range_reference_t, ranges::sentinel_t; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using TV = transform_view; + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(!contiguous_range); + + // Validate range adaptor object and range adaptor closure + constexpr auto transform_incr = views::transform(add8); + + // ... with lvalue argument + STATIC_ASSERT(CanViewTransform == (!is_view || copyable) ); + if constexpr (CanViewTransform) { // Validate lvalue + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(rng, add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>); + STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewTransform&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(as_const(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = transform_view&>, Fun>; + constexpr bool is_noexcept = is_nothrow_constructible_v&, decltype((add8))>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(as_const(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewTransform> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(move(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = transform_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(move(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewTransform> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(as_const(rng), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = transform_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::transform(move(as_const(rng)), add8)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | transform_incr) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + const bool is_empty = ranges::empty(expected); + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as auto r = transform_view{forward(rng), add8}; + using R = decltype(r); + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(!contiguous_range); + + // Validate transform_view::size + STATIC_ASSERT(CanMemberSize == CanSize); + STATIC_ASSERT(CanSize == CanMemberSize); + STATIC_ASSERT(CanMemberSize == CanSize); + STATIC_ASSERT(CanSize == CanMemberSize); + if constexpr (CanMemberSize) { + assert(r.size() == static_cast(ranges::size(expected))); + if constexpr (CanMemberSize) { + assert(as_const(r).size() == static_cast(ranges::size(expected))); + } + } + + constexpr bool const_invocable = regular_invocable>; + + // Validate view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(r) == is_empty); + assert(static_cast(r) == !is_empty); + } + } + + STATIC_ASSERT(CanMemberEmpty == (forward_range && const_invocable)); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(as_const(r)) == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + } + + // Validate content + assert(ranges::equal(r, expected)); + + // Validate transform_view::begin + STATIC_ASSERT(CanMemberBegin); + STATIC_ASSERT(CanBegin == (range && const_invocable)); + if (forward_range) { // intentionally not if constexpr + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (CanBegin) { + const same_as> auto i3 = as_const(r).begin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Validate transform_view::end + STATIC_ASSERT(CanMemberEnd); + STATIC_ASSERT(CanEnd == (range && const_invocable)); + if (!is_empty) { + same_as> auto i = r.end(); + static_assert(is_same_v, iterator_t> == common_range); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i) == *prev(end(expected))); + } + + if constexpr (CanEnd) { + same_as> auto i2 = as_const(r).end(); + static_assert(is_same_v, iterator_t> == common_range); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i2) == *prev(end(expected))); + } + } + } + + // Validate view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + // Validate view_interface::front and back + if (!is_empty) { + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(r.front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + STATIC_ASSERT(CanMemberFront == (forward_range && const_invocable)); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); + } + + STATIC_ASSERT( + CanMemberBack == (bidirectional_range && common_range && const_invocable)); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate view_interface::operator[] + if (!is_empty) { + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(r[0] == *r.begin()); + } + + STATIC_ASSERT(CanIndex == (random_access_range && const_invocable)); + if constexpr (CanIndex) { + assert(as_const(r)[0] == *as_const(r).begin()); + } + } + + // Validate transform_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible && forward_range) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b1.end()) == 7); // NB: depends on the test data + } + } + } + + // Validate transform_view::base() && (NB: do this last since it leaves r moved-from) +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b2.end()) == 7); // NB: depends on the test data + } + } + } + + return true; +} + +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static constexpr int transformed_ints[] = {8, 9, 10, 11, 12, 13, 14, 15}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, transformed_ints); + } +}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + // The view is sensitive to category, commonality, size, and differencing, but oblivious to proxyness. + using test::Common, test::Sized; + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template +using move_only_view = test::range}, IsCommon, + test::CanCompare{derived_from}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +constexpr void move_only_view_tests() { + using test::Common, test::Sized; + + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); + test_one(move_only_view{some_ints}, transformed_ints); +} + +struct iterator_instantiator { + template + static constexpr void call() { + // Pre: Iter is a specialization of test::iterator whose element type is const int + int mutable_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const auto make_view = [&] { + return views::transform( + ranges::subrange{Iter{mutable_ints}, test::sentinel{ranges::end(mutable_ints)}}, add8); + }; + using R = decltype(make_view()); + using I = ranges::iterator_t; + using S = ranges::sentinel_t; + + // Validate nested types + STATIC_ASSERT(is_same_v, random_access_iterator_tag, + conditional_t, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>>); + + using C = typename iterator_traits::iterator_category; + STATIC_ASSERT(is_same_v>>, + conditional_t, random_access_iterator_tag, C>, + input_iterator_tag>>); + + { // Validate iterator special member functions and base + I defaultConstructed{}; + assert(std::move(defaultConstructed).base().peek() == nullptr); + STATIC_ASSERT(is_nothrow_default_constructible_v); + + auto r0 = make_view(); + I valueConstructed{r0, Iter{mutable_ints}}; + STATIC_ASSERT(is_nothrow_constructible_v); + + if constexpr (copyable) { + I copyConstructed{valueConstructed}; + assert(copyConstructed == valueConstructed); + STATIC_ASSERT(is_nothrow_copy_constructible_v); + + defaultConstructed = copyConstructed; + assert(defaultConstructed == valueConstructed); + STATIC_ASSERT(is_nothrow_copy_assignable_v); + } + assert(std::move(valueConstructed).base().peek() == mutable_ints); + + if constexpr (forward_iterator) { + auto r1 = make_view(); + const auto i = r1.begin(); + using CI = ranges::iterator_t; + CI conversionConstructed{i}; + assert(conversionConstructed.base().peek() == mutable_ints); + STATIC_ASSERT(is_nothrow_constructible_v); + } + } + + { // Validate sentinel constructors and base + S defaultConstructed{}; + assert(defaultConstructed.base().peek() == nullptr); + STATIC_ASSERT(is_nothrow_default_constructible_v); + + const test::sentinel s{mutable_ints + 2}; + S valueConstructed{s}; + assert(valueConstructed.base().peek() == s.peek()); + STATIC_ASSERT(is_nothrow_constructible_v&>); + + S copyConstructed{valueConstructed}; + assert(copyConstructed.base().peek() == valueConstructed.base().peek()); + STATIC_ASSERT(is_nothrow_copy_constructible_v); + + defaultConstructed = copyConstructed; + assert(defaultConstructed.base().peek() == valueConstructed.base().peek()); + STATIC_ASSERT(is_nothrow_copy_assignable_v); + + if constexpr (forward_iterator && indirectly_swappable) { + auto r = make_view(); + using CS = ranges::sentinel_t; + const S last = r.end(); + CS conversionConstructed{last}; + assert(conversionConstructed.base().peek() == ranges::end(mutable_ints)); + STATIC_ASSERT(is_nothrow_constructible_v); + } + } + + { // Validate dereference ops + auto r0 = make_view(); + auto i0 = r0.begin(); + assert(*i0 == add8(mutable_ints[0])); + STATIC_ASSERT(NOEXCEPT_IDL0(*i0)); + + assert(ranges::iter_move(i0) == add8(mutable_ints[0])); // NB: moving from int leaves it unchanged + STATIC_ASSERT(NOEXCEPT_IDL0(ranges::iter_move(i0))); + + if constexpr (forward_iterator) { + auto i1 = ranges::next(i0); + ranges::iter_swap(i0, i1); + assert(mutable_ints[0] == 1); + assert(mutable_ints[1] == 0); + ranges::iter_swap(i1, i0); + assert(mutable_ints[0] == 0); + assert(mutable_ints[1] == 1); + STATIC_ASSERT(NOEXCEPT_IDL0(ranges::iter_swap(i0, i1))); + } + } + + { // Validate increments + auto r0 = make_view(); + auto i0 = r0.begin(); + assert(&++i0 == &i0); + assert(std::move(i0).base().peek() == mutable_ints + 1); + STATIC_ASSERT(noexcept(++i0)); + + auto r1 = make_view(); + auto i1 = r1.begin(); + if constexpr (forward_iterator) { + assert(i1++ == r1.begin()); + } else { + i1++; + } + assert(std::move(i1).base().peek() == mutable_ints + 1); + STATIC_ASSERT(noexcept(i0++)); + } + + if constexpr (bidirectional_iterator) { // Validate decrements + auto r = make_view(); + const auto second = ranges::next(r.begin()); + auto i = second; + assert(&--i == &i); + assert(i.base().peek() == mutable_ints); + STATIC_ASSERT(noexcept(--i)); + + i = second; + assert(i-- == second); + assert(i.base().peek() == mutable_ints); + STATIC_ASSERT(noexcept(i--)); + } + + if constexpr (random_access_iterator) { // Validate seek operations and [] + auto r = make_view(); + auto i = r.begin(); + assert((i + 2).base().peek() == mutable_ints + 2); + assert((I{} + 0) == I{}); + STATIC_ASSERT(NOEXCEPT_IDL0(i + 2)); + + assert((2 + i).base().peek() == mutable_ints + 2); + assert((0 + I{}).base().peek() == nullptr); + STATIC_ASSERT(NOEXCEPT_IDL0(2 + i)); + + auto vi = I{}; + assert(&(i += 5) == &i); + assert(i.base().peek() == mutable_ints + 5); + assert(&(vi += 0) == &vi); + assert(vi.base().peek() == nullptr); + STATIC_ASSERT(NOEXCEPT_IDL0(i += 5)); + + assert((i - 2).base().peek() == mutable_ints + 3); + assert((I{} - 0).base().peek() == nullptr); + STATIC_ASSERT(NOEXCEPT_IDL0(i - 2)); + + assert(&(i -= 3) == &i); + assert(i.base().peek() == mutable_ints + 2); + assert(&(vi -= 0) == &vi); + assert(vi.base().peek() == nullptr); + STATIC_ASSERT(NOEXCEPT_IDL0(i -= 3)); + + assert(i[4] == add8(mutable_ints[6])); + STATIC_ASSERT(NOEXCEPT_IDL0(i[4])); + } + + if constexpr (equality_comparable) { + // Validate == and != + auto r = make_view(); + const auto first = r.begin(); + const auto last = r.end(); + + assert(first == first); + assert(I{} == I{}); + STATIC_ASSERT(noexcept(first == first)); + + assert(!(first == last)); + STATIC_ASSERT(noexcept(first == last)); + assert(!(last == first)); + STATIC_ASSERT(noexcept(last == first)); + + assert(!(first != first)); + assert(!(I{} != I{})); + STATIC_ASSERT(noexcept(first != first)); + + if constexpr (forward_iterator) { + const auto final = ranges::next(first, last); + assert(!(first == final)); + assert(first != final); + + assert(last == final); + assert(final == last); + + assert(!(last != final)); + assert(!(final != last)); + + if constexpr (sized_sentinel_for) { // Validate difference + assert(first - first == 0); + assert(final - first == ranges::ssize(mutable_ints)); + assert(first - final == -ranges::ssize(mutable_ints)); + assert(I{} - I{} == 0); + STATIC_ASSERT(noexcept(first - first)); + } + + if constexpr (sized_sentinel_for, Iter>) { + assert(last - first == ranges::ssize(mutable_ints)); + assert(first - last == -ranges::ssize(mutable_ints)); + STATIC_ASSERT(noexcept(last - first)); + STATIC_ASSERT(noexcept(first - last)); + } + + if constexpr (random_access_iterator) { // Validate relational operators + assert(!(first < first)); + assert(first < final); + assert(!(final < first)); + assert(!(I{} < I{})); + STATIC_ASSERT(noexcept(first < final)); + + assert(!(first > first)); + assert(!(first > final)); + assert(final > first); + assert(!(I{} > I{})); + STATIC_ASSERT(noexcept(first > final)); + + assert(first <= first); + assert(first <= final); + assert(!(final <= first)); + assert(I{} <= I{}); + STATIC_ASSERT(noexcept(first <= final)); + + assert(first >= first); + assert(!(first >= final)); + assert(final >= first); + assert(I{} >= I{}); + STATIC_ASSERT(noexcept(first >= final)); + + if constexpr (three_way_comparable) { // Validate spaceship + assert((first <=> first) == strong_ordering::equal); + assert((first <=> final) == strong_ordering::less); + assert((final <=> first) == strong_ordering::greater); + assert((I{} <=> I{}) == strong_ordering::equal); + STATIC_ASSERT(noexcept(first <=> final)); + } + } + } + } + } +}; + +template +using test_iterator = + test::iterator}, + test::ProxyRef{!derived_from}>; + +constexpr void iterator_instantiation_test() { + using test::CanDifference; + + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); + + iterator_instantiator::call>(); + iterator_instantiator::call>(); +} + +int main() { + { // Validate copyable views + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, transformed_ints)); + test_one(s, transformed_ints); + } + + // Validate move-only views + STATIC_ASSERT((move_only_view_tests(), true)); + move_only_view_tests(); + + { // Validate non-views + STATIC_ASSERT(test_one(some_ints, transformed_ints)); + test_one(some_ints, transformed_ints); + + { + vector vec(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(vec, transformed_ints); + } + { + forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(lst, transformed_ints); + } + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); + } + + { // Validate a non-view borrowed range + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, transformed_ints)); + test_one(s, transformed_ints); + } + + STATIC_ASSERT((iterator_instantiation_test(), true)); + iterator_instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_transform_death/env.lst b/tests/std/tests/P0896R4_views_transform_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0896R4_views_transform_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_transform_death/test.cpp b/tests/std/tests/P0896R4_views_transform_death/test.cpp new file mode 100644 index 00000000000..5dfa72da1b3 --- /dev/null +++ b/tests/std/tests/P0896R4_views_transform_death/test.cpp @@ -0,0 +1,395 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include + +#include +using namespace std; + +static int some_ints[] = {0, 1, 2, 3}; + +[[maybe_unused]] constexpr auto lambda = [x = 42](int) { return x == 42; }; +using TV = decltype(ranges::transform_view{some_ints, lambda}); + +void test_constructor_wrong_range() { + vector vec0{0, 1, 2, 3}; + vector vec1{4, 5, 6, 7}; + auto r0 = views::transform(vec0, lambda); + using R = decltype(r0); + same_as auto r1 = views::transform(vec1, lambda); + ranges::iterator_t i{r0, r1.begin().base()}; // vector iterators in range are from different containers +} + +void test_operator_star_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) (*i); // cannot dereference value-initialized transform_view iterator +} + +void test_operator_star_end_iterator() { + TV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + (void) (*i); // cannot dereference end transform_view iterator +} + +void test_operator_preincrement_value_initialized_iterator() { + ranges::iterator_t i{}; + ++i; // cannot increment value-initialized transform_view iterator +} + +void test_operator_preincrement_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + ++i; // cannot increment transform_view iterator past end +} + +void test_operator_postincrement_value_initialized_iterator() { + ranges::iterator_t i{}; + i++; // cannot increment value-initialized transform_view iterator +} + +void test_operator_postincrement_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = ranges::next(r.begin(), r.end()); + i++; // cannot increment transform_view iterator past end +} + +void test_operator_predecrement_value_initialized_iterator() { + ranges::iterator_t i{}; + --i; // cannot decrement value-initialized transform_view iterator +} + +void test_operator_predecrement_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + --i; // cannot decrement transform_view iterator before begin +} + +void test_operator_postdecrement_value_initialized_iterator() { + ranges::iterator_t i{}; + i--; // cannot decrement value-initialized transform_view iterator +} + +void test_operator_postdecrement_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + i--; // cannot decrement transform_view iterator before begin +} + +void test_operator_seek_forward_value_initialized_iterator() { + ranges::iterator_t i{}; + i += 42; // cannot seek value-initialized transform_view iterator +} + +void test_operator_seek_forward_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + i += ranges::distance(some_ints); // cannot seek transform_view iterator past end +} + +void test_operator_seek_backward_value_initialized_iterator() { + ranges::iterator_t i{}; + i -= 42; // cannot seek value-initialized transform_view iterator +} + +void test_operator_seek_backward_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + i -= ranges::distance(some_ints); // cannot seek transform_view iterator before begin +} + +void test_operator_bracket_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) i[42]; // cannot seek value-initialized transform_view iterator +} + +void test_operator_bracket_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) i[-ranges::distance(some_ints)]; // cannot seek transform_view iterator before begin +} + +void test_operator_bracket_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) i[ranges::distance(some_ints)]; // cannot seek transform_view iterator after end +} + +void test_operator_equal_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 == i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_equal_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i == ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_not_equal_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 != i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_not_equal_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i != ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_less_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 < i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_less_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i < ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_greater_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 > i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_greater_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i > ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_less_equal_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 <= i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_less_equal_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i <= ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_greater_equal_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 >= i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_greater_equal_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i >= ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_spaceship_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 <=> i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_spaceship_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i <=> ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_operator_plus_value_initialized() { + ranges::iterator_t i{}; + (void) (i + 42); // cannot seek value-initialized transform_view iterator +} + +void test_operator_plus_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (i + -ranges::distance(some_ints)); // cannot seek transform_view iterator before begin +} + +void test_operator_plus_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (i + ranges::distance(some_ints)); // cannot seek transform_view iterator after end +} + +void test_flipped_operator_plus_value_initialized() { + ranges::iterator_t i{}; + (void) (42 + i); // cannot seek value-initialized transform_view iterator +} + +void test_flipped_operator_plus_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (-ranges::distance(some_ints) + i); // cannot seek transform_view iterator before begin +} + +void test_flipped_operator_plus_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (ranges::distance(some_ints) + i); // cannot seek transform_view iterator after end +} + +void test_operator_minus_value_initialized() { + ranges::iterator_t i{}; + (void) (i - 42); // cannot seek value-initialized transform_view iterator +} + +void test_operator_minus_before_begin() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (i - ranges::distance(some_ints)); // cannot seek transform_view iterator before begin +} + +void test_operator_minus_after_end() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin() + ranges::distance(some_ints) / 2; + (void) (i - -ranges::distance(some_ints)); // cannot seek transform_view iterator after end +} + +void test_operator_minus_incompatible_different() { + TV r0{some_ints, lambda}; + ranges::iterator_t i0 = r0.begin(); + TV r1{some_ints, lambda}; + ranges::iterator_t i1 = r1.begin(); + (void) (i0 - i1); // cannot compare incompatible transform_view iterators +} + +void test_operator_minus_incompatible_value_initialized() { + TV r{some_ints, lambda}; + ranges::iterator_t i = r.begin(); + (void) (i - ranges::iterator_t{}); // cannot compare incompatible transform_view iterators +} + +void test_iter_move_value_initialized_iterator() { + ranges::iterator_t i{}; + (void) ranges::iter_move(i); // cannot dereference value-initialized transform_view iterator +} + +void test_iter_swap_value_initialized_iterators() { + ranges::iterator_t i0{}; + ranges::iterator_t i1{}; + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized transform_view iterator +} + +void test_iter_swap_value_initialized_iterator_left() { + ranges::iterator_t i0{}; + TV r{some_ints, lambda}; + ranges::iterator_t i1 = r.begin(); + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized transform_view iterator +} + +void test_iter_swap_value_initialized_iterator_right() { + TV r{some_ints, lambda}; + ranges::iterator_t i0 = r.begin(); + ranges::iterator_t i1{}; + (void) ranges::iter_swap(i0, i1); // cannot dereference value-initialized transform_view iterator +} + +void test_sentinel_compare_value_initialized() { + auto r = ranges::subrange{counted_iterator{some_ints, ranges::distance(some_ints)}, default_sentinel} + | views::transform(lambda); + using R = decltype(r); + static_assert(!ranges::common_range); + (void) (ranges::iterator_t{} == r.end()); +} + +void test_sentinel_difference_value_initialized() { + auto r = ranges::subrange{counted_iterator{some_ints, ranges::distance(some_ints)}, default_sentinel} + | views::transform(lambda); + using R = decltype(r); + static_assert(!ranges::common_range); + static_assert(sized_sentinel_for, ranges::iterator_t>); + (void) (ranges::iterator_t{} - r.end()); +} + +void test_flipped_sentinel_difference_value_initialized() { + auto r = ranges::subrange{counted_iterator{some_ints, ranges::distance(some_ints)}, default_sentinel} + | views::transform(lambda); + using R = decltype(r); + static_assert(!ranges::common_range); + static_assert(sized_sentinel_for, ranges::iterator_t>); + (void) (r.end() - ranges::iterator_t{}); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec([] {}); + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_constructor_wrong_range, + test_operator_star_value_initialized_iterator, + test_operator_star_end_iterator, + test_operator_preincrement_value_initialized_iterator, + test_operator_preincrement_after_end, + test_operator_postincrement_value_initialized_iterator, + test_operator_postincrement_after_end, + test_operator_predecrement_value_initialized_iterator, + test_operator_predecrement_before_begin, + test_operator_postdecrement_value_initialized_iterator, + test_operator_postdecrement_before_begin, + test_operator_seek_forward_value_initialized_iterator, + test_operator_seek_forward_after_end, + test_operator_seek_backward_value_initialized_iterator, + test_operator_seek_backward_before_begin, + test_operator_bracket_value_initialized_iterator, + test_operator_bracket_before_begin, + test_operator_bracket_after_end, + test_operator_equal_incompatible_different, + test_operator_equal_incompatible_value_initialized, + test_operator_not_equal_incompatible_different, + test_operator_not_equal_incompatible_value_initialized, + test_operator_less_incompatible_different, + test_operator_less_incompatible_value_initialized, + test_operator_greater_incompatible_different, + test_operator_greater_incompatible_value_initialized, + test_operator_less_equal_incompatible_different, + test_operator_less_equal_incompatible_value_initialized, + test_operator_greater_equal_incompatible_different, + test_operator_greater_equal_incompatible_value_initialized, + test_operator_spaceship_incompatible_different, + test_operator_spaceship_incompatible_value_initialized, + test_operator_plus_value_initialized, + test_operator_plus_before_begin, + test_operator_plus_after_end, + test_flipped_operator_plus_value_initialized, + test_flipped_operator_plus_before_begin, + test_flipped_operator_plus_after_end, + test_operator_minus_value_initialized, + test_operator_minus_before_begin, + test_operator_minus_after_end, + test_operator_minus_incompatible_different, + test_operator_minus_incompatible_value_initialized, + test_iter_move_value_initialized_iterator, + test_iter_swap_value_initialized_iterators, + test_iter_swap_value_initialized_iterator_left, + test_iter_swap_value_initialized_iterator_right, + test_sentinel_compare_value_initialized, + test_sentinel_difference_value_initialized, + test_flipped_sentinel_difference_value_initialized, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +}