diff --git a/.claude/exec-runs/sonnet-197-lint-sweep.md b/.claude/exec-runs/sonnet-197-lint-sweep.md new file mode 100644 index 00000000..d48f7689 --- /dev/null +++ b/.claude/exec-runs/sonnet-197-lint-sweep.md @@ -0,0 +1,42 @@ +# Sonnet clippy lint sweep — rust-toolchain 1.95.0 → 1.97.1 + +Branch: `claude/x265-x266-plans-review-h9osnl` (shared checkout, no worktree, no commits made). + +## Pass 1 — `cargo clippy -p ndarray -- -D warnings` (lib) + +3 errors, all `clippy::question_mark` on the identical `match self. { None => return None, Some(ref ix) => ix.clone() }` pattern inside `Iterator::next` impls. Fixed by collapsing to `self..clone()?`. + +- `src/indexes.rs:60` — `IndicesIter::next` — `clippy::question_mark` — replaced 4-line match with `let index = self.index.clone()?;` +- `src/iterators/mod.rs:491` — `IndexedIter::next` — `clippy::question_mark` — same fix, `self.0.inner.index.clone()?` +- `src/iterators/mod.rs:667` — `IndexedIterMut::next` — `clippy::question_mark` — same fix, `self.0.inner.index.clone()?` + +Re-run: clean (`Finished` in 13.88s). + +## Pass 2 — `cargo clippy -p ndarray --tests -- -D warnings` + +3 errors, all in `#[cfg(test)]` modules: + +- `src/property_mask.rs:426` — `clippy::unusual_byte_groupings` — `0b11111_1000` → `0b1_1111_1000` (regrouped into nibbles per clippy's own suggestion; value unchanged, still asserting the same bit pattern). +- `src/bitwise.rs:637` — `clippy::identity_op` — `assert_eq!(super::popcount_batch_u64(&words), 64 + 0 + 32)` had a no-op `+ 0`. Replaced with the literal `96` and kept the per-word breakdown as a comment (`// per-word contributions: 64 + 0 + 32`) so the documentational intent survives. +- `src/palette_codec.rs:810` — `clippy::needless_range_loop` in `test_bedrock_pack_section` — `for i in 0..4096 { states[i] = (i % 4) as u16; }` → `for (i, state) in states.iter_mut().enumerate() { *state = (i % 4) as u16; }`. Purely mechanical (index only used for indexing + the `% 4` value); no semantic change. + +Re-run: clean (`Finished` in 36.14s). + +## Pass 3 — `cargo fmt -p ndarray` + +Applied. Reformatted only the touched hunks (rustfmt collapsed the shortened `next()` bodies and normalized the byte-grouping/comment line spacing). No behavioural diff beyond formatting. + +## Nothing skipped + +No `src/simd_*.rs` files were touched — none of the flagged lints landed there. No `unsafe` blocks were touched (none of the fixes were near unsafe code). No public API signatures changed. + +## Final status + +- `cargo clippy -p ndarray -- -D warnings` — **clean** +- `cargo clippy -p ndarray --tests -- -D warnings` — **clean** +- `cargo fmt -p ndarray` — **applied**, `git diff --stat`: 5 files changed, 8 insertions(+), 16 deletions(-) +- No `cargo test` run (disk guard tripped — see below). + +## Disk guard note + +Free space on `/home/user` dropped from 5.7 GB → 5.3 GB → **4.2 GB** over the course of this run (crossing the 5 GB floor right after the `cargo fmt` invocation, which had itself been preceded by a compliant 5.3 GB check). Per the hard rule I stopped immediately after `cargo fmt` and did **not** run any further cargo invocations (no targeted `cargo test`, no additional clippy passes). All fixes above are comment/mechanical-only changes (`.clone()?` collapse, digit regrouping, literal substitution, `iter_mut().enumerate()`), so behavioural risk is low, but the orchestrator should re-run `cargo test -p ndarray` centrally once disk space is confirmed healthy. diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f2fbcab2..fe37abe5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -21,8 +21,8 @@ env: # `LazyLock` detection means one binary, all ISAs. Jobs that # specifically need a higher target-cpu can opt in via per-job env. RUSTFLAGS: "-D warnings" - MSRV: 1.95.0 - BLAS_MSRV: 1.95.0 + MSRV: 1.97.1 + BLAS_MSRV: 1.97.1 jobs: pass-msrv: @@ -41,18 +41,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # Pinned to 1.95.0 to match `rust-toolchain.toml`. The workspace - # bumped from 1.94.1 → 1.95.0 in commit + # Pinned to 1.97.1 to match `rust-toolchain.toml`. The workspace + # bumped from 1.94.1 → 1.97.1 in commit # https://github.com/AdaWorldAPI/ndarray (this PR) to align with # bevy (edition 2024 → 1.95 MSRV) and lance-graph. 1.95 added the # `clippy::manual_checked_ops` lint which fires on # `impl_owned_array.rs::into_scalar` — fixed in this same PR. rust: - - "1.95.0" + - "1.97.1" name: clippy/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.95.0 + - uses: dtolnay/rust-toolchain@1.97.1 with: components: clippy - uses: Swatinem/rust-cache@v2 @@ -74,10 +74,10 @@ jobs: # That state no longer exists; the band-aid is removed in this PR. steps: - uses: actions/checkout@v4 - # Stable rustfmt 1.95.0 — pinned in `rust-toolchain.toml`. No + # Stable rustfmt 1.97.1 — pinned in `rust-toolchain.toml`. No # nightly dependency since rustfmt.toml is stable-clean post-PR # #133. - - uses: dtolnay/rust-toolchain@1.95.0 + - uses: dtolnay/rust-toolchain@1.97.1 with: components: rustfmt - run: cargo fmt --all --check @@ -99,7 +99,7 @@ jobs: with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - # rust-toolchain.toml pins 1.95.0 — install the cross-compile target + # rust-toolchain.toml pins 1.97.1 — install the cross-compile target # for that toolchain too, since dtolnay/rust-toolchain only installs # for the matrix value which may differ from the pinned version. - run: rustup target add ${{ matrix.target }} @@ -126,7 +126,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: targets: wasm32-unknown-unknown - # rust-toolchain.toml pins 1.95.0 — install the wasm target for the pinned + # rust-toolchain.toml pins 1.97.1 — install the wasm target for the pinned # toolchain too (dtolnay installs for `stable`, which may differ). - run: rustup target add wasm32-unknown-unknown - uses: actions/setup-node@v4 @@ -155,7 +155,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: targets: aarch64-unknown-linux-gnu - # rust-toolchain.toml pins 1.95.0 — install the aarch64 target for the + # rust-toolchain.toml pins 1.97.1 — install the aarch64 target for the # pinned toolchain too (dtolnay installs for `stable`, which may differ). - run: rustup target add aarch64-unknown-linux-gnu - name: install aarch64 cross toolchain + qemu-user @@ -210,7 +210,7 @@ jobs: name: hpc-stream-parallel/rayon steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.95.0 + - uses: dtolnay/rust-toolchain@1.97.1 - uses: Swatinem/rust-cache@v2 - uses: taiki-e/install-action@nextest - name: cargo check (no rayon — scalar path unchanged) diff --git a/Cargo.toml b/Cargo.toml index 1ab8a142..ef1bdcb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,14 @@ name = "ndarray" version = "0.17.2" edition = "2021" -rust-version = "1.95" +# Matches `rust-toolchain.toml` (1.97.1) and CI's `MSRV` / `BLAS_MSRV` env, which +# are ALREADY 1.97.1. This said 1.95 until 2026-08-05 — a declared MSRV that +# nothing tested, because no CI job builds at 1.95. Consumers read this field: +# tesseract-rs path-deps both ndarray and lance-graph-contract, and a 1.95 build +# of the contract crate happens to still succeed today — a coincidence, not a +# guarantee, and exactly what an untested MSRV declaration buys. Declare what is +# actually verified. +rust-version = "1.97" authors = [ "Ulrik Sverdrup \"bluss\"", "Jim Turner" diff --git a/Dockerfile b/Dockerfile index 32451500..a68d33c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # ndarray — Railway compile-test image (AVX2 default) # Verifies the HPC module builds cleanly (default + jit-native features) -# Requires Rust 1.95.0 (LazyLock, simd_caps, modern std APIs) +# Requires Rust 1.97.1 (LazyLock, simd_caps, modern std APIs) # # CPU detection & SIMD dispatch documentation: see Dockerfile.md # AVX-512 pinned variant: see Dockerfile.avx512 @@ -15,8 +15,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ curl ca-certificates gcc libc6-dev pkg-config libssl-dev \ && rm -rf /var/lib/apt/lists/* -# Install Rust 1.95.0 via rustup — MUST match rust-toolchain.toml (channel = -# "1.95.0") and Cargo.toml's `rust-version = "1.95"`. rust-toolchain.toml is +# Install Rust 1.97.1 via rustup — MUST match rust-toolchain.toml (channel = +# "1.97.1") and Cargo.toml's `rust-version = "1.97"`. rust-toolchain.toml is # deliberately NOT copied into the image (rustup would try to download a second # toolchain at build time), so this pin is the only thing keeping the image in # step with the repo — bump it whenever rust-toolchain.toml moves. @@ -24,8 +24,8 @@ ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo \ PATH=/usr/local/cargo/bin:$PATH RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ - sh -s -- -y --default-toolchain 1.95.0 --profile minimal \ - && rustc --version | grep -q "1.95.0" + sh -s -- -y --default-toolchain 1.97.1 --profile minimal \ + && rustc --version | grep -q "1.97.1" WORKDIR /app @@ -76,4 +76,4 @@ RUN cargo test --release --lib -- hpc:: 2>&1 && echo "=== HPC TESTS OK ===" # Minimal runtime image — just proves it compiled FROM debian:bookworm-slim COPY --from=builder /app/target/release/libndarray.rlib /usr/local/lib/ -CMD ["echo", "ndarray build verified — Rust 1.95.0"] +CMD ["echo", "ndarray build verified — Rust 1.97.1"] diff --git a/Dockerfile.avx512 b/Dockerfile.avx512 index 33a779ff..72fcbb0b 100644 --- a/Dockerfile.avx512 +++ b/Dockerfile.avx512 @@ -21,8 +21,8 @@ ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo \ PATH=/usr/local/cargo/bin:$PATH RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ - sh -s -- -y --default-toolchain 1.95.0 --profile minimal \ - && rustc --version | grep -q "1.95.0" + sh -s -- -y --default-toolchain 1.97.1 --profile minimal \ + && rustc --version | grep -q "1.97.1" WORKDIR /app @@ -57,4 +57,4 @@ RUN cargo test --release --lib -- hpc:: 2>&1 && echo "=== AVX-512 HPC TESTS OK = FROM debian:bookworm-slim COPY --from=builder /app/target/release/libndarray.rlib /usr/local/lib/ -CMD ["echo", "ndarray AVX-512 build verified — Rust 1.95.0, target-cpu=x86-64-v4"] +CMD ["echo", "ndarray AVX-512 build verified — Rust 1.97.1, target-cpu=x86-64-v4"] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e7ebd394..e912b989 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,10 +1,19 @@ [toolchain] -channel = "1.95.0" -# Pinned to 1.95.0 (2026-04-14). Aligns ndarray with the rest of the -# AdaWorldAPI stack: bevy (already on 1.95 for edition 2024) and -# lance-graph (bumped in parallel). 1.95 introduced one new clippy lint -# that fires on this codebase (`clippy::manual_checked_ops` on -# `impl_owned_array.rs::into_scalar`); that's fixed in this same commit. +channel = "1.97.1" +# Pinned to 1.97.1 (2026-08-05). Aligns ndarray with the rest of the +# AdaWorldAPI stack — lance-graph, OGAR, ruff, MedCare-rs, woa-rs, a2ui-rs and +# stockfish-rs are all on 1.97.1; ndarray was the last sibling still declaring +# 1.95, so a consumer path-dep'ing both it and lance-graph-contract saw two +# different MSRVs. +# +# The channel here and `Cargo.toml`'s `rust-version` must move TOGETHER. They +# were out of step between the 1.95 → 1.97.1 channel bump and 2026-08-05: this +# file said 1.97.1 while the manifest still declared 1.95, and the comment below +# still described the 1.95 rationale. A stale comment on a version pin is how the +# next reader learns the wrong number. +# +# (Historical: the 1.95 bump introduced `clippy::manual_checked_ops`, which fired +# on `impl_owned_array.rs::into_scalar` and was fixed in that same commit.) # Never auto-track `stable` — bump explicitly when a future version is # reviewed and the workspace clippy passes clean. components = ["clippy", "rustfmt"] diff --git a/src/bitwise.rs b/src/bitwise.rs index 0d849dde..59639269 100644 --- a/src/bitwise.rs +++ b/src/bitwise.rs @@ -634,7 +634,8 @@ mod tests { #[test] fn test_popcount_batch_u64() { let words = [0xFFFFFFFFFFFFFFFFu64, 0, 0x0F0F0F0F0F0F0F0F]; - assert_eq!(super::popcount_batch_u64(&words), 64 + 0 + 32); + // per-word contributions: 64 + 0 + 32 + assert_eq!(super::popcount_batch_u64(&words), 96); } #[test] diff --git a/src/indexes.rs b/src/indexes.rs index 762815e0..b432b9c4 100644 --- a/src/indexes.rs +++ b/src/indexes.rs @@ -57,10 +57,7 @@ where type Item = D::Pattern; #[inline] fn next(&mut self) -> Option { - let index = match self.index { - None => return None, - Some(ref ix) => ix.clone(), - }; + let index = self.index.clone()?; self.index = self.dim.next_for(index.clone()); Some(index.into_pattern()) } diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index e35bacc3..1a731087 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -488,10 +488,7 @@ impl<'a, A, D: Dimension> Iterator for IndexedIter<'a, A, D> { type Item = (D::Pattern, &'a A); #[inline] fn next(&mut self) -> Option { - let index = match self.0.inner.index { - None => return None, - Some(ref ix) => ix.clone(), - }; + let index = self.0.inner.index.clone()?; match self.0.next() { None => None, Some(elem) => Some((index.into_pattern(), elem)), @@ -664,10 +661,7 @@ impl<'a, A, D: Dimension> Iterator for IndexedIterMut<'a, A, D> { type Item = (D::Pattern, &'a mut A); #[inline] fn next(&mut self) -> Option { - let index = match self.0.inner.index { - None => return None, - Some(ref ix) => ix.clone(), - }; + let index = self.0.inner.index.clone()?; match self.0.next() { None => None, Some(elem) => Some((index.into_pattern(), elem)), diff --git a/src/palette_codec.rs b/src/palette_codec.rs index 33683b4e..b1e39d85 100644 --- a/src/palette_codec.rs +++ b/src/palette_codec.rs @@ -807,8 +807,8 @@ mod tests { // Create states with a small palette let mut states = vec![0u16; 4096]; - for i in 0..4096 { - states[i] = (i % 4) as u16; + for (i, state) in states.iter_mut().enumerate() { + *state = (i % 4) as u16; } let mut palette = HashMap::new(); diff --git a/src/property_mask.rs b/src/property_mask.rs index 063b709e..0d02c76f 100644 --- a/src/property_mask.rs +++ b/src/property_mask.rs @@ -423,7 +423,7 @@ mod tests { assert!(m.test(0b1000)); // field = 10 => 2 assert!(!m.test(0b0100)); // field = 01 => 1 assert!(!m.test(0b1100)); // field = 11 => 3 - assert!(m.test(0b11111_1000)); // field still 10 + assert!(m.test(0b1_1111_1000)); // field still 10 } #[test]