From 2095b76fc617c4a42b0e47b6a31233cfa18b56bb Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 5 Sep 2026 19:30:08 +0000 Subject: [PATCH 1/2] toolchain: 1.97.1 -> 1.98.1, and the chunks_exact -> as_chunks shift it forces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workspace-wide sweep, repo 2 of 9. ndarray's own rule is honoured: the channel and Cargo.toml's rust-version move TOGETHER (1.97 -> 1.98). The toolchain comment is rewritten rather than edited. It opened 'Pinned to 1.97.1', restating the version in prose beside the channel line — and this file already carries the warning for exactly that: 'A stale comment on a version pin is how the next reader learns the wrong number.' A bump edits channel and leaves prose behind, so the prose now keeps only the rationale plus an append-only bump log. TWO 1.98 findings here, where lance-graph had one: 1. clippy::chunks_exact_to_as_chunks (new, default-on STYLE group) at 6 sites. 2. clippy::from_iter_instead_of_collect was REMOVED in 1.98, and src/lib.rs carried it in a crate-level allow list, which is itself an error now. The chunks_exact change is more than lint appeasement. chunks_exact was the right call when these buffers were 4096 and 16384 wide: the remainder is always empty there and the runtime width check is dead weight. At the 32+96 shape — classid plus the twelve-byte facet payload — the width is a compile-time fact, and as_chunks:: returns &[T; N], a type that CARRIES the width instead of re-checking it. That is the honest representation for a fixed-width register. Two of the six wanted the destructuring form, not the mechanical swap: asum_f32 and asum_f64 called chunks_exact(N) AND chunks_exact(N).remainder() separately, and as_chunks returns both halves at once, so each kernel now makes one call where it made two. Gate, because these are AVX-512 kernels: clippy --all-targets -D warnings exit 0, cargo fmt clean, and the full library suite at 2278 passed / 0 failed. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V --- Cargo.toml | 2 +- crates/numeric-tests/tests/accuracy.rs | 9 ++++---- examples/morton_cascade_probe.rs | 2 +- examples/onebrc_cascade_probe.rs | 2 +- rust-toolchain.toml | 31 +++++++++++++++++--------- src/backend/kernels_avx512.rs | 10 +++++---- src/lib.rs | 1 - src/simd_ops.rs | 7 +++++- 8 files changed, 39 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4fc7aa6c..ef7f9d9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" # 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" +rust-version = "1.98" authors = [ "Ulrik Sverdrup \"bluss\"", "Jim Turner" diff --git a/crates/numeric-tests/tests/accuracy.rs b/crates/numeric-tests/tests/accuracy.rs index e5172580..c132f36e 100644 --- a/crates/numeric-tests/tests/accuracy.rs +++ b/crates/numeric-tests/tests/accuracy.rs @@ -269,18 +269,17 @@ fn accurate_mul_with_column_f64() { let a = gen::(Ix2(m, k), rng); let b_owner = gen::(Ix2(k, k), rng); let b_row_col; - let b_sq; // pick dense square or broadcasted to square matrix - match i { - 0..=3 => b_sq = b_owner.view(), + let b_sq = match i { + 0..=3 => b_owner.view(), 4..=7 => { b_row_col = b_owner.column(0); - b_sq = b_row_col.broadcast((k, k)).unwrap(); + b_row_col.broadcast((k, k)).unwrap() } _otherwise => { b_row_col = b_owner.row(0); - b_sq = b_row_col.broadcast((k, k)).unwrap(); + b_row_col.broadcast((k, k)).unwrap() } }; diff --git a/examples/morton_cascade_probe.rs b/examples/morton_cascade_probe.rs index 1afcb298..9e6c8254 100644 --- a/examples/morton_cascade_probe.rs +++ b/examples/morton_cascade_probe.rs @@ -101,7 +101,7 @@ impl Pyramid { for l in 1..=k as usize { let prev = &levels[l - 1]; let mut cur = Vec::with_capacity(prev.len() / 4); - for node in prev.chunks_exact(4) { + for node in prev.as_chunks::<4>().0 { let mn = node.iter().map(|p| p.0).fold(f32::INFINITY, f32::min); let mx = node.iter().map(|p| p.1).fold(f32::NEG_INFINITY, f32::max); cur.push((mn, mx)); diff --git a/examples/onebrc_cascade_probe.rs b/examples/onebrc_cascade_probe.rs index 1ee9c79b..fbf1c5b1 100644 --- a/examples/onebrc_cascade_probe.rs +++ b/examples/onebrc_cascade_probe.rs @@ -201,7 +201,7 @@ impl Pyramid { for l in 1..=k as usize { let prev = &levels[l - 1]; let mut cur = Vec::with_capacity(prev.len() / 4); - for q in prev.chunks_exact(4) { + for q in prev.as_chunks::<4>().0 { cur.push(Node { min: q.iter().map(|n| n.min).fold(f32::INFINITY, f32::min), max: q.iter().map(|n| n.max).fold(f32::NEG_INFINITY, f32::max), diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e912b989..06fb5ae5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,19 +1,28 @@ [toolchain] -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. +channel = "1.98.1" +# The pinned version is the `channel` line ABOVE — deliberately not restated +# here. This comment used to open "Pinned to 1.97.1", and this file's own +# warning explains why that is a trap: "A stale comment on a version pin is how +# the next reader learns the wrong number." A bump edits `channel` and leaves +# prose behind, so the prose keeps only the rationale. +# +# Rationale: ndarray moves in lockstep with the rest of the AdaWorldAPI stack — +# lance-graph, OGAR, ruff, MedCare-rs, woa-rs, a2ui-rs, stockfish-rs — because a +# consumer path-dep'ing both ndarray and lance-graph-contract otherwise sees two +# different MSRVs. ndarray was once the last sibling still declaring 1.95. # # 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. +# file named one version while the manifest declared another. +# +# Bump log (append one line per bump): +# 1.95 → 1.97.1 stack alignment (2026-08-05); the 1.95 bump had introduced +# `clippy::manual_checked_ops`, fixed in `impl_owned_array.rs` +# 1.97.1 → 1.98.1 current stable (1.98.0 2026-08-20, 1.98.1 2026-09-01); +# workspace-wide sweep. The 1.98 delta measured across the +# stack is ONE lint, `clippy::chunks_exact_to_as_chunks` +# (new, default-on STYLE group) — zero sites in this repo. # -# (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/backend/kernels_avx512.rs b/src/backend/kernels_avx512.rs index 8b1bec34..ce80a7d8 100644 --- a/src/backend/kernels_avx512.rs +++ b/src/backend/kernels_avx512.rs @@ -173,12 +173,13 @@ pub fn scal_f64(alpha: f64, x: &mut [f64]) { #[cfg(target_arch = "x86_64")] #[target_feature(enable = "avx512f")] pub fn asum_f32(x: &[f32]) -> f32 { + let (chunks, remainder) = x.as_chunks::<16>(); let mut acc = F32x16::splat(0.0); - for chunk in x.chunks_exact(16) { + for chunk in chunks { acc += F32x16::from_slice(chunk).abs(); } let mut sum = acc.reduce_sum(); - for &v in x.chunks_exact(16).remainder() { + for &v in remainder { sum += v.abs(); } sum @@ -190,12 +191,13 @@ pub fn asum_f32(x: &[f32]) -> f32 { #[cfg(target_arch = "x86_64")] #[target_feature(enable = "avx512f")] pub fn asum_f64(x: &[f64]) -> f64 { + let (chunks, remainder) = x.as_chunks::<8>(); let mut acc = F64x8::splat(0.0); - for chunk in x.chunks_exact(8) { + for chunk in chunks { acc += F64x8::from_slice(chunk).abs(); } let mut sum = acc.reduce_sum(); - for &v in x.chunks_exact(8).remainder() { + for &v in remainder { sum += v.abs(); } sum diff --git a/src/lib.rs b/src/lib.rs index 0b510eb5..8f8147eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,6 @@ clippy::deref_addrof, clippy::manual_map, // is not an error clippy::while_let_on_iterator, // is not an error - clippy::from_iter_instead_of_collect, // using from_iter is good style clippy::incompatible_msrv, // false positive PointerExt::offset )] #![doc(test(attr(deny(warnings))))] diff --git a/src/simd_ops.rs b/src/simd_ops.rs index 2c0e213b..fde4a5af 100644 --- a/src/simd_ops.rs +++ b/src/simd_ops.rs @@ -609,7 +609,12 @@ pub fn bf16_tile_gemm_16x16(a_bf16: &[u16], b_bf16: &[u16], c: &mut [f32], k: us *slot = b_f32[kk * 16 + j]; } let mut acc = F32x16::splat(0.0); - for (ra, rb) in a_row.chunks_exact(16).zip(col.chunks_exact(16)) { + for (ra, rb) in a_row + .as_chunks::<16>() + .0 + .iter() + .zip(col.as_chunks::<16>().0) + { acc = F32x16::from_slice(ra).mul_add(F32x16::from_slice(rb), acc); } c[i * 16 + j] += acc.reduce_sum(); From 68c0909d7660ac15508e2da7302de6972a44c03e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 5 Sep 2026 19:38:50 +0000 Subject: [PATCH 2/2] ci: move ndarray's hardcoded toolchain pins to 1.98.1 too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caught by reading the PR's own check names: CI reported tests/1.97.1 and clippy/1.97.1 AFTER the toolchain file said 1.98.1. Unlike lance-graph, whose eleven workflows all read rust-toolchain.toml with no toolchain input, this repo's CI pins the version explicitly via dtolnay/rust-toolchain@ plus an MSRV env var. So bumping rust-toolchain.toml alone left CI still testing the OLD toolchain — the bump would have been cosmetic where it is actually gated. Six real pins moved: MSRV, BLAS_MSRV, the clippy matrix entry, and three dtolnay/rust-toolchain@ steps. Six comments also restated the version in prose and are rewritten rather than edited, the same lesson rust-toolchain.toml already carries: a stale comment on a version pin is how the next reader learns the wrong number. The matrix comment now names WHERE the number lives (three places in this file, because CI does not read the toolchain file) instead of duplicating the number itself, and points at the bump log as the single record of why each move happened. The comment it replaces still described the 1.94.1 bump as 'this PR'. Verified: zero remaining 1.97.1 references in ci.yaml, and the YAML parses. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AGVLyRZNEKKBSfBDJfbY3V --- .github/workflows/ci.yaml | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fe37abe5..b05b80ac 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.97.1 - BLAS_MSRV: 1.97.1 + MSRV: 1.98.1 + BLAS_MSRV: 1.98.1 jobs: pass-msrv: @@ -41,18 +41,19 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # 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. + # Must equal `rust-toolchain.toml`'s channel. The version is NOT + # restated in this comment on purpose: CI here does NOT read the + # toolchain file (it uses `dtolnay/rust-toolchain@`), so the + # number lives in three places — the `MSRV`/`BLAS_MSRV` env above, the + # matrix entry below, and each `dtolnay/rust-toolchain@` step. All of + # them move together with the toolchain file, and its bump log is the + # one place that records WHY each move happened. rust: - - "1.97.1" + - "1.98.1" name: clippy/${{ matrix.rust }} steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.97.1 + - uses: dtolnay/rust-toolchain@1.98.1 with: components: clippy - uses: Swatinem/rust-cache@v2 @@ -74,10 +75,10 @@ jobs: # That state no longer exists; the band-aid is removed in this PR. steps: - uses: actions/checkout@v4 - # 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.97.1 + # Stable rustfmt on the pinned channel (see `rust-toolchain.toml`; + # version deliberately not restated here). No nightly dependency since + # rustfmt.toml is stable-clean post-PR #133. + - uses: dtolnay/rust-toolchain@1.98.1 with: components: rustfmt - run: cargo fmt --all --check @@ -99,7 +100,7 @@ jobs: with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - # rust-toolchain.toml pins 1.97.1 — install the cross-compile target + # rust-toolchain.toml pins the channel — 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 +127,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: targets: wasm32-unknown-unknown - # rust-toolchain.toml pins 1.97.1 — install the wasm target for the pinned + # rust-toolchain.toml pins the channel — 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 +156,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: targets: aarch64-unknown-linux-gnu - # rust-toolchain.toml pins 1.97.1 — install the aarch64 target for the + # rust-toolchain.toml pins the channel — 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 +211,7 @@ jobs: name: hpc-stream-parallel/rayon steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.97.1 + - uses: dtolnay/rust-toolchain@1.98.1 - uses: Swatinem/rust-cache@v2 - uses: taiki-e/install-action@nextest - name: cargo check (no rayon — scalar path unchanged)