Latest Results
Fix infinite loop in xt::roll on arrays with a zero-length dimension (#2922)
# Checklist
- [x] The title and commit message(s) are descriptive.
- [x] Small commits made to fix your PR have been squashed to avoid
history pollution.
- [x] Tests have been added for new features or bug fixes.
- [x] API of new functions and classes are documented. *(N/A — bug fix
only, no API change)*
# Description
`xt::roll` enters an infinite loop (and then divides by zero) when the
input
has a zero-length axis:
- No-axis overload `roll(e, shift)` when `e.size() == 0`:
`while (shift < 0) shift += flat_size;` loops forever with `flat_size ==
0`,
then `shift %= 0` is undefined behaviour.
- Axis-aware overload `roll(e, shift, axis)` when the rolled axis has
length 0:
same issue on `axis_dim == 0`.
### Fix
For a shape $s = (s_0, \dots, s_{n-1})$,
if $\exists k: s_k = 0$, then $\mathrm{roll}(a, \mathrm{shift},
\mathrm{axis}) = a$,
i.e. roll is the identity.
In both overloads, after constructing `cpy = empty_like(e)`, return
`cpy`
early when `cpy.size() == 0`. The no-axis overload's hand-rolled
`std::accumulate(shape, 1L, multiplies<size_t>())` is replaced by
`cpy.size()`
(numerically equivalent, and O(1) on contiguous layouts).
When the rolled axis is non-empty but another axis has length 0
(e.g. `shape={3,0,4}` rolled along axis 0), the original code didn't
loop —
`detail::roll`'s inner loops became no-ops because the stride product
collapses to 0 — but the recursion was wasted work. The widened guard
`cpy.size() == 0` skips it cleanly. Observable output is unchanged.
### Tests
Three regression cases in `test_xmanipulation.cpp`, one per code path:
| Shape | Axis | Path exercised |
|-------------|---------|-----------------------------------------------|
| `{0}` | (ravel) | no-axis overload's empty-input guard |
| `{3, 0}` | `1` | axis-aware overload, rolled axis itself is 0 |
| `{3, 0, 4}` | `0` | axis-aware overload, *other* axis is 0 |
All `test_xmanipulation` tests pass: 28 cases, 188 assertions. Verified
against `numpy.roll`, which returns a same-shape empty array for these
inputs. formatAlex-PLACET:xexpresion_fix Latest Branches
0%
f14XuanLv:fix/roll-empty-array N/A
Alex-PLACET:codspeed_runner 0%
Alex-PLACET:add_comparative_benchmarks_with_numpy © 2026 CodSpeed Technology