diff --git a/stl/inc/xutility b/stl/inc/xutility index c38cab33263..54d3154b0ae 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3568,29 +3568,34 @@ namespace ranges { _EXPORT_STD inline constexpr _Advance_fn advance; + template + using _Distance_iterator_t = // per LWG-4303 + conditional_t>, decay_t<_Ty>, remove_const_t>>; + class _Distance_fn { public: template _Se> requires (!sized_sentinel_for<_Se, _It>) _NODISCARD _STATIC_CALL_OPERATOR constexpr iter_difference_t<_It> operator()( - _It _First, _Se _Last) _CONST_CALL_OPERATOR - noexcept(noexcept(_Distance_unchecked( - _STD _Get_unwrapped(_STD move(_First)), _STD _Get_unwrapped(_STD move(_Last))))) /* strengthened */ { + _It _First, _Se _Last) _CONST_CALL_OPERATOR noexcept(_Calculate_noexcept<_It, _Se>()) /* strengthened */ { _STD _Adl_verify_range(_First, _Last); return _Distance_unchecked( _RANGES _Unwrap_iter<_Se>(_STD move(_First)), _RANGES _Unwrap_sent<_It>(_STD move(_Last))); } - template > _Se> - _NODISCARD _STATIC_CALL_OPERATOR constexpr iter_difference_t> operator()( - _It&& _First, const _Se _Last) _CONST_CALL_OPERATOR - noexcept(noexcept(_Last - static_cast&>(_First))) /* strengthened */ { - return _Last - static_cast&>(_First); + template > _Se> + _NODISCARD _STATIC_CALL_OPERATOR constexpr iter_difference_t<_Distance_iterator_t<_It>> operator()(_It&& _First, + const _Se _Last) _CONST_CALL_OPERATOR noexcept(_Calculate_noexcept<_It, _Se>()) /* strengthened */ { + if constexpr (!is_array_v>) { + return _Last - _First; + } else { + return _Last - static_cast>(_First); + } } template _NODISCARD _STATIC_CALL_OPERATOR constexpr range_difference_t<_Rng> operator()( - _Rng&& _Range) _CONST_CALL_OPERATOR noexcept(_Nothrow_size<_Rng>) /* strengthened */ { + _Rng&& _Range) _CONST_CALL_OPERATOR noexcept(_Calculate_noexcept<_Rng>()) /* strengthened */ { if constexpr (sized_range<_Rng>) { return static_cast>(_RANGES size(_Range)); } else { @@ -3612,12 +3617,30 @@ namespace ranges { return _Count; } - template - static constexpr bool _Nothrow_size = - noexcept(_Distance_unchecked(_Ubegin(_STD declval<_Rng&>()), _Uend(_STD declval<_Rng&>()))); + template + _NODISCARD static consteval bool _Calculate_noexcept() noexcept { + if constexpr (sized_sentinel_for<_Se, _Distance_iterator_t<_It>>) { + if constexpr (!is_array_v>) { + return noexcept(_STD declval() - _STD declval<_It&>()); + } else { + return noexcept(_STD declval() - _STD declval>()); + } + } else if constexpr (sentinel_for<_Se, _It> && !sized_sentinel_for<_Se, _It>) { + return noexcept(_Distance_unchecked( + _STD _Get_unwrapped(_STD declval<_It>()), _STD _Get_unwrapped(_STD declval<_Se>()))); + } else { + return false; + } + } - template - static constexpr bool _Nothrow_size<_Rng> = noexcept(_RANGES size(_STD declval<_Rng&>())); + template + _NODISCARD static consteval bool _Calculate_noexcept() noexcept { + if constexpr (sized_range<_Rng>) { + return noexcept(_RANGES size(_STD declval<_Rng&>())); + } else { + return noexcept(_Distance_unchecked(_Ubegin(_STD declval<_Rng&>()), _Uend(_STD declval<_Rng&>()))); + } + } }; _EXPORT_STD inline constexpr _Distance_fn distance; diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index abc1699c201..4aa6767376a 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -2107,6 +2107,10 @@ struct std::common_type + concept can_call_ranges_difference = + requires(I&& it, S&& se) { distance(std::forward(it), std::forward(se)); }; + constexpr bool test_iter_forms() { { // Call next(i), validating that ++i is called once @@ -3098,7 +3102,6 @@ namespace iter_ops { } { -#ifndef __EDG__ // TRANSITION, VSO-1898890 // Call distance(i, s) with arrays which must be decayed to pointers. // (This behavior was regressed by LWG-3392.) int some_ints[] = {1, 2, 3}; @@ -3116,7 +3119,23 @@ namespace iter_ops { static_assert(noexcept(distance(const_ints + 1, const_ints))); assert(distance(const_ints, const_ints) == 0); static_assert(noexcept(distance(const_ints, const_ints))); -#endif // ^^^ no workaround ^^^ + } + + { // Test LWG-4242 "ranges::distance does not work with volatile iterators" + static_assert(can_call_ranges_difference); + static_assert(can_call_ranges_difference); + + // Per LWG-4303, ranges::distance should be well-constrained for non-pointer volatile iterators. + static_assert( + !can_call_ranges_difference, std::reverse_iterator>); + static_assert( + !can_call_ranges_difference&, std::reverse_iterator>); + + if (!std::is_constant_evaluated()) { + int arr[]{1, 2, 3}; + int* volatile ptr = arr; + assert(distance(ptr, arr + 3) == 3); + } } return true;