From c351a240ad514199b17b56e1ecff20701d956643 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 12 Mar 2026 15:15:57 +0000 Subject: [PATCH] libstdc++: Make ranges::distance work with volatile iterators (LWG 4242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements LWG 4242 which was approved in Sofia 2025. I don't think the change from static_cast&> to just static_cast> is observable, but it doesn't hurt. What fixes the problem identified in the issue is the is_array_v check, which avoids the static_cast entirely for volatile-qualified iterators. libstdc++-v3/ChangeLog: * include/bits/ranges_base.h (distance(It&&, Sent)): Only decay arrays to pointers when the type is actually an array, as per LWG 4242. * testsuite/24_iterators/range_operations/distance.cc: Add test for LWG 4242. Reviewed-by: Tomasz KamiƄski --- libstdc++-v3/include/bits/ranges_base.h | 13 ++++++++++--- .../24_iterators/range_operations/distance.cc | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index 6cfa6fb6afe..519927f5613 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -969,8 +969,6 @@ namespace ranges struct __distance_fn final { - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 3664. LWG 3392 broke std::ranges::distance(a, a+3) template _Sent> requires (!sized_sentinel_for<_Sent, _It>) constexpr iter_difference_t<_It> @@ -985,11 +983,20 @@ namespace ranges return __n; } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3392. cannot be used on a move-only iterator with a sized sentinel + // 3664. LWG 3392 broke std::ranges::distance(a, a+3) + // 4242. ranges::distance does not work with volatile iterators template> _Sent> [[nodiscard, __gnu__::__always_inline__]] constexpr iter_difference_t> operator()(_It&& __first, _Sent __last) const - { return __last - static_cast&>(__first); } + { + if constexpr (!is_array_v>) + return __last - __first; + else + return __last - static_cast>(__first); + } template [[nodiscard, __gnu__::__always_inline__]] diff --git a/libstdc++-v3/testsuite/24_iterators/range_operations/distance.cc b/libstdc++-v3/testsuite/24_iterators/range_operations/distance.cc index 46e33a2aa70..65edeb8ab02 100644 --- a/libstdc++-v3/testsuite/24_iterators/range_operations/distance.cc +++ b/libstdc++-v3/testsuite/24_iterators/range_operations/distance.cc @@ -154,6 +154,23 @@ test06() VERIFY( std::ranges::distance(a+3, a) == -3 ); } +void +test_lwg4242() +{ + // LWG 4242. ranges::distance does not work with volatile iterators + int arr[] = {1, 2, 3}; + int* volatile ptr = arr; + auto d1 = std::distance(ptr, arr + 3); + auto d2 = std::ranges::distance(ptr, arr + 3); + VERIFY( d1 == d2 ); + + // This is not part of LWG 4242 but it doesn't hurt to check it anyway: + volatile int vol_arr[1]{}; + auto d3 = std::distance(vol_arr, vol_arr + 1); + auto d4 = std::ranges::distance(vol_arr, vol_arr + 1); + VERIFY( d3 == d4 ); +} + int main() { @@ -163,4 +180,5 @@ main() test04(); test05(); test06(); + test_lwg4242(); }