From 43023331c58195d1b0f44ba3420e392823ce030e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 21 Jun 2025 22:15:53 +0800 Subject: [PATCH 1/3] Implement LWG-4242 --- stl/inc/xutility | 25 +++++++++++++++++-- .../test.cpp | 23 +++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index dbfbad6ce35..203585a8692 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3568,6 +3568,26 @@ namespace ranges { _EXPORT_STD inline constexpr _Advance_fn advance; + // ranges::distance should be well-constrained when volatile iterators are involved. + template + concept _Additionally_subtractable_for_distance = + is_array_v> || requires(_IterOrArr& __it, _Sent& __se) { + _STD _Fake_copy_init>>(__se - __it); + }; + + template + struct _Distance_subtract_source { + using type = _Iter&; + }; + template + requires is_array_v> + struct _Distance_subtract_source<_Arr> { + using type = decay_t<_Arr>; + }; + + template + using _Distance_subtract_source_t = _Distance_subtract_source<_IterOrArr>::type; + class _Distance_fn { public: template _Se> @@ -3582,10 +3602,11 @@ namespace ranges { } template > _Se> + requires _Additionally_subtractable_for_distance _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); + noexcept(noexcept(_Last - static_cast<_Distance_subtract_source_t<_It>>(_First))) /* strengthened */ { + return _Last - static_cast<_Distance_subtract_source_t<_It>>(_First); } template diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index abc1699c201..9699bb7435c 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); + + // ranges::distance should be well-constrained for non-pointer volatile iterators. + static_assert( + !can_call_ranges_difference volatile, std::reverse_iterator>); + static_assert( + !can_call_ranges_difference volatile&, 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; From 294cd27ba7b08231c769e91d19f215b70f6c82d9 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 24 Jul 2025 12:11:26 +0800 Subject: [PATCH 2/3] Address review comments --- stl/inc/xutility | 70 ++++++++++--------- .../test.cpp | 7 +- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 332b6af0877..13da9342766 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3568,50 +3568,34 @@ namespace ranges { _EXPORT_STD inline constexpr _Advance_fn advance; - // ranges::distance should be well-constrained when volatile iterators are involved. - template - concept _Additionally_subtractable_for_distance = - is_array_v> || requires(_IterOrArr& __it, _Sent& __se) { - _STD _Fake_copy_init>>(__se - __it); - }; - - template - struct _Distance_subtract_source { - using type = _Iter&; - }; - template - requires is_array_v> - struct _Distance_subtract_source<_Arr> { - using type = decay_t<_Arr>; - }; - - template - using _Distance_subtract_source_t = _Distance_subtract_source<_IterOrArr>::type; + template + using _Distance_iterator_t = // per an unnumbered LWG issue filed on 2025-07-24 + 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> - requires _Additionally_subtractable_for_distance - _NODISCARD _STATIC_CALL_OPERATOR constexpr iter_difference_t> operator()( - _It&& _First, const _Se _Last) _CONST_CALL_OPERATOR - noexcept(noexcept(_Last - static_cast<_Distance_subtract_source_t<_It>>(_First))) /* strengthened */ { - return _Last - static_cast<_Distance_subtract_source_t<_It>>(_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 { @@ -3633,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 9699bb7435c..5a620d02251 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -3125,11 +3125,12 @@ namespace iter_ops { static_assert(can_call_ranges_difference); static_assert(can_call_ranges_difference); - // ranges::distance should be well-constrained for non-pointer volatile iterators. + // Per an unnumbered LWG issue filed on 2025-07-24, ranges::distance should be well-constrained for + // non-pointer volatile iterators. static_assert( - !can_call_ranges_difference volatile, std::reverse_iterator>); + !can_call_ranges_difference, std::reverse_iterator>); static_assert( - !can_call_ranges_difference volatile&, std::reverse_iterator>); + !can_call_ranges_difference&, std::reverse_iterator>); if (!std::is_constant_evaluated()) { int arr[]{1, 2, 3}; From 15f17339c4526565f3ccae3eeed46dd77d26903f Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 28 Jul 2025 15:12:58 +0800 Subject: [PATCH 3/3] LWG-4303 is submitted --- stl/inc/xutility | 2 +- tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 13da9342766..54d3154b0ae 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3569,7 +3569,7 @@ namespace ranges { _EXPORT_STD inline constexpr _Advance_fn advance; template - using _Distance_iterator_t = // per an unnumbered LWG issue filed on 2025-07-24 + using _Distance_iterator_t = // per LWG-4303 conditional_t>, decay_t<_Ty>, remove_const_t>>; class _Distance_fn { diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 5a620d02251..4aa6767376a 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -3125,8 +3125,7 @@ namespace iter_ops { static_assert(can_call_ranges_difference); static_assert(can_call_ranges_difference); - // Per an unnumbered LWG issue filed on 2025-07-24, ranges::distance should be well-constrained for - // non-pointer volatile iterators. + // 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(