From 92a97355512ec69023b5f3cc96ffd98c27d35f02 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 20 Feb 2023 22:41:36 +0100 Subject: [PATCH 01/43] Feature test macro --- stl/inc/yvals_core.h | 35 ++++++++++--------- .../test.compile.pass.cpp | 14 ++++++++ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 1a4168eba2..9b000e6046 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -337,6 +337,7 @@ // P2321R2 zip // (missing views::adjacent_transform) // P2322R6 ranges::fold_left, ranges::fold_right, Etc. +// P2374R4 views::cartesian_product // P2387R3 Pipe Support For User-Defined Range Adaptors // P2404R3 Move-Only Types For Comparison Concepts // P2417R2 More constexpr bitset @@ -353,6 +354,7 @@ // P2494R2 Relaxing Range Adaptors To Allow Move-Only Types // P2499R0 string_view Range Constructor Should Be explicit // P2505R5 Monadic Functions For expected +// P2540R1 Empty Product For Certain Views // P2549R1 unexpected::error() // P2652R2 Disallowing User Specialization Of allocator_traits @@ -1723,22 +1725,23 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_move_only_function 202110L #ifdef __cpp_lib_concepts -#define __cpp_lib_out_ptr 202106L -#define __cpp_lib_ranges_as_const 202207L -#define __cpp_lib_ranges_as_rvalue 202207L -#define __cpp_lib_ranges_chunk 202202L -#define __cpp_lib_ranges_chunk_by 202202L -#define __cpp_lib_ranges_contains 202207L -#define __cpp_lib_ranges_enumerate 202302L -#define __cpp_lib_ranges_find_last 202207L -#define __cpp_lib_ranges_fold 202207L -#define __cpp_lib_ranges_iota 202202L -#define __cpp_lib_ranges_join_with 202202L -#define __cpp_lib_ranges_repeat 202207L -#define __cpp_lib_ranges_slide 202202L -#define __cpp_lib_ranges_starts_ends_with 202106L -#define __cpp_lib_ranges_stride 202207L -#define __cpp_lib_ranges_to_container 202202L +#define __cpp_lib_out_ptr 202106L +#define __cpp_lib_ranges_as_const 202207L +#define __cpp_lib_ranges_as_rvalue 202207L +#define __cpp_lib_ranges_cartesian_product 202207L +#define __cpp_lib_ranges_chunk 202202L +#define __cpp_lib_ranges_chunk_by 202202L +#define __cpp_lib_ranges_contains 202207L +#define __cpp_lib_ranges_enumerate 202302L +#define __cpp_lib_ranges_find_last 202207L +#define __cpp_lib_ranges_fold 202207L +#define __cpp_lib_ranges_iota 202202L +#define __cpp_lib_ranges_join_with 202202L +#define __cpp_lib_ranges_repeat 202207L +#define __cpp_lib_ranges_slide 202202L +#define __cpp_lib_ranges_starts_ends_with 202106L +#define __cpp_lib_ranges_stride 202207L +#define __cpp_lib_ranges_to_container 202202L #endif // __cpp_lib_concepts #define __cpp_lib_spanstream 202106L diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 1e545ab4f6..9fc1308938 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1580,6 +1580,20 @@ STATIC_ASSERT(__cpp_lib_ranges_as_rvalue == 202207L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges_cartesian_product +#error __cpp_lib_ranges_cartesian_product is not defined +#elif __cpp_lib_ranges_cartesian_product != 202207L +#error __cpp_lib_ranges_cartesian_product is not 202207L +#else +STATIC_ASSERT(__cpp_lib_ranges_cartesian_product == 202207L); +#endif +#else +#ifdef __cpp_lib_ranges_cartesian_product +#error __cpp_lib_ranges_cartesian_product is defined +#endif +#endif + #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_ranges_chunk #error __cpp_lib_ranges_chunk is not defined From d7ea13ddc3f1e81077d1e4b251ead4f07cfac3cb Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 11 Mar 2023 13:31:25 +0100 Subject: [PATCH 02/43] Implement `views::cartesian_product` --- stl/inc/ranges | 626 ++++++++++ tests/std/test.lst | 2 + .../P2374R4_views_cartesian_product/env.lst | 4 + .../P2374R4_views_cartesian_product/test.cpp | 1047 +++++++++++++++++ .../env.lst | 4 + .../test.cpp | 89 ++ 6 files changed, 1772 insertions(+) create mode 100644 tests/std/tests/P2374R4_views_cartesian_product/env.lst create mode 100644 tests/std/tests/P2374R4_views_cartesian_product/test.cpp create mode 100644 tests/std/tests/P2374R4_views_cartesian_product_death/env.lst create mode 100644 tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp diff --git a/stl/inc/ranges b/stl/inc/ranges index 5835189800..a661f18d71 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9169,6 +9169,632 @@ namespace ranges { _EXPORT_STD inline constexpr _Adjacent_fn<2> pairwise; } // namespace views + template <_Integer_like _Int> + _NODISCARD constexpr bool _Add_with_overflow_check(_Int _Left, _Int _Right, _Int& _Out) { +#ifdef __clang__ + if constexpr (integral<_Int>) { + return __builtin_add_overflow(_Left, _Right, &_Out); + } else +#endif // __clang__ + { + if constexpr (!_Signed_integer_like<_Int>) { + _Out = _Left + _Right; + return _Out < _Left || _Out < _Right; + } else { + using _UInt = _Make_unsigned_like_t<_Int>; + _Out = static_cast<_Int>(static_cast<_UInt>(_Left) + static_cast<_UInt>(_Right)); + return (_Left > 0 && _Right > 0 && _Out <= 0) || (_Left < 0 && _Right < 0 && _Out >= 0); + } + } + } + + template <_Integer_like _Int> + _NODISCARD constexpr bool _Multiply_with_overflow_check(_Int _Left, _Int _Right, _Int& _Out) { +#ifdef __clang__ + if constexpr (integral<_Int>) { + return __builtin_mul_overflow(_Left, _Right, &_Out); + } else +#endif // __clang__ + { + if constexpr (!_Signed_integer_like<_Int>) { + _Out = _Left * _Right; + return _Left != 0 && _Right != 0 && (_Out < _Left || _Out < _Right); + } else { + // Based on llvm::MulOverflow + // See https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Support/MathExtras.h + using _UInt = _Make_unsigned_like_t<_Int>; + const _UInt _ULeft = _Left < 0 ? (0 - static_cast<_UInt>(_Left)) : static_cast<_UInt>(_Left); + const _UInt _URight = _Right < 0 ? (0 - static_cast<_UInt>(_Right)) : static_cast<_UInt>(_Right); + const _UInt _UResult = _ULeft * _URight; + + const bool _Negative = (_Left < 0) ^ (_Right < 0); + _Out = static_cast<_Int>(_Negative ? (0 - _UResult) : _UResult); + if (_ULeft == 0 || _URight == 0) { + return false; + } + + if (_Negative) { + return _ULeft > (static_cast<_UInt>((numeric_limits<_Int>::max)()) + _UInt{1}) / _URight; + } else { + return _ULeft > static_cast<_UInt>((numeric_limits<_Int>::max)()) / _URight; + } + } + } + } + + template + concept _Cartesian_product_is_random_access = (random_access_range<_Maybe_const<_Const, _First>> && ... + && (random_access_range<_Maybe_const<_Const, _Rest>> + && sized_range<_Maybe_const<_Const, _Rest>>) ); + + template + concept _Cartesian_product_common_arg = common_range<_Rng> || (sized_range<_Rng> && random_access_range<_Rng>); + + template + concept _Cartesian_product_is_bidirectional = (bidirectional_range<_Maybe_const<_Const, _First>> && ... + && (bidirectional_range<_Maybe_const<_Const, _Rest>> + && _Cartesian_product_common_arg<_Maybe_const<_Const, _Rest>>) ); + + template + concept _Cartesian_product_is_common = _Cartesian_product_common_arg<_First>; + + template + concept _Cartesian_product_is_sized = (sized_range<_Rngs> && ...); + + template class _FirstSent, class _First, class... _Rest> + concept _Cartesian_is_sized_sentinel = + (sized_sentinel_for<_FirstSent<_Maybe_const<_Const, _First>>, iterator_t<_Maybe_const<_Const, _First>>> && ... + && (sized_range<_Maybe_const<_Const, _Rest>> + && sized_sentinel_for>, + iterator_t<_Maybe_const<_Const, _Rest>>>) ); + + template <_Cartesian_product_common_arg _Rng> + _NODISCARD constexpr auto _Cartesian_common_arg_end(_Rng& _Range) { + if constexpr (common_range<_Rng>) { + return _RANGES end(_Range); + } else { + return _RANGES begin(_Range) + _RANGES distance(_Range); + } + } + + template + inline constexpr auto _Compile_time_max_size = (numeric_limits>::max)(); + + template + concept _Constant_sized_range = + sized_range<_Ty> && requires { typename _Require_constant::size()>; }; + + template <_Constant_sized_range _Ty> + inline constexpr auto _Compile_time_max_size<_Ty> = remove_reference_t<_Ty>::size(); + + template + inline constexpr auto _Compile_time_max_size> = _Size; + + template + inline constexpr auto _Compile_time_max_size> = _Size; + + template + requires (_Extent != dynamic_extent) + inline constexpr auto _Compile_time_max_size> = _Extent; + + template + requires (_Extent != dynamic_extent) + inline constexpr auto _Compile_time_max_size> = _Extent; + + template + inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size<_Ty>; + + template + requires sized_range + inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size; + + template + inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size<_Ty>; + + template + requires sized_range + inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size; + + template + _NODISCARD consteval int _Cartesian_product_max_size_bit_width() noexcept { + if constexpr (sized_range<_Rng>) { + if constexpr (requires(range_size_t<_Rng> _Val) { _STD bit_width(_Val); }) { + return _STD bit_width(_Compile_time_max_size<_Rng>); + } else { + return numeric_limits>::digits; + } + } else { + return numeric_limits<_Make_unsigned_like_t>>::digits; + } + } + + template + _NODISCARD static consteval auto _Cartesian_product_optimal_size_type() noexcept { + constexpr int _Optimal_size_type_bit_width = + (_Cartesian_product_max_size_bit_width<_First>() + ... + _Cartesian_product_max_size_bit_width<_Rest>()); + if constexpr (_Optimal_size_type_bit_width <= 8) { + return uint_least8_t{}; + } else if constexpr (_Optimal_size_type_bit_width <= 16) { + return uint_least16_t{}; + } else if constexpr (_Optimal_size_type_bit_width <= 32) { + return uint_least32_t{}; + } else if constexpr (_Optimal_size_type_bit_width <= 64) { + return uint_least64_t{}; + } else { + return _Unsigned128{}; + } + } + + _EXPORT_STD template + requires (view<_First> && ... && view<_Rest>) + class cartesian_product_view : public view_interface> { + private: + template + using _Optimal_size_type = decltype(_Cartesian_product_optimal_size_type<_Maybe_const<_Const, _First>, + _Maybe_const<_Const, _Rest>...>()); + + template + using _Difference_type = common_type_t<_Make_signed_like_t<_Optimal_size_type<_Const>>, + range_difference_t<_Maybe_const<_Const, _First>>, range_difference_t<_Maybe_const<_Const, _Rest>>...>; + + template + using _Size_type = common_type_t<_Optimal_size_type<_Const>, range_size_t<_Maybe_const<_Const, _First>>, + range_size_t<_Maybe_const<_Const, _Rest>>...>; + + /* [[no_unique_address]] */ tuple<_First, _Rest...> _Bases; + + template + class _Iterator { + private: + friend cartesian_product_view; + using _Parent_t = _Maybe_const<_Const, cartesian_product_view>; + + _Parent_t* _Parent = nullptr; + tuple>, iterator_t<_Maybe_const<_Const, _Rest>>...> _Current; + + template + constexpr void _Next() { + auto& _It = _STD get<_Index>(_Current); + ++_It; + if constexpr (_Index > 0) { + auto& _Range = _STD get<_Index>(_Parent->_Bases); + if (_It == _RANGES end(_Range)) { + _It = _RANGES begin(_Range); + _Next<_Index - 1>(); + } + } + } + + template + constexpr void _Prev() { + auto& _It = _STD get<_Index>(_Current); + if constexpr (_Index > 0) { + auto& _Range = _STD get<_Index>(_Parent->_Bases); + if (_It == _RANGES begin(_Range)) { + _It = _Cartesian_common_arg_end(_Range); + _Prev<_Index - 1>(); + } + } + --_It; + } + + template + _NODISCARD constexpr bool _Entire_tail_at_begin(index_sequence<_Indices...>) const { + return ((_STD get<_Indices + 1>(_Current) == _RANGES begin(_STD get<_Indices + 1>(_Parent->_Bases))) + && ...); + } + + template + constexpr void _Advance(const _Difference_type<_Const> _Off) { + if (_Off == 0) { + return; + } + + auto& _Range = _STD get<_Index>(_Parent->_Bases); + auto& _It = _STD get<_Index>(_Current); + using _Rng = remove_reference_t; + using _Iter = remove_reference_t; + + if constexpr (_Index > 0) { + _STL_INTERNAL_STATIC_ASSERT(sized_range<_Rng>); + const auto _Size = static_cast<_Difference_type<_Const>>(_RANGES ssize(_Range)); + const auto _Begin = _RANGES begin(_Range); + const auto _It_off = static_cast<_Difference_type<_Const>>(_It - _Begin); + const auto _It_new_off = static_cast<_Difference_type<_Const>>((_It_off + _Off) % _Size); + const auto _Next_off = static_cast<_Difference_type<_Const>>((_It_off + _Off) / _Size); + if (_It_new_off < 0) { + _It = _Begin + static_cast>(_It_new_off + _Size); + _Advance<_Index - 1>(_Next_off - 1); + } else { + _It = _Begin + static_cast>(_It_new_off); + _Advance<_Index - 1>(_Next_off); + } + } else { +#if _ITERATOR_DEBUG_LEVEL != 0 + const auto _It_off = static_cast<_Difference_type<_Const>>(_It - _RANGES begin(_Range)); + _STL_VERIFY(_It_off + _Off >= 0, "Cannot advance cartesian_product_view iterator before begin " + "(N4928 [range.cartesian.iterator]/19)."); + if constexpr (sized_range<_Rng>) { + const auto _Range_size = static_cast<_Difference_type<_Const>>(_RANGES ssize(_Range)); + _STL_VERIFY(_It_off + _Off < _Range_size + || (_It_off + _Off == _Range_size + && _Entire_tail_at_begin(make_index_sequence{})), + "Cannot advance cartesian_product_view iterator past end (N4928 " + "[range.cartesian.iterator]/19)."); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _It += static_cast>(_Off); + } + } + + template + _NODISCARD constexpr bool _Is_end(index_sequence<_Indices...>) const { + return ((_STD get<_Indices>(_Current) == _RANGES end(_STD get<_Indices>(_Parent->_Bases))) || ...); + } + + template + _NODISCARD constexpr auto _End_tuple(index_sequence<_Indices...>) const { + return tuple{_RANGES end(_STD get<0>(_Parent->_Bases)), + _RANGES begin(_STD get<_Indices + 1>(_Parent->_Bases))...}; + } + + template + _NODISCARD constexpr _Difference_type<_Const> _Scaled_size() const { + if constexpr (_Index <= sizeof...(_Rest)) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Difference_type<_Const> _Product{}; + const bool _Overflow = _Multiply_with_overflow_check( + static_cast<_Difference_type<_Const>>(_RANGES size(_STD get<_Index>(_Parent->_Bases))), + _Scaled_size<_Index + 1>(), _Product); + _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by _Difference_type (N4928 " + "[range.cartesian.iterator]/8)."); + return _Product; +#else // ^^^ _ITERATOR_DEBUG_LEVEL != 0 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return static_cast<_Difference_type<_Const>>(_RANGES size(_STD get<_Index>(_Parent->_Bases))) + * _Scaled_size<_Index + 1>(); +#endif //_ITERATOR_DEBUG_LEVEL == 0 + } else { + return static_cast<_Difference_type<_Const>>(1); + } + } + + template + _NODISCARD constexpr _Difference_type<_Const> _Scaled_distance(const _Tuple& _Tpl) const { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Difference_type<_Const> _Product{}; + const bool _Overflow = _Multiply_with_overflow_check( + static_cast<_Difference_type<_Const>>(_STD get<_Index>(_Current) - _STD get<_Index>(_Tpl)), + _Scaled_size<_Index + 1>(), _Product); + _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by _Difference_type (N4928 " + "[range.cartesian.iterator]/8)."); + return _Product; +#else // ^^^ _ITERATOR_DEBUG_LEVEL != 0 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return static_cast<_Difference_type<_Const>>(_STD get<_Index>(_Current) - _STD get<_Index>(_Tpl)) + * _Scaled_size<_Index + 1>(); +#endif //_ITERATOR_DEBUG_LEVEL == 0 + } + + template + _NODISCARD constexpr _Difference_type<_Const> _Scaled_sum( + const _Tuple& _Tpl, index_sequence<_Indices...>) const { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Difference_type<_Const> _Sum{}; + const bool _Overflow = (_Add_with_overflow_check(_Sum, _Scaled_distance<_Indices>(_Tpl), _Sum) || ...); + _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by _Difference_type (N4928 " + "[range.cartesian.iterator]/8)."); + return _Sum; +#else // ^^^ _ITERATOR_DEBUG_LEVEL != 0 / _ITERATOR_DEBUG_LEVEL == 0 vvv + return (_Scaled_distance<_Indices>(_Tpl) + ...); +#endif //_ITERATOR_DEBUG_LEVEL == 0 + } + + template + _NODISCARD constexpr _Difference_type<_Const> _Distance_from(const _Tuple& _Tpl) const { + return _Scaled_sum(_Tpl, make_index_sequence<1 + sizeof...(_Rest)>{}); + } + + template + _NODISCARD static _CONSTEVAL bool _Is_iter_move_nothrow(index_sequence<_Indices...>) noexcept { + return conjunction_v< + is_nothrow_move_constructible>>, + is_nothrow_move_constructible>>...> + && (noexcept(_RANGES iter_move(_STD get<_Indices>(_STD declval()._Current))) + && ...); + } + + template + _NODISCARD static _CONSTEVAL bool _Is_iter_swap_nothrow(index_sequence<_Indices...>) noexcept { + return (noexcept(_RANGES iter_swap(_STD get<_Indices>(_STD declval()._Current), + _STD get<_Indices>(_STD declval()._Current))) + && ...); + } + + constexpr _Iterator(_Parent_t& _Parent_, + tuple>, iterator_t<_Maybe_const<_Const, _Rest>>...> _Current_) + : _Parent(_STD addressof(_Parent_)), _Current(_STD move(_Current_)) {} + + public: + using iterator_category = input_iterator_tag; + using iterator_concept = conditional_t<_Cartesian_product_is_random_access<_Const, _First, _Rest...>, + random_access_iterator_tag, + conditional_t<_Cartesian_product_is_bidirectional<_Const, _First, _Rest...>, bidirectional_iterator_tag, + conditional_t>, forward_iterator_tag, + input_iterator_tag>>>; + using value_type = + tuple>, range_value_t<_Maybe_const<_Const, _Rest>>...>; + using reference = tuple>, + range_reference_t<_Maybe_const<_Const, _Rest>>...>; + using difference_type = _Difference_type<_Const>; + + _Iterator() = default; + + constexpr _Iterator(_Iterator _It) + requires _Const + && (convertible_to, iterator_t> && ... + && convertible_to, iterator_t>) + : _Parent(_It._Parent), _Current(_STD move(_It._Current)) {} + + _NODISCARD constexpr auto operator*() const { + return _RANGES _Tuple_transform([](auto& _It) -> decltype(auto) { return *_It; }, _Current); + } + + constexpr _Iterator& operator++() { + _Next(); + return *this; + } + + constexpr void operator++(int) { + ++*this; + } + + constexpr _Iterator operator++(int) + requires forward_range<_Maybe_const<_Const, _First>> + { + auto _Tmp = *this; + ++*this; + return _Tmp; + } + + constexpr _Iterator& operator--() + requires _Cartesian_product_is_bidirectional<_Const, _First, _Rest...> + { + _Prev(); + return *this; + } + + constexpr _Iterator operator--(int) + requires _Cartesian_product_is_bidirectional<_Const, _First, _Rest...> + { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr _Iterator& operator+=(const difference_type _Off) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + _Advance(_Off); + return *this; + } + + constexpr _Iterator& operator-=(const difference_type _Off) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + _Advance(-_Off); + return *this; + } + + _NODISCARD constexpr reference operator[](const difference_type _Off) const + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + return *((*this) + _Off); + } + + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) + requires equality_comparable>> + { + return _Left._Current == _Right._Current; + } + + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _It, default_sentinel_t) { + return _It._Is_end(make_index_sequence<1 + sizeof...(_Rest)>{}); + } + + _NODISCARD_FRIEND constexpr auto operator<=>(const _Iterator& _Left, const _Iterator& _Right) + requires _All_random_access<_Const, _First, _Rest...> + { + return _Left._Current <=> _Right._Current; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const _Iterator& _It, const difference_type _Off) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + return _Iterator{_It} += _Off; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const difference_type _Off, const _Iterator& _It) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + return _It + _Off; + } + + _NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _It, difference_type _Off) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + return _Iterator{_It} -= _Off; + } + + _NODISCARD_FRIEND constexpr difference_type operator-(const _Iterator& _Left, const _Iterator& _Right) + requires _Cartesian_is_sized_sentinel<_Const, iterator_t, _First, _Rest...> + { + return _Left._Distance_from(_Right._Current); + } + + _NODISCARD_FRIEND constexpr difference_type operator-(const _Iterator& _It, default_sentinel_t) + requires _Cartesian_is_sized_sentinel<_Const, sentinel_t, _First, _Rest...> + { + return _It._Distance_from(_It._End_tuple(make_index_sequence{})); + } + + _NODISCARD_FRIEND constexpr difference_type operator-(default_sentinel_t _Se, const _Iterator& _It) + requires _Cartesian_is_sized_sentinel<_Const, sentinel_t, _First, _Rest...> + { + return -(_It - _Se); + } + + _NODISCARD_FRIEND constexpr auto iter_move(const _Iterator& _It) noexcept( + _Is_iter_move_nothrow(make_index_sequence<1 + sizeof...(_Rest)>{})) { + return _RANGES _Tuple_transform(_RANGES iter_move, _It._Current); + } + + _NODISCARD_FRIEND constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( + _Is_iter_swap_nothrow(make_index_sequence<1 + sizeof...(_Rest)>{})) + requires (indirectly_swappable>> && ... + && indirectly_swappable>>) + { + return [&](index_sequence<_Indices...>) { + return (_RANGES iter_swap(_STD get<_Indices>(_Left._Current), _STD get<_Indices>(_Right._Current)), + ...); + } + (make_index_sequence<1 + sizeof...(_Rest)>{}); + } + }; + + template + _NODISCARD constexpr auto _Begin_or_first_end([[maybe_unused]] const bool _Is_empty) { + if constexpr (_Index == 0) { + return _Is_empty ? _RANGES begin(_STD get<_Index>(_Bases)) + : _Cartesian_common_arg_end(_STD get<_Index>(_Bases)); + } else { + return _RANGES begin(_STD get<_Index>(_Bases)); + } + } + + template + _NODISCARD constexpr auto _Begin_or_first_end([[maybe_unused]] const bool _Is_empty) const { + if constexpr (_Index == 0) { + return _Is_empty ? _RANGES begin(_STD get<_Index>(_Bases)) + : _Cartesian_common_arg_end(_STD get<_Index>(_Bases)); + } else { + return _RANGES begin(_STD get<_Index>(_Bases)); + } + } + + public: + constexpr cartesian_product_view() = default; + + constexpr explicit cartesian_product_view(_First _FirstBase, _Rest... _OtherBases) noexcept( + (is_nothrow_move_constructible_v<_First> && ... && is_nothrow_move_constructible_v<_Rest>) ) // strengthened + : _Bases(_STD move(_FirstBase), _STD move(_OtherBases)...) {} + + _NODISCARD constexpr _Iterator begin() + requires (!_Simple_view<_First> || ... || !_Simple_view<_Rest>) + { + return _Iterator{*this, _RANGES _Tuple_transform(_RANGES begin, _Bases)}; + } + + _NODISCARD constexpr _Iterator begin() const + requires (range && ... && range) + { + return _Iterator{*this, _RANGES _Tuple_transform(_RANGES begin, _Bases)}; + } + + _NODISCARD constexpr _Iterator end() + requires ((!_Simple_view<_First> || ... || !_Simple_view<_Rest>) + && _Cartesian_product_is_common<_First, _Rest...>) + { + const bool _Is_empty = [&](index_sequence<_Indices...>) { + return (_RANGES empty(_STD get<_Indices + 1>(_Bases)) || ...); + } + (make_index_sequence{}); + + const auto _Make_iter_tuple = [&](index_sequence<_Indices...>) { + return tuple{_Begin_or_first_end<_Indices>(_Is_empty)...}; + }; + return _Iterator{*this, _Make_iter_tuple(make_index_sequence<1 + sizeof...(_Rest)>{})}; + } + + _NODISCARD constexpr _Iterator end() const + requires _Cartesian_product_is_common + { + const bool _Is_empty = [&](index_sequence<_Indices...>) { + return (_RANGES empty(_STD get<_Indices + 1>(_Bases)) || ...); + } + (make_index_sequence{}); + + const auto _Make_iter_tuple = [&](index_sequence<_Indices...>) { + return tuple{_Begin_or_first_end<_Indices>(_Is_empty)...}; + }; + return _Iterator{*this, _Make_iter_tuple(make_index_sequence<1 + sizeof...(_Rest)>{})}; + } + + _NODISCARD constexpr default_sentinel_t end() const noexcept { + return default_sentinel; + } + + _NODISCARD constexpr auto size() + requires _Cartesian_product_is_sized<_First, _Rest...> + { + return [&](index_sequence<_Indices...>) { +#if _CONTAINER_DEBUG_LEVEL > 0 + _Size_type _Product{1}; + const bool _Overflow = + (_Multiply_with_overflow_check( + _Product, static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))), _Product) + || ...); + _STL_VERIFY(!_Overflow, "Size of cartesian product cannot be represented by _Size_type (N4928 " + "[range.cartesian.view]/10)."); + return _Product; +#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv + return (static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))) * ...); +#endif // _CONTAINER_DEBUG_LEVEL == 0 + } + (make_index_sequence<1 + sizeof...(_Rest)>{}); + } + + _NODISCARD constexpr auto size() const + requires _Cartesian_product_is_sized + { + return [&](index_sequence<_Indices...>) { +#if _CONTAINER_DEBUG_LEVEL > 0 + _Size_type _Product{1}; + const bool _Overflow = + (_Multiply_with_overflow_check( + _Product, static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))), _Product) + || ...); + _STL_VERIFY(!_Overflow, "Size of cartesian product cannot be represented by _Size_type (N4928 " + "[range.cartesian.view]/10)."); + return _Product; +#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv + return (static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))) * ...); +#endif // _CONTAINER_DEBUG_LEVEL == 0 + } + (make_index_sequence<1 + sizeof...(_Rest)>{}); + } + }; + + template + cartesian_product_view(_Rngs&&...) -> cartesian_product_view...>; + + namespace views { + class _Cartesian_product_fn { + public: + _NODISCARD constexpr auto operator()() const noexcept { + return views::single(tuple{}); + } + + template + _NODISCARD constexpr auto operator()(_Rngs&&... _Ranges) const + noexcept(noexcept(cartesian_product_view...>{_STD forward<_Rngs>(_Ranges)...})) + requires requires { cartesian_product_view...>{_STD forward<_Rngs>(_Ranges)...}; } + { + return cartesian_product_view...>{_STD forward<_Rngs>(_Ranges)...}; + } + }; + + _EXPORT_STD inline constexpr _Cartesian_product_fn cartesian_product; + } // namespace views + #ifdef __cpp_lib_ranges_to_container // clang-format off template diff --git a/tests/std/test.lst b/tests/std/test.lst index fc8574d240..5d058c2417 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -572,6 +572,8 @@ tests\P2321R2_views_adjacent tests\P2321R2_views_zip tests\P2321R2_views_zip_transform tests\P2322R6_ranges_alg_fold +tests\P2374R4_views_cartesian_product +tests\P2374R4_views_cartesian_product_death tests\P2387R3_bind_back tests\P2387R3_pipe_support_for_user_defined_range_adaptors tests\P2401R0_conditional_noexcept_for_exchange diff --git a/tests/std/tests/P2374R4_views_cartesian_product/env.lst b/tests/std/tests/P2374R4_views_cartesian_product/env.lst new file mode 100644 index 0000000000..8ac7033b20 --- /dev/null +++ b/tests/std/tests/P2374R4_views_cartesian_product/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp new file mode 100644 index 0000000000..12bafd5770 --- /dev/null +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -0,0 +1,1047 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using ranges::bidirectional_range, ranges::random_access_range, ranges::sized_range, ranges::common_range, + ranges::iterator_t; + +template +using maybe_const = conditional_t; + +// Helper concepts from [range.cartesian.view] +template +concept CartesianProductIsRandomAccess = (random_access_range> && ... + && (random_access_range> + && sized_range>) ); + +template +concept CartesianProductCommonArg = common_range || (sized_range && random_access_range); + +template +concept CartesianProductIsBidirectional = (bidirectional_range> && ... + && (bidirectional_range> + && CartesianProductCommonArg>) ); + +template +concept CartesianProductIsCommon = CartesianProductCommonArg; + +template +concept CartesianProductIsSized = (sized_range && ...); + +template class FirstSent, class First, class... Rest> +concept CartesianIsSizedSentinel = + (sized_sentinel_for>, iterator_t>> && ... + && (sized_range> + && sized_sentinel_for>, iterator_t>>) ); + +template +concept CanViewCartesianProduct = requires(Ranges&&... rs) { views::cartesian_product(forward(rs)...); }; + +template +concept UnsignedIntegerLike = _Integer_like && (!_Signed_integer_like); + +template +constexpr bool is_iter_move_nothrow(index_sequence) { + constexpr bool is_inner_iter_move_nothrow = + (noexcept(ranges::iter_move(declval&>())) + && ...&& noexcept(ranges::iter_move(declval&>()))); + constexpr bool are_references_nothrow_movable = + conjunction_v>, + is_nothrow_move_constructible>...>; + return is_inner_iter_move_nothrow && are_references_nothrow_movable; +} + +template +constexpr bool is_iter_swap_nothrow(index_sequence) { + return (noexcept(ranges::iter_swap(declval&>())) + && ...&& noexcept(ranges::iter_swap(declval&>()))); +} + +template +constexpr bool test_equal(R1&& r1, R2&& r2) { // TRANSITION, GH-3550 + auto first1 = ranges::begin(r1); + auto last1 = ranges::end(r1); + auto first2 = ranges::begin(r2); + auto last2 = ranges::end(r2); + while (true) { + if (first1 == last1) { + return first2 == last2; + } else if (first2 == last2 || *first1 != *first2) { + return false; + } + ++first1; + ++first2; + } +} + +template +constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest) { + using ranges::cartesian_product_view, ranges::view, ranges::input_range, ranges::input_range, ranges::forward_range, + ranges::range, ranges::range_value_t, ranges::range_reference_t, ranges::range_rvalue_reference_t, + ranges::range_difference_t, ranges::sentinel_t, ranges::prev, ranges::const_iterator_t, + ranges::const_sentinel_t; + using views::all_t; + + using VFirst = all_t; + using R = cartesian_product_view...>; + + constexpr bool is_view = (view> && ... && view>); + constexpr bool all_copy_constructible = (copy_constructible && ... && copy_constructible>); + constexpr bool is_bidirectional = CartesianProductIsBidirectional...>; + constexpr bool is_const_bidirectional = CartesianProductIsBidirectional...>; + constexpr bool is_random_access = CartesianProductIsRandomAccess...>; + constexpr bool is_const_random_access = CartesianProductIsRandomAccess...>; + constexpr bool is_sized = CartesianProductIsSized...>; + constexpr bool is_const_sized = CartesianProductIsSized...>; + constexpr bool is_common = CartesianProductIsCommon...>; + constexpr bool is_const_common = CartesianProductIsCommon...>; + + STATIC_ASSERT(view); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == is_bidirectional); + STATIC_ASSERT(random_access_range == is_random_access); + STATIC_ASSERT(!ranges::contiguous_range); + STATIC_ASSERT(sized_range == is_sized); + STATIC_ASSERT(common_range == is_common); + + // Check non-default-initializability + STATIC_ASSERT(is_default_constructible_v + == (is_default_constructible_v && ... && is_default_constructible_v>) ); + + // Check borrowed_range + static_assert(!ranges::borrowed_range); + + // Check range closure object + constexpr auto closure = views::cartesian_product; + + // ... with lvalue argument + STATIC_ASSERT(CanViewCartesianProduct + == (!is_view || (copy_constructible && ... && copy_constructible>) )); + if constexpr (CanViewCartesianProduct) { + constexpr bool is_noexcept = + !is_view + || (is_nothrow_copy_constructible_v && ... && is_nothrow_copy_constructible_v>); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(first, rest...)) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewCartesianProduct&, const remove_reference_t&...> + == (!is_view || (copy_constructible && ... && copy_constructible>) )); + if constexpr (CanViewCartesianProduct&, const remove_reference_t&...>) { + using RC = + cartesian_product_view&>, all_t&>...>; + constexpr bool is_noexcept = + !is_view + || (is_nothrow_copy_constructible_v && ... && is_nothrow_copy_constructible_v>); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(as_const(first), as_const(rest)...)) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewCartesianProduct, remove_reference_t...> + == (is_view || (movable> && ... && movable>) )); + if constexpr (CanViewCartesianProduct, remove_reference_t...>) { + using RS = cartesian_product_view>, all_t>...>; + constexpr bool is_noexcept = + (is_nothrow_move_constructible_v && ... && is_nothrow_move_constructible_v>); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(first), move(rest)...)) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewCartesianProduct, const remove_reference_t...> + == (is_view && (copy_constructible && ... && copy_constructible>) )); + if constexpr (CanViewCartesianProduct, const remove_reference_t...>) { + constexpr bool is_noexcept = + (is_nothrow_copy_constructible_v && ... && is_nothrow_copy_constructible_v>); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(as_const(first)), move(as_const(rest))...)) == is_noexcept); + } + + // Check deduction guide + same_as auto r = cartesian_product_view{forward(first), forward(rest)...}; + + // Check cartesian_view::size + STATIC_ASSERT(CanMemberSize == is_sized); + if constexpr (CanMemberSize) { + UnsignedIntegerLike auto s = r.size(); + assert(s == ranges::size(expected_range)); + } + + // Check cartesian_view::size (const) + STATIC_ASSERT(CanMemberSize == is_const_sized); + if constexpr (CanMemberSize) { + UnsignedIntegerLike auto s = as_const(r).size(); + assert(s == ranges::size(expected_range)); + } + + const bool is_empty = ranges::empty(expected_range); + + // Check view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == (forward_range || is_sized)); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } + + // Check view_interface::empty and operator bool (const) + STATIC_ASSERT(CanMemberEmpty == (forward_range || is_const_sized)); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + + assert(test_equal(r, expected_range)); // TRANSITION, GH-3550 (use ranges::equal) + if (!forward_range) { // intentionally not if constexpr + return true; + } + + // Check cartesian_view::begin + STATIC_ASSERT(CanMemberBegin); + { + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected_range)); + } + + if constexpr (all_copy_constructible) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + } + + // Check cartesian_view::begin (const) + STATIC_ASSERT(CanMemberBegin == (range && ... && range>) ); + if constexpr (CanMemberBegin) { + const same_as> auto ci = as_const(r).begin(); + if (!is_empty) { + assert(*ci == *begin(expected_range)); + } + + if constexpr (all_copy_constructible) { + const auto r2 = r; + const same_as> auto ci2 = r2.begin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + } + + // Check cartesian_view::end + STATIC_ASSERT(CanMemberEnd); + { + const same_as> auto s = r.end(); + assert((r.begin() == s) == is_empty); + STATIC_ASSERT(common_range == is_common); + + if constexpr (same_as, default_sentinel_t>) { + STATIC_ASSERT(!is_common); + STATIC_ASSERT(noexcept(r.end())); + } else if constexpr (common_range && is_bidirectional) { + if (!is_empty) { + assert(*prev(s) == *prev(end(expected_range))); + } + + if constexpr (all_copy_constructible) { + auto r2 = r; + if (!is_empty) { + assert(*prev(r2.end()) == *prev(end(expected_range))); + } + } + } + } + + // Check cartesian_view::end (const) + STATIC_ASSERT(CanMemberEnd); + if constexpr (CanMemberEnd) { + const same_as> auto cs = as_const(r).end(); + assert((r.begin() == cs) == is_empty); + STATIC_ASSERT(common_range == is_const_common); + + if constexpr (same_as, default_sentinel_t>) { + STATIC_ASSERT(!is_const_common); + STATIC_ASSERT(noexcept(as_const(r).end())); + } else if constexpr (common_range && is_const_bidirectional) { + if (!is_empty) { + assert(*prev(cs) == *prev(end(expected_range))); + } + + if constexpr (all_copy_constructible) { + const auto r2 = r; + if (!is_empty) { + assert(*prev(r2.end()) == *prev(end(expected_range))); + } + } + } + } + + // Check view_interface::cbegin + STATIC_ASSERT(CanMemberCBegin); + STATIC_ASSERT(CanMemberCBegin == (range && ... && range>) ); + { + const same_as> auto i = r.cbegin(); + if (!is_empty) { + assert(*i == *cbegin(expected_range)); + } + + if constexpr (all_copy_constructible) { + auto r2 = r; + const same_as> auto i2 = r2.cbegin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (CanCBegin) { + const same_as> auto i3 = as_const(r).cbegin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Check view_interface::cend + STATIC_ASSERT(CanMemberCEnd); + STATIC_ASSERT(CanMemberCEnd); + if (!is_empty) { + same_as> auto i = r.cend(); + if constexpr (common_range && is_bidirectional) { + assert(*prev(i) == *prev(cend(expected_range))); + } + + if constexpr (CanCEnd) { + same_as> auto i2 = as_const(r).cend(); + if constexpr (common_range && is_const_bidirectional) { + assert(*prev(i2) == *prev(cend(expected_range))); + } + } + } + + if (is_empty) { + return true; + } + + // Check view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + // Check view_interface::operator[] + STATIC_ASSERT(CanIndex == is_random_access); + if constexpr (CanIndex) { + assert(r[0] == expected_range[0]); + } + + // Check view_interface::operator[] (const) + STATIC_ASSERT(CanIndex == is_const_random_access); + if constexpr (CanIndex) { + assert(as_const(r)[0] == expected_range[0]); + } + + // Check view_interface::front + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(r.front() == *begin(expected_range)); + } + + // Check view_interface::front (const) + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected_range)); + } + + // Check view_interface::back + STATIC_ASSERT(CanMemberBack == (is_bidirectional && is_common)); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected_range))); + } + + // Check view_interface::back (const) + STATIC_ASSERT(CanMemberBack == (is_const_bidirectional && is_const_common)); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected_range))); + } + + { // Check cartesian_view::iterator + using I = iterator_t; + STATIC_ASSERT(input_iterator); + + // Check iterator_category + STATIC_ASSERT(same_as); + + // Check iterator_concept + using IterConcept = typename I::iterator_concept; + STATIC_ASSERT(is_random_access == same_as); + STATIC_ASSERT((is_bidirectional && !is_random_access) == same_as); + STATIC_ASSERT((forward_range && !is_bidirectional) == same_as); + + // Check value_type + STATIC_ASSERT(same_as, range_value_t...>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable == default_initializable>); + + auto i = r.begin(); + + { // Check dereference + same_as, range_reference_t...>> decltype(auto) v = *as_const(i); + assert(v == expected_range[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) i2 = ++i; + assert(&i2 == &i); + if (i != r.end()) { + assert(*i == expected_range[1]); + } + i = r.begin(); + } + + if constexpr (forward_range) { // Check post-incrementation + same_as decltype(auto) i2 = i++; + assert(*i2 == expected_range[0]); + if (i != r.end()) { + assert(*i == expected_range[1]); + } + i = r.begin(); + } else { + STATIC_ASSERT(is_void_v); + } + + if constexpr (is_bidirectional) { + { // Check pre-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = --i; + assert(&i2 == &i); + assert(*i2 == expected_range[0]); + } + + { // Check post-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = i--; + if (i2 != r.end()) { + assert(*i2 == expected_range[1]); + } + assert(*i == expected_range[0]); + } + } + + if constexpr (is_random_access) { + const auto half_max_distance = ranges::distance(r) / 2; + + { // Check advancement operators + same_as decltype(auto) i2 = (i += half_max_distance); + assert(&i2 == &i); + if (i != r.end()) { + assert(*i == expected_range[static_cast(half_max_distance)]); + } + + same_as decltype(auto) i3 = (i -= half_max_distance); + assert(&i3 == &i); + assert(*i == expected_range[0]); + } + + { // Check subscript operator + same_as, range_reference_t...>> decltype(auto) v = i[0]; + assert(v == expected_range[0]); + } + + if constexpr (equality_comparable>) { // Check equality comparisons + auto i2 = r.begin(); + same_as auto b1 = i == i2; + assert(b1); + ++i2; + same_as auto b2 = i != i2; + assert(b2); + same_as auto b3 = i2 != default_sentinel; + assert(b3); + ranges::advance(i2, r.end()); + same_as auto b4 = i2 == default_sentinel; + assert(b4); + } + + if constexpr ((random_access_range && ... + && random_access_range>) ) { // Check 3way comparisons + using Cat = common_comparison_category_t>, + compare_three_way_result_t>>...>; + auto i2 = r.begin(); + same_as auto cmp1 = i <=> i2; + assert(cmp1 == Cat::equivalent); + ++i2; + assert((i <=> i2) == Cat::less); + assert((i2 <=> i) == Cat::greater); + + same_as auto b1 = i < i2; + assert(b1); + same_as auto b2 = i2 > i; + assert(b2); + same_as auto b3 = i <= i2; + assert(b3); + same_as auto b4 = i2 >= i; + assert(b4); + } + + { // Check operator+ + same_as auto i2 = i + half_max_distance; + if (i2 != r.end()) { + assert(*i2 == expected_range[static_cast(half_max_distance)]); + } + + same_as auto i3 = half_max_distance + i; + if (i3 != r.end()) { + assert(*i3 == expected_range[static_cast(half_max_distance)]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto i2 = (i + half_max_distance) - half_max_distance; + assert(*i2 == expected_range[0]); + } + } + + if constexpr (CartesianIsSizedSentinel...>) { // Check differencing + _Signed_integer_like auto diff = i - i; + assert(diff == 0); + assert((i - ranges::next(i)) == -1); + assert((ranges::next(i) - i) == 1); + } + + STATIC_ASSERT(sized_sentinel_for + == CartesianIsSizedSentinel...>); + if constexpr (sized_sentinel_for) { // Check differencing with default_sentinel + const auto size = ranges::ssize(expected_range); + const _Signed_integer_like auto diff1 = i - default_sentinel; + assert(diff1 == -size); + const _Signed_integer_like auto diff2 = default_sentinel - i; + assert(diff2 == size); + } + + { // Check iter_move (hidden friend available via ADL) + same_as, range_rvalue_reference_t>...>> decltype(auto) + rval = iter_move(as_const(i)); + assert(rval == expected_range[0]); + static_assert(noexcept(iter_move(i)) + == is_iter_move_nothrow...>(make_index_sequence<1 + sizeof...(Rest)>{})); + } + + if constexpr ((indirectly_swappable> && ... + && indirectly_swappable>>) ) { + // Check iter_swap, other tests are defined in test_iter_swap function + static_assert(is_void_v); + static_assert(noexcept(iter_swap(i, i)) + == is_iter_swap_nothrow...>(make_index_sequence<1 + sizeof...(Rest)>{})); + } + } + + // Check cartesian_view::iterator + if constexpr (CanMemberBegin) { + using CI = iterator_t; + STATIC_ASSERT(input_iterator); + + // Check iterator_category + STATIC_ASSERT(same_as); + + // Check iterator_concept + using IterConcept = typename CI::iterator_concept; + STATIC_ASSERT(is_const_random_access == same_as); + STATIC_ASSERT( + (is_const_bidirectional && !is_const_random_access) == same_as); + STATIC_ASSERT( + (forward_range && !is_const_bidirectional) == same_as); + + // Check value_type + static_assert( + same_as, range_value_t...>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable == default_initializable>); + + // Check conversion from non-const iterator + if constexpr ((convertible_to, iterator_t> && ... + && convertible_to, iterator_t>) ) { + auto i = r.begin(); + [[maybe_unused]] CI ci{move(i)}; + } + + auto i = r.begin(); + CI ci = as_const(r).begin(); + + { // Check dereference + same_as, range_reference_t...>> decltype(auto) v = + *as_const(ci); + assert(v == expected_range[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) ci2 = ++ci; + assert(&ci2 == &ci); + if (ci != as_const(r).end()) { + assert(*ci == expected_range[1]); + } + ci = as_const(r).begin(); + } + + if constexpr (forward_range) { // Check post-incrementation + same_as decltype(auto) ci2 = ci++; + assert(*ci2 == expected_range[0]); + if (ci != as_const(r).end()) { + assert(*ci == expected_range[1]); + } + ci = as_const(r).begin(); + } else { + STATIC_ASSERT(is_void_v); + } + + if constexpr (is_const_bidirectional) { + { // Check pre-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = --ci; + assert(&ci2 == &ci); + assert(*ci2 == expected_range[0]); + } + + { // Check post-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = ci--; + if (ci2 != r.end()) { + assert(*ci2 == expected_range[1]); + } + assert(*ci == expected_range[0]); + } + } + + if constexpr (is_const_random_access) { + const auto half_max_distance = ranges::distance(r) / 2; + + { // Check advancement operators + same_as decltype(auto) ci2 = (ci += half_max_distance); + assert(&ci2 == &ci); + if (ci != r.end()) { + assert(*ci == expected_range[static_cast(half_max_distance)]); + } + + same_as decltype(auto) ci3 = (ci -= half_max_distance); + assert(&ci3 == &ci); + assert(*ci == expected_range[0]); + } + + { // Check subscript operator + same_as, range_reference_t...>> decltype(auto) v = + ci[0]; + assert(v == expected_range[0]); + } + + if constexpr (equality_comparable>) { // Check equality comparisons + CI ci2 = as_const(r).begin(); + same_as auto b1 = ci == ci2; + assert(b1); + ++ci2; + same_as auto b2 = ci != ci2; + assert(b2); + same_as auto b3 = ci2 != default_sentinel; + assert(b3); + ranges::advance(ci2, r.end()); + same_as auto b4 = ci2 == default_sentinel; + assert(b4); + } + + if constexpr (equality_comparable_with, + iterator_t>) { // Check equality comparisons (mixed) + CI ci2 = as_const(r).begin(); + same_as auto b1 = i == ci2; + assert(b1); + ++ci2; + same_as auto b2 = i != ci2; + assert(b2); + same_as auto b3 = ci2 != default_sentinel; + assert(b3); + ranges::advance(ci2, r.end()); + same_as auto b4 = ci2 == default_sentinel; + assert(b4); + } + + if constexpr ((random_access_range && ... + && random_access_range>) ) { // Check 3way comparisons + using Cat = common_comparison_category_t>, + compare_three_way_result_t>>...>; + CI ci2 = as_const(r).begin(); + same_as auto cmp1 = ci <=> ci2; + assert(cmp1 == Cat::equivalent); + ++ci2; + assert((ci <=> ci2) == Cat::less); + assert((ci2 <=> ci) == Cat::greater); + + same_as auto b1 = ci < ci2; + assert(b1); + same_as auto b2 = ci2 > ci; + assert(b2); + same_as auto b3 = ci <= ci2; + assert(b3); + same_as auto b4 = ci2 >= ci; + assert(b4); + } + + if constexpr ((random_access_range && ... + && random_access_range>) ) { // Check 3way comparisons (mixed) + using Cat = common_comparison_category_t>, + compare_three_way_result_t>>...>; + CI ci2 = as_const(r).begin(); + same_as auto cmp1 = i <=> ci2; + assert(cmp1 == Cat::equivalent); + ++ci2; + assert((i <=> ci2) == Cat::less); + assert((ci2 <=> i) == Cat::greater); + + same_as auto b1 = i < ci2; + assert(b1); + same_as auto b2 = ci2 > i; + assert(b2); + same_as auto b3 = i <= ci2; + assert(b3); + same_as auto b4 = ci2 >= i; + assert(b4); + } + + { // Check operator+ + same_as auto ci2 = ci + 1; + if (ci2 != r.end()) { + assert(*ci2 == expected_range[1]); + } + + same_as auto ci3 = 1 + ci; + if (ci3 != r.end()) { + assert(*ci3 == expected_range[1]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto ci2 = ranges::next(ci) - 1; + assert(*ci2 == expected_range[0]); + } + } + + if constexpr (CartesianIsSizedSentinel...>) { // Check differencing + _Signed_integer_like auto diff = ci - ci; + assert(diff == 0); + assert((ci - ranges::next(ci)) == -1); + assert((ranges::next(ci) - ci) == 1); + } + + STATIC_ASSERT(sized_sentinel_for + == CartesianIsSizedSentinel...>); + if constexpr (sized_sentinel_for) { // Check differencing with default_sentinel + const auto size = ranges::ssize(expected_range); + const _Signed_integer_like auto diff1 = ci - default_sentinel; + assert(diff1 == -size); + const _Signed_integer_like auto diff2 = default_sentinel - ci; + assert(diff2 == size); + } + + { // Check iter_move + same_as, + range_rvalue_reference_t>...>> decltype(auto) rval = iter_move(as_const(ci)); + assert(rval == expected_range[0]); + static_assert(noexcept(iter_move(ci)) + == is_iter_move_nothrow...>( + make_index_sequence<1 + sizeof...(Rest)>{})); + } + + if constexpr ((indirectly_swappable> && ... + && indirectly_swappable>>) ) { + // Check iter_swap other tests are defined in test_iter_swap function + static_assert(is_void_v); + static_assert(noexcept(iter_swap(ci, ci)) + == is_iter_swap_nothrow...>(make_index_sequence<1 + sizeof...(Rest)>{})); + } + } + + return true; +} + +// Check calling views::cartesian_product without arguments +STATIC_ASSERT(same_as); + +template + requires (indirectly_swappable> && ...) +constexpr void test_iter_swap(Rngs&... rngs) { + // This test assumes that 'ranges::size(rng)' is at least 2 for each rng in rngs + auto r = views::cartesian_product(rngs...); + using R = decltype(r); + using Val = ranges::range_value_t; + + { // Check iter_swap for cartesian_product_view::iterator + auto i = r.begin(); + Val first = *i; + auto j = ranges::next(i); + Val second = *j; + + iter_swap(i, j); + assert(*i == second); + assert(*j == first); + + ranges::iter_swap(i, j); + assert(*i == first); + assert(*j == second); + } + + // Check iter_swap for cartesian_product_view::iterator + if constexpr (((CanMemberBegin && indirectly_swappable>) &&...)) { + using CVal = ranges::range_value_t; + auto i = as_const(r).begin(); + CVal first = *i; + auto j = ranges::next(i); + CVal second = *j; + + iter_swap(i, j); + assert(*i == second); + assert(*j == first); + + ranges::iter_swap(i, j); + assert(*i == first); + assert(*j == second); + } +} + +constexpr tuple some_ranges = { + array{0, 1, 2, 3, 4}, + array{11, 22, 33}, + array{'7'}, + array{"4"sv, "2"sv, "0"sv}, +}; + +constexpr tuple expected_results = { + // Expected result of views::cartesian_product(get<0>(some_ranges)) + to_array>({{0}, {1}, {2}, {3}, {4}}), + + // Expected result of views::cartesian_product(get<0>(some_ranges), get<1>(some_ranges)) + to_array>({{0, 11}, {0, 22}, {0, 33}, {1, 11}, {1, 22}, {1, 33}, {2, 11}, {2, 22}, {2, 33}, {3, 11}, + {3, 22}, {3, 33}, {4, 11}, {4, 22}, {4, 33}}), + + // Expected result of views::cartesian_product(get<0>(some_ranges), ..., get<2>(some_ranges)) + to_array>( + {{0, 11, '7'}, {0, 22, '7'}, {0, 33, '7'}, {1, 11, '7'}, {1, 22, '7'}, {1, 33, '7'}, {2, 11, '7'}, {2, 22, '7'}, + {2, 33, '7'}, {3, 11, '7'}, {3, 22, '7'}, {3, 33, '7'}, {4, 11, '7'}, {4, 22, '7'}, {4, 33, '7'}}), + + // Expected result of views::cartesian_product(get<0>(some_ranges), ..., get<3>(some_ranges)) + to_array>( + {{0, 11, '7', "4"sv}, {0, 11, '7', "2"sv}, {0, 11, '7', "0"sv}, {0, 22, '7', "4"sv}, {0, 22, '7', "2"sv}, + {0, 22, '7', "0"sv}, {0, 33, '7', "4"sv}, {0, 33, '7', "2"sv}, {0, 33, '7', "0"sv}, {1, 11, '7', "4"sv}, + {1, 11, '7', "2"sv}, {1, 11, '7', "0"sv}, {1, 22, '7', "4"sv}, {1, 22, '7', "2"sv}, {1, 22, '7', "0"sv}, + {1, 33, '7', "4"sv}, {1, 33, '7', "2"sv}, {1, 33, '7', "0"sv}, {2, 11, '7', "4"sv}, {2, 11, '7', "2"sv}, + {2, 11, '7', "0"sv}, {2, 22, '7', "4"sv}, {2, 22, '7', "2"sv}, {2, 22, '7', "0"sv}, {2, 33, '7', "4"sv}, + {2, 33, '7', "2"sv}, {2, 33, '7', "0"sv}, {3, 11, '7', "4"sv}, {3, 11, '7', "2"sv}, {3, 11, '7', "0"sv}, + {3, 22, '7', "4"sv}, {3, 22, '7', "2"sv}, {3, 22, '7', "0"sv}, {3, 33, '7', "4"sv}, {3, 33, '7', "2"sv}, + {3, 33, '7', "0"sv}, {4, 11, '7', "4"sv}, {4, 11, '7', "2"sv}, {4, 11, '7', "0"sv}, {4, 22, '7', "4"sv}, + {4, 22, '7', "2"sv}, {4, 22, '7', "0"sv}, {4, 33, '7', "4"sv}, {4, 33, '7', "2"sv}, {4, 33, '7', "0"sv}}), +}; + +template +struct test_input_range { + template + using type = test::range; +}; + +template +struct test_range { + template + using type = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; +}; + +struct instantiator { + template + static constexpr void call() { + typename R::template type r0{get<0>(some_ranges)}; + test_one(get<0>(expected_results), r0); + + if constexpr (ranges::forward_range>) { + typename R::template type r1{get<1>(some_ranges)}; + test_one(get<1>(expected_results), r0, r1); + +#if !(defined(__clang__) && defined(_DEBUG) && defined(_MT) && !defined(_DLL)) // constexpr limit + typename R::template type r2{get<2>(some_ranges)}; + test_one(get<2>(expected_results), r0, r1, r2); +#endif // "Clang /MTd" configuration + + int swap_a1[] = {1, 2, 3}; + typename R::template type swap_r1{swap_a1}; + int swap_a2[] = {9, 8, 7}; + typename R::template type swap_r2{swap_a2}; + test_iter_swap(swap_r1, swap_r2); + } + } +}; + +constexpr void instantiation_test() { + // The cartesian_product_view is sensitive to category, commonality, and size, but oblivious to + // differencing and proxyness. + using test::Common, test::Sized, test::CanDifference; + + // When the base range is an input range, the view is sensitive to differencing + 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>(); + instantiator::call>(); + instantiator::call>(); +} + +template > +using move_only_view = test::range}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +namespace check_recommended_practice_implementation { // MSVC STL specific behavior + using ranges::cartesian_product_view, ranges::empty_view, ranges::single_view, views::all_t, ranges::range_size_t, + ranges::range_difference_t, ranges::ref_view, ranges::owning_view; + using Arr = array; + using Vec = vector; + using Span = span; + + // Computing product for such small array does not require big range_size_t + STATIC_ASSERT(sizeof(range_size_t>>) <= sizeof(size_t)); + STATIC_ASSERT(sizeof(range_size_t, all_t>>) <= sizeof(size_t)); + STATIC_ASSERT(sizeof(range_size_t, all_t, all_t>>) <= sizeof(size_t)); + + // Same thing with range_difference_t + STATIC_ASSERT(sizeof(range_difference_t>>) <= sizeof(ptrdiff_t)); + STATIC_ASSERT(sizeof(range_difference_t, all_t>>) <= sizeof(ptrdiff_t)); + STATIC_ASSERT( + sizeof(range_difference_t, all_t, all_t>>) <= sizeof(ptrdiff_t)); + + // Computing product for such small span does not require big range_size_t + STATIC_ASSERT(sizeof(range_size_t>>) <= sizeof(size_t)); + STATIC_ASSERT(sizeof(range_size_t, all_t>>) <= sizeof(size_t)); + STATIC_ASSERT( + sizeof(range_size_t, all_t, all_t>>) <= sizeof(size_t)); + + // Same thing with range_difference_t + STATIC_ASSERT(sizeof(range_difference_t>>) <= sizeof(ptrdiff_t)); + STATIC_ASSERT(sizeof(range_difference_t, all_t>>) <= sizeof(ptrdiff_t)); + STATIC_ASSERT( + sizeof(range_difference_t, all_t, all_t>>) <= sizeof(ptrdiff_t)); + + // Check 'single_view' and 'empty_view' + STATIC_ASSERT(sizeof(range_size_t, single_view>>) <= sizeof(size_t)); + STATIC_ASSERT( + sizeof(range_difference_t, single_view>>) <= sizeof(ptrdiff_t)); + + // Check 'ref_view<(const) V>' and 'owning_view' + STATIC_ASSERT(sizeof(range_size_t, ref_view, owning_view>>) + <= sizeof(size_t)); + STATIC_ASSERT( + sizeof(range_difference_t, ref_view, owning_view>>) + <= sizeof(ptrdiff_t)); + + // One vector should not use big integer-class type... + STATIC_ASSERT(sizeof(range_size_t>>) <= sizeof(size_t)); + STATIC_ASSERT(sizeof(range_difference_t>>) <= sizeof(ptrdiff_t)); + + // ...but two vectors will + STATIC_ASSERT(sizeof(range_size_t, all_t>>) > sizeof(size_t)); + STATIC_ASSERT(sizeof(range_difference_t, all_t>>) > sizeof(ptrdiff_t)); +} // namespace check_recommended_practice_implementation + +int main() { + // Check views + { // ... copyable + constexpr span s{get<0>(some_ranges)}; + STATIC_ASSERT(test_one(get<0>(expected_results), s)); + test_one(get<0>(expected_results), s); + } + + { // ... move-only + using test::Common, test::Sized; + test_one(get<3>(expected_results), // + move_only_view{get<0>(some_ranges)}, + move_only_view{get<1>(some_ranges)}, + move_only_view{get<2>(some_ranges)}, + move_only_view{get<3>(some_ranges)}); + test_one(get<3>(expected_results), // + move_only_view{get<0>(some_ranges)}, + move_only_view{get<1>(some_ranges)}, + move_only_view{get<2>(some_ranges)}, + move_only_view{get<3>(some_ranges)}); + test_one(get<2>(expected_results), // + move_only_view{get<0>(some_ranges)}, + move_only_view{get<1>(some_ranges)}, + move_only_view{get<2>(some_ranges)}); + test_one(get<2>(expected_results), // + move_only_view{get<0>(some_ranges)}, + move_only_view{get<1>(some_ranges)}, + move_only_view{get<2>(some_ranges)}); + } + + // Check non-views + { + constexpr auto& r0 = get<0>(some_ranges); + STATIC_ASSERT(test_one(get<0>(expected_results), r0)); + test_one(get<0>(expected_results), r0); + + auto r1 = get<1>(some_ranges) | ranges::to(); + test_one(get<1>(expected_results), r0, r1); + + auto r2 = get<2>(some_ranges) | ranges::to(); + test_one(get<2>(expected_results), r0, r1, r2); + + auto r3 = get<3>(some_ranges) | ranges::to(); + test_one(get<3>(expected_results), r0, r1, r2, r3); + } + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/P2374R4_views_cartesian_product_death/env.lst b/tests/std/tests/P2374R4_views_cartesian_product_death/env.lst new file mode 100644 index 0000000000..8ac7033b20 --- /dev/null +++ b/tests/std/tests/P2374R4_views_cartesian_product_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp new file mode 100644 index 0000000000..f8e9639d7c --- /dev/null +++ b/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include +#include + +#include + +using namespace std; + +constexpr auto much_ints = views::iota(0ull, (numeric_limits::max)()); +constexpr array little_ints = {1, 2, 3, 4}; +void test_view_size() { + auto v = views::cartesian_product(much_ints, much_ints, much_ints); + // Size of cartesian product cannot be represented by _Size_type (N4928 [range.cartesian.view]/10). + (void) v.size(); +} + +void test_view_const_size() { + auto v = views::cartesian_product(much_ints, much_ints, much_ints); + // Size of cartesian product cannot be represented by _Size_type (N4928 [range.cartesian.view]/10). + (void) as_const(v).size(); +} + +void test_iterator_advance_past_end_with_small_offset() { + // This preconditions check works only when all ranges model ranges::sized_range + auto v = views::cartesian_product(little_ints, little_ints, little_ints); + auto i = v.begin(); + // Cannot advance cartesian_product_view iterator past end (N4928 [range.cartesian.iterator]/19). + i += 65; +} + +void test_iterator_advance_past_end_with_big_offset() { + // This preconditions check works only when all ranges model ranges::sized_range + auto v = views::cartesian_product(little_ints, little_ints, little_ints); + auto i = v.begin(); + // Cannot advance cartesian_product_view iterator past end (N4928 [range.cartesian.iterator]/19). + i += 1000; +} + +void test_iterator_advance_before_begin() { + auto v = views::cartesian_product(little_ints, little_ints, little_ints); + auto i = v.end(); + // Cannot advance cartesian_product_view iterator before begin (N4928 [range.cartesian.iterator]/19). + i += -65; +} + +void test_iterator_differencing() { + auto v = views::cartesian_product(much_ints, much_ints, much_ints); + auto i1 = v.begin(); + auto i2 = v.end(); + // Scaled-sum cannot be represented by _Difference_type (N4928 [range.cartesian.iterator]/8). + (void) (i2 - i1); +} + +void test_iterator_and_default_sentinel_differencing() { + auto v = views::cartesian_product(much_ints, much_ints, much_ints); + auto i = v.begin(); + // Scaled-sum cannot be represented by _Difference_type (N4928 [range.cartesian.iterator]/8). + (void) (default_sentinel - i); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_view_size, + test_view_const_size, + test_iterator_advance_past_end_with_small_offset, + test_iterator_advance_past_end_with_big_offset, + test_iterator_advance_before_begin, + test_iterator_differencing, + test_iterator_and_default_sentinel_differencing, + }); +#else // ^^^ test everything / test only _CONTAINER_DEBUG_LEVEL cases vvv + exec.add_death_tests({ + test_view_size, + test_view_const_size, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +} From 550cb1da3b5006f6a2433eb3e5206d830e3e3b1e Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 13 Mar 2023 22:38:01 +0100 Subject: [PATCH 03/43] Simplify `_Cartesian_product_is_common` --- stl/inc/ranges | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index a661f18d71..b3a863823b 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9235,7 +9235,7 @@ namespace ranges { && (bidirectional_range<_Maybe_const<_Const, _Rest>> && _Cartesian_product_common_arg<_Maybe_const<_Const, _Rest>>) ); - template + template concept _Cartesian_product_is_common = _Cartesian_product_common_arg<_First>; template @@ -9701,7 +9701,7 @@ namespace ranges { _NODISCARD constexpr _Iterator end() requires ((!_Simple_view<_First> || ... || !_Simple_view<_Rest>) - && _Cartesian_product_is_common<_First, _Rest...>) + && _Cartesian_product_is_common<_First>) { const bool _Is_empty = [&](index_sequence<_Indices...>) { return (_RANGES empty(_STD get<_Indices + 1>(_Bases)) || ...); @@ -9715,7 +9715,7 @@ namespace ranges { } _NODISCARD constexpr _Iterator end() const - requires _Cartesian_product_is_common + requires _Cartesian_product_is_common { const bool _Is_empty = [&](index_sequence<_Indices...>) { return (_RANGES empty(_STD get<_Indices + 1>(_Bases)) || ...); From c1c2c8c41a971258df33d9ddf2fb41f904d01c5a Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 13 Mar 2023 23:08:16 +0100 Subject: [PATCH 04/43] Apply @tylerbrawl's suggestion --- stl/inc/ranges | 67 +++++++++++--------------------------------------- 1 file changed, 15 insertions(+), 52 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index b3a863823b..6ef0236aad 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9438,61 +9438,25 @@ namespace ranges { _RANGES begin(_STD get<_Indices + 1>(_Parent->_Bases))...}; } - template - _NODISCARD constexpr _Difference_type<_Const> _Scaled_size() const { - if constexpr (_Index <= sizeof...(_Rest)) { -#if _ITERATOR_DEBUG_LEVEL != 0 - _Difference_type<_Const> _Product{}; - const bool _Overflow = _Multiply_with_overflow_check( - static_cast<_Difference_type<_Const>>(_RANGES size(_STD get<_Index>(_Parent->_Bases))), - _Scaled_size<_Index + 1>(), _Product); - _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by _Difference_type (N4928 " + template + _NODISCARD constexpr _Difference_type<_Const> _Distance_from(const _Tuple& _Tpl) const { + const auto _Diff = + static_cast<_Difference_type<_Const>>(_STD get<_Index>(_Current) - _STD get<_Index>(_Tpl)); + if constexpr (_Index > 0) { + _Difference_type<_Const> _Result{1}; + const auto _Size = + static_cast<_Difference_type<_Const>>(_RANGES size(_STD get<_Index>(_Parent->_Bases))); + const bool _Overflow = + _Multiply_with_overflow_check(_Size, _Distance_from<_Index - 1>(_Tpl), _Result) + || _Add_with_overflow_check(_Result, _Diff, _Result); + _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by difference_type (N4928 " "[range.cartesian.iterator]/8)."); - return _Product; -#else // ^^^ _ITERATOR_DEBUG_LEVEL != 0 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return static_cast<_Difference_type<_Const>>(_RANGES size(_STD get<_Index>(_Parent->_Bases))) - * _Scaled_size<_Index + 1>(); -#endif //_ITERATOR_DEBUG_LEVEL == 0 + return _Result; } else { - return static_cast<_Difference_type<_Const>>(1); + return _Diff; } } - template - _NODISCARD constexpr _Difference_type<_Const> _Scaled_distance(const _Tuple& _Tpl) const { -#if _ITERATOR_DEBUG_LEVEL != 0 - _Difference_type<_Const> _Product{}; - const bool _Overflow = _Multiply_with_overflow_check( - static_cast<_Difference_type<_Const>>(_STD get<_Index>(_Current) - _STD get<_Index>(_Tpl)), - _Scaled_size<_Index + 1>(), _Product); - _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by _Difference_type (N4928 " - "[range.cartesian.iterator]/8)."); - return _Product; -#else // ^^^ _ITERATOR_DEBUG_LEVEL != 0 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return static_cast<_Difference_type<_Const>>(_STD get<_Index>(_Current) - _STD get<_Index>(_Tpl)) - * _Scaled_size<_Index + 1>(); -#endif //_ITERATOR_DEBUG_LEVEL == 0 - } - - template - _NODISCARD constexpr _Difference_type<_Const> _Scaled_sum( - const _Tuple& _Tpl, index_sequence<_Indices...>) const { -#if _ITERATOR_DEBUG_LEVEL != 0 - _Difference_type<_Const> _Sum{}; - const bool _Overflow = (_Add_with_overflow_check(_Sum, _Scaled_distance<_Indices>(_Tpl), _Sum) || ...); - _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by _Difference_type (N4928 " - "[range.cartesian.iterator]/8)."); - return _Sum; -#else // ^^^ _ITERATOR_DEBUG_LEVEL != 0 / _ITERATOR_DEBUG_LEVEL == 0 vvv - return (_Scaled_distance<_Indices>(_Tpl) + ...); -#endif //_ITERATOR_DEBUG_LEVEL == 0 - } - - template - _NODISCARD constexpr _Difference_type<_Const> _Distance_from(const _Tuple& _Tpl) const { - return _Scaled_sum(_Tpl, make_index_sequence<1 + sizeof...(_Rest)>{}); - } - template _NODISCARD static _CONSTEVAL bool _Is_iter_move_nothrow(index_sequence<_Indices...>) noexcept { return conjunction_v< @@ -9700,8 +9664,7 @@ namespace ranges { } _NODISCARD constexpr _Iterator end() - requires ((!_Simple_view<_First> || ... || !_Simple_view<_Rest>) - && _Cartesian_product_is_common<_First>) + requires ((!_Simple_view<_First> || ... || !_Simple_view<_Rest>) && _Cartesian_product_is_common<_First>) { const bool _Is_empty = [&](index_sequence<_Indices...>) { return (_RANGES empty(_STD get<_Indices + 1>(_Bases)) || ...); From 9c9ad3349a8cbb83a09b8760d5afc9c88ea57f63 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 14 Mar 2023 01:05:28 +0100 Subject: [PATCH 05/43] Remove workaround for GH-3550 --- .../P2374R4_views_cartesian_product/test.cpp | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 12bafd5770..4a9ebb3e8f 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -71,23 +71,6 @@ constexpr bool is_iter_swap_nothrow(index_sequence) { && ...&& noexcept(ranges::iter_swap(declval&>()))); } -template -constexpr bool test_equal(R1&& r1, R2&& r2) { // TRANSITION, GH-3550 - auto first1 = ranges::begin(r1); - auto last1 = ranges::end(r1); - auto first2 = ranges::begin(r2); - auto last2 = ranges::end(r2); - while (true) { - if (first1 == last1) { - return first2 == last2; - } else if (first2 == last2 || *first1 != *first2) { - return false; - } - ++first1; - ++first2; - } -} - template constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest) { using ranges::cartesian_product_view, ranges::view, ranges::input_range, ranges::input_range, ranges::forward_range, @@ -213,7 +196,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest assert(static_cast(as_const(r)) == !is_empty); } - assert(test_equal(r, expected_range)); // TRANSITION, GH-3550 (use ranges::equal) + assert(ranges::equal(r, expected_range)); if (!forward_range) { // intentionally not if constexpr return true; } From cb8b529a4c0f05dde48dfc1651a0d1b61e508ad5 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 10:11:25 +0100 Subject: [PATCH 06/43] Make `_Left` and `_Right` arguments const in `_(Multiply/Add)_with_overflow_check` https://github.com/microsoft/STL/pull/3561#discussion_r1142786563 --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 03580d4314..b0be2ee630 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9554,7 +9554,7 @@ namespace ranges { } // namespace views template <_Integer_like _Int> - _NODISCARD constexpr bool _Add_with_overflow_check(_Int _Left, _Int _Right, _Int& _Out) { + _NODISCARD constexpr bool _Add_with_overflow_check(const _Int _Left, const _Int _Right, _Int& _Out) { #ifdef __clang__ if constexpr (integral<_Int>) { return __builtin_add_overflow(_Left, _Right, &_Out); @@ -9573,7 +9573,7 @@ namespace ranges { } template <_Integer_like _Int> - _NODISCARD constexpr bool _Multiply_with_overflow_check(_Int _Left, _Int _Right, _Int& _Out) { + _NODISCARD constexpr bool _Multiply_with_overflow_check(const _Int _Left, const _Int _Right, _Int& _Out) { #ifdef __clang__ if constexpr (integral<_Int>) { return __builtin_mul_overflow(_Left, _Right, &_Out); From f3bb66501987aff1eedf306809712a50ab6b7f91 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 10:13:44 +0100 Subject: [PATCH 07/43] Remove `static` from `_Cartesian_product_optimal_size_type` https://github.com/microsoft/STL/pull/3561#discussion_r1142813218 --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index b0be2ee630..dae60b00e9 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9693,7 +9693,7 @@ namespace ranges { } template - _NODISCARD static consteval auto _Cartesian_product_optimal_size_type() noexcept { + _NODISCARD consteval auto _Cartesian_product_optimal_size_type() noexcept { constexpr int _Optimal_size_type_bit_width = (_Cartesian_product_max_size_bit_width<_First>() + ... + _Cartesian_product_max_size_bit_width<_Rest>()); if constexpr (_Optimal_size_type_bit_width <= 8) { From 72d6cbf890bf038212fbf007e4d403e5799624d9 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 10:15:21 +0100 Subject: [PATCH 08/43] `_Range_size` -> `_Size` https://github.com/microsoft/STL/pull/3561#discussion_r1142825477 --- stl/inc/ranges | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index dae60b00e9..fc8a8c4912 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9799,9 +9799,9 @@ namespace ranges { _STL_VERIFY(_It_off + _Off >= 0, "Cannot advance cartesian_product_view iterator before begin " "(N4928 [range.cartesian.iterator]/19)."); if constexpr (sized_range<_Rng>) { - const auto _Range_size = static_cast<_Difference_type<_Const>>(_RANGES ssize(_Range)); - _STL_VERIFY(_It_off + _Off < _Range_size - || (_It_off + _Off == _Range_size + const auto _Size = static_cast<_Difference_type<_Const>>(_RANGES ssize(_Range)); + _STL_VERIFY(_It_off + _Off < _Size + || (_It_off + _Off == _Size && _Entire_tail_at_begin(make_index_sequence{})), "Cannot advance cartesian_product_view iterator past end (N4928 " "[range.cartesian.iterator]/19)."); From c9b6490514574fb96886c17389a9e8df7c9b447d Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 10:16:17 +0100 Subject: [PATCH 09/43] `*((*this) + _Off)` -> `*(*this + _Off)` https://github.com/microsoft/STL/pull/3561#discussion_r1142835197 --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index fc8a8c4912..9a375b5fe2 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9935,7 +9935,7 @@ namespace ranges { _NODISCARD constexpr reference operator[](const difference_type _Off) const requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> { - return *((*this) + _Off); + return *(*this + _Off); } _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) From f5f502f9bffd7c0ac9a64f8a8161639bba87692b Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 10:17:03 +0100 Subject: [PATCH 10/43] `operator-`: `difference_type _Off` should be const https://github.com/microsoft/STL/pull/3561#discussion_r1142837094 --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 9a375b5fe2..67b6ac3553 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9966,7 +9966,7 @@ namespace ranges { return _It + _Off; } - _NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _It, difference_type _Off) + _NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _It, const difference_type _Off) requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> { return _Iterator{_It} -= _Off; From 8525f6b80ca8e2704bab60515a3302e2816368e7 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 10:17:56 +0100 Subject: [PATCH 11/43] `iter_swap` should be just `friend` --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 67b6ac3553..7b48f7df8b 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9995,7 +9995,7 @@ namespace ranges { return _RANGES _Tuple_transform(_RANGES iter_move, _It._Current); } - _NODISCARD_FRIEND constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( _Is_iter_swap_nothrow(make_index_sequence<1 + sizeof...(_Rest)>{})) requires (indirectly_swappable>> && ... && indirectly_swappable>>) From 95a9d02b774af7eac6ac7a465715c472aadadfcd Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 10:18:33 +0100 Subject: [PATCH 12/43] `_FirstBase, _OtherBases` -> `_First_base, _Other_bases` https://github.com/microsoft/STL/pull/3561#discussion_r1142840595 --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 7b48f7df8b..ec6e67e1b8 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -10031,9 +10031,9 @@ namespace ranges { public: constexpr cartesian_product_view() = default; - constexpr explicit cartesian_product_view(_First _FirstBase, _Rest... _OtherBases) noexcept( + constexpr explicit cartesian_product_view(_First _First_base, _Rest... _Other_bases) noexcept( (is_nothrow_move_constructible_v<_First> && ... && is_nothrow_move_constructible_v<_Rest>) ) // strengthened - : _Bases(_STD move(_FirstBase), _STD move(_OtherBases)...) {} + : _Bases(_STD move(_First_base), _STD move(_Other_bases)...) {} _NODISCARD constexpr _Iterator begin() requires (!_Simple_view<_First> || ... || !_Simple_view<_Rest>) From 31035c3365f76b4aa400058527116bfb63df7182 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 10:19:24 +0100 Subject: [PATCH 13/43] Add `^^^` for `_CONTAINER_DEBUG_LEVEL == 0` comment https://github.com/microsoft/STL/pull/3561#discussion_r1142843840 --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index ec6e67e1b8..79f8953acd 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -10094,7 +10094,7 @@ namespace ranges { return _Product; #else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv return (static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))) * ...); -#endif // _CONTAINER_DEBUG_LEVEL == 0 +#endif // ^^^ _CONTAINER_DEBUG_LEVEL == 0 ^^^ } (make_index_sequence<1 + sizeof...(_Rest)>{}); } @@ -10114,7 +10114,7 @@ namespace ranges { return _Product; #else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv return (static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))) * ...); -#endif // _CONTAINER_DEBUG_LEVEL == 0 +#endif // ^^^ _CONTAINER_DEBUG_LEVEL == 0 ^^^ } (make_index_sequence<1 + sizeof...(_Rest)>{}); } From 1e0134742c0267241e28794b3ece1b8929d10e73 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 14:59:18 +0100 Subject: [PATCH 14/43] Add LLVM banner and remove link to file - permalink is too long :( https://github.com/microsoft/STL/pull/3561#discussion_r1142801313 --- stl/inc/ranges | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 79f8953acd..0deb1fc3ac 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9584,8 +9584,14 @@ namespace ranges { _Out = _Left * _Right; return _Left != 0 && _Right != 0 && (_Out < _Left || _Out < _Right); } else { - // Based on llvm::MulOverflow - // See https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Support/MathExtras.h + // vvv Based on llvm::MulOverflow vvv + //===----------------------------------------------------------------------===// + // + // 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 + // + //===----------------------------------------------------------------------===// using _UInt = _Make_unsigned_like_t<_Int>; const _UInt _ULeft = _Left < 0 ? (0 - static_cast<_UInt>(_Left)) : static_cast<_UInt>(_Left); const _UInt _URight = _Right < 0 ? (0 - static_cast<_UInt>(_Right)) : static_cast<_UInt>(_Right); @@ -9602,6 +9608,7 @@ namespace ranges { } else { return _ULeft > static_cast<_UInt>((numeric_limits<_Int>::max)()) / _URight; } + // ^^^ Based on llvm::MulOverflow ^^^ } } } From 428d3e45d431879f2ed58beb1730c9b1cf60477a Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 15:05:18 +0100 Subject: [PATCH 15/43] Add alias for `_Difference_type` in `_Advance` https://github.com/microsoft/STL/pull/3561#discussion_r1142823856 --- stl/inc/ranges | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 0deb1fc3ac..c393e50302 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9785,14 +9785,15 @@ namespace ranges { auto& _It = _STD get<_Index>(_Current); using _Rng = remove_reference_t; using _Iter = remove_reference_t; + using _Diff = _Difference_type<_Const>; if constexpr (_Index > 0) { _STL_INTERNAL_STATIC_ASSERT(sized_range<_Rng>); - const auto _Size = static_cast<_Difference_type<_Const>>(_RANGES ssize(_Range)); + const auto _Size = static_cast<_Diff>(_RANGES ssize(_Range)); const auto _Begin = _RANGES begin(_Range); - const auto _It_off = static_cast<_Difference_type<_Const>>(_It - _Begin); - const auto _It_new_off = static_cast<_Difference_type<_Const>>((_It_off + _Off) % _Size); - const auto _Next_off = static_cast<_Difference_type<_Const>>((_It_off + _Off) / _Size); + const auto _It_off = static_cast<_Diff>(_It - _Begin); + const auto _It_new_off = static_cast<_Diff>((_It_off + _Off) % _Size); + const auto _Next_off = static_cast<_Diff>((_It_off + _Off) / _Size); if (_It_new_off < 0) { _It = _Begin + static_cast>(_It_new_off + _Size); _Advance<_Index - 1>(_Next_off - 1); @@ -9802,11 +9803,11 @@ namespace ranges { } } else { #if _ITERATOR_DEBUG_LEVEL != 0 - const auto _It_off = static_cast<_Difference_type<_Const>>(_It - _RANGES begin(_Range)); + const auto _It_off = static_cast<_Diff>(_It - _RANGES begin(_Range)); _STL_VERIFY(_It_off + _Off >= 0, "Cannot advance cartesian_product_view iterator before begin " "(N4928 [range.cartesian.iterator]/19)."); if constexpr (sized_range<_Rng>) { - const auto _Size = static_cast<_Difference_type<_Const>>(_RANGES ssize(_Range)); + const auto _Size = static_cast<_Diff>(_RANGES ssize(_Range)); _STL_VERIFY(_It_off + _Off < _Size || (_It_off + _Off == _Size && _Entire_tail_at_begin(make_index_sequence{})), From 6a933383f1428ae922605ba961b2929b74a99100 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 15:12:41 +0100 Subject: [PATCH 16/43] Yeet away internal assertion https://github.com/microsoft/STL/pull/3561#discussion_r1142824607 --- stl/inc/ranges | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index c393e50302..18b8864a72 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9785,10 +9785,9 @@ namespace ranges { auto& _It = _STD get<_Index>(_Current); using _Rng = remove_reference_t; using _Iter = remove_reference_t; - using _Diff = _Difference_type<_Const>; + using _Diff = _Difference_type<_Const>; if constexpr (_Index > 0) { - _STL_INTERNAL_STATIC_ASSERT(sized_range<_Rng>); const auto _Size = static_cast<_Diff>(_RANGES ssize(_Range)); const auto _Begin = _RANGES begin(_Range); const auto _It_off = static_cast<_Diff>(_It - _Begin); From 759879cc6e95bf557c40f49c34f0df1ebe0cec4d Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 15:13:50 +0100 Subject: [PATCH 17/43] =?UTF-8?q?=F0=9F=90=BF=EF=B8=8F=20=E2=9A=94=20`^`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/microsoft/STL/pull/3561#discussion_r1142783538 --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 18b8864a72..07ac65ca55 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9597,7 +9597,7 @@ namespace ranges { const _UInt _URight = _Right < 0 ? (0 - static_cast<_UInt>(_Right)) : static_cast<_UInt>(_Right); const _UInt _UResult = _ULeft * _URight; - const bool _Negative = (_Left < 0) ^ (_Right < 0); + const bool _Negative = (_Left < 0) != (_Right < 0); _Out = static_cast<_Int>(_Negative ? (0 - _UResult) : _UResult); if (_ULeft == 0 || _URight == 0) { return false; From 8923a30c89960178a26b444a07a732785de603b0 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 15:17:44 +0100 Subject: [PATCH 18/43] Don't mention internal machinery: `_Size_type<>` -> `size type` https://github.com/microsoft/STL/pull/3561#discussion_r1142844546 --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 07ac65ca55..aec7ffd546 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -10096,7 +10096,7 @@ namespace ranges { (_Multiply_with_overflow_check( _Product, static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))), _Product) || ...); - _STL_VERIFY(!_Overflow, "Size of cartesian product cannot be represented by _Size_type (N4928 " + _STL_VERIFY(!_Overflow, "Size of cartesian product cannot be represented by size type (N4928 " "[range.cartesian.view]/10)."); return _Product; #else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv @@ -10116,7 +10116,7 @@ namespace ranges { (_Multiply_with_overflow_check( _Product, static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))), _Product) || ...); - _STL_VERIFY(!_Overflow, "Size of cartesian product cannot be represented by _Size_type (N4928 " + _STL_VERIFY(!_Overflow, "Size of cartesian product cannot be represented by size type (N4928 " "[range.cartesian.view]/10)."); return _Product; #else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv From eab66c1a4b4b536eb8e09c500b63b486635cf6a5 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 15:20:20 +0100 Subject: [PATCH 19/43] `consteval` -> `_CONSTEVAL` for both `cartesian_product` and `adjacent_transform` (as drive-by) https://github.com/microsoft/STL/pull/3561#discussion_r1142829542 --- stl/inc/ranges | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index aec7ffd546..e670e17aa8 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9230,7 +9230,7 @@ namespace ranges { is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened : _Parent(_STD addressof(_Parent_)), _Inner(_STD move(_Inner_)) {} - _NODISCARD static consteval auto _Get_iterator_category() noexcept { + _NODISCARD static _CONSTEVAL auto _Get_iterator_category() noexcept { if constexpr (!is_reference_v<_Invoke_result_with_repeated_type<_Maybe_const<_Const, _Fn>&, range_reference_t<_Base>, _Nx>>) { return input_iterator_tag{}; @@ -9250,7 +9250,7 @@ namespace ranges { } template - _NODISCARD static consteval bool _Is_indirection_nothrow(index_sequence<_Indices...>) noexcept { + _NODISCARD static _CONSTEVAL bool _Is_indirection_nothrow(index_sequence<_Indices...>) noexcept { return noexcept(_STD invoke(_STD declval<_Maybe_const<_Const, _Fn>&>(), *_STD get<_Indices>(_STD declval&>()._Current)...)); } @@ -9687,7 +9687,7 @@ namespace ranges { inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size; template - _NODISCARD consteval int _Cartesian_product_max_size_bit_width() noexcept { + _NODISCARD _CONSTEVAL int _Cartesian_product_max_size_bit_width() noexcept { if constexpr (sized_range<_Rng>) { if constexpr (requires(range_size_t<_Rng> _Val) { _STD bit_width(_Val); }) { return _STD bit_width(_Compile_time_max_size<_Rng>); @@ -9700,7 +9700,7 @@ namespace ranges { } template - _NODISCARD consteval auto _Cartesian_product_optimal_size_type() noexcept { + _NODISCARD _CONSTEVAL auto _Cartesian_product_optimal_size_type() noexcept { constexpr int _Optimal_size_type_bit_width = (_Cartesian_product_max_size_bit_width<_First>() + ... + _Cartesian_product_max_size_bit_width<_Rest>()); if constexpr (_Optimal_size_type_bit_width <= 8) { From 3a3f595e3f7873d48c20ec580cf7b0e7d71d74c7 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 15:29:15 +0100 Subject: [PATCH 20/43] Use permalink to LLVM code https://github.com/microsoft/STL/pull/3561#discussion_r1142797699 --- stl/inc/ranges | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index e670e17aa8..88c2dc00b1 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9585,6 +9585,7 @@ namespace ranges { return _Left != 0 && _Right != 0 && (_Out < _Left || _Out < _Right); } else { // vvv Based on llvm::MulOverflow vvv + // https://github.com/llvm/llvm-project/blob/88e5206/llvm/include/llvm/Support/MathExtras.h#L725-L750 //===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. @@ -9626,7 +9627,7 @@ namespace ranges { && (bidirectional_range<_Maybe_const<_Const, _Rest>> && _Cartesian_product_common_arg<_Maybe_const<_Const, _Rest>>) ); - template + template concept _Cartesian_product_is_common = _Cartesian_product_common_arg<_First>; template From e18ae03faa959578546a8bf66426f26bffa49885 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 15:35:21 +0100 Subject: [PATCH 21/43] Cast to `_Int` to avoid truncation warnings https://github.com/microsoft/STL/pull/3561/files/86a83e58605c909eddba7be763c86cbfc9a7c17f#r1142788979 https://github.com/microsoft/STL/pull/3561#discussion_r1142803586 --- stl/inc/ranges | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 88c2dc00b1..56b617098e 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9562,7 +9562,7 @@ namespace ranges { #endif // __clang__ { if constexpr (!_Signed_integer_like<_Int>) { - _Out = _Left + _Right; + _Out = static_cast<_Int>(_Left + _Right); return _Out < _Left || _Out < _Right; } else { using _UInt = _Make_unsigned_like_t<_Int>; @@ -9581,7 +9581,7 @@ namespace ranges { #endif // __clang__ { if constexpr (!_Signed_integer_like<_Int>) { - _Out = _Left * _Right; + _Out = static_cast<_Int>(_Left * _Right); return _Left != 0 && _Right != 0 && (_Out < _Left || _Out < _Right); } else { // vvv Based on llvm::MulOverflow vvv @@ -9596,7 +9596,7 @@ namespace ranges { using _UInt = _Make_unsigned_like_t<_Int>; const _UInt _ULeft = _Left < 0 ? (0 - static_cast<_UInt>(_Left)) : static_cast<_UInt>(_Left); const _UInt _URight = _Right < 0 ? (0 - static_cast<_UInt>(_Right)) : static_cast<_UInt>(_Right); - const _UInt _UResult = _ULeft * _URight; + const _UInt _UResult = static_cast<_UInt>(_ULeft * _URight); const bool _Negative = (_Left < 0) != (_Right < 0); _Out = static_cast<_Int>(_Negative ? (0 - _UResult) : _UResult); From 90ed8806ffbbf6dfe5ae62a22a214cfd8ee139f2 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 15:42:45 +0100 Subject: [PATCH 22/43] Fix formatting --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 56b617098e..5e59652b86 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9627,7 +9627,7 @@ namespace ranges { && (bidirectional_range<_Maybe_const<_Const, _Rest>> && _Cartesian_product_common_arg<_Maybe_const<_Const, _Rest>>) ); - template + template concept _Cartesian_product_is_common = _Cartesian_product_common_arg<_First>; template From fcf546940d1429391509f6419138da9b16ea80cd Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 16:08:06 +0100 Subject: [PATCH 23/43] Change overflow detection method https://github.com/microsoft/STL/pull/3561#discussion_r1142795871 --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 5e59652b86..eb39b5a05a 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9582,7 +9582,7 @@ namespace ranges { { if constexpr (!_Signed_integer_like<_Int>) { _Out = static_cast<_Int>(_Left * _Right); - return _Left != 0 && _Right != 0 && (_Out < _Left || _Out < _Right); + return _Left != 0 && _Right > (numeric_limits<_Int>::max)() / _Left; } else { // vvv Based on llvm::MulOverflow vvv // https://github.com/llvm/llvm-project/blob/88e5206/llvm/include/llvm/Support/MathExtras.h#L725-L750 From feb062c8775d3473e0ee04f9e6e9bf6f3bb4a48d Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 21 Mar 2023 16:47:10 +0100 Subject: [PATCH 24/43] Fix CI --- stl/inc/ranges | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index eb39b5a05a..823e2d0f21 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9784,7 +9784,6 @@ namespace ranges { auto& _Range = _STD get<_Index>(_Parent->_Bases); auto& _It = _STD get<_Index>(_Current); - using _Rng = remove_reference_t; using _Iter = remove_reference_t; using _Diff = _Difference_type<_Const>; @@ -9806,7 +9805,7 @@ namespace ranges { const auto _It_off = static_cast<_Diff>(_It - _RANGES begin(_Range)); _STL_VERIFY(_It_off + _Off >= 0, "Cannot advance cartesian_product_view iterator before begin " "(N4928 [range.cartesian.iterator]/19)."); - if constexpr (sized_range<_Rng>) { + if constexpr (sized_range) { const auto _Size = static_cast<_Diff>(_RANGES ssize(_Range)); _STL_VERIFY(_It_off + _Off < _Size || (_It_off + _Off == _Size From 91284ba89332d4acf1b1cc2e9111d61919ccd59a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 12:35:37 -0700 Subject: [PATCH 25/43] Perform overflow checking in the iterator for IDL != 0. --- stl/inc/ranges | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 823e2d0f21..72af84ec39 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9837,11 +9837,13 @@ namespace ranges { _Difference_type<_Const> _Result{1}; const auto _Size = static_cast<_Difference_type<_Const>>(_RANGES size(_STD get<_Index>(_Parent->_Bases))); - const bool _Overflow = + [[maybe_unused]] const bool _Overflow = _Multiply_with_overflow_check(_Size, _Distance_from<_Index - 1>(_Tpl), _Result) || _Add_with_overflow_check(_Result, _Diff, _Result); +#if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by difference_type (N4928 " "[range.cartesian.iterator]/8)."); +#endif // _ITERATOR_DEBUG_LEVEL != 0 return _Result; } else { return _Diff; From 8d727c740908cf11617ba7c982784e95dec594f8 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 13:47:15 -0700 Subject: [PATCH 26/43] `ranges::input_range` was repeated. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 4a9ebb3e8f..3740c18886 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -73,10 +73,9 @@ constexpr bool is_iter_swap_nothrow(index_sequence) { template constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest) { - using ranges::cartesian_product_view, ranges::view, ranges::input_range, ranges::input_range, ranges::forward_range, - ranges::range, ranges::range_value_t, ranges::range_reference_t, ranges::range_rvalue_reference_t, - ranges::range_difference_t, ranges::sentinel_t, ranges::prev, ranges::const_iterator_t, - ranges::const_sentinel_t; + using ranges::cartesian_product_view, ranges::view, ranges::input_range, ranges::forward_range, ranges::range, + ranges::range_value_t, ranges::range_reference_t, ranges::range_rvalue_reference_t, ranges::range_difference_t, + ranges::sentinel_t, ranges::prev, ranges::const_iterator_t, ranges::const_sentinel_t; using views::all_t; using VFirst = all_t; From 41bfec183bf83e2a3cafebe640788c885f1d609b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 13:47:41 -0700 Subject: [PATCH 27/43] `ranges::iterator_t` was unnecessarily qualified. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 3740c18886..4e102f6b45 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -773,7 +773,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest STATIC_ASSERT(same_as); template - requires (indirectly_swappable> && ...) + requires (indirectly_swappable> && ...) constexpr void test_iter_swap(Rngs&... rngs) { // This test assumes that 'ranges::size(rng)' is at least 2 for each rng in rngs auto r = views::cartesian_product(rngs...); @@ -796,7 +796,7 @@ constexpr void test_iter_swap(Rngs&... rngs) { } // Check iter_swap for cartesian_product_view::iterator - if constexpr (((CanMemberBegin && indirectly_swappable>) &&...)) { + if constexpr (((CanMemberBegin && indirectly_swappable>) &&...)) { using CVal = ranges::range_value_t; auto i = as_const(r).begin(); CVal first = *i; From 1a0562c2c399773698051ac16687d1c4b4822eaf Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 13:50:54 -0700 Subject: [PATCH 28/43] Improve clang-formatting. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 4e102f6b45..14e4c8e0da 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -58,7 +58,8 @@ template constexpr bool is_iter_move_nothrow(index_sequence) { constexpr bool is_inner_iter_move_nothrow = (noexcept(ranges::iter_move(declval&>())) - && ...&& noexcept(ranges::iter_move(declval&>()))); + && ... // + && noexcept(ranges::iter_move(declval&>()))); constexpr bool are_references_nothrow_movable = conjunction_v>, is_nothrow_move_constructible>...>; @@ -68,7 +69,8 @@ constexpr bool is_iter_move_nothrow(index_sequence) { template constexpr bool is_iter_swap_nothrow(index_sequence) { return (noexcept(ranges::iter_swap(declval&>())) - && ...&& noexcept(ranges::iter_swap(declval&>()))); + && ... // + && noexcept(ranges::iter_swap(declval&>()))); } template From 98b3a7da4347568065afe5ea11ba7be39082275e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 13:57:45 -0700 Subject: [PATCH 29/43] Comment nitpicks. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 14e4c8e0da..60d1816608 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -761,7 +761,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest if constexpr ((indirectly_swappable> && ... && indirectly_swappable>>) ) { - // Check iter_swap other tests are defined in test_iter_swap function + // Check iter_swap, other tests are defined in test_iter_swap function static_assert(is_void_v); static_assert(noexcept(iter_swap(ci, ci)) == is_iter_swap_nothrow...>(make_index_sequence<1 + sizeof...(Rest)>{})); @@ -975,7 +975,7 @@ namespace check_recommended_practice_implementation { // MSVC STL specific behav STATIC_ASSERT(sizeof(range_size_t>>) <= sizeof(size_t)); STATIC_ASSERT(sizeof(range_difference_t>>) <= sizeof(ptrdiff_t)); - // ...but two vectors will + // ... but two vectors will STATIC_ASSERT(sizeof(range_size_t, all_t>>) > sizeof(size_t)); STATIC_ASSERT(sizeof(range_difference_t, all_t>>) > sizeof(ptrdiff_t)); } // namespace check_recommended_practice_implementation From a916aef8998639aad47e36ec8cf2b94a2450fcca Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:05:36 -0700 Subject: [PATCH 30/43] Fix `is_iter_swap_nothrow()` to call `ranges::iter_swap` with 2 args. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 60d1816608..a44b76a12e 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -68,9 +68,9 @@ constexpr bool is_iter_move_nothrow(index_sequence) { template constexpr bool is_iter_swap_nothrow(index_sequence) { - return (noexcept(ranges::iter_swap(declval&>())) + return (noexcept(ranges::iter_swap(declval&>(), declval&>())) && ... // - && noexcept(ranges::iter_swap(declval&>()))); + && noexcept(ranges::iter_swap(declval&>(), declval&>()))); } template From 0296071b8ce4d4f6270c25ffeac85d7ef7ad24af Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:15:31 -0700 Subject: [PATCH 31/43] Test modifiable elements to exercise `is_iter_swap_nothrow`. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index a44b76a12e..bab8423d4e 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -988,6 +988,12 @@ int main() { test_one(get<0>(expected_results), s); } + { // ... modifiable elements (so iterators are indirectly swappable) + auto arr = get<0>(some_ranges); + span s{arr}; + test_one(get<0>(expected_results), s); + } + { // ... move-only using test::Common, test::Sized; test_one(get<3>(expected_results), // From d9e4dfa27be15fd00dc405ec6308575b3fa37aa6 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:22:22 -0700 Subject: [PATCH 32/43] We take `input_range First` so this is guaranteed. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index bab8423d4e..9026e839f9 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -95,7 +95,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest constexpr bool is_const_common = CartesianProductIsCommon...>; STATIC_ASSERT(view); - STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(input_range); STATIC_ASSERT(forward_range == forward_range); STATIC_ASSERT(bidirectional_range == is_bidirectional); STATIC_ASSERT(random_access_range == is_random_access); From 7b916900aafba1a361c2d62f5e9455b04c9a9a52 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:27:11 -0700 Subject: [PATCH 33/43] `cartesian_view` => `cartesian_product_view` --- .../P2374R4_views_cartesian_product/test.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 9026e839f9..06bcb1a6a2 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -165,14 +165,14 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest // Check deduction guide same_as auto r = cartesian_product_view{forward(first), forward(rest)...}; - // Check cartesian_view::size + // Check cartesian_product_view::size STATIC_ASSERT(CanMemberSize == is_sized); if constexpr (CanMemberSize) { UnsignedIntegerLike auto s = r.size(); assert(s == ranges::size(expected_range)); } - // Check cartesian_view::size (const) + // Check cartesian_product_view::size (const) STATIC_ASSERT(CanMemberSize == is_const_sized); if constexpr (CanMemberSize) { UnsignedIntegerLike auto s = as_const(r).size(); @@ -202,7 +202,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest return true; } - // Check cartesian_view::begin + // Check cartesian_product_view::begin STATIC_ASSERT(CanMemberBegin); { const same_as> auto i = r.begin(); @@ -219,7 +219,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest } } - // Check cartesian_view::begin (const) + // Check cartesian_product_view::begin (const) STATIC_ASSERT(CanMemberBegin == (range && ... && range>) ); if constexpr (CanMemberBegin) { const same_as> auto ci = as_const(r).begin(); @@ -236,7 +236,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest } } - // Check cartesian_view::end + // Check cartesian_product_view::end STATIC_ASSERT(CanMemberEnd); { const same_as> auto s = r.end(); @@ -260,7 +260,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest } } - // Check cartesian_view::end (const) + // Check cartesian_product_view::end (const) STATIC_ASSERT(CanMemberEnd); if constexpr (CanMemberEnd) { const same_as> auto cs = as_const(r).end(); @@ -370,7 +370,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest assert(as_const(r).back() == *prev(end(expected_range))); } - { // Check cartesian_view::iterator + { // Check cartesian_product_view::iterator using I = iterator_t; STATIC_ASSERT(input_iterator); @@ -543,7 +543,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest } } - // Check cartesian_view::iterator + // Check cartesian_product_view::iterator if constexpr (CanMemberBegin) { using CI = iterator_t; STATIC_ASSERT(input_iterator); From 16b2db4e41994b624223876639935d060543450b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:32:34 -0700 Subject: [PATCH 34/43] Include ``, ``. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 1 + tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 06bcb1a6a2..e0dcfd8f57 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp index f8e9639d7c..65b850caaf 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include From ce0f0b313167a02313dd6998cae7b35a8df3e0b7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:35:45 -0700 Subject: [PATCH 35/43] Drop unnecessary parens. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index e0dcfd8f57..510002b483 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -513,8 +513,8 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest if constexpr (CartesianIsSizedSentinel...>) { // Check differencing _Signed_integer_like auto diff = i - i; assert(diff == 0); - assert((i - ranges::next(i)) == -1); - assert((ranges::next(i) - i) == 1); + assert(i - ranges::next(i) == -1); + assert(ranges::next(i) - i == 1); } STATIC_ASSERT(sized_sentinel_for @@ -737,8 +737,8 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest const all_t...>) { // Check differencing _Signed_integer_like auto diff = ci - ci; assert(diff == 0); - assert((ci - ranges::next(ci)) == -1); - assert((ranges::next(ci) - ci) == 1); + assert(ci - ranges::next(ci) == -1); + assert(ranges::next(ci) - ci == 1); } STATIC_ASSERT(sized_sentinel_for From 83866ac2d9756960e27cd1ae0bd3df235631962a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:38:04 -0700 Subject: [PATCH 36/43] `size` => `expected_size` --- .../tests/P2374R4_views_cartesian_product/test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 510002b483..30a7141223 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -520,11 +520,11 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest STATIC_ASSERT(sized_sentinel_for == CartesianIsSizedSentinel...>); if constexpr (sized_sentinel_for) { // Check differencing with default_sentinel - const auto size = ranges::ssize(expected_range); + const auto expected_size = ranges::ssize(expected_range); const _Signed_integer_like auto diff1 = i - default_sentinel; - assert(diff1 == -size); + assert(diff1 == -expected_size); const _Signed_integer_like auto diff2 = default_sentinel - i; - assert(diff2 == size); + assert(diff2 == expected_size); } { // Check iter_move (hidden friend available via ADL) @@ -744,11 +744,11 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest STATIC_ASSERT(sized_sentinel_for == CartesianIsSizedSentinel...>); if constexpr (sized_sentinel_for) { // Check differencing with default_sentinel - const auto size = ranges::ssize(expected_range); + const auto expected_size = ranges::ssize(expected_range); const _Signed_integer_like auto diff1 = ci - default_sentinel; - assert(diff1 == -size); + assert(diff1 == -expected_size); const _Signed_integer_like auto diff2 = default_sentinel - ci; - assert(diff2 == size); + assert(diff2 == expected_size); } { // Check iter_move From 931f3481bd528a949278b54c05022c4c21d81261 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:43:13 -0700 Subject: [PATCH 37/43] Check `forward_range` when testing const iterators. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 30a7141223..bd804ec705 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -592,7 +592,7 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest ci = as_const(r).begin(); } - if constexpr (forward_range) { // Check post-incrementation + if constexpr (forward_range) { // Check post-incrementation same_as decltype(auto) ci2 = ci++; assert(*ci2 == expected_range[0]); if (ci != as_const(r).end()) { From b9b776ee771237abe2a5cdca006fc99e86c5382f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:47:32 -0700 Subject: [PATCH 38/43] Drop copy-pasted code that isn't testing mixed comparisons. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index bd804ec705..5abded7ad5 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -666,11 +666,6 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest ++ci2; same_as auto b2 = i != ci2; assert(b2); - same_as auto b3 = ci2 != default_sentinel; - assert(b3); - ranges::advance(ci2, r.end()); - same_as auto b4 = ci2 == default_sentinel; - assert(b4); } if constexpr ((random_access_range && ... From 198b5f11bc1881c32d3fd590f3cc6145e5bc67c5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 14:53:40 -0700 Subject: [PATCH 39/43] Add missing `const`s when calling `is_iter_swap_nothrow`. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 5abded7ad5..ea8e7b609a 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -760,7 +760,8 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest // Check iter_swap, other tests are defined in test_iter_swap function static_assert(is_void_v); static_assert(noexcept(iter_swap(ci, ci)) - == is_iter_swap_nothrow...>(make_index_sequence<1 + sizeof...(Rest)>{})); + == is_iter_swap_nothrow...>( + make_index_sequence<1 + sizeof...(Rest)>{})); } } From 11f61e1734c2760b02139577fb03c4bcd0bf3c7d Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 15:01:02 -0700 Subject: [PATCH 40/43] Drop `Indices` in `is_iter_move_nothrow`, `is_iter_swap_nothrow`. --- .../P2374R4_views_cartesian_product/test.cpp | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index ea8e7b609a..c3cde47ade 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -55,8 +55,8 @@ concept CanViewCartesianProduct = requires(Ranges&&... rs) { views::cartesian_pr template concept UnsignedIntegerLike = _Integer_like && (!_Signed_integer_like); -template -constexpr bool is_iter_move_nothrow(index_sequence) { +template +constexpr bool is_iter_move_nothrow() { constexpr bool is_inner_iter_move_nothrow = (noexcept(ranges::iter_move(declval&>())) && ... // @@ -67,8 +67,8 @@ constexpr bool is_iter_move_nothrow(index_sequence) { return is_inner_iter_move_nothrow && are_references_nothrow_movable; } -template -constexpr bool is_iter_swap_nothrow(index_sequence) { +template +constexpr bool is_iter_swap_nothrow() { return (noexcept(ranges::iter_swap(declval&>(), declval&>())) && ... // && noexcept(ranges::iter_swap(declval&>(), declval&>()))); @@ -531,16 +531,14 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest same_as, range_rvalue_reference_t>...>> decltype(auto) rval = iter_move(as_const(i)); assert(rval == expected_range[0]); - static_assert(noexcept(iter_move(i)) - == is_iter_move_nothrow...>(make_index_sequence<1 + sizeof...(Rest)>{})); + static_assert(noexcept(iter_move(i)) == is_iter_move_nothrow...>()); } if constexpr ((indirectly_swappable> && ... && indirectly_swappable>>) ) { // Check iter_swap, other tests are defined in test_iter_swap function static_assert(is_void_v); - static_assert(noexcept(iter_swap(i, i)) - == is_iter_swap_nothrow...>(make_index_sequence<1 + sizeof...(Rest)>{})); + static_assert(noexcept(iter_swap(i, i)) == is_iter_swap_nothrow...>()); } } @@ -750,18 +748,14 @@ constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest same_as, range_rvalue_reference_t>...>> decltype(auto) rval = iter_move(as_const(ci)); assert(rval == expected_range[0]); - static_assert(noexcept(iter_move(ci)) - == is_iter_move_nothrow...>( - make_index_sequence<1 + sizeof...(Rest)>{})); + static_assert(noexcept(iter_move(ci)) == is_iter_move_nothrow...>()); } if constexpr ((indirectly_swappable> && ... && indirectly_swappable>>) ) { // Check iter_swap, other tests are defined in test_iter_swap function static_assert(is_void_v); - static_assert(noexcept(iter_swap(ci, ci)) - == is_iter_swap_nothrow...>( - make_index_sequence<1 + sizeof...(Rest)>{})); + static_assert(noexcept(iter_swap(ci, ci)) == is_iter_swap_nothrow...>()); } } From 530b69fa84f9cf58f3160528419b9ccdff292c2e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 15:06:24 -0700 Subject: [PATCH 41/43] Extend `test_iter_swap()` to check `is_iter_swap_nothrow`. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index c3cde47ade..49fee102bb 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -786,6 +786,8 @@ constexpr void test_iter_swap(Rngs&... rngs) { ranges::iter_swap(i, j); assert(*i == first); assert(*j == second); + + static_assert(noexcept(iter_swap(i, j)) == is_iter_swap_nothrow()); } // Check iter_swap for cartesian_product_view::iterator @@ -803,6 +805,8 @@ constexpr void test_iter_swap(Rngs&... rngs) { ranges::iter_swap(i, j); assert(*i == first); assert(*j == second); + + static_assert(noexcept(iter_swap(i, j)) == is_iter_swap_nothrow()); } } From bcd58144d44b4360d433c285ea10c3c29493f43a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Mar 2023 15:10:52 -0700 Subject: [PATCH 42/43] Don't need to check `defined(_MT)`. --- tests/std/tests/P2374R4_views_cartesian_product/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 49fee102bb..988bd41bbb 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -869,7 +869,7 @@ struct instantiator { typename R::template type r1{get<1>(some_ranges)}; test_one(get<1>(expected_results), r0, r1); -#if !(defined(__clang__) && defined(_DEBUG) && defined(_MT) && !defined(_DLL)) // constexpr limit +#if !(defined(__clang__) && defined(_DEBUG) && !defined(_DLL)) // constexpr limit typename R::template type r2{get<2>(some_ranges)}; test_one(get<2>(expected_results), r0, r1, r2); #endif // "Clang /MTd" configuration From 0fc54a9475f7a95fddb9d7f14b2e497b1ca14b0c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 29 Mar 2023 21:26:02 -0700 Subject: [PATCH 43/43] Work around `/analyze` bugs. --- .../P2374R4_views_cartesian_product/test.cpp | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp index 988bd41bbb..1c9f016d17 100644 --- a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -817,31 +817,29 @@ constexpr tuple some_ranges = { array{"4"sv, "2"sv, "0"sv}, }; -constexpr tuple expected_results = { - // Expected result of views::cartesian_product(get<0>(some_ranges)) - to_array>({{0}, {1}, {2}, {3}, {4}}), - - // Expected result of views::cartesian_product(get<0>(some_ranges), get<1>(some_ranges)) - to_array>({{0, 11}, {0, 22}, {0, 33}, {1, 11}, {1, 22}, {1, 33}, {2, 11}, {2, 22}, {2, 33}, {3, 11}, - {3, 22}, {3, 33}, {4, 11}, {4, 22}, {4, 33}}), - - // Expected result of views::cartesian_product(get<0>(some_ranges), ..., get<2>(some_ranges)) - to_array>( - {{0, 11, '7'}, {0, 22, '7'}, {0, 33, '7'}, {1, 11, '7'}, {1, 22, '7'}, {1, 33, '7'}, {2, 11, '7'}, {2, 22, '7'}, - {2, 33, '7'}, {3, 11, '7'}, {3, 22, '7'}, {3, 33, '7'}, {4, 11, '7'}, {4, 22, '7'}, {4, 33, '7'}}), - - // Expected result of views::cartesian_product(get<0>(some_ranges), ..., get<3>(some_ranges)) - to_array>( - {{0, 11, '7', "4"sv}, {0, 11, '7', "2"sv}, {0, 11, '7', "0"sv}, {0, 22, '7', "4"sv}, {0, 22, '7', "2"sv}, - {0, 22, '7', "0"sv}, {0, 33, '7', "4"sv}, {0, 33, '7', "2"sv}, {0, 33, '7', "0"sv}, {1, 11, '7', "4"sv}, - {1, 11, '7', "2"sv}, {1, 11, '7', "0"sv}, {1, 22, '7', "4"sv}, {1, 22, '7', "2"sv}, {1, 22, '7', "0"sv}, - {1, 33, '7', "4"sv}, {1, 33, '7', "2"sv}, {1, 33, '7', "0"sv}, {2, 11, '7', "4"sv}, {2, 11, '7', "2"sv}, - {2, 11, '7', "0"sv}, {2, 22, '7', "4"sv}, {2, 22, '7', "2"sv}, {2, 22, '7', "0"sv}, {2, 33, '7', "4"sv}, - {2, 33, '7', "2"sv}, {2, 33, '7', "0"sv}, {3, 11, '7', "4"sv}, {3, 11, '7', "2"sv}, {3, 11, '7', "0"sv}, - {3, 22, '7', "4"sv}, {3, 22, '7', "2"sv}, {3, 22, '7', "0"sv}, {3, 33, '7', "4"sv}, {3, 33, '7', "2"sv}, - {3, 33, '7', "0"sv}, {4, 11, '7', "4"sv}, {4, 11, '7', "2"sv}, {4, 11, '7', "0"sv}, {4, 22, '7', "4"sv}, - {4, 22, '7', "2"sv}, {4, 22, '7', "0"sv}, {4, 33, '7', "4"sv}, {4, 33, '7', "2"sv}, {4, 33, '7', "0"sv}}), -}; +// Expected result of views::cartesian_product(get<0>(some_ranges)) +constexpr array, 5> expected_result_0{{{0}, {1}, {2}, {3}, {4}}}; + +// Expected result of views::cartesian_product(get<0>(some_ranges), get<1>(some_ranges)) +constexpr array, 15> expected_result_1{{{0, 11}, {0, 22}, {0, 33}, {1, 11}, {1, 22}, {1, 33}, {2, 11}, + {2, 22}, {2, 33}, {3, 11}, {3, 22}, {3, 33}, {4, 11}, {4, 22}, {4, 33}}}; + +// Expected result of views::cartesian_product(get<0>(some_ranges), ..., get<2>(some_ranges)) +constexpr array, 15> expected_result_2{ + {{0, 11, '7'}, {0, 22, '7'}, {0, 33, '7'}, {1, 11, '7'}, {1, 22, '7'}, {1, 33, '7'}, {2, 11, '7'}, {2, 22, '7'}, + {2, 33, '7'}, {3, 11, '7'}, {3, 22, '7'}, {3, 33, '7'}, {4, 11, '7'}, {4, 22, '7'}, {4, 33, '7'}}}; + +// Expected result of views::cartesian_product(get<0>(some_ranges), ..., get<3>(some_ranges)) +constexpr array, 45> expected_result_3{ + {{0, 11, '7', "4"sv}, {0, 11, '7', "2"sv}, {0, 11, '7', "0"sv}, {0, 22, '7', "4"sv}, {0, 22, '7', "2"sv}, + {0, 22, '7', "0"sv}, {0, 33, '7', "4"sv}, {0, 33, '7', "2"sv}, {0, 33, '7', "0"sv}, {1, 11, '7', "4"sv}, + {1, 11, '7', "2"sv}, {1, 11, '7', "0"sv}, {1, 22, '7', "4"sv}, {1, 22, '7', "2"sv}, {1, 22, '7', "0"sv}, + {1, 33, '7', "4"sv}, {1, 33, '7', "2"sv}, {1, 33, '7', "0"sv}, {2, 11, '7', "4"sv}, {2, 11, '7', "2"sv}, + {2, 11, '7', "0"sv}, {2, 22, '7', "4"sv}, {2, 22, '7', "2"sv}, {2, 22, '7', "0"sv}, {2, 33, '7', "4"sv}, + {2, 33, '7', "2"sv}, {2, 33, '7', "0"sv}, {3, 11, '7', "4"sv}, {3, 11, '7', "2"sv}, {3, 11, '7', "0"sv}, + {3, 22, '7', "4"sv}, {3, 22, '7', "2"sv}, {3, 22, '7', "0"sv}, {3, 33, '7', "4"sv}, {3, 33, '7', "2"sv}, + {3, 33, '7', "0"sv}, {4, 11, '7', "4"sv}, {4, 11, '7', "2"sv}, {4, 11, '7', "0"sv}, {4, 22, '7', "4"sv}, + {4, 22, '7', "2"sv}, {4, 22, '7', "0"sv}, {4, 33, '7', "4"sv}, {4, 33, '7', "2"sv}, {4, 33, '7', "0"sv}}}; template struct test_input_range { @@ -863,15 +861,15 @@ struct instantiator { template static constexpr void call() { typename R::template type r0{get<0>(some_ranges)}; - test_one(get<0>(expected_results), r0); + test_one(expected_result_0, r0); if constexpr (ranges::forward_range>) { typename R::template type r1{get<1>(some_ranges)}; - test_one(get<1>(expected_results), r0, r1); + test_one(expected_result_1, r0, r1); #if !(defined(__clang__) && defined(_DEBUG) && !defined(_DLL)) // constexpr limit typename R::template type r2{get<2>(some_ranges)}; - test_one(get<2>(expected_results), r0, r1, r2); + test_one(expected_result_2, r0, r1, r2); #endif // "Clang /MTd" configuration int swap_a1[] = {1, 2, 3}; @@ -979,33 +977,33 @@ int main() { // Check views { // ... copyable constexpr span s{get<0>(some_ranges)}; - STATIC_ASSERT(test_one(get<0>(expected_results), s)); - test_one(get<0>(expected_results), s); + STATIC_ASSERT(test_one(expected_result_0, s)); + test_one(expected_result_0, s); } { // ... modifiable elements (so iterators are indirectly swappable) auto arr = get<0>(some_ranges); span s{arr}; - test_one(get<0>(expected_results), s); + test_one(expected_result_0, s); } { // ... move-only using test::Common, test::Sized; - test_one(get<3>(expected_results), // + test_one(expected_result_3, // move_only_view{get<0>(some_ranges)}, move_only_view{get<1>(some_ranges)}, move_only_view{get<2>(some_ranges)}, move_only_view{get<3>(some_ranges)}); - test_one(get<3>(expected_results), // + test_one(expected_result_3, // move_only_view{get<0>(some_ranges)}, move_only_view{get<1>(some_ranges)}, move_only_view{get<2>(some_ranges)}, move_only_view{get<3>(some_ranges)}); - test_one(get<2>(expected_results), // + test_one(expected_result_2, // move_only_view{get<0>(some_ranges)}, move_only_view{get<1>(some_ranges)}, move_only_view{get<2>(some_ranges)}); - test_one(get<2>(expected_results), // + test_one(expected_result_2, // move_only_view{get<0>(some_ranges)}, move_only_view{get<1>(some_ranges)}, move_only_view{get<2>(some_ranges)}); @@ -1014,19 +1012,21 @@ int main() { // Check non-views { constexpr auto& r0 = get<0>(some_ranges); - STATIC_ASSERT(test_one(get<0>(expected_results), r0)); - test_one(get<0>(expected_results), r0); + STATIC_ASSERT(test_one(expected_result_0, r0)); + test_one(expected_result_0, r0); auto r1 = get<1>(some_ranges) | ranges::to(); - test_one(get<1>(expected_results), r0, r1); + test_one(expected_result_1, r0, r1); auto r2 = get<2>(some_ranges) | ranges::to(); - test_one(get<2>(expected_results), r0, r1, r2); + test_one(expected_result_2, r0, r1, r2); auto r3 = get<3>(some_ranges) | ranges::to(); - test_one(get<3>(expected_results), r0, r1, r2, r3); + test_one(expected_result_3, r0, r1, r2, r3); } +#ifndef _PREFAST_ // TRANSITION, GH-1030 STATIC_ASSERT((instantiation_test(), true)); +#endif // TRANSITION, GH-1030 instantiation_test(); }