Describe the deficiency
operator== of the iterator type does not take cache_or_index into account when comparing with a sentinel. When has_index is true, advancing an iterator using operator+= just increments cache_or_index. But then in operator==, only dispatch<sentinel_compare_t>(poly); is called, ignoring cache_or_index.
To Reproduce
I have no simple reproducer yet as I just encountered this bug. It should be as easy as iterating over a random access any_view, maybe having sanitizers enabled.
edit: Here is a reproducer on Compiler Explorer: https://godbolt.org/z/GfsEWW5rE
#include <iostream>
#include <vector>
#include <beman/any_view/any_view.hpp>
namespace bav = beman::any_view;
using opt = bav::any_view_options;
int main() {
auto v1 = std::vector<std::tuple<int, int>>{{1, 1}, {2, 2}, {3, 3}} |
std::views::transform(
[](const auto& el) -> std::tuple<const int&, const int&> {
return el;
});
bav::any_view<std::tuple<int, int>, opt::random_access,
std::tuple<const int&, const int&>>
v = std::move(v1);
for (auto [i1, i2] : v) {
std::cerr << i1 << ' ' << i2 << '\n';
}
std::cerr << "end\n";
}
Expected Behavior
Iteration over a random access any_view should reliably stop.
Additional Discussions
To solve this, maybe there should be a sentinel_compare_at_t capability? operator== could look like this then:
[[nodiscard]] constexpr bool operator==(std::default_sentinel_t) const {
// sentinel comparison must dispatch for a contiguous iterator
if constexpr (has_cache and not contiguous) {
return cache_or_index == cache_type{};
} else if constexpr (has_index) {
return dispatch<sentinel_compare_at_t<DiffT>>(poly, cache_or_index);
} else {
return dispatch<sentinel_compare_t>(poly);
}
}
...and the new capability:
struct sentinel_compare_t : unary_protocol {
template <not_adaptor T>
static bool fn(const T& self);
template <adaptor IteratorAdaptorT>
[[nodiscard]] static constexpr bool fn(const IteratorAdaptorT& adaptor) {
return adaptor.iterator == adaptor.sentinel;
}
};
// This is new:
template <class DiffT>
struct sentinel_compare_at_t : unary_protocol {
template <not_adaptor T>
static bool fn(const T& self, DiffT n);
template <adaptor IteratorAdaptorT>
[[nodiscard]] static constexpr bool fn(const IteratorAdaptorT& adaptor, DiffT n) {
return adaptor.iterator + n == adaptor.sentinel;
}
};
I tried this to hack around my crash, and it seems to work.
Describe the deficiency
operator==of theiteratortype does not takecache_or_indexinto account when comparing with a sentinel. Whenhas_indexis true, advancing an iterator usingoperator+=just incrementscache_or_index. But then inoperator==, onlydispatch<sentinel_compare_t>(poly);is called, ignoringcache_or_index.To Reproduce
I have no simple reproducer yet as I just encountered this bug. It should be as easy as iterating over a random accessany_view, maybe having sanitizers enabled.edit: Here is a reproducer on Compiler Explorer: https://godbolt.org/z/GfsEWW5rE
Expected Behavior
Iteration over a random access
any_viewshould reliably stop.Additional Discussions
To solve this, maybe there should be a
sentinel_compare_at_tcapability?operator==could look like this then:...and the new capability:
I tried this to hack around my crash, and it seems to work.