This implements LWG 4242 which was approved in Sofia 2025.

I don't think the change from static_cast<const decay_t<I>&> to just
static_cast<decay_t<I>> 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.
---

v2: No longer using auto(x), as that's not supported by Clang.
Harmonized the _GLIBCXX_RESOLVE_LIB_DEFECTS comments and added the
missing one for 3392. Better tests, and combined the new tests with the
existing distance.cc file.

 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 6cfa6fb6afe6..519927f5613e 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<typename _It, sentinel_for<_It> _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<typename _It, sized_sentinel_for<decay_t<_It>> _Sent>
       [[nodiscard, __gnu__::__always_inline__]]
       constexpr iter_difference_t<decay_t<_It>>
       operator()(_It&& __first, _Sent __last) const
-      { return __last - static_cast<const decay_t<_It>&>(__first); }
+      {
+       if constexpr (!is_array_v<remove_reference_t<_It>>)
+         return __last - __first;
+       else
+         return __last - static_cast<decay_t<_It>>(__first);
+      }
 
     template<range _Range>
       [[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 46e33a2aa701..65edeb8ab029 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();
 }
-- 
2.53.0

Reply via email to