From 7637ff46510ffe93b4d1c3996a7178c6e52adb16 Mon Sep 17 00:00:00 2001 From: ShryukGrandhi <85529990+ShryukGrandhi@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:36:14 -0700 Subject: [PATCH 1/4] fix(linalg): drop std HashSet from ordered_iter_mut debug check Signed-off-by: ShryukGrandhi <85529990+ShryukGrandhi@users.noreply.github.com> --- src/linalg/basic/matrix.rs | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/linalg/basic/matrix.rs b/src/linalg/basic/matrix.rs index 2abf44a3..f70a931b 100644 --- a/src/linalg/basic/matrix.rs +++ b/src/linalg/basic/matrix.rs @@ -74,16 +74,18 @@ where #[cfg(debug_assertions)] { let len = values.len(); - let mut seen = std::collections::HashSet::new(); for &o in &desired { assert!( o < len, "iterator_mut: offset {o} out of bounds (len={len})" ); - assert!( - seen.insert(o), - "iterator_mut: aliasing detected at offset {o}" - ); + } + // Sorted-`Vec` duplicate check instead of a `HashSet`, so that this + // check works with `alloc` only (no `std` hash collections). + let mut sorted_check = desired.clone(); + sorted_check.sort_unstable(); + if let Some(w) = sorted_check.windows(2).find(|w| w[0] == w[1]) { + panic!("iterator_mut: aliasing detected at offset {}", w[0]); } } @@ -848,6 +850,23 @@ mod tests { assert_eq!(vec![1, 4, 3, 4, 7, 6], *x.values); } + #[cfg(debug_assertions)] + #[test] + #[should_panic(expected = "iterator_mut: aliasing detected at offset 1")] + fn test_ordered_iter_mut_detects_aliasing() { + // stride 1 < ncols 2: offsets of row 0 and row 1 overlap. + let mut values = vec![0, 1, 2]; + let _ = ordered_iter_mut(&mut values, 1, 2, 2, false, 0); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic(expected = "iterator_mut: offset 3 out of bounds (len=3)")] + fn test_ordered_iter_mut_detects_out_of_bounds() { + let mut values = vec![0, 1, 2]; + let _ = ordered_iter_mut(&mut values, 2, 2, 2, false, 0); + } + #[test] fn test_get_slice() { let x = DenseMatrix::from_2d_array(&[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9], &[10, 11, 12]]) From d5e4bfe1572d26f7e679210da7abc06f23bb0352 Mon Sep 17 00:00:00 2001 From: ShryukGrandhi <85529990+ShryukGrandhi@users.noreply.github.com> Date: Fri, 23 Oct 2026 05:01:51 -0700 Subject: [PATCH 2/4] fix(linalg): reuse sorted offsets for ordered_iter_mut alias check Signed-off-by: ShryukGrandhi <85529990+ShryukGrandhi@users.noreply.github.com> --- src/linalg/basic/matrix.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/linalg/basic/matrix.rs b/src/linalg/basic/matrix.rs index f70a931b..d8662463 100644 --- a/src/linalg/basic/matrix.rs +++ b/src/linalg/basic/matrix.rs @@ -34,7 +34,7 @@ use crate::error::Failed; /// the sub-slice spans gaps). The traversal order is identical to the previous /// raw-pointer implementation; only the borrow-proving mechanism changes. /// -/// `debug_assert!`s verify the computed offsets are in-bounds and distinct +/// Debug-only checks verify the computed offsets are in-bounds and distinct /// (mirroring the old `#[cfg(debug_assertions)]` aliasing checks). fn ordered_iter_mut<'b, T>( values: &'b mut [T], @@ -80,13 +80,6 @@ where "iterator_mut: offset {o} out of bounds (len={len})" ); } - // Sorted-`Vec` duplicate check instead of a `HashSet`, so that this - // check works with `alloc` only (no `std` hash collections). - let mut sorted_check = desired.clone(); - sorted_check.sort_unstable(); - if let Some(w) = sorted_check.windows(2).find(|w| w[0] == w[1]) { - panic!("iterator_mut: aliasing detected at offset {}", w[0]); - } } // Fast path: the desired order is the natural storage order, so the slice @@ -103,6 +96,14 @@ where let mut sorted: Vec<(usize, usize)> = desired.iter().enumerate().map(|(i, &o)| (o, i)).collect(); sorted.sort_unstable_by_key(|&(o, _)| o); + // Duplicate check on the sorted offsets instead of a `HashSet`, so that + // this check works with `alloc` only (no `std` hash collections). + #[cfg(debug_assertions)] + { + if let Some(w) = sorted.windows(2).find(|w| w[0].0 == w[1].0) { + panic!("iterator_mut: aliasing detected at offset {}", w[0].0); + } + } let mut result: Vec> = (0..n).map(|_| None).collect(); let mut rest = values; From e3aa78203b99bff4a30177a8607bb1c6f787d58c Mon Sep 17 00:00:00 2001 From: ShryukGrandhi <85529990+ShryukGrandhi@users.noreply.github.com> Date: Fri, 23 Oct 2026 05:30:07 -0700 Subject: [PATCH 3/4] test(linalg): cover ordered_iter_mut duplicate check in release builds Signed-off-by: ShryukGrandhi <85529990+ShryukGrandhi@users.noreply.github.com> --- src/linalg/basic/matrix.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/linalg/basic/matrix.rs b/src/linalg/basic/matrix.rs index d8662463..0128f564 100644 --- a/src/linalg/basic/matrix.rs +++ b/src/linalg/basic/matrix.rs @@ -99,10 +99,8 @@ where // Duplicate check on the sorted offsets instead of a `HashSet`, so that // this check works with `alloc` only (no `std` hash collections). #[cfg(debug_assertions)] - { - if let Some(w) = sorted.windows(2).find(|w| w[0].0 == w[1].0) { - panic!("iterator_mut: aliasing detected at offset {}", w[0].0); - } + if let Some(o) = first_duplicate_offset(&sorted) { + panic!("iterator_mut: aliasing detected at offset {o}"); } let mut result: Vec> = (0..n).map(|_| None).collect(); @@ -119,6 +117,13 @@ where Box::new(result.into_iter().map(|r| r.expect("filled"))) } +/// Returns the first offset that occurs more than once in `sorted`, which +/// must be sorted by offset. +#[cfg(any(debug_assertions, test))] +fn first_duplicate_offset(sorted: &[(usize, usize)]) -> Option { + sorted.windows(2).find(|w| w[0].0 == w[1].0).map(|w| w[0].0) +} + /// Dense matrix #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] @@ -851,6 +856,16 @@ mod tests { assert_eq!(vec![1, 4, 3, 4, 7, 6], *x.values); } + #[test] + fn test_first_duplicate_offset() { + assert_eq!(first_duplicate_offset(&[]), None); + assert_eq!(first_duplicate_offset(&[(0, 0), (1, 1), (2, 2)]), None); + assert_eq!( + first_duplicate_offset(&[(0, 0), (1, 2), (1, 1), (3, 3)]), + Some(1) + ); + } + #[cfg(debug_assertions)] #[test] #[should_panic(expected = "iterator_mut: aliasing detected at offset 1")] From d70c054e67642740a4c3f85366958a459e39c1a3 Mon Sep 17 00:00:00 2001 From: ShryukGrandhi <85529990+ShryukGrandhi@users.noreply.github.com> Date: Fri, 23 Oct 2026 05:59:14 -0700 Subject: [PATCH 4/4] test(linalg): move aliasing panic into helper covered in release builds Signed-off-by: ShryukGrandhi <85529990+ShryukGrandhi@users.noreply.github.com> --- src/linalg/basic/matrix.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/linalg/basic/matrix.rs b/src/linalg/basic/matrix.rs index 0128f564..340cee99 100644 --- a/src/linalg/basic/matrix.rs +++ b/src/linalg/basic/matrix.rs @@ -99,9 +99,7 @@ where // Duplicate check on the sorted offsets instead of a `HashSet`, so that // this check works with `alloc` only (no `std` hash collections). #[cfg(debug_assertions)] - if let Some(o) = first_duplicate_offset(&sorted) { - panic!("iterator_mut: aliasing detected at offset {o}"); - } + assert_distinct_offsets(&sorted); let mut result: Vec> = (0..n).map(|_| None).collect(); let mut rest = values; @@ -117,11 +115,13 @@ where Box::new(result.into_iter().map(|r| r.expect("filled"))) } -/// Returns the first offset that occurs more than once in `sorted`, which -/// must be sorted by offset. +/// Panics if an offset occurs more than once in `sorted`, which must be +/// sorted by offset. #[cfg(any(debug_assertions, test))] -fn first_duplicate_offset(sorted: &[(usize, usize)]) -> Option { - sorted.windows(2).find(|w| w[0].0 == w[1].0).map(|w| w[0].0) +fn assert_distinct_offsets(sorted: &[(usize, usize)]) { + if let Some(w) = sorted.windows(2).find(|w| w[0].0 == w[1].0) { + panic!("iterator_mut: aliasing detected at offset {}", w[0].0); + } } /// Dense matrix @@ -857,13 +857,15 @@ mod tests { } #[test] - fn test_first_duplicate_offset() { - assert_eq!(first_duplicate_offset(&[]), None); - assert_eq!(first_duplicate_offset(&[(0, 0), (1, 1), (2, 2)]), None); - assert_eq!( - first_duplicate_offset(&[(0, 0), (1, 2), (1, 1), (3, 3)]), - Some(1) - ); + fn test_assert_distinct_offsets() { + assert_distinct_offsets(&[]); + assert_distinct_offsets(&[(0, 0), (1, 1), (2, 2)]); + } + + #[test] + #[should_panic(expected = "iterator_mut: aliasing detected at offset 1")] + fn test_assert_distinct_offsets_detects_duplicate() { + assert_distinct_offsets(&[(0, 0), (1, 2), (1, 1), (3, 3)]); } #[cfg(debug_assertions)]