https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121917

Jonathan Wakely <redi at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
            Summary|ranges::shuffle incorrectly |[16 Regression]
                   |requires its arguments to   |ranges::shuffle incorrectly
                   |model sized_sentinel_for    |requires its arguments to
                   |                            |model sized_sentinel_for
     Ever confirmed|0                           |1
            Version|15.2.0                      |16.0
      Known to work|                            |15.2.1
             Status|UNCONFIRMED                 |NEW
      Known to fail|                            |16.0
   Last reconfirmed|                            |2025-09-11

--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> ---
Oops, fixed testcase:

#include <algorithm>
#include <iterator>
#include <random>

struct non_default_sentinel_t { };

template<std::input_or_output_iterator I>
bool operator==(const I& i, non_default_sentinel_t)
{ return i == std::default_sentinel; }

int main()
{
  int a[2]{};
  std::counted_iterator iter(a, 2);
  std::default_random_engine e;
  std::ranges::shuffle(iter, non_default_sentinel_t{}, e);
}


The problem is that we assume that last - first is valid, because we have a
random access iterator and its sentinel. But an arbitrary sentinel doesn't have
to model sized_sentinel_for.

This code was copied from std::shuffle where we always have two iterators of
the same type, so last - first is always valid. This is a regression because in
GCC 15 ranges::shuffle just forwarded its arguments to std::shuffle, so didn't
have this problem (but was non-conforming in other ways).

Patrick suggested that we just disable the optimization to use the URBG more
efficiently for the non-sized sentinel case:

-- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -1968,6 +1968,8 @@ namespace ranges
        using __uc_type
          = common_type_t<typename remove_reference_t<_Gen>::result_type,
__ud_type>;

+       if constexpr (sized_sentinel_for<_Sent, _Iter>)
+         {
        const __uc_type __urngrange = __g.max() - __g.min();
        const __uc_type __urange = __uc_type(__last - __first);

@@ -2003,6 +2005,7 @@ namespace ranges

            return __i;
          }
+         }

        __distr_type __d;

Reply via email to