Skip to content

Commit 65b4269

Browse files
fix: name the axis in slicing panic messages
Out-of-bounds and zero-step slice panics now say which axis was being sliced, e.g. "Slice end 9 is past end of axis 2 of length 5" instead of "Slice end 9 is past end of axis of length 5". The axis is the index in the input array, so `NewAxis` entries in `s![]` do not shift it. This covers `slice*`, `slice_axis*`, `slice_collapse` and the overlap check in `multi_slice_*`. Closes #1121 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 53df84a commit 65b4269

3 files changed

Lines changed: 97 additions & 43 deletions

File tree

‎src/dimension/mod.rs‎

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -402,11 +402,14 @@ pub fn abs_index(len: Ix, index: Ixs) -> Ix
402402

403403
/// Determines nonnegative start and end indices, and performs sanity checks.
404404
///
405+
/// `axis` is the index of the axis being sliced; it is only used in panic
406+
/// messages.
407+
///
405408
/// The return value is (start, end, step).
406409
///
407410
/// **Panics** if stride is 0 or if any index is out of bounds.
408411
#[track_caller]
409-
fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize)
412+
fn to_abs_slice(axis: usize, axis_len: usize, slice: Slice) -> (usize, usize, isize)
410413
{
411414
let Slice { start, end, step } = slice;
412415
let start = abs_index(axis_len, start);
@@ -416,17 +419,19 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize)
416419
}
417420
ndassert!(
418421
start <= axis_len,
419-
"Slice begin {} is past end of axis of length {}",
422+
"Slice begin {} is past end of axis {} of length {}",
420423
start,
424+
axis,
421425
axis_len,
422426
);
423427
ndassert!(
424428
end <= axis_len,
425-
"Slice end {} is past end of axis of length {}",
429+
"Slice end {} is past end of axis {} of length {}",
426430
end,
431+
axis,
427432
axis_len,
428433
);
429-
ndassert!(step != 0, "Slice stride must not be zero");
434+
ndassert!(step != 0, "Slice stride must not be zero (axis {})", axis);
430435
(start, end, step)
431436
}
432437

@@ -448,11 +453,14 @@ pub fn offset_from_low_addr_ptr_to_logical_ptr<D: Dimension>(dim: &D, strides: &
448453

449454
/// Modify dimension, stride and return data pointer offset
450455
///
456+
/// `axis` is the index of the axis being sliced; it is only used in panic
457+
/// messages.
458+
///
451459
/// **Panics** if stride is 0 or if any index is out of bounds.
452460
#[track_caller]
453-
pub fn do_slice(dim: &mut usize, stride: &mut usize, slice: Slice) -> isize
461+
pub fn do_slice(axis: usize, dim: &mut usize, stride: &mut usize, slice: Slice) -> isize
454462
{
455-
let (start, end, step) = to_abs_slice(*dim, slice);
463+
let (start, end, step) = to_abs_slice(axis, *dim, slice);
456464

457465
let m = end - start;
458466
let s = (*stride) as isize;
@@ -617,9 +625,9 @@ fn arith_seq_intersect((min1, max1, step1): (isize, isize, isize), (min2, max2,
617625
/// Returns the minimum and maximum values of the indices (inclusive).
618626
///
619627
/// If the slice is empty, then returns `None`, otherwise returns `Some((min, max))`.
620-
fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)>
628+
fn slice_min_max(axis: usize, axis_len: usize, slice: Slice) -> Option<(usize, usize)>
621629
{
622-
let (start, end, step) = to_abs_slice(axis_len, slice);
630+
let (start, end, step) = to_abs_slice(axis, axis_len, slice);
623631
if start == end {
624632
None
625633
} else if step > 0 {
@@ -633,7 +641,8 @@ fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)>
633641
pub fn slices_intersect<D: Dimension>(dim: &D, indices1: impl SliceArg<D>, indices2: impl SliceArg<D>) -> bool
634642
{
635643
debug_assert_eq!(indices1.in_ndim(), indices2.in_ndim());
636-
for (&axis_len, &si1, &si2) in izip!(
644+
for (axis, &axis_len, &si1, &si2) in izip!(
645+
0..,
637646
dim.slice(),
638647
indices1.as_ref().iter().filter(|si| !si.is_new_axis()),
639648
indices2.as_ref().iter().filter(|si| !si.is_new_axis()),
@@ -652,11 +661,11 @@ pub fn slices_intersect<D: Dimension>(dim: &D, indices1: impl SliceArg<D>, indic
652661
step: step2,
653662
},
654663
) => {
655-
let (min1, max1) = match slice_min_max(axis_len, Slice::new(start1, end1, step1)) {
664+
let (min1, max1) = match slice_min_max(axis, axis_len, Slice::new(start1, end1, step1)) {
656665
Some(m) => m,
657666
None => return false,
658667
};
659-
let (min2, max2) = match slice_min_max(axis_len, Slice::new(start2, end2, step2)) {
668+
let (min2, max2) = match slice_min_max(axis, axis_len, Slice::new(start2, end2, step2)) {
660669
Some(m) => m,
661670
None => return false,
662671
};
@@ -667,7 +676,7 @@ pub fn slices_intersect<D: Dimension>(dim: &D, indices1: impl SliceArg<D>, indic
667676
(SliceInfoElem::Slice { start, end, step }, SliceInfoElem::Index(ind))
668677
| (SliceInfoElem::Index(ind), SliceInfoElem::Slice { start, end, step }) => {
669678
let ind = abs_index(axis_len, ind);
670-
let (min, max) = match slice_min_max(axis_len, Slice::new(start, end, step)) {
679+
let (min, max) = match slice_min_max(axis, axis_len, Slice::new(start, end, step)) {
671680
Some(m) => m,
672681
None => return false,
673682
};
@@ -1071,51 +1080,51 @@ mod test
10711080
#[test]
10721081
fn slice_min_max_empty()
10731082
{
1074-
assert_eq!(slice_min_max(0, Slice::new(0, None, 3)), None);
1075-
assert_eq!(slice_min_max(10, Slice::new(1, Some(1), 3)), None);
1076-
assert_eq!(slice_min_max(10, Slice::new(-1, Some(-1), 3)), None);
1077-
assert_eq!(slice_min_max(10, Slice::new(1, Some(1), -3)), None);
1078-
assert_eq!(slice_min_max(10, Slice::new(-1, Some(-1), -3)), None);
1083+
assert_eq!(slice_min_max(0, 0, Slice::new(0, None, 3)), None);
1084+
assert_eq!(slice_min_max(0, 10, Slice::new(1, Some(1), 3)), None);
1085+
assert_eq!(slice_min_max(0, 10, Slice::new(-1, Some(-1), 3)), None);
1086+
assert_eq!(slice_min_max(0, 10, Slice::new(1, Some(1), -3)), None);
1087+
assert_eq!(slice_min_max(0, 10, Slice::new(-1, Some(-1), -3)), None);
10791088
}
10801089

10811090
#[test]
10821091
fn slice_min_max_pos_step()
10831092
{
1084-
assert_eq!(slice_min_max(10, Slice::new(1, Some(8), 3)), Some((1, 7)));
1085-
assert_eq!(slice_min_max(10, Slice::new(1, Some(9), 3)), Some((1, 7)));
1086-
assert_eq!(slice_min_max(10, Slice::new(-9, Some(8), 3)), Some((1, 7)));
1087-
assert_eq!(slice_min_max(10, Slice::new(-9, Some(9), 3)), Some((1, 7)));
1088-
assert_eq!(slice_min_max(10, Slice::new(1, Some(-2), 3)), Some((1, 7)));
1089-
assert_eq!(slice_min_max(10, Slice::new(1, Some(-1), 3)), Some((1, 7)));
1090-
assert_eq!(slice_min_max(10, Slice::new(-9, Some(-2), 3)), Some((1, 7)));
1091-
assert_eq!(slice_min_max(10, Slice::new(-9, Some(-1), 3)), Some((1, 7)));
1092-
assert_eq!(slice_min_max(10, Slice::new(1, None, 3)), Some((1, 7)));
1093-
assert_eq!(slice_min_max(10, Slice::new(-9, None, 3)), Some((1, 7)));
1094-
assert_eq!(slice_min_max(11, Slice::new(1, None, 3)), Some((1, 10)));
1095-
assert_eq!(slice_min_max(11, Slice::new(-10, None, 3)), Some((1, 10)));
1093+
assert_eq!(slice_min_max(0, 10, Slice::new(1, Some(8), 3)), Some((1, 7)));
1094+
assert_eq!(slice_min_max(0, 10, Slice::new(1, Some(9), 3)), Some((1, 7)));
1095+
assert_eq!(slice_min_max(0, 10, Slice::new(-9, Some(8), 3)), Some((1, 7)));
1096+
assert_eq!(slice_min_max(0, 10, Slice::new(-9, Some(9), 3)), Some((1, 7)));
1097+
assert_eq!(slice_min_max(0, 10, Slice::new(1, Some(-2), 3)), Some((1, 7)));
1098+
assert_eq!(slice_min_max(0, 10, Slice::new(1, Some(-1), 3)), Some((1, 7)));
1099+
assert_eq!(slice_min_max(0, 10, Slice::new(-9, Some(-2), 3)), Some((1, 7)));
1100+
assert_eq!(slice_min_max(0, 10, Slice::new(-9, Some(-1), 3)), Some((1, 7)));
1101+
assert_eq!(slice_min_max(0, 10, Slice::new(1, None, 3)), Some((1, 7)));
1102+
assert_eq!(slice_min_max(0, 10, Slice::new(-9, None, 3)), Some((1, 7)));
1103+
assert_eq!(slice_min_max(0, 11, Slice::new(1, None, 3)), Some((1, 10)));
1104+
assert_eq!(slice_min_max(0, 11, Slice::new(-10, None, 3)), Some((1, 10)));
10961105
}
10971106

10981107
#[test]
10991108
fn slice_min_max_neg_step()
11001109
{
1101-
assert_eq!(slice_min_max(10, Slice::new(1, Some(8), -3)), Some((1, 7)));
1102-
assert_eq!(slice_min_max(10, Slice::new(2, Some(8), -3)), Some((4, 7)));
1103-
assert_eq!(slice_min_max(10, Slice::new(-9, Some(8), -3)), Some((1, 7)));
1104-
assert_eq!(slice_min_max(10, Slice::new(-8, Some(8), -3)), Some((4, 7)));
1105-
assert_eq!(slice_min_max(10, Slice::new(1, Some(-2), -3)), Some((1, 7)));
1106-
assert_eq!(slice_min_max(10, Slice::new(2, Some(-2), -3)), Some((4, 7)));
1110+
assert_eq!(slice_min_max(0, 10, Slice::new(1, Some(8), -3)), Some((1, 7)));
1111+
assert_eq!(slice_min_max(0, 10, Slice::new(2, Some(8), -3)), Some((4, 7)));
1112+
assert_eq!(slice_min_max(0, 10, Slice::new(-9, Some(8), -3)), Some((1, 7)));
1113+
assert_eq!(slice_min_max(0, 10, Slice::new(-8, Some(8), -3)), Some((4, 7)));
1114+
assert_eq!(slice_min_max(0, 10, Slice::new(1, Some(-2), -3)), Some((1, 7)));
1115+
assert_eq!(slice_min_max(0, 10, Slice::new(2, Some(-2), -3)), Some((4, 7)));
11071116
assert_eq!(
1108-
slice_min_max(10, Slice::new(-9, Some(-2), -3)),
1117+
slice_min_max(0, 10, Slice::new(-9, Some(-2), -3)),
11091118
Some((1, 7))
11101119
);
11111120
assert_eq!(
1112-
slice_min_max(10, Slice::new(-8, Some(-2), -3)),
1121+
slice_min_max(0, 10, Slice::new(-8, Some(-2), -3)),
11131122
Some((4, 7))
11141123
);
1115-
assert_eq!(slice_min_max(9, Slice::new(2, None, -3)), Some((2, 8)));
1116-
assert_eq!(slice_min_max(9, Slice::new(-7, None, -3)), Some((2, 8)));
1117-
assert_eq!(slice_min_max(9, Slice::new(3, None, -3)), Some((5, 8)));
1118-
assert_eq!(slice_min_max(9, Slice::new(-6, None, -3)), Some((5, 8)));
1124+
assert_eq!(slice_min_max(0, 9, Slice::new(2, None, -3)), Some((2, 8)));
1125+
assert_eq!(slice_min_max(0, 9, Slice::new(-7, None, -3)), Some((2, 8)));
1126+
assert_eq!(slice_min_max(0, 9, Slice::new(3, None, -3)), Some((5, 8)));
1127+
assert_eq!(slice_min_max(0, 9, Slice::new(-6, None, -3)), Some((5, 8)));
11191128
}
11201129

11211130
#[test]

‎src/impl_methods.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,8 +711,12 @@ impl<A, D: Dimension> LayoutRef<A, D>
711711
pub fn slice_axis_inplace(&mut self, axis: Axis, indices: Slice)
712712
{
713713
let parts = &mut self.0;
714-
let offset =
715-
do_slice(&mut parts.dim.slice_mut()[axis.index()], &mut parts.strides.slice_mut()[axis.index()], indices);
714+
let offset = do_slice(
715+
axis.index(),
716+
&mut parts.dim.slice_mut()[axis.index()],
717+
&mut parts.strides.slice_mut()[axis.index()],
718+
indices,
719+
);
716720
unsafe {
717721
self.0.ptr = self._ptr().offset(offset);
718722
}

‎tests/array.rs‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,47 @@ fn slice_axis_oob()
511511
let _vi = a.slice_axis(Axis(0), Slice::new(0, Some(10), 1));
512512
}
513513

514+
#[should_panic(expected = "Slice begin 10 is past end of axis 1 of length 4")]
515+
#[test]
516+
fn slice_oob_begin_message_names_axis()
517+
{
518+
let a = Array::<i32, _>::zeros((3, 4, 5));
519+
let _vi = a.slice(s![.., 10.., ..]);
520+
}
521+
522+
#[should_panic(expected = "Slice end 9 is past end of axis 2 of length 5")]
523+
#[test]
524+
fn slice_oob_end_message_names_axis()
525+
{
526+
let a = Array::<i32, _>::zeros((3, 4, 5));
527+
let _vi = a.slice(s![.., 1.., ..9]);
528+
}
529+
530+
#[should_panic(expected = "Slice end 9 is past end of axis 1 of length 4")]
531+
#[test]
532+
fn slice_oob_message_names_input_axis_with_newaxis()
533+
{
534+
// The reported axis is the axis of the input array, not counting `NewAxis`.
535+
let a = Array::<i32, _>::zeros((3, 4));
536+
let _vi = a.slice(s![NewAxis, .., ..9]);
537+
}
538+
539+
#[should_panic(expected = "Slice stride must not be zero (axis 1)")]
540+
#[test]
541+
fn slice_zero_step_message_names_axis()
542+
{
543+
let a = Array::<i32, _>::zeros((3, 4));
544+
let _vi = a.slice_axis(Axis(1), Slice { start: 0, end: None, step: 0 });
545+
}
546+
547+
#[should_panic(expected = "Slice end 9 is past end of axis 1 of length 4")]
548+
#[test]
549+
fn multi_slice_oob_message_names_axis()
550+
{
551+
let mut a = Array::<i32, _>::zeros((3, 4));
552+
let _views = a.multi_slice_mut((s![.., ..9], s![.., ..1]));
553+
}
554+
514555
#[should_panic]
515556
#[test]
516557
fn slice_wrong_dim()

0 commit comments

Comments
 (0)