diff --git a/libcxx/include/__iterator/advance.h b/libcxx/include/__iterator/advance.h --- a/libcxx/include/__iterator/advance.h +++ b/libcxx/include/__iterator/advance.h @@ -76,8 +76,8 @@ private: template _LIBCPP_HIDE_FROM_ABI - static constexpr _Tp __abs(_Tp __n) noexcept { - return __n < 0 ? -__n : __n; + static constexpr _Tp __magnitude_geq(_Tp __a, _Tp __b) { + return __a < 0 ? (__a <= __b) : (__a >= __b); } template @@ -153,30 +153,30 @@ template _Sp> _LIBCPP_HIDE_FROM_ABI constexpr iter_difference_t<_Ip> operator()(_Ip& __i, iter_difference_t<_Ip> __n, _Sp __bound) const { - _LIBCPP_ASSERT(__n >= 0 || (bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>), + _LIBCPP_ASSERT((bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>) || __n >= iter_difference_t<_Ip>(0), "If `n < 0`, then `bidirectional_iterator && same_as` must be true."); // If `S` and `I` model `sized_sentinel_for`: if constexpr (sized_sentinel_for<_Sp, _Ip>) { // If |n| >= |bound - i|, equivalent to `ranges::advance(i, bound)`. - if (const auto __M = __bound - __i; __abs(__n) >= __abs(__M)) { + if (iter_difference_t<_Ip> __M = __bound - __i; __magnitude_geq(__n, __M)) { (*this)(__i, __bound); return __n - __M; + } else { + // Otherwise, equivalent to `ranges::advance(i, n)`. + (*this)(__i, __n); + return 0; } - - // Otherwise, equivalent to `ranges::advance(i, n)`. - (*this)(__i, __n); - return 0; } else { // Otherwise, if `n` is non-negative, while `bool(i != bound)` is true, increments `i` but at // most `n` times. - while (__i != __bound && __n > 0) { + while (bool(__i != __bound) && __n > iter_difference_t<_Ip>(0)) { ++__i; --__n; } // Otherwise, while `bool(i != bound)` is true, decrements `i` but at most `-n` times. if constexpr (bidirectional_iterator<_Ip> && same_as<_Ip, _Sp>) { - while (__i != __bound && __n < 0) { + while (bool(__i != __bound) && __n < iter_difference_t<_Ip>(0)) { --__i; ++__n; } diff --git a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp --- a/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp +++ b/libcxx/test/std/iterators/iterator.primitives/range.iter.ops/range.iter.ops.advance/iterator_count_sentinel.pass.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "test_iterators.h" @@ -100,6 +101,25 @@ assert(current.stride_count() == -current.stride_displacement()); } +struct I { + using difference_type = int; + using value_type = int; + + int x; + + constexpr int operator*() const { return x; } + constexpr I& operator++() { ++x; return *this; } + constexpr I operator++(int) { ++x; return {x - 1}; } + constexpr bool operator==(const I&) const = default; + + constexpr int operator-(const I& that) const { return x - that.x; } + + constexpr I& operator--() { --x; return *this; } + constexpr I operator--(int) { --x; return {x - 1}; } +}; +static_assert(std::bidirectional_iterator); +static_assert(std::sized_sentinel_for); + constexpr bool test() { auto range = range_t{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; check_forward_sized_sentinel >(1, {range.begin() + 1, 0}, range); @@ -136,6 +156,19 @@ check_forward >(1000, {range.end(), 990}, range); check_backward >(1000, {range.begin(), -990}, range); + // regression-test that INT_MIN doesn't cause any undefined behavior + { + auto i = I{+1}; + assert(std::ranges::advance(i, INT_MIN, I{-2}) == INT_MIN+3); + assert(i == I{-2}); + i = I{+1}; + assert(std::ranges::advance(i, -2, I{INT_MIN+1}) == 0); + assert(i == I{-1}); + i = I{+1}; + assert(std::ranges::advance(i, INT_MIN, I{INT_MIN+1}) == 0); + assert(i == I{INT_MIN+1}); + } + return true; }