From 1982d871475ffb8c2c160a96916a76fdcb6eb11c Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 18 Jan 2025 15:37:14 -0500 Subject: [PATCH 01/43] Merge commit '3383cfbd3572465febc7a8f816a46304373de46a' into sync-from-portable-simd-2025-01-18 --- .github/workflows/ci.yml | 125 +++++----- .github/workflows/doc.yml | 2 +- .gitignore | 1 + Cargo.toml | 6 + Cross.toml | 2 + crates/core_simd/Cargo.toml | 3 +- crates/core_simd/src/lane_count.rs | 8 +- crates/core_simd/src/lib.rs | 4 +- crates/core_simd/src/masks.rs | 42 ---- crates/core_simd/src/masks/bitmask.rs | 17 -- crates/core_simd/src/masks/full_masks.rs | 58 +---- crates/core_simd/src/ops.rs | 25 +- crates/core_simd/src/simd/cmp/eq.rs | 2 +- crates/core_simd/src/simd/num/float.rs | 30 ++- crates/core_simd/src/simd/num/int.rs | 43 +++- crates/core_simd/src/simd/num/uint.rs | 42 +++- crates/core_simd/src/simd/ptr/const_ptr.rs | 21 ++ crates/core_simd/src/simd/ptr/mut_ptr.rs | 21 ++ crates/core_simd/src/swizzle.rs | 257 ++++++++++++++++++++- crates/core_simd/src/swizzle_dyn.rs | 59 ++++- crates/core_simd/src/vector.rs | 43 +++- crates/core_simd/src/vendor.rs | 3 + crates/core_simd/src/vendor/loongarch64.rs | 31 +++ crates/core_simd/tests/layout.rs | 35 +++ crates/core_simd/tests/masks.rs | 43 ---- crates/core_simd/tests/ops_macros.rs | 38 +++ crates/core_simd/tests/swizzle.rs | 18 ++ crates/test_helpers/Cargo.toml | 3 - crates/test_helpers/src/lib.rs | 216 ++++++++--------- rust-toolchain.toml | 3 + subtree-sync.sh | 52 +++++ 31 files changed, 865 insertions(+), 388 deletions(-) create mode 100644 Cross.toml create mode 100644 crates/core_simd/src/vendor/loongarch64.rs create mode 100644 crates/core_simd/tests/layout.rs create mode 100644 rust-toolchain.toml create mode 100755 subtree-sync.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b292be2d6f999..3984d8f0d8d99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: env: CARGO_NET_RETRY: 10 RUSTUP_MAX_RETRIES: 10 + PROPTEST_CASES: 64 jobs: rustfmt: @@ -16,12 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup component add rustfmt + - uses: actions/checkout@v4 - name: Run rustfmt run: cargo fmt --all -- --check @@ -37,7 +33,9 @@ jobs: - i686-unknown-linux-gnu - i586-unknown-linux-gnu - aarch64-unknown-linux-gnu + - arm64ec-pc-windows-msvc - armv7-unknown-linux-gnueabihf + - loongarch64-unknown-linux-gnu # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu # - mips64-unknown-linux-gnuabi64 @@ -49,13 +47,9 @@ jobs: - wasm32-unknown-unknown steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} - rustup component add clippy + run: rustup target add ${{ matrix.target }} - name: Run Clippy run: cargo clippy --all-targets --target ${{ matrix.target }} @@ -65,26 +59,19 @@ jobs: strategy: fail-fast: false matrix: - target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu, x86_64-apple-darwin] + target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu] # `default` means we use the default target config for the target, # `native` means we run with `-Ctarget-cpu=native`, and anything else is # an arg to `-Ctarget-feature` target_feature: [default, native, +sse3, +ssse3, +sse4.1, +sse4.2, +avx, +avx2] exclude: - # The macos runners seem to only reliably support up to `avx`. - - { target: x86_64-apple-darwin, target_feature: +avx2 } - # These features are statically known to be present for all 64 bit - # macs, and thus are covered by the `default` test - - { target: x86_64-apple-darwin, target_feature: +sse3 } - - { target: x86_64-apple-darwin, target_feature: +ssse3 } # -Ctarget-cpu=native sounds like bad-news if target != host - { target: i686-pc-windows-msvc, target_feature: native } - { target: i586-pc-windows-msvc, target_feature: native } include: # Populate the `matrix.os` field - - { target: x86_64-apple-darwin, os: macos-latest } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } - { target: x86_64-pc-windows-msvc, os: windows-latest } - { target: i686-pc-windows-msvc, os: windows-latest } @@ -98,12 +85,9 @@ jobs: # avx512vl, but occasionally doesn't. Maybe one day we can enable it. steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} + run: rustup target add ${{ matrix.target }} - name: Configure RUSTFLAGS shell: bash @@ -145,6 +129,35 @@ jobs: run: cargo doc --verbose --target=${{ matrix.target }} env: RUSTDOCFLAGS: -Dwarnings + + macos-tests: + name: ${{ matrix.target }} + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + target: + - aarch64-apple-darwin + - x86_64-apple-darwin + steps: + - uses: actions/checkout@v4 + - name: Setup Rust + run: rustup target add ${{ matrix.target }} + + - name: Configure RUSTFLAGS + shell: bash + run: echo "RUSTFLAGS=-Dwarnings" >> $GITHUB_ENV + + - name: Test (debug) + run: cargo test --verbose --target=${{ matrix.target }} + + - name: Test (release) + run: cargo test --verbose --target=${{ matrix.target }} --release + + - name: Generate docs + run: cargo doc --verbose --target=${{ matrix.target }} + env: + RUSTDOCFLAGS: -Dwarnings wasm-tests: name: "wasm (firefox, ${{ matrix.name }})" @@ -155,11 +168,7 @@ jobs: - { name: default, RUSTFLAGS: "" } - { name: simd128, RUSTFLAGS: "-C target-feature=+simd128" } steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly + - uses: actions/checkout@v4 - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Test (debug) @@ -174,6 +183,8 @@ jobs: cross-tests: name: "${{ matrix.target_feature }} on ${{ matrix.target }} (via cross)" runs-on: ubuntu-latest + env: + PROPTEST_CASES: 16 strategy: fail-fast: false @@ -185,6 +196,7 @@ jobs: - powerpc-unknown-linux-gnu - powerpc64le-unknown-linux-gnu # includes altivec by default - riscv64gc-unknown-linux-gnu + - loongarch64-unknown-linux-gnu # MIPS uses a nonstandard binary representation for NaNs which makes it worth testing # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu @@ -201,24 +213,14 @@ jobs: # - { target: riscv64gc-unknown-linux-gnu, target_feature: "+v,+zvl128b" } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} - rustup component add rust-src + run: rustup target add ${{ matrix.target }} - name: Install Cross - # Equivalent to `cargo install cross`, but downloading a prebuilt - # binary. Ideally we wouldn't hardcode a version, but the version number - # being part of the tarball means we can't just use the download/latest - # URL :( + # Install the latest git version for newer targets. run: | - CROSS_URL=https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-gnu.tar.gz - mkdir -p "$HOME/.bin" - curl -sfSL --retry-delay 10 --retry 5 "${CROSS_URL}" | tar zxf - -C "$HOME/.bin" - echo "$HOME/.bin" >> $GITHUB_PATH + cargo install cross --git https://github.com/cross-rs/cross --rev 4090beca3cfffa44371a5bba524de3a578aa46c3 - name: Configure Emulated CPUs run: | @@ -242,34 +244,11 @@ jobs: - name: Test (release) run: cross test --verbose --target=${{ matrix.target }} --release - features: - name: "Test cargo features (${{ matrix.simd }} × ${{ matrix.features }})" + miri: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - simd: - - "" - - "avx512" - features: - - "" - - "--features std" - - "--features all_lane_counts" - - "--all-features" - + env: + PROPTEST_CASES: 16 steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - - name: Detect AVX512 - run: echo "CPU_FEATURE=$(lscpu | grep -o avx512[a-z]* | sed s/avx/+avx/ | tr '\n' ',' )" >> $GITHUB_ENV - - name: Check build - if: ${{ matrix.simd == '' }} - run: RUSTFLAGS="-Dwarnings" cargo test --all-targets --no-default-features ${{ matrix.features }} - - name: Check AVX - if: ${{ matrix.simd == 'avx512' && contains(env.CPU_FEATURE, 'avx512') }} - run: | - echo "Found AVX features: $CPU_FEATURE" - RUSTFLAGS="-Dwarnings -Ctarget-feature=$CPU_FEATURE" cargo test --all-targets --no-default-features ${{ matrix.features }} + - uses: actions/checkout@v4 + - name: Test (Miri) + run: cargo miri test diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 9d1fa66ccb595..22c2cb3f67f1b 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Setup Rust run: | diff --git a/.gitignore b/.gitignore index ea8c4bf7f35f6..9673e52dcadba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +git-subtree.sh diff --git a/Cargo.toml b/Cargo.toml index d1732aaec2f92..21d4584a9f4d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,9 @@ members = [ "crates/std_float", "crates/test_helpers", ] + +[profile.test.package."*"] +opt-level = 2 + +[profile.test.package.test_helpers] +opt-level = 2 diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000000000..d21e76b92dd1a --- /dev/null +++ b/Cross.toml @@ -0,0 +1,2 @@ +[build.env] +passthrough = ["PROPTEST_CASES"] diff --git a/crates/core_simd/Cargo.toml b/crates/core_simd/Cargo.toml index b4a8fd70f4c0e..a7a6d43b11d3c 100644 --- a/crates/core_simd/Cargo.toml +++ b/crates/core_simd/Cargo.toml @@ -9,10 +9,9 @@ categories = ["hardware-support", "no-std"] license = "MIT OR Apache-2.0" [features] -default = ["as_crate"] +default = ["as_crate", "std"] as_crate = [] std = [] -all_lane_counts = [] [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen = "0.2" diff --git a/crates/core_simd/src/lane_count.rs b/crates/core_simd/src/lane_count.rs index 4cd7265ed671e..280b27bc9bc6f 100644 --- a/crates/core_simd/src/lane_count.rs +++ b/crates/core_simd/src/lane_count.rs @@ -33,10 +33,8 @@ macro_rules! supported_lane_count { }; } -supported_lane_count!(1, 2, 4, 8, 16, 32, 64); -#[cfg(feature = "all_lane_counts")] supported_lane_count!( - 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 ); diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 992a7705e3c52..7f57847c9c234 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![feature( - const_refs_to_cell, - const_mut_refs, + const_eval_select, convert_float_to_int, core_intrinsics, decl_macro, @@ -26,6 +25,7 @@ all(target_arch = "arm", target_feature = "v7"), feature(stdarch_arm_neon_intrinsics) )] +#![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))] #![cfg_attr( any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index 04de3a968276d..b763a7c75a5a6 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -308,48 +308,6 @@ where Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } - /// Creates a bitmask vector from a mask. - /// - /// Each bit is set if the corresponding element in the mask is `true`. - /// The remaining bits are unset. - /// - /// The bits are packed into the first N bits of the vector: - /// ``` - /// # #![feature(portable_simd)] - /// # #[cfg(feature = "as_crate")] use core_simd::simd; - /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::mask32x8; - /// let mask = mask32x8::from_array([true, false, true, false, false, false, true, false]); - /// assert_eq!(mask.to_bitmask_vector()[0], 0b01000101); - /// ``` - #[inline] - #[must_use = "method returns a new integer and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - self.0.to_bitmask_vector() - } - - /// Creates a mask from a bitmask vector. - /// - /// For each bit, if it is set, the corresponding element in the mask is set to `true`. - /// - /// The bits are packed into the first N bits of the vector: - /// ``` - /// # #![feature(portable_simd)] - /// # #[cfg(feature = "as_crate")] use core_simd::simd; - /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{mask32x8, u8x8}; - /// let bitmask = u8x8::from_array([0b01000101, 0, 0, 0, 0, 0, 0, 0]); - /// assert_eq!( - /// mask32x8::from_bitmask_vector(bitmask), - /// mask32x8::from_array([true, false, true, false, false, false, true, false]), - /// ); - /// ``` - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - Self(mask_impl::Mask::from_bitmask_vector(bitmask)) - } - /// Finds the index of the first set element. /// /// ``` diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index 96c553426ee74..db4312d5bf88a 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -122,23 +122,6 @@ where unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } } - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - let mut bitmask = Simd::splat(0); - bitmask.as_mut_array()[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); - bitmask - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); - let len = bytes.as_ref().len(); - bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); - Self(bytes, PhantomData) - } - #[inline] pub fn to_bitmask_integer(self) -> u64 { let mut bitmask = [0u8; 8]; diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 87f031a9f367a..2d01946b5747c 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -140,62 +140,6 @@ where unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) } } - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - let mut bitmask = Simd::splat(0); - - // Safety: Bytes is the right size array - unsafe { - // Compute the bitmask - let mut bytes: as SupportedLaneCount>::BitMask = - core::intrinsics::simd::simd_bitmask(self.0); - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - for x in bytes.as_mut() { - *x = x.reverse_bits() - } - if N % 8 > 0 { - bytes.as_mut()[N / 8] >>= 8 - N % 8; - } - } - - bitmask.as_mut_array()[..bytes.as_ref().len()].copy_from_slice(bytes.as_ref()); - } - - bitmask - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); - - // Safety: Bytes is the right size array - unsafe { - let len = bytes.as_ref().len(); - bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - for x in bytes.as_mut() { - *x = x.reverse_bits(); - } - if N % 8 > 0 { - bytes.as_mut()[N / 8] >>= 8 - N % 8; - } - } - - // Compute the regular mask - Self::from_int_unchecked(core::intrinsics::simd::simd_select_bitmask( - bytes, - Self::splat(true).to_int(), - Self::splat(false).to_int(), - )) - } - } - #[inline] unsafe fn to_bitmask_impl(self) -> U where @@ -283,7 +227,7 @@ where } #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] + #[must_use = "method returns a new bool and does not mutate the original value"] pub fn all(self) -> bool { // Safety: use `self` as an integer vector unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index dd7303a97b197..d3bd14a340278 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -77,7 +77,7 @@ macro_rules! int_divrem_guard { ( $lhs:ident, $rhs:ident, { const PANIC_ZERO: &'static str = $zero:literal; - $simd_call:ident + $simd_call:ident, $op:tt }, $int:ident ) => { if $rhs.simd_eq(Simd::splat(0 as _)).any() { @@ -96,8 +96,23 @@ macro_rules! int_divrem_guard { // Nice base case to make it easy to const-fold away the other branch. $rhs }; - // Safety: $lhs and rhs are vectors - unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } + + // aarch64 div fails for arbitrary `v % 0`, mod fails when rhs is MIN, for non-powers-of-two + // these operations aren't vectorized on aarch64 anyway + #[cfg(target_arch = "aarch64")] + { + let mut out = Simd::splat(0 as _); + for i in 0..Self::LEN { + out[i] = $lhs[i] $op rhs[i]; + } + out + } + + #[cfg(not(target_arch = "aarch64"))] + { + // Safety: $lhs and rhs are vectors + unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } + } } }; } @@ -205,14 +220,14 @@ for_base_ops! { impl Div::div { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to divide by zero"; - simd_div + simd_div, / } } impl Rem::rem { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to calculate the remainder with a divisor of zero"; - simd_rem + simd_rem, % } } diff --git a/crates/core_simd/src/simd/cmp/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs index 5b4615ce51d79..93989ce91b89d 100644 --- a/crates/core_simd/src/simd/cmp/eq.rs +++ b/crates/core_simd/src/simd/cmp/eq.rs @@ -12,7 +12,7 @@ pub trait SimdPartialEq { #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_eq(self, other: Self) -> Self::Mask; - /// Test if each element is equal to the corresponding element in `other`. + /// Test if each element is not equal to the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_ne(self, other: Self) -> Self::Mask; } diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index 59e43851ea8da..79954b937b397 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -255,6 +255,7 @@ macro_rules! impl_trait { type Bits = Simd<$bits_ty, N>; type Cast = Simd; + #[cfg(not(target_arch = "aarch64"))] #[inline] fn cast(self) -> Self::Cast { @@ -262,6 +263,33 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_as(self) } } + // https://github.com/llvm/llvm-project/issues/94694 + #[cfg(target_arch = "aarch64")] + #[inline] + fn cast(self) -> Self::Cast + { + const { assert!(N <= 64) }; + if N <= 2 || N == 4 || N == 8 || N == 16 || N == 32 || N == 64 { + // Safety: supported types are guaranteed by SimdCast + unsafe { core::intrinsics::simd::simd_as(self) } + } else if N < 4 { + let x = self.resize::<4>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 8 { + let x = self.resize::<8>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 16 { + let x = self.resize::<16>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 32 { + let x = self.resize::<32>(Default::default()).cast(); + x.resize::(x[0]) + } else { + let x = self.resize::<64>(Default::default()).cast(); + x.resize::(x[0]) + } + } + #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn to_int_unchecked(self) -> Self::Cast @@ -391,7 +419,7 @@ macro_rules! impl_trait { self.as_array().iter().sum() } else { // Safety: `self` is a float vector - unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0.) } + unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, -0.) } } } diff --git a/crates/core_simd/src/simd/num/int.rs b/crates/core_simd/src/simd/num/int.rs index d7598d9ceaf92..3a51235ff954e 100644 --- a/crates/core_simd/src/simd/num/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, + cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, }; @@ -70,11 +70,27 @@ pub trait SimdInt: Copy + Sealed { /// # #[cfg(not(feature = "as_crate"))] use core::simd; /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; - /// let xs = Simd::from_array([MIN, MIN +1, -5, 0]); + /// let xs = Simd::from_array([MIN, MIN + 1, -5, 0]); /// assert_eq!(xs.abs(), Simd::from_array([MIN, MAX, 5, 0])); /// ``` fn abs(self) -> Self; + /// Lanewise absolute difference. + /// Every element becomes the absolute difference of `self` and `second`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::prelude::*; + /// use core::i32::{MIN, MAX}; + /// let a = Simd::from_array([MIN, MAX, 100, -100]); + /// let b = Simd::from_array([MAX, MIN, -80, -120]); + /// assert_eq!(a.abs_diff(b), Simd::from_array([u32::MAX, u32::MAX, 180, 20])); + /// ``` + fn abs_diff(self, second: Self) -> Self::Unsigned; + /// Lanewise saturating absolute value, implemented in Rust. /// As abs(), except the MIN value becomes MAX instead of itself. /// @@ -203,6 +219,12 @@ pub trait SimdInt: Copy + Sealed { /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. fn reverse_bits(self) -> Self; + /// Returns the number of ones in the binary representation of each element. + fn count_ones(self) -> Self::Unsigned; + + /// Returns the number of zeros in the binary representation of each element. + fn count_zeros(self) -> Self::Unsigned; + /// Returns the number of leading zeros in the binary representation of each element. fn leading_zeros(self) -> Self::Unsigned; @@ -259,6 +281,13 @@ macro_rules! impl_trait { (self^m) - m } + #[inline] + fn abs_diff(self, second: Self) -> Self::Unsigned { + let max = self.simd_max(second); + let min = self.simd_min(second); + (max - min).cast() + } + #[inline] fn saturating_abs(self) -> Self { // arith shift for -1 or 0 mask based on sign bit, giving 2s complement @@ -344,6 +373,16 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_bitreverse(self) } } + #[inline] + fn count_ones(self) -> Self::Unsigned { + self.cast::<$unsigned>().count_ones() + } + + #[inline] + fn count_zeros(self) -> Self::Unsigned { + self.cast::<$unsigned>().count_zeros() + } + #[inline] fn leading_zeros(self) -> Self::Unsigned { self.cast::<$unsigned>().leading_zeros() diff --git a/crates/core_simd/src/simd/num/uint.rs b/crates/core_simd/src/simd/num/uint.rs index 53dd97f501c63..1ab2d8c7b7316 100644 --- a/crates/core_simd/src/simd/num/uint.rs +++ b/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{cmp::SimdOrd, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { @@ -57,6 +57,22 @@ pub trait SimdUint: Copy + Sealed { /// assert_eq!(sat, Simd::splat(0)); fn saturating_sub(self, second: Self) -> Self; + /// Lanewise absolute difference. + /// Every element becomes the absolute difference of `self` and `second`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::prelude::*; + /// use core::u32::MAX; + /// let a = Simd::from_array([0, MAX, 100, 20]); + /// let b = Simd::from_array([MAX, 0, 80, 200]); + /// assert_eq!(a.abs_diff(b), Simd::from_array([MAX, MAX, 20, 180])); + /// ``` + fn abs_diff(self, second: Self) -> Self; + /// Returns the sum of the elements of the vector, with wrapping addition. fn reduce_sum(self) -> Self::Scalar; @@ -85,6 +101,12 @@ pub trait SimdUint: Copy + Sealed { /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. fn reverse_bits(self) -> Self; + /// Returns the number of ones in the binary representation of each element. + fn count_ones(self) -> Self; + + /// Returns the number of zeros in the binary representation of each element. + fn count_zeros(self) -> Self; + /// Returns the number of leading zeros in the binary representation of each element. fn leading_zeros(self) -> Self; @@ -138,6 +160,13 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_saturating_sub(self, second) } } + #[inline] + fn abs_diff(self, second: Self) -> Self { + let max = self.simd_max(second); + let min = self.simd_min(second); + max - min + } + #[inline] fn reduce_sum(self) -> Self::Scalar { // Safety: `self` is an integer vector @@ -192,6 +221,17 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_bitreverse(self) } } + #[inline] + fn count_ones(self) -> Self { + // Safety: `self` is an integer vector + unsafe { core::intrinsics::simd::simd_ctpop(self) } + } + + #[inline] + fn count_zeros(self) -> Self { + (!self).count_ones() + } + #[inline] fn leading_zeros(self) -> Self { // Safety: `self` is an integer vector diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index be635ea640b86..47383809ffbae 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -42,6 +42,19 @@ pub trait SimdConstPtr: Copy + Sealed { /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; + /// Converts an address to a pointer without giving it any provenance. + /// + /// Without provenance, this pointer is not associated with any actual allocation. Such a + /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but + /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers + /// are little more than a usize address in disguise. + /// + /// This is different from [`Self::with_exposed_provenance`], which creates a pointer that picks up a + /// previously exposed provenance. + /// + /// Equivalent to calling [`core::ptr::without_provenance`] on each element. + fn without_provenance(addr: Self::Usize) -> Self; + /// Creates a new pointer with the given address. /// /// This performs the same operation as a cast, but copies the *address-space* and @@ -118,6 +131,14 @@ where unsafe { core::mem::transmute_copy(&self) } } + #[inline] + fn without_provenance(addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Integer-to-pointer transmutes are valid (if you are okay with not getting any + // provenance). + unsafe { core::mem::transmute_copy(&addr) } + } + #[inline] fn with_addr(self, addr: Self::Usize) -> Self { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index f6823a949e32a..3f20eef21a312 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -39,6 +39,19 @@ pub trait SimdMutPtr: Copy + Sealed { /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; + /// Converts an address to a pointer without giving it any provenance. + /// + /// Without provenance, this pointer is not associated with any actual allocation. Such a + /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but + /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers + /// are little more than a usize address in disguise. + /// + /// This is different from [`Self::with_exposed_provenance`], which creates a pointer that picks up a + /// previously exposed provenance. + /// + /// Equivalent to calling [`core::ptr::without_provenance`] on each element. + fn without_provenance(addr: Self::Usize) -> Self; + /// Creates a new pointer with the given address. /// /// This performs the same operation as a cast, but copies the *address-space* and @@ -115,6 +128,14 @@ where unsafe { core::mem::transmute_copy(&self) } } + #[inline] + fn without_provenance(addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Integer-to-pointer transmutes are valid (if you are okay with not getting any + // provenance). + unsafe { core::mem::transmute_copy(&addr) } + } + #[inline] fn with_addr(self, addr: Self::Usize) -> Self { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index d62642fb9061b..42425ef37e50b 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -155,8 +155,7 @@ pub trait Swizzle { /// Creates a new mask from the elements of `mask`. /// - /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of - /// `first` and `second`. + /// Element `i` of the output is `mask[Self::INDEX[i]]`. #[inline] #[must_use = "method returns a new mask and does not mutate the original inputs"] fn swizzle_mask(mask: Mask) -> Mask @@ -260,6 +259,50 @@ where Rotate::::swizzle(self) } + /// Shifts the vector elements to the left by `OFFSET`, filling in with + /// `padding` from the right. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn shift_elements_left(self, padding: T) -> Self { + struct Shift; + + impl Swizzle for Shift { + const INDEX: [usize; N] = const { + let mut index = [N; N]; + let mut i = 0; + while i + OFFSET < N { + index[i] = i + OFFSET; + i += 1; + } + index + }; + } + + Shift::::concat_swizzle(self, Simd::splat(padding)) + } + + /// Shifts the vector elements to the right by `OFFSET`, filling in with + /// `padding` from the left. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn shift_elements_right(self, padding: T) -> Self { + struct Shift; + + impl Swizzle for Shift { + const INDEX: [usize; N] = const { + let mut index = [N; N]; + let mut i = OFFSET; + while i < N { + index[i] = i - OFFSET; + i += 1; + } + index + }; + } + + Shift::::concat_swizzle(self, Simd::splat(padding)) + } + /// Interleave two vectors. /// /// The resulting vectors contain elements taken alternatively from `self` and `other`, first @@ -320,7 +363,9 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::Simd; + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; /// let a = Simd::from_array([0, 4, 1, 5]); /// let b = Simd::from_array([2, 6, 3, 7]); /// let (x, y) = a.deinterleave(b); @@ -391,4 +436,210 @@ where } Resize::::concat_swizzle(self, Simd::splat(value)) } + + /// Extract a vector from another vector. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::u32x4; + /// let x = u32x4::from_array([0, 1, 2, 3]); + /// assert_eq!(x.extract::<1, 2>().to_array(), [1, 2]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn extract(self) -> Simd + where + LaneCount: SupportedLaneCount, + { + struct Extract; + impl Swizzle for Extract { + const INDEX: [usize; LEN] = const { + assert!(START + LEN <= N, "index out of bounds"); + let mut index = [0; LEN]; + let mut i = 0; + while i < LEN { + index[i] = START + i; + i += 1; + } + index + }; + } + Extract::::swizzle(self) + } +} + +impl Mask +where + T: MaskElement, + LaneCount: SupportedLaneCount, +{ + /// Reverse the order of the elements in the mask. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn reverse(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().reverse()) } + } + + /// Rotates the mask such that the first `OFFSET` elements of the slice move to the end + /// while the last `self.len() - OFFSET` elements move to the front. After calling `rotate_elements_left`, + /// the element previously at index `OFFSET` will become the first element in the slice. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn rotate_elements_left(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::()) } + } + + /// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to + /// the end while the last `OFFSET` elements move to the front. After calling `rotate_elements_right`, + /// the element previously at index `self.len() - OFFSET` will become the first element in the slice. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn rotate_elements_right(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::()) } + } + + /// Shifts the mask elements to the left by `OFFSET`, filling in with + /// `padding` from the right. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original inputs"] + pub fn shift_elements_left(self, padding: bool) -> Self { + // Safety: swizzles are safe for masks + unsafe { + Self::from_int_unchecked(self.to_int().shift_elements_left::(if padding { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Shifts the mask elements to the right by `OFFSET`, filling in with + /// `padding` from the left. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original inputs"] + pub fn shift_elements_right(self, padding: bool) -> Self { + // Safety: swizzles are safe for masks + unsafe { + Self::from_int_unchecked(self.to_int().shift_elements_right::(if padding { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Interleave two masks. + /// + /// The resulting masks contain elements taken alternatively from `self` and `other`, first + /// filling the first result, and then the second. + /// + /// The reverse of this operation is [`Mask::deinterleave`]. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let a = mask32x4::from_array([false, true, false, true]); + /// let b = mask32x4::from_array([false, false, true, true]); + /// let (x, y) = a.interleave(b); + /// assert_eq!(x.to_array(), [false, false, true, false]); + /// assert_eq!(y.to_array(), [false, true, true, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn interleave(self, other: Self) -> (Self, Self) { + let (lo, hi) = self.to_int().interleave(other.to_int()); + // Safety: swizzles are safe for masks + unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } + } + + /// Deinterleave two masks. + /// + /// The first result takes every other element of `self` and then `other`, starting with + /// the first element. + /// + /// The second result takes every other element of `self` and then `other`, starting with + /// the second element. + /// + /// The reverse of this operation is [`Mask::interleave`]. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let a = mask32x4::from_array([false, true, false, true]); + /// let b = mask32x4::from_array([false, false, true, true]); + /// let (x, y) = a.deinterleave(b); + /// assert_eq!(x.to_array(), [false, false, false, true]); + /// assert_eq!(y.to_array(), [true, true, false, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn deinterleave(self, other: Self) -> (Self, Self) { + let (even, odd) = self.to_int().deinterleave(other.to_int()); + // Safety: swizzles are safe for masks + unsafe { + ( + Self::from_int_unchecked(even), + Self::from_int_unchecked(odd), + ) + } + } + + /// Resize a mask. + /// + /// If `M` > `N`, extends the length of a mask, setting the new elements to `value`. + /// If `M` < `N`, truncates the mask to the first `M` elements. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let x = mask32x4::from_array([false, true, true, false]); + /// assert_eq!(x.resize::<8>(true).to_array(), [false, true, true, false, true, true, true, true]); + /// assert_eq!(x.resize::<2>(true).to_array(), [false, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn resize(self, value: bool) -> Mask + where + LaneCount: SupportedLaneCount, + { + // Safety: swizzles are safe for masks + unsafe { + Mask::::from_int_unchecked(self.to_int().resize::(if value { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Extract a vector from another vector. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let x = mask32x4::from_array([false, true, true, false]); + /// assert_eq!(x.extract::<1, 2>().to_array(), [true, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn extract(self) -> Mask + where + LaneCount: SupportedLaneCount, + { + // Safety: swizzles are safe for masks + unsafe { Mask::::from_int_unchecked(self.to_int().extract::()) } + } } diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index 3b6388d0f2759..773bd028bae09 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -59,15 +59,40 @@ where target_endian = "little" ))] 16 => transize(vqtbl1q_u8, self, idxs), + #[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" + ))] + 16 => transize(armv7_neon_swizzle_u8x16, self, idxs), #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] 32 => transize(avx2_pshufb, self, idxs), #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] - 32 => transize(x86::_mm256_permutexvar_epi8, zeroing_idxs(idxs), self), - // Notable absence: avx512bw shuffle - // If avx512bw is available, odds of avx512vbmi are good - // FIXME: initial AVX512VBMI variant didn't actually pass muster - // #[cfg(target_feature = "avx512vbmi")] - // 64 => transize(x86::_mm512_permutexvar_epi8, self, idxs), + 32 => { + // Unlike vpshufb, vpermb doesn't zero out values in the result based on the index high bit + let swizzler = |bytes, idxs| { + let mask = x86::_mm256_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>( + idxs, + Simd::::splat(N as u8).into(), + ); + x86::_mm256_maskz_permutexvar_epi8(mask, idxs, bytes) + }; + transize(swizzler, self, idxs) + } + // Notable absence: avx512bw pshufb shuffle + #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] + 64 => { + // Unlike vpshufb, vpermb doesn't zero out values in the result based on the index high bit + let swizzler = |bytes, idxs| { + let mask = x86::_mm512_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>( + idxs, + Simd::::splat(N as u8).into(), + ); + x86::_mm512_maskz_permutexvar_epi8(mask, idxs, bytes) + }; + transize(swizzler, self, idxs) + } _ => { let mut array = [0; N]; for (i, k) in idxs.to_array().into_iter().enumerate() { @@ -82,6 +107,28 @@ where } } +/// armv7 neon supports swizzling `u8x16` by swizzling two u8x8 blocks +/// with a u8x8x2 lookup table. +/// +/// # Safety +/// This requires armv7 neon to work +#[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" +))] +unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd, idxs: Simd) -> Simd { + use core::arch::arm::{uint8x8x2_t, vcombine_u8, vget_high_u8, vget_low_u8, vtbl2_u8}; + // SAFETY: Caller promised arm neon support + unsafe { + let bytes = uint8x8x2_t(vget_low_u8(bytes.into()), vget_high_u8(bytes.into())); + let lo = vtbl2_u8(bytes, vget_low_u8(idxs.into())); + let hi = vtbl2_u8(bytes, vget_high_u8(idxs.into())); + vcombine_u8(lo, hi).into() + } +} + /// "vpshufb like it was meant to be" on AVX2 /// /// # Safety diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 3e23916914963..9c4dd36c24fe8 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -99,7 +99,7 @@ use crate::simd::{ // directly constructing an instance of the type (i.e. `let vector = Simd(array)`) should be // avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also // causes rustc to emit illegal LLVM IR in some cases. -#[repr(simd)] +#[repr(simd, packed)] pub struct Simd([T; N]) where LaneCount: SupportedLaneCount, @@ -144,14 +144,32 @@ where /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` #[inline] - pub fn splat(value: T) -> Self { - // This is preferred over `[value; N]`, since it's explicitly a splat: - // https://github.com/rust-lang/rust/issues/97804 - struct Splat; - impl Swizzle for Splat { - const INDEX: [usize; N] = [0; N]; + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] + pub const fn splat(value: T) -> Self { + const fn splat_const(value: T) -> Simd + where + T: SimdElement, + LaneCount: SupportedLaneCount, + { + Simd::from_array([value; N]) } - Splat::swizzle::(Simd::::from([value])) + + fn splat_rt(value: T) -> Simd + where + T: SimdElement, + LaneCount: SupportedLaneCount, + { + // This is preferred over `[value; N]`, since it's explicitly a splat: + // https://github.com/rust-lang/rust/issues/97804 + struct Splat; + impl Swizzle for Splat { + const INDEX: [usize; N] = [0; N]; + } + + Splat::swizzle::(Simd::::from([value])) + } + + core::intrinsics::const_eval_select((value,), splat_const, splat_rt) } /// Returns an array reference containing the entire SIMD vector. @@ -425,6 +443,9 @@ where /// /// When the element is disabled, that memory location is not accessed and the corresponding /// value from `or` is passed through. + /// + /// # Safety + /// Enabled loads must not exceed the length of `slice`. #[must_use] #[inline] pub unsafe fn load_select_unchecked( @@ -442,6 +463,9 @@ where /// /// When the element is disabled, that memory location is not accessed and the corresponding /// value from `or` is passed through. + /// + /// # Safety + /// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`. #[must_use] #[inline] pub unsafe fn load_select_ptr( @@ -924,6 +948,7 @@ where } } +/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl PartialOrd for Simd where LaneCount: SupportedLaneCount, @@ -943,6 +968,7 @@ where { } +/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl Ord for Simd where LaneCount: SupportedLaneCount, @@ -1195,6 +1221,7 @@ fn lane_indices() -> Simd where LaneCount: SupportedLaneCount, { + #![allow(clippy::needless_range_loop)] let mut index = [0; N]; for i in 0..N { index[i] = i; diff --git a/crates/core_simd/src/vendor.rs b/crates/core_simd/src/vendor.rs index 1a34a3a8de5c4..57536e4fc77dc 100644 --- a/crates/core_simd/src/vendor.rs +++ b/crates/core_simd/src/vendor.rs @@ -29,3 +29,6 @@ mod arm; #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] mod powerpc; + +#[cfg(target_arch = "loongarch64")] +mod loongarch64; diff --git a/crates/core_simd/src/vendor/loongarch64.rs b/crates/core_simd/src/vendor/loongarch64.rs new file mode 100644 index 0000000000000..1290bc166b2b8 --- /dev/null +++ b/crates/core_simd/src/vendor/loongarch64.rs @@ -0,0 +1,31 @@ +use crate::simd::*; +use core::arch::loongarch64::*; + +from_transmute! { unsafe u8x16 => v16u8 } +from_transmute! { unsafe u8x32 => v32u8 } +from_transmute! { unsafe i8x16 => v16i8 } +from_transmute! { unsafe i8x32 => v32i8 } + +from_transmute! { unsafe u16x8 => v8u16 } +from_transmute! { unsafe u16x16 => v16u16 } +from_transmute! { unsafe i16x8 => v8i16 } +from_transmute! { unsafe i16x16 => v16i16 } + +from_transmute! { unsafe u32x4 => v4u32 } +from_transmute! { unsafe u32x8 => v8u32 } +from_transmute! { unsafe i32x4 => v4i32 } +from_transmute! { unsafe i32x8 => v8i32 } +from_transmute! { unsafe f32x4 => v4f32 } +from_transmute! { unsafe f32x8 => v8f32 } + +from_transmute! { unsafe u64x2 => v2u64 } +from_transmute! { unsafe u64x4 => v4u64 } +from_transmute! { unsafe i64x2 => v2i64 } +from_transmute! { unsafe i64x4 => v4i64 } +from_transmute! { unsafe f64x2 => v2f64 } +from_transmute! { unsafe f64x4 => v4f64 } + +from_transmute! { unsafe usizex2 => v2u64 } +from_transmute! { unsafe usizex4 => v4u64 } +from_transmute! { unsafe isizex2 => v2i64 } +from_transmute! { unsafe isizex4 => v4i64 } diff --git a/crates/core_simd/tests/layout.rs b/crates/core_simd/tests/layout.rs new file mode 100644 index 0000000000000..24114c2d261e7 --- /dev/null +++ b/crates/core_simd/tests/layout.rs @@ -0,0 +1,35 @@ +#![feature(portable_simd)] + +macro_rules! layout_tests { + { $($mod:ident, $ty:ty,)* } => { + $( + mod $mod { + test_helpers::test_lanes! { + fn no_padding() { + assert_eq!( + core::mem::size_of::>(), + core::mem::size_of::<[$ty; LANES]>(), + ); + } + } + } + )* + } +} + +layout_tests! { + i8, i8, + i16, i16, + i32, i32, + i64, i64, + isize, isize, + u8, u8, + u16, u16, + u32, u32, + u64, u64, + usize, usize, + f32, f32, + f64, f64, + mut_ptr, *mut (), + const_ptr, *const (), +} diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index fc6a3476b7c60..48786d02440b3 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -99,7 +99,6 @@ macro_rules! test_mask_api { assert_eq!(Mask::<$type, 2>::from_bitmask(bitmask), mask); } - #[cfg(feature = "all_lane_counts")] #[test] fn roundtrip_bitmask_conversion_odd() { let values = [ @@ -134,48 +133,6 @@ macro_rules! test_mask_api { cast_impl::(); cast_impl::(); } - - #[test] - fn roundtrip_bitmask_vector_conversion() { - use core_simd::simd::ToBytes; - let values = [ - true, false, false, true, false, false, true, false, - true, true, false, false, false, false, false, true, - ]; - let mask = Mask::<$type, 16>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b01001001, 0b10000011]); - assert_eq!(Mask::<$type, 16>::from_bitmask_vector(bitmask), mask); - } - - // rust-lang/portable-simd#379 - #[test] - fn roundtrip_bitmask_vector_conversion_small() { - use core_simd::simd::ToBytes; - let values = [ - true, false, true, true - ]; - let mask = Mask::<$type, 4>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<1>(0).to_ne_bytes()[0], 0b00001101); - assert_eq!(Mask::<$type, 4>::from_bitmask_vector(bitmask), mask); - } - - /* FIXME doesn't work with non-powers-of-two, yet - // rust-lang/portable-simd#379 - #[cfg(feature = "all_lane_counts")] - #[test] - fn roundtrip_bitmask_vector_conversion_odd() { - use core_simd::simd::ToBytes; - let values = [ - true, false, true, false, true, true, false, false, false, true, true, - ]; - let mask = Mask::<$type, 11>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b00110101, 0b00000110]); - assert_eq!(Mask::<$type, 11>::from_bitmask_vector(bitmask), mask); - } - */ } } } diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index aa565a137527e..6de78f51e59df 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -216,6 +216,22 @@ macro_rules! impl_common_integer_tests { ) } + fn count_ones() { + test_helpers::test_unary_elementwise( + &$vector::::count_ones, + &|x| x.count_ones() as _, + &|_| true, + ) + } + + fn count_zeros() { + test_helpers::test_unary_elementwise( + &$vector::::count_zeros, + &|x| x.count_zeros() as _, + &|_| true, + ) + } + fn leading_zeros() { test_helpers::test_unary_elementwise( &$vector::::leading_zeros, @@ -307,6 +323,14 @@ macro_rules! impl_signed_tests { assert_eq!(a % b, Vector::::splat(0)); } + fn abs_diff() { + test_helpers::test_binary_elementwise( + &Vector::::abs_diff, + &Scalar::abs_diff, + &|_, _| true, + ) + } + fn simd_min() { use core_simd::simd::cmp::SimdOrd; let a = Vector::::splat(Scalar::MIN); @@ -419,6 +443,14 @@ macro_rules! impl_unsigned_tests { &|_| true, ); } + + fn abs_diff() { + test_helpers::test_binary_elementwise( + &Vector::::abs_diff, + &Scalar::abs_diff, + &|_, _| true, + ) + } } impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add); @@ -495,6 +527,9 @@ macro_rules! impl_float_tests { } fn is_normal() { + // Arm v7 Neon violates float opsem re: subnormals, see + // https://github.com/rust-lang/portable-simd/issues/439 + #[cfg(not(target_arch = "arm"))] test_helpers::test_unary_mask_elementwise( &Vector::::is_normal, &Scalar::is_normal, @@ -503,6 +538,9 @@ macro_rules! impl_float_tests { } fn is_subnormal() { + // Arm v7 Neon violates float opsem re: subnormals, see + // https://github.com/rust-lang/portable-simd/issues/439 + #[cfg(not(target_arch = "arm"))] test_helpers::test_unary_mask_elementwise( &Vector::::is_subnormal, &Scalar::is_subnormal, diff --git a/crates/core_simd/tests/swizzle.rs b/crates/core_simd/tests/swizzle.rs index 522d71439b77d..7001e5f6bf87b 100644 --- a/crates/core_simd/tests/swizzle.rs +++ b/crates/core_simd/tests/swizzle.rs @@ -48,6 +48,24 @@ fn rotate() { assert_eq!(a.rotate_elements_right::<5>().to_array(), [4, 1, 2, 3]); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn shift() { + let a = Simd::from_array([1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<0>(0).to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<1>(0).to_array(), [2, 3, 4, 0]); + assert_eq!(a.shift_elements_left::<2>(9).to_array(), [3, 4, 9, 9]); + assert_eq!(a.shift_elements_left::<3>(8).to_array(), [4, 8, 8, 8]); + assert_eq!(a.shift_elements_left::<4>(7).to_array(), [7, 7, 7, 7]); + assert_eq!(a.shift_elements_left::<5>(6).to_array(), [6, 6, 6, 6]); + assert_eq!(a.shift_elements_right::<0>(0).to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_right::<1>(0).to_array(), [0, 1, 2, 3]); + assert_eq!(a.shift_elements_right::<2>(-1).to_array(), [-1, -1, 1, 2]); + assert_eq!(a.shift_elements_right::<3>(-2).to_array(), [-2, -2, -2, 1]); + assert_eq!(a.shift_elements_right::<4>(-3).to_array(), [-3, -3, -3, -3]); + assert_eq!(a.shift_elements_right::<5>(-4).to_array(), [-4, -4, -4, -4]); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn interleave() { diff --git a/crates/test_helpers/Cargo.toml b/crates/test_helpers/Cargo.toml index 23dae7c93381e..a5359b9abc84d 100644 --- a/crates/test_helpers/Cargo.toml +++ b/crates/test_helpers/Cargo.toml @@ -6,6 +6,3 @@ publish = false [dependencies] proptest = { version = "0.10", default-features = false, features = ["alloc"] } - -[features] -all_lane_counts = [] diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index 51b860a863560..197c920e11eac 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -539,32 +539,22 @@ macro_rules! test_lanes { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_1 1; lanes_2 2; - lanes_4 4; - ); - - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - $crate::test_lanes_helper!( - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; - lanes_8 8; - lanes_16 16; - lanes_32 32; - lanes_64 64; - ); - - #[cfg(feature = "all_lane_counts")] - $crate::test_lanes_helper!( - // test some odd and even non-power-of-2 lengths on miri - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + // Cover an odd and an even non-power-of-2 length in Miri. + // (Even non-power-of-2 vectors have alignment between element + // and vector size, so we want to cover that case as well.) lanes_3 3; - lanes_5 5; + lanes_6 6; ); - #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_4 4; + lanes_5 5; + lanes_7 7; + lanes_8 8; lanes_9 9; lanes_10 10; lanes_11 11; @@ -572,52 +562,55 @@ macro_rules! test_lanes { lanes_13 13; lanes_14 14; lanes_15 15; + lanes_16 16; lanes_17 17; - lanes_18 18; - lanes_19 19; - lanes_20 20; - lanes_21 21; - lanes_22 22; - lanes_23 23; + //lanes_18 18; + //lanes_19 19; + //lanes_20 20; + //lanes_21 21; + //lanes_22 22; + //lanes_23 23; lanes_24 24; - lanes_25 25; - lanes_26 26; - lanes_27 27; - lanes_28 28; - lanes_29 29; - lanes_30 30; - lanes_31 31; - lanes_33 33; - lanes_34 34; - lanes_35 35; - lanes_36 36; - lanes_37 37; - lanes_38 38; - lanes_39 39; - lanes_40 40; - lanes_41 41; - lanes_42 42; - lanes_43 43; - lanes_44 44; - lanes_45 45; - lanes_46 46; + //lanes_25 25; + //lanes_26 26; + //lanes_27 27; + //lanes_28 28; + //lanes_29 29; + //lanes_30 30; + //lanes_31 31; + lanes_32 32; + //lanes_33 33; + //lanes_34 34; + //lanes_35 35; + //lanes_36 36; + //lanes_37 37; + //lanes_38 38; + //lanes_39 39; + //lanes_40 40; + //lanes_41 41; + //lanes_42 42; + //lanes_43 43; + //lanes_44 44; + //lanes_45 45; + //lanes_46 46; lanes_47 47; - lanes_48 48; - lanes_49 49; - lanes_50 50; - lanes_51 51; - lanes_52 52; - lanes_53 53; - lanes_54 54; - lanes_55 55; + //lanes_48 48; + //lanes_49 49; + //lanes_50 50; + //lanes_51 51; + //lanes_52 52; + //lanes_53 53; + //lanes_54 54; + //lanes_55 55; lanes_56 56; lanes_57 57; - lanes_58 58; - lanes_59 59; - lanes_60 60; - lanes_61 61; - lanes_62 62; + //lanes_58 58; + //lanes_59 59; + //lanes_60 60; + //lanes_61 61; + //lanes_62 62; lanes_63 63; + lanes_64 64; ); } )* @@ -639,36 +632,24 @@ macro_rules! test_lanes_panic { core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body + // test some odd and even non-power-of-2 lengths on miri $crate::test_lanes_helper!( #[should_panic]; lanes_1 1; lanes_2 2; - lanes_4 4; - ); - - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - $crate::test_lanes_helper!( - #[should_panic]; - lanes_8 8; - lanes_16 16; - lanes_32 32; - lanes_64 64; - ); - - #[cfg(feature = "all_lane_counts")] - $crate::test_lanes_helper!( - // test some odd and even non-power-of-2 lengths on miri - #[should_panic]; lanes_3 3; - lanes_5 5; + lanes_6 6; ); - #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[should_panic]; + lanes_4 4; + lanes_5 5; + lanes_7 7; + lanes_8 8; lanes_9 9; lanes_10 10; lanes_11 11; @@ -676,52 +657,55 @@ macro_rules! test_lanes_panic { lanes_13 13; lanes_14 14; lanes_15 15; + lanes_16 16; lanes_17 17; - lanes_18 18; - lanes_19 19; - lanes_20 20; - lanes_21 21; - lanes_22 22; - lanes_23 23; + //lanes_18 18; + //lanes_19 19; + //lanes_20 20; + //lanes_21 21; + //lanes_22 22; + //lanes_23 23; lanes_24 24; - lanes_25 25; - lanes_26 26; - lanes_27 27; - lanes_28 28; - lanes_29 29; - lanes_30 30; - lanes_31 31; - lanes_33 33; - lanes_34 34; - lanes_35 35; - lanes_36 36; - lanes_37 37; - lanes_38 38; - lanes_39 39; - lanes_40 40; - lanes_41 41; - lanes_42 42; - lanes_43 43; - lanes_44 44; - lanes_45 45; - lanes_46 46; + //lanes_25 25; + //lanes_26 26; + //lanes_27 27; + //lanes_28 28; + //lanes_29 29; + //lanes_30 30; + //lanes_31 31; + lanes_32 32; + //lanes_33 33; + //lanes_34 34; + //lanes_35 35; + //lanes_36 36; + //lanes_37 37; + //lanes_38 38; + //lanes_39 39; + //lanes_40 40; + //lanes_41 41; + //lanes_42 42; + //lanes_43 43; + //lanes_44 44; + //lanes_45 45; + //lanes_46 46; lanes_47 47; - lanes_48 48; - lanes_49 49; - lanes_50 50; - lanes_51 51; - lanes_52 52; - lanes_53 53; - lanes_54 54; - lanes_55 55; + //lanes_48 48; + //lanes_49 49; + //lanes_50 50; + //lanes_51 51; + //lanes_52 52; + //lanes_53 53; + //lanes_54 54; + //lanes_55 55; lanes_56 56; lanes_57 57; - lanes_58 58; - lanes_59 59; - lanes_60 60; - lanes_61 61; - lanes_62 62; + //lanes_58 58; + //lanes_59 59; + //lanes_60 60; + //lanes_61 61; + //lanes_62 62; lanes_63 63; + lanes_64 64; ); } )* diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000000..d17c6d2e88946 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2025-01-16" +components = ["rustfmt", "clippy", "miri", "rust-src"] diff --git a/subtree-sync.sh b/subtree-sync.sh new file mode 100755 index 0000000000000..18360077623b1 --- /dev/null +++ b/subtree-sync.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +set -eou pipefail + +git fetch origin +pushd $2 +git fetch origin +popd + +if [ "$(git rev-parse --show-prefix)" != "" ]; then + echo "Run this script from the git root" >&2 + exit 1 +fi + +if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/master)" ]; then + echo "$(pwd) is not at origin/master" >&2 + exit 1 +fi + +if [ ! -f library/portable-simd/git-subtree.sh ]; then + curl -sS https://raw.githubusercontent.com/bjorn3/git/tqc-subtree-portable/contrib/subtree/git-subtree.sh -o library/portable-simd/git-subtree.sh + chmod +x library/portable-simd/git-subtree.sh +fi + +today=$(date +%Y-%m-%d) + +case $1 in + "push") + upstream=rust-upstream-$today + merge=sync-from-rust-$today + + pushd $2 + git checkout master + git pull + popd + + library/portable-simd/git-subtree.sh push -P library/portable-simd $2 $upstream + + pushd $2 + git checkout -B $merge origin/master + git merge $upstream + popd + echo "Branch \`$merge\` created in \`$2\`. You may need to resolve merge conflicts." + ;; + "pull") + branch=sync-from-portable-simd-$today + + git checkout -B $branch + echo "Creating branch \`$branch\`... You may need to resolve merge conflicts." + library/portable-simd/git-subtree.sh pull -P library/portable-simd $2 origin/master + ;; +esac From 02a28b29e2d1ab50b8d80c18018c33402cefc054 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 12 Feb 2025 10:35:32 +0100 Subject: [PATCH 02/43] Remove ignored `#[must_use]` attributes from portable-simd The `#[must_use]` attribute has no effect when applied to methods in trait implementations. --- crates/core_simd/src/masks.rs | 13 ------------- crates/core_simd/src/masks/full_masks.rs | 5 ----- crates/core_simd/src/ops.rs | 1 - crates/core_simd/src/ops/deref.rs | 3 --- crates/core_simd/src/ops/unary.rs | 2 -- crates/core_simd/src/simd/num/float.rs | 1 - 6 files changed, 25 deletions(-) diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index b763a7c75a5a6..19d45f4d3b31a 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -401,7 +401,6 @@ where LaneCount: SupportedLaneCount, { #[inline] - #[must_use = "method returns a defaulted mask with all elements set to false (0)"] fn default() -> Self { Self::splat(false) } @@ -413,7 +412,6 @@ where LaneCount: SupportedLaneCount, { #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] fn eq(&self, other: &Self) -> bool { self.0 == other.0 } @@ -425,7 +423,6 @@ where LaneCount: SupportedLaneCount, { #[inline] - #[must_use = "method returns a new Ordering and does not mutate the original value"] fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(&other.0) } @@ -451,7 +448,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitand(self, rhs: Self) -> Self { Self(self.0 & rhs.0) } @@ -464,7 +460,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitand(self, rhs: bool) -> Self { self & Self::splat(rhs) } @@ -477,7 +472,6 @@ where { type Output = Mask; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitand(self, rhs: Mask) -> Mask { Mask::splat(self) & rhs } @@ -490,7 +484,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitor(self, rhs: Self) -> Self { Self(self.0 | rhs.0) } @@ -503,7 +496,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitor(self, rhs: bool) -> Self { self | Self::splat(rhs) } @@ -516,7 +508,6 @@ where { type Output = Mask; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitor(self, rhs: Mask) -> Mask { Mask::splat(self) | rhs } @@ -529,7 +520,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitxor(self, rhs: Self) -> Self::Output { Self(self.0 ^ rhs.0) } @@ -542,7 +532,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitxor(self, rhs: bool) -> Self::Output { self ^ Self::splat(rhs) } @@ -555,7 +544,6 @@ where { type Output = Mask; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitxor(self, rhs: Mask) -> Self::Output { Mask::splat(self) ^ rhs } @@ -568,7 +556,6 @@ where { type Output = Mask; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn not(self) -> Self::Output { Self(!self.0) } diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 2d01946b5747c..387b508c4b4ef 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -21,7 +21,6 @@ where LaneCount: SupportedLaneCount, { #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn clone(&self) -> Self { *self } @@ -252,7 +251,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitand(self, rhs: Self) -> Self { // Safety: `self` is an integer vector unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) } @@ -266,7 +264,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitor(self, rhs: Self) -> Self { // Safety: `self` is an integer vector unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) } @@ -280,7 +277,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitxor(self, rhs: Self) -> Self { // Safety: `self` is an integer vector unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) } @@ -294,7 +290,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn not(self) -> Self::Output { Self::splat(true) ^ self } diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index d3bd14a340278..4ac64a253a3bd 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -135,7 +135,6 @@ macro_rules! for_base_types { type Output = $out; #[inline] - #[must_use = "operator returns a new vector without mutating the inputs"] // TODO: only useful for int Div::div, but we hope that this // will essentially always get inlined anyway. #[track_caller] diff --git a/crates/core_simd/src/ops/deref.rs b/crates/core_simd/src/ops/deref.rs index 0ff76cfba39bb..913cbbe977c46 100644 --- a/crates/core_simd/src/ops/deref.rs +++ b/crates/core_simd/src/ops/deref.rs @@ -18,7 +18,6 @@ macro_rules! deref_lhs { type Output = Simd; #[inline] - #[must_use = "operator returns a new vector without mutating the inputs"] fn $call(self, rhs: $simd) -> Self::Output { (*self).$call(rhs) } @@ -39,7 +38,6 @@ macro_rules! deref_rhs { type Output = Simd; #[inline] - #[must_use = "operator returns a new vector without mutating the inputs"] fn $call(self, rhs: &$simd) -> Self::Output { self.$call(*rhs) } @@ -71,7 +69,6 @@ macro_rules! deref_ops { type Output = $simd; #[inline] - #[must_use = "operator returns a new vector without mutating the inputs"] fn $call(self, rhs: &'rhs $simd) -> Self::Output { (*self).$call(*rhs) } diff --git a/crates/core_simd/src/ops/unary.rs b/crates/core_simd/src/ops/unary.rs index bdae96332a3ae..412a5b801171b 100644 --- a/crates/core_simd/src/ops/unary.rs +++ b/crates/core_simd/src/ops/unary.rs @@ -11,7 +11,6 @@ macro_rules! neg { type Output = Self; #[inline] - #[must_use = "operator returns a new vector without mutating the input"] fn neg(self) -> Self::Output { // Safety: `self` is a signed vector unsafe { core::intrinsics::simd::simd_neg(self) } @@ -46,7 +45,6 @@ macro_rules! not { type Output = Self; #[inline] - #[must_use = "operator returns a new vector without mutating the input"] fn not(self) -> Self::Output { self ^ (Simd::splat(!(0 as $scalar))) } diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index 79954b937b397..db705dfe20221 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -371,7 +371,6 @@ macro_rules! impl_trait { } #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn is_normal(self) -> Self::Mask { !(self.abs().simd_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite()) } From aaf8ff1f9ec9b7049a7896a3c278f9e990306cb8 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 19 Mar 2025 00:58:47 -0400 Subject: [PATCH 03/43] Merge commit 'c14f2fc3eb69c164d8bf8d36d91ebd60bd5261e6' into sync-from-portable-simd-2025-03-19 --- beginners-guide.md | 4 +- crates/core_simd/Cargo.toml | 2 +- crates/core_simd/src/lib.rs | 6 ++- crates/core_simd/src/masks/bitmask.rs | 22 +++++------ crates/core_simd/src/masks/full_masks.rs | 20 +++++----- crates/core_simd/src/ops.rs | 2 +- crates/core_simd/src/simd/cmp/eq.rs | 2 +- crates/core_simd/src/simd/cmp/ord.rs | 2 +- crates/core_simd/src/simd/num/float.rs | 9 +++-- crates/core_simd/src/simd/num/int.rs | 4 +- crates/core_simd/src/simd/num/uint.rs | 2 +- crates/core_simd/src/simd/prelude.rs | 3 +- crates/core_simd/src/simd/ptr/const_ptr.rs | 2 +- crates/core_simd/src/simd/ptr/mut_ptr.rs | 2 +- crates/core_simd/src/swizzle.rs | 44 ++++++++++++++++++++++ crates/core_simd/src/to_bytes.rs | 2 +- crates/core_simd/src/vector.rs | 4 +- crates/core_simd/tests/layout.rs | 4 +- crates/core_simd/tests/pointers.rs | 2 +- crates/core_simd/tests/round.rs | 2 +- crates/test_helpers/src/subnormals.rs | 2 +- 21 files changed, 96 insertions(+), 46 deletions(-) diff --git a/beginners-guide.md b/beginners-guide.md index 17ade06ae80f9..dc08d847ced50 100644 --- a/beginners-guide.md +++ b/beginners-guide.md @@ -80,12 +80,12 @@ Most of the portable SIMD API is designed to allow the user to gloss over the de Fortunately, most SIMD types have a fairly predictable size. `i32x4` is bit-equivalent to `[i32; 4]` and so can be bitcast to it, e.g. using [`mem::transmute`], though the API usually offers a safe cast you can use instead. -However, this is not the same as alignment. Computer architectures generally prefer aligned accesses, especially when moving data between memory and vector registers, and while some support specialized operations that can bend the rules to help with this, unaligned access is still typically slow, or even undefined behavior. In addition, different architectures can require different alignments when interacting with their native SIMD types. For this reason, any `#[repr(simd)]` type has a non-portable alignment. If it is necessary to directly interact with the alignment of these types, it should be via [`mem::align_of`]. +However, this is not the same as alignment. Computer architectures generally prefer aligned accesses, especially when moving data between memory and vector registers, and while some support specialized operations that can bend the rules to help with this, unaligned access is still typically slow, or even undefined behavior. In addition, different architectures can require different alignments when interacting with their native SIMD types. For this reason, any `#[repr(simd)]` type has a non-portable alignment. If it is necessary to directly interact with the alignment of these types, it should be via [`align_of`]. When working with slices, data correctly aligned for SIMD can be acquired using the [`as_simd`] and [`as_simd_mut`] methods of the slice primitive. [`mem::transmute`]: https://doc.rust-lang.org/core/mem/fn.transmute.html -[`mem::align_of`]: https://doc.rust-lang.org/core/mem/fn.align_of.html +[`align_of`]: https://doc.rust-lang.org/core/mem/fn.align_of.html [`as_simd`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_simd [`as_simd_mut`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.as_simd_mut diff --git a/crates/core_simd/Cargo.toml b/crates/core_simd/Cargo.toml index a7a6d43b11d3c..537ce459c07cd 100644 --- a/crates/core_simd/Cargo.toml +++ b/crates/core_simd/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "core_simd" version = "0.1.0" -edition = "2021" +edition = "2024" homepage = "https://github.com/rust-lang/portable-simd" repository = "https://github.com/rust-lang/portable-simd" keywords = ["core", "simd", "intrinsics"] diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 7f57847c9c234..717b882b64ba1 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -35,7 +35,11 @@ feature(stdarch_x86_avx512) )] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really -#![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] +#![deny( + unsafe_op_in_unsafe_fn, + unreachable_pub, + clippy::undocumented_unsafe_blocks +)] #![doc(test(attr(deny(warnings))))] #![allow(internal_features)] #![unstable(feature = "portable_simd", issue = "86656")] diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index db4312d5bf88a..8221d8f17e90e 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; /// A mask where each lane is represented by a single bit. #[repr(transparent)] -pub struct Mask( +pub(crate) struct Mask( as SupportedLaneCount>::BitMask, PhantomData, ) @@ -78,7 +78,7 @@ where { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn splat(value: bool) -> Self { + pub(crate) fn splat(value: bool) -> Self { let mut mask = as SupportedLaneCount>::BitMask::default(); if value { mask.as_mut().fill(u8::MAX) @@ -93,12 +93,12 @@ where #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub unsafe fn test_unchecked(&self, lane: usize) -> bool { + pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { (self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0 } #[inline] - pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { + pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { unsafe { self.0.as_mut()[lane / 8] ^= ((value ^ self.test_unchecked(lane)) as u8) << (lane % 8) } @@ -106,7 +106,7 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { + pub(crate) fn to_int(self) -> Simd { unsafe { core::intrinsics::simd::simd_select_bitmask( self.0, @@ -118,19 +118,19 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } } #[inline] - pub fn to_bitmask_integer(self) -> u64 { + pub(crate) fn to_bitmask_integer(self) -> u64 { let mut bitmask = [0u8; 8]; bitmask[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); u64::from_ne_bytes(bitmask) } #[inline] - pub fn from_bitmask_integer(bitmask: u64) -> Self { + pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { let mut bytes = as SupportedLaneCount>::BitMask::default(); let len = bytes.as_mut().len(); bytes @@ -141,7 +141,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn convert(self) -> Mask + pub(crate) fn convert(self) -> Mask where U: MaskElement, { @@ -151,13 +151,13 @@ where #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub fn any(self) -> bool { + pub(crate) fn any(self) -> bool { self != Self::splat(false) } #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub fn all(self) -> bool { + pub(crate) fn all(self) -> bool { self == Self::splat(true) } } diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 387b508c4b4ef..4e98db4070a9d 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -3,7 +3,7 @@ use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount}; #[repr(transparent)] -pub struct Mask(Simd) +pub(crate) struct Mask(Simd) where T: MaskElement, LaneCount: SupportedLaneCount; @@ -80,7 +80,7 @@ macro_rules! impl_reverse_bits { #[inline(always)] fn reverse_bits(self, n: usize) -> Self { let rev = <$int>::reverse_bits(self); - let bitsize = core::mem::size_of::<$int>() * 8; + let bitsize = size_of::<$int>() * 8; if n < bitsize { // Shift things back to the right rev >> (bitsize - n) @@ -102,36 +102,36 @@ where { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn splat(value: bool) -> Self { + pub(crate) fn splat(value: bool) -> Self { Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) } #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub unsafe fn test_unchecked(&self, lane: usize) -> bool { + pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { T::eq(self.0[lane], T::TRUE) } #[inline] - pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { + pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { self.0[lane] = if value { T::TRUE } else { T::FALSE } } #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { + pub(crate) fn to_int(self) -> Simd { self.0 } #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { Self(value) } #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn convert(self) -> Mask + pub(crate) fn convert(self) -> Mask where U: MaskElement, { @@ -220,14 +220,14 @@ where #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub fn any(self) -> bool { + pub(crate) fn any(self) -> bool { // Safety: use `self` as an integer vector unsafe { core::intrinsics::simd::simd_reduce_any(self.to_int()) } } #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] - pub fn all(self) -> bool { + pub(crate) fn all(self) -> bool { // Safety: use `self` as an integer vector unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } } diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index 4ac64a253a3bd..f36e8d01a73bb 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -1,4 +1,4 @@ -use crate::simd::{cmp::SimdPartialEq, LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount, cmp::SimdPartialEq}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; use core::ops::{Div, Rem, Sub}; diff --git a/crates/core_simd/src/simd/cmp/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs index 93989ce91b89d..2312ba401fa78 100644 --- a/crates/core_simd/src/simd/cmp/eq.rs +++ b/crates/core_simd/src/simd/cmp/eq.rs @@ -1,6 +1,6 @@ use crate::simd::{ - ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, Simd, SimdElement, SupportedLaneCount, + ptr::{SimdConstPtr, SimdMutPtr}, }; /// Parallel `PartialEq`. diff --git a/crates/core_simd/src/simd/cmp/ord.rs b/crates/core_simd/src/simd/cmp/ord.rs index 899f00a831641..e813e7613032c 100644 --- a/crates/core_simd/src/simd/cmp/ord.rs +++ b/crates/core_simd/src/simd/cmp/ord.rs @@ -1,7 +1,7 @@ use crate::simd::{ + LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, ptr::{SimdConstPtr, SimdMutPtr}, - LaneCount, Mask, Simd, SupportedLaneCount, }; /// Parallel `PartialOrd`. diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index db705dfe20221..b5972c47373bb 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -1,7 +1,7 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::{SimdPartialEq, SimdPartialOrd}, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, + cmp::{SimdPartialEq, SimdPartialOrd}, }; /// Operations on SIMD vectors of floats. @@ -263,7 +263,8 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_as(self) } } - // https://github.com/llvm/llvm-project/issues/94694 + // workaround for https://github.com/llvm/llvm-project/issues/94694 (fixed in LLVM 20) + // tracked in: https://github.com/rust-lang/rust/issues/135982 #[cfg(target_arch = "aarch64")] #[inline] fn cast(self) -> Self::Cast @@ -302,14 +303,14 @@ macro_rules! impl_trait { #[inline] fn to_bits(self) -> Simd<$bits_ty, N> { - assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + assert_eq!(size_of::(), size_of::()); // Safety: transmuting between vector types is safe unsafe { core::mem::transmute_copy(&self) } } #[inline] fn from_bits(bits: Simd<$bits_ty, N>) -> Self { - assert_eq!(core::mem::size_of::(), core::mem::size_of::()); + assert_eq!(size_of::(), size_of::()); // Safety: transmuting between vector types is safe unsafe { core::mem::transmute_copy(&bits) } } diff --git a/crates/core_simd/src/simd/num/int.rs b/crates/core_simd/src/simd/num/int.rs index 3a51235ff954e..d25050c3e4b47 100644 --- a/crates/core_simd/src/simd/num/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -1,7 +1,7 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, - SupportedLaneCount, + LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd, + cmp::SimdPartialOrd, num::SimdUint, }; /// Operations on SIMD vectors of signed integers. diff --git a/crates/core_simd/src/simd/num/uint.rs b/crates/core_simd/src/simd/num/uint.rs index 1ab2d8c7b7316..45d978068b664 100644 --- a/crates/core_simd/src/simd/num/uint.rs +++ b/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{cmp::SimdOrd, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { diff --git a/crates/core_simd/src/simd/prelude.rs b/crates/core_simd/src/simd/prelude.rs index 4b7c744c01326..e5d7a2aeb73df 100644 --- a/crates/core_simd/src/simd/prelude.rs +++ b/crates/core_simd/src/simd/prelude.rs @@ -7,10 +7,11 @@ #[doc(no_inline)] pub use super::{ + Mask, Simd, cmp::{SimdOrd, SimdPartialEq, SimdPartialOrd}, num::{SimdFloat, SimdInt, SimdUint}, ptr::{SimdConstPtr, SimdMutPtr}, - simd_swizzle, Mask, Simd, + simd_swizzle, }; #[rustfmt::skip] diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index 47383809ffbae..36452e7ae920d 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{cmp::SimdPartialEq, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount}; +use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index 3f20eef21a312..c644f390c20a5 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{cmp::SimdPartialEq, num::SimdUint, LaneCount, Mask, Simd, SupportedLaneCount}; +use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 42425ef37e50b..dbdd6ef40eba7 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -214,6 +214,17 @@ where /// Rotates the vector such that the first `OFFSET` elements of the slice move to the end /// while the last `self.len() - OFFSET` elements move to the front. After calling `rotate_elements_left`, /// the element previously at index `OFFSET` will become the first element in the slice. + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let a = Simd::from_array([0, 1, 2, 3]); + /// let x = a.rotate_elements_left::<3>(); + /// assert_eq!(x.to_array(), [3, 0, 1, 2]); + /// + /// let y = a.rotate_elements_left::<7>(); + /// assert_eq!(y.to_array(), [3, 0, 1, 2]); + /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_left(self) -> Self { @@ -238,6 +249,17 @@ where /// Rotates the vector such that the first `self.len() - OFFSET` elements of the vector move to /// the end while the last `OFFSET` elements move to the front. After calling `rotate_elements_right`, /// the element previously at index `self.len() - OFFSET` will become the first element in the slice. + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let a = Simd::from_array([0, 1, 2, 3]); + /// let x = a.rotate_elements_right::<3>(); + /// assert_eq!(x.to_array(), [1, 2, 3, 0]); + /// + /// let y = a.rotate_elements_right::<7>(); + /// assert_eq!(y.to_array(), [1, 2, 3, 0]); + /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_right(self) -> Self { @@ -261,6 +283,17 @@ where /// Shifts the vector elements to the left by `OFFSET`, filling in with /// `padding` from the right. + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let a = Simd::from_array([0, 1, 2, 3]); + /// let x = a.shift_elements_left::<3>(255); + /// assert_eq!(x.to_array(), [3, 255, 255, 255]); + /// + /// let y = a.shift_elements_left::<7>(255); + /// assert_eq!(y.to_array(), [255, 255, 255, 255]); + /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn shift_elements_left(self, padding: T) -> Self { @@ -283,6 +316,17 @@ where /// Shifts the vector elements to the right by `OFFSET`, filling in with /// `padding` from the left. + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd::Simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd::Simd; + /// let a = Simd::from_array([0, 1, 2, 3]); + /// let x = a.shift_elements_right::<3>(255); + /// assert_eq!(x.to_array(), [255, 255, 255, 0]); + /// + /// let y = a.shift_elements_right::<7>(255); + /// assert_eq!(y.to_array(), [255, 255, 255, 255]); + /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn shift_elements_right(self, padding: T) -> Self { diff --git a/crates/core_simd/src/to_bytes.rs b/crates/core_simd/src/to_bytes.rs index 4833ea9e11362..fee2cc06c5b09 100644 --- a/crates/core_simd/src/to_bytes.rs +++ b/crates/core_simd/src/to_bytes.rs @@ -1,6 +1,6 @@ use crate::simd::{ - num::{SimdFloat, SimdInt, SimdUint}, LaneCount, Simd, SimdElement, SupportedLaneCount, + num::{SimdFloat, SimdInt, SimdUint}, }; mod sealed { diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 9c4dd36c24fe8..d76a6cd52bfc5 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -1,8 +1,8 @@ use crate::simd::{ + LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, cmp::SimdPartialOrd, num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, - LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; /// A SIMD vector with the shape of `[T; N]` but the operations of `T`. @@ -83,7 +83,7 @@ use crate::simd::{ /// converting `[T]` to `[Simd]`, and allows soundly operating on an aligned SIMD body, /// but it may cost more time when handling the scalar head and tail. /// If these are not enough, it is most ideal to design data structures to be already aligned -/// to `mem::align_of::>()` before using `unsafe` Rust to read or write. +/// to `align_of::>()` before using `unsafe` Rust to read or write. /// Other ways to compensate for these facts, like materializing `Simd` to or from an array first, /// are handled by safe methods like [`Simd::from_array`] and [`Simd::from_slice`]. /// diff --git a/crates/core_simd/tests/layout.rs b/crates/core_simd/tests/layout.rs index 24114c2d261e7..3b4666249b0d7 100644 --- a/crates/core_simd/tests/layout.rs +++ b/crates/core_simd/tests/layout.rs @@ -7,8 +7,8 @@ macro_rules! layout_tests { test_helpers::test_lanes! { fn no_padding() { assert_eq!( - core::mem::size_of::>(), - core::mem::size_of::<[$ty; LANES]>(), + size_of::>(), + size_of::<[$ty; LANES]>(), ); } } diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs index d7db4e82b3ca2..6e74c2d18b1ed 100644 --- a/crates/core_simd/tests/pointers.rs +++ b/crates/core_simd/tests/pointers.rs @@ -1,8 +1,8 @@ #![feature(portable_simd)] use core_simd::simd::{ - ptr::{SimdConstPtr, SimdMutPtr}, Simd, + ptr::{SimdConstPtr, SimdMutPtr}, }; macro_rules! common_tests { diff --git a/crates/core_simd/tests/round.rs b/crates/core_simd/tests/round.rs index 847766ec41ed2..4c1ac3c36f894 100644 --- a/crates/core_simd/tests/round.rs +++ b/crates/core_simd/tests/round.rs @@ -58,7 +58,7 @@ macro_rules! float_rounding_test { // all of the mantissa digits set to 1, pushed up to the MSB. const ALL_MANTISSA_BITS: IntScalar = ((1 << ::MANTISSA_DIGITS) - 1); const MAX_REPRESENTABLE_VALUE: Scalar = - (ALL_MANTISSA_BITS << (core::mem::size_of::() * 8 - ::MANTISSA_DIGITS as usize - 1)) as Scalar; + (ALL_MANTISSA_BITS << (size_of::() * 8 - ::MANTISSA_DIGITS as usize - 1)) as Scalar; let mut runner = test_helpers::make_runner(); runner.run( diff --git a/crates/test_helpers/src/subnormals.rs b/crates/test_helpers/src/subnormals.rs index ec0f1fb24b936..b5f19ba47b819 100644 --- a/crates/test_helpers/src/subnormals.rs +++ b/crates/test_helpers/src/subnormals.rs @@ -12,7 +12,7 @@ macro_rules! impl_float { $( impl FlushSubnormals for $ty { fn flush(self) -> Self { - let is_f32 = core::mem::size_of::() == 4; + let is_f32 = size_of::() == 4; let ppc_flush = is_f32 && cfg!(all( any(target_arch = "powerpc", all(target_arch = "powerpc64", target_endian = "big")), target_feature = "altivec", From cdac23c86a06003d37ad102edbdc74c8d3d9f933 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Mon, 7 Apr 2025 18:13:32 +0200 Subject: [PATCH 04/43] Fix grammar in beginners-guide.md --- beginners-guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beginners-guide.md b/beginners-guide.md index dc08d847ced50..4250a18315a6e 100644 --- a/beginners-guide.md +++ b/beginners-guide.md @@ -25,7 +25,7 @@ SIMD has a few special vocabulary terms you should know: * **Scalar:** "Scalar" in mathematical contexts refers to values that can be represented as a single element, mostly numbers like 6, 3.14, or -2. It can also be used to describe "scalar operations" that use strictly scalar values, like addition. This term is mostly used to differentiate between vectorized operations that use SIMD instructions and scalar operations that don't. -* **Lane:** A single element position within a vector is called a lane. If you have `N` lanes available then they're numbered from `0` to `N-1` when referring to them, again like an array. The biggest difference between an array element and a vector lane is that in general is *relatively costly* to access an individual lane value. On most architectures, the vector has to be pushed out of the SIMD register onto the stack, then an individual lane is accessed while it's on the stack (and possibly the stack value is read back into a register). For this reason, when working with SIMD you should avoid reading or writing the value of an individual lane during hot loops. +* **Lane:** A single element position within a vector is called a lane. If you have `N` lanes available then they're numbered from `0` to `N-1` when referring to them, again like an array. The biggest difference between an array element and a vector lane is that in general it is *relatively costly* to access an individual lane value. On most architectures, the vector has to be pushed out of the SIMD register onto the stack, then an individual lane is accessed while it's on the stack (and possibly the stack value is read back into a register). For this reason, when working with SIMD you should avoid reading or writing the value of an individual lane during hot loops. * **Bit Widths:** When talking about SIMD, the bit widths used are the bit size of the vectors involved, *not* the individual elements. So "128-bit SIMD" has 128-bit vectors, and that might be `f32x4`, `i32x4`, `i16x8`, or other variations. While 128-bit SIMD is the most common, there's also 64-bit, 256-bit, and even 512-bit on the newest CPUs. From 6951b685b8d8bd42b88765432e125959cb5fbdd8 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:45:38 -0400 Subject: [PATCH 05/43] Remove usize/isize `From` impls for vendor vector types Remove usize/isize impls from - `loongarch64.rs` - `wasm32.rs` - `x86.rs` --- crates/core_simd/src/vendor/loongarch64.rs | 5 ----- crates/core_simd/src/vendor/wasm32.rs | 14 -------------- crates/core_simd/src/vendor/x86.rs | 22 ---------------------- 3 files changed, 41 deletions(-) diff --git a/crates/core_simd/src/vendor/loongarch64.rs b/crates/core_simd/src/vendor/loongarch64.rs index 1290bc166b2b8..13dda4769aba8 100644 --- a/crates/core_simd/src/vendor/loongarch64.rs +++ b/crates/core_simd/src/vendor/loongarch64.rs @@ -24,8 +24,3 @@ from_transmute! { unsafe i64x2 => v2i64 } from_transmute! { unsafe i64x4 => v4i64 } from_transmute! { unsafe f64x2 => v2f64 } from_transmute! { unsafe f64x4 => v4f64 } - -from_transmute! { unsafe usizex2 => v2u64 } -from_transmute! { unsafe usizex4 => v4u64 } -from_transmute! { unsafe isizex2 => v2i64 } -from_transmute! { unsafe isizex4 => v4i64 } diff --git a/crates/core_simd/src/vendor/wasm32.rs b/crates/core_simd/src/vendor/wasm32.rs index ef3baf885b0fb..1fdb2bc86d346 100644 --- a/crates/core_simd/src/vendor/wasm32.rs +++ b/crates/core_simd/src/vendor/wasm32.rs @@ -14,17 +14,3 @@ from_transmute! { unsafe f32x4 => v128 } from_transmute! { unsafe u64x2 => v128 } from_transmute! { unsafe i64x2 => v128 } from_transmute! { unsafe f64x2 => v128 } - -#[cfg(target_pointer_width = "32")] -mod p32 { - use super::*; - from_transmute! { unsafe usizex4 => v128 } - from_transmute! { unsafe isizex4 => v128 } -} - -#[cfg(target_pointer_width = "64")] -mod p64 { - use super::*; - from_transmute! { unsafe usizex2 => v128 } - from_transmute! { unsafe isizex2 => v128 } -} diff --git a/crates/core_simd/src/vendor/x86.rs b/crates/core_simd/src/vendor/x86.rs index 66aaf90eef597..eae42e6fd0d0a 100644 --- a/crates/core_simd/src/vendor/x86.rs +++ b/crates/core_simd/src/vendor/x86.rs @@ -39,25 +39,3 @@ from_transmute! { unsafe i64x8 => __m512i } from_transmute! { unsafe f64x2 => __m128d } from_transmute! { unsafe f64x4 => __m256d } from_transmute! { unsafe f64x8 => __m512d } - -#[cfg(target_pointer_width = "32")] -mod p32 { - use super::*; - from_transmute! { unsafe usizex4 => __m128i } - from_transmute! { unsafe usizex8 => __m256i } - from_transmute! { unsafe Simd => __m512i } - from_transmute! { unsafe isizex4 => __m128i } - from_transmute! { unsafe isizex8 => __m256i } - from_transmute! { unsafe Simd => __m512i } -} - -#[cfg(target_pointer_width = "64")] -mod p64 { - use super::*; - from_transmute! { unsafe usizex2 => __m128i } - from_transmute! { unsafe usizex4 => __m256i } - from_transmute! { unsafe usizex8 => __m512i } - from_transmute! { unsafe isizex2 => __m128i } - from_transmute! { unsafe isizex4 => __m256i } - from_transmute! { unsafe isizex8 => __m512i } -} From 97a3a5008470239510262f378350132da358edaf Mon Sep 17 00:00:00 2001 From: ltdk Date: Thu, 12 Jun 2025 12:03:40 -0400 Subject: [PATCH 06/43] Add const to as_mut_array, copy_to_slice --- crates/core_simd/src/vector.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index d76a6cd52bfc5..984a356c6552b 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -195,7 +195,7 @@ where /// Returns a mutable array reference containing the entire SIMD vector. #[inline] - pub fn as_mut_array(&mut self) -> &mut [T; N] { + pub const fn as_mut_array(&mut self) -> &mut [T; N] { // SAFETY: `Simd` is just an overaligned `[T; N]` with // potential padding at the end, so pointer casting to a // `&mut [T; N]` is safe. @@ -324,7 +324,7 @@ where /// ``` #[inline] #[track_caller] - pub fn copy_to_slice(self, slice: &mut [T]) { + pub const fn copy_to_slice(self, slice: &mut [T]) { assert!( slice.len() >= Self::LEN, "slice length must be at least the number of elements" From a85790961605af9967fe5b76c45671c37c17f56b Mon Sep 17 00:00:00 2001 From: ltdk Date: Fri, 13 Jun 2025 00:38:09 -0400 Subject: [PATCH 07/43] Make Mask::splat const --- crates/core_simd/src/lane_count.rs | 15 ++++++++++++++- crates/core_simd/src/masks.rs | 3 ++- crates/core_simd/src/masks/bitmask.rs | 23 +++++++++++------------ crates/core_simd/src/masks/full_masks.rs | 3 ++- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/crates/core_simd/src/lane_count.rs b/crates/core_simd/src/lane_count.rs index 280b27bc9bc6f..c62b9d3b7d9be 100644 --- a/crates/core_simd/src/lane_count.rs +++ b/crates/core_simd/src/lane_count.rs @@ -18,7 +18,11 @@ impl LaneCount { /// Only SIMD vectors with supported lane counts are constructable. pub trait SupportedLaneCount: Sealed { #[doc(hidden)] - type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>; + type BitMask: Copy + AsRef<[u8]> + AsMut<[u8]>; + #[doc(hidden)] + const EMPTY_BIT_MASK: Self::BitMask; + #[doc(hidden)] + const FULL_BIT_MASK: Self::BitMask; } impl Sealed for LaneCount {} @@ -28,6 +32,15 @@ macro_rules! supported_lane_count { $( impl SupportedLaneCount for LaneCount<$lanes> { type BitMask = [u8; ($lanes + 7) / 8]; + const EMPTY_BIT_MASK: Self::BitMask = [0; ($lanes + 7) / 8]; + const FULL_BIT_MASK: Self::BitMask = { + const LEN: usize = ($lanes + 7) / 8; + let mut array = [!0u8; LEN]; + if $lanes % 8 > 0 { + array[LEN - 1] = (!0) >> (8 - $lanes % 8); + } + array + }; } )+ }; diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index 19d45f4d3b31a..2b6be3676279d 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -139,7 +139,8 @@ where { /// Constructs a mask by setting all elements to the given value. #[inline] - pub fn splat(value: bool) -> Self { + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] + pub const fn splat(value: bool) -> Self { Self(mask_impl::Mask::splat(value)) } diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index 8221d8f17e90e..ef03ec7f71591 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -78,17 +78,16 @@ where { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn splat(value: bool) -> Self { - let mut mask = as SupportedLaneCount>::BitMask::default(); - if value { - mask.as_mut().fill(u8::MAX) - } else { - mask.as_mut().fill(u8::MIN) - } - if N % 8 > 0 { - *mask.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8); - } - Self(mask, PhantomData) + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] + pub(crate) const fn splat(value: bool) -> Self { + Self( + if value { + as SupportedLaneCount>::FULL_BIT_MASK + } else { + as SupportedLaneCount>::EMPTY_BIT_MASK + }, + PhantomData, + ) } #[inline] @@ -131,7 +130,7 @@ where #[inline] pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); + let mut bytes = as SupportedLaneCount>::BitMask::EMPTY_BIT_MASK; let len = bytes.as_mut().len(); bytes .as_mut() diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 4e98db4070a9d..ddd7fb69b1891 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -102,7 +102,8 @@ where { #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn splat(value: bool) -> Self { + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] + pub(crate) const fn splat(value: bool) -> Self { Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) } From b47a091b7df34d0a95913974419076c67a91643d Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 4 Jul 2025 14:22:09 +0000 Subject: [PATCH 08/43] clippy fix: use div_ceil --- crates/core_simd/src/lane_count.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/core_simd/src/lane_count.rs b/crates/core_simd/src/lane_count.rs index c62b9d3b7d9be..839195c38eda7 100644 --- a/crates/core_simd/src/lane_count.rs +++ b/crates/core_simd/src/lane_count.rs @@ -8,7 +8,7 @@ pub struct LaneCount; impl LaneCount { /// The number of bytes in a bitmask with this many lanes. - pub const BITMASK_LEN: usize = (N + 7) / 8; + pub const BITMASK_LEN: usize = N.div_ceil(8); } /// Statically guarantees that a lane count is marked as supported. @@ -31,13 +31,12 @@ macro_rules! supported_lane_count { ($($lanes:literal),+) => { $( impl SupportedLaneCount for LaneCount<$lanes> { - type BitMask = [u8; ($lanes + 7) / 8]; - const EMPTY_BIT_MASK: Self::BitMask = [0; ($lanes + 7) / 8]; + type BitMask = [u8; Self::BITMASK_LEN]; + const EMPTY_BIT_MASK: Self::BitMask = [0; Self::BITMASK_LEN]; const FULL_BIT_MASK: Self::BitMask = { - const LEN: usize = ($lanes + 7) / 8; - let mut array = [!0u8; LEN]; + let mut array = [!0u8; Self::BITMASK_LEN]; if $lanes % 8 > 0 { - array[LEN - 1] = (!0) >> (8 - $lanes % 8); + array[Self::BITMASK_LEN - 1] = (!0) >> (8 - $lanes % 8); } array }; From 5a26848a5aa9fbaf75eed785acb40fc0010a38ba Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 5 Jul 2025 08:36:27 +0200 Subject: [PATCH 09/43] use `div_ceil` instead of manual logic --- crates/core_simd/src/lane_count.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/lane_count.rs b/crates/core_simd/src/lane_count.rs index 280b27bc9bc6f..bbdfd5f5f3ed3 100644 --- a/crates/core_simd/src/lane_count.rs +++ b/crates/core_simd/src/lane_count.rs @@ -8,7 +8,7 @@ pub struct LaneCount; impl LaneCount { /// The number of bytes in a bitmask with this many lanes. - pub const BITMASK_LEN: usize = (N + 7) / 8; + pub const BITMASK_LEN: usize = N.div_ceil(8); } /// Statically guarantees that a lane count is marked as supported. From 3a0909c7677e0903145fb84a66683d5989eb7b69 Mon Sep 17 00:00:00 2001 From: burgerindividual Date: Mon, 4 Aug 2025 11:29:22 -0400 Subject: [PATCH 10/43] Fix incorrect reference to EMPTY_BIT_MASK --- crates/core_simd/src/masks/bitmask.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index ef03ec7f71591..780162c5b4bbb 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -130,7 +130,7 @@ where #[inline] pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::EMPTY_BIT_MASK; + let mut bytes = as SupportedLaneCount>::EMPTY_BIT_MASK; let len = bytes.as_mut().len(); bytes .as_mut() From 3f01c753fd24607e20de083a407a5e9275f1ec78 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:36:53 -0400 Subject: [PATCH 11/43] Update nightly toolchain and fix broken examples Update examples to remove features that have been stabilized --- crates/core_simd/examples/dot_product.rs | 34 ++++++++++++------- crates/core_simd/examples/matrix_inversion.rs | 2 +- rust-toolchain.toml | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/crates/core_simd/examples/dot_product.rs b/crates/core_simd/examples/dot_product.rs index 75d152ae7f0e3..4ef32bfa60b5e 100644 --- a/crates/core_simd/examples/dot_product.rs +++ b/crates/core_simd/examples/dot_product.rs @@ -1,8 +1,6 @@ //! Code taken from the `packed_simd` crate. //! Run this code with `cargo test --example dot_product`. -#![feature(array_chunks)] -#![feature(slice_as_chunks)] // Add these imports to use the stdsimd library #![feature(portable_simd)] use core_simd::simd::prelude::*; @@ -33,7 +31,7 @@ pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 { } // We now move on to the SIMD implementations: notice the following constructs: -// `array_chunks::<4>`: mapping this over the vector will let use construct SIMD vectors +// `as_chunks::<4>`: mapping this over the vector will let us construct SIMD vectors // `f32x4::from_array`: construct the SIMD vector from a slice // `(a * b).reduce_sum()`: Multiply both f32x4 vectors together, and then reduce them. // This approach essentially uses SIMD to produce a vector of length N/4 of all the products, @@ -42,9 +40,11 @@ pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 { pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .map(|(a, b)| (a * b).reduce_sum()) .sum() } @@ -60,9 +60,11 @@ pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 { pub fn dot_prod_simd_1(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .fold(f32x4::splat(0.0), |acc, zipped| acc + zipped.0 * zipped.1) .reduce_sum() } @@ -74,9 +76,11 @@ pub fn dot_prod_simd_2(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 let mut res = f32x4::splat(0.0); - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .for_each(|(a, b)| { res = a.mul_add(b, res); }); @@ -113,9 +117,11 @@ pub fn dot_prod_simd_3(a: &[f32], b: &[f32]) -> f32 { // next example. pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 { let mut sum = a - .array_chunks::<4>() + .as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .map(|(a, b)| a * b) .fold(f32x4::splat(0.0), std::ops::Add::add) .reduce_sum(); @@ -131,9 +137,11 @@ pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 { // This version allocates a single `XMM` register for accumulation, and the folds don't allocate on top of that. // Notice the use of `mul_add`, which can do a multiply and an add operation ber iteration. pub fn dot_prod_simd_5(a: &[f32], b: &[f32]) -> f32 { - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .fold(f32x4::splat(0.), |acc, (a, b)| a.mul_add(b, acc)) .reduce_sum() } diff --git a/crates/core_simd/examples/matrix_inversion.rs b/crates/core_simd/examples/matrix_inversion.rs index bad86414401d7..ad2eea9153e08 100644 --- a/crates/core_simd/examples/matrix_inversion.rs +++ b/crates/core_simd/examples/matrix_inversion.rs @@ -1,7 +1,7 @@ //! 4x4 matrix inverse // Code ported from the `packed_simd` crate // Run this code with `cargo test --example matrix_inversion` -#![feature(array_chunks, portable_simd)] +#![feature(portable_simd)] use core_simd::simd::prelude::*; // Gotta define our own 4x4 matrix since Rust doesn't ship multidim arrays yet :^) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d17c6d2e88946..df4bd75ecfe2f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-01-16" +channel = "nightly-2025-08-17" components = ["rustfmt", "clippy", "miri", "rust-src"] From 28e79b143328822ac43d9d72f5f0fbf60479c0a5 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Sun, 17 Aug 2025 21:42:22 -0400 Subject: [PATCH 12/43] Update Cargo.lock --- Cargo.lock | 263 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 204 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1584c704fb221..d7accf71ab693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" @@ -16,31 +16,30 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "cfg-if" -version = "1.0.0" +name = "cc" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +dependencies = [ + "shlex", +] [[package]] -name = "console_error_panic_hook" -version = "0.1.7" +name = "cfg-if" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "core_simd" @@ -55,45 +54,59 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "log" -version = "0.4.20" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -114,9 +127,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -167,10 +180,25 @@ dependencies = [ ] [[package]] -name = "scoped-tls" -version = "1.0.1" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "std_float" @@ -184,9 +212,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -202,29 +230,40 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -233,21 +272,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -255,9 +295,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -268,19 +308,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.37" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -288,20 +330,123 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.37" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", + "syn", ] [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] From 07e9de0f6d73d5c9ffc87dbdc496687fc2ad7200 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Sun, 17 Aug 2025 21:46:26 -0400 Subject: [PATCH 13/43] Remove `i586-pc-windows-msvc` from CI --- .github/workflows/ci.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3984d8f0d8d99..96881687af055 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: strategy: fail-fast: false matrix: - target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu] + target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, x86_64-unknown-linux-gnu] # `default` means we use the default target config for the target, # `native` means we run with `-Ctarget-cpu=native`, and anything else is # an arg to `-Ctarget-feature` @@ -68,18 +68,12 @@ jobs: exclude: # -Ctarget-cpu=native sounds like bad-news if target != host - { target: i686-pc-windows-msvc, target_feature: native } - - { target: i586-pc-windows-msvc, target_feature: native } include: # Populate the `matrix.os` field - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } - { target: x86_64-pc-windows-msvc, os: windows-latest } - { target: i686-pc-windows-msvc, os: windows-latest } - - { target: i586-pc-windows-msvc, os: windows-latest } - - # These are globally available on all the other targets. - - { target: i586-pc-windows-msvc, target_feature: +sse, os: windows-latest } - - { target: i586-pc-windows-msvc, target_feature: +sse2, os: windows-latest } # Annoyingly, the x86_64-unknown-linux-gnu runner *almost* always has # avx512vl, but occasionally doesn't. Maybe one day we can enable it. @@ -129,7 +123,7 @@ jobs: run: cargo doc --verbose --target=${{ matrix.target }} env: RUSTDOCFLAGS: -Dwarnings - + macos-tests: name: ${{ matrix.target }} runs-on: macos-latest From 323484c8278886ffe58e8d355107a5b59ffd85cd Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Mon, 18 Aug 2025 00:42:20 -0400 Subject: [PATCH 14/43] Add no-extra-rounding-error flag to Miri CI config --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96881687af055..5344efe1db0e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -244,5 +244,10 @@ jobs: PROPTEST_CASES: 16 steps: - uses: actions/checkout@v4 + - name: Download and install nightly + run: | + rustup toolchain install nightly --component miri,rust-src + rustup override set nightly + cargo miri setup - name: Test (Miri) - run: cargo miri test + run: MIRIFLAGS="-Zmiri-no-extra-rounding-error" cargo miri test From c43c8d25a8c4f7035d4265e672f41848effbe615 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 18 Aug 2025 01:31:25 -0400 Subject: [PATCH 15/43] Check some float ops approximately --- Cargo.lock | 10 +++ crates/std_float/tests/float.rs | 29 +++++++- crates/test_helpers/Cargo.toml | 1 + crates/test_helpers/src/approxeq.rs | 110 ++++++++++++++++++++++++++++ crates/test_helpers/src/lib.rs | 76 +++++++++++++++++++ 5 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 crates/test_helpers/src/approxeq.rs diff --git a/Cargo.lock b/Cargo.lock index d7accf71ab693..5a5f0d8907ae3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,15 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -225,6 +234,7 @@ dependencies = [ name = "test_helpers" version = "0.1.0" dependencies = [ + "float-cmp", "proptest", ] diff --git a/crates/std_float/tests/float.rs b/crates/std_float/tests/float.rs index c66c968f8c667..c608ba49564e0 100644 --- a/crates/std_float/tests/float.rs +++ b/crates/std_float/tests/float.rs @@ -16,15 +16,33 @@ macro_rules! unary_test { } } -macro_rules! binary_test { +macro_rules! unary_approx_test { { $scalar:tt, $($func:tt),+ } => { test_helpers::test_lanes! { $( fn $func() { - test_helpers::test_binary_elementwise( + test_helpers::test_unary_elementwise_approx( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + 8, + ) + } + )* + } + } +} + +macro_rules! binary_approx_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_binary_elementwise_approx( &core_simd::simd::Simd::<$scalar, LANES>::$func, &$scalar::$func, &|_, _| true, + 16, ) } )* @@ -53,10 +71,13 @@ macro_rules! impl_tests { mod $scalar { use std_float::StdFloat; - unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } - binary_test! { $scalar, log } + unary_test! { $scalar, sqrt, ceil, floor, round, trunc } ternary_test! { $scalar, mul_add } + // https://github.com/rust-lang/miri/issues/3555 + unary_approx_test! { $scalar, sin, cos, exp, exp2, ln, log2, log10 } + binary_approx_test! { $scalar, log } + test_helpers::test_lanes! { fn fract() { test_helpers::test_unary_elementwise_flush_subnormals( diff --git a/crates/test_helpers/Cargo.toml b/crates/test_helpers/Cargo.toml index a5359b9abc84d..408bb04c7aa40 100644 --- a/crates/test_helpers/Cargo.toml +++ b/crates/test_helpers/Cargo.toml @@ -6,3 +6,4 @@ publish = false [dependencies] proptest = { version = "0.10", default-features = false, features = ["alloc"] } +float-cmp = "0.10" diff --git a/crates/test_helpers/src/approxeq.rs b/crates/test_helpers/src/approxeq.rs new file mode 100644 index 0000000000000..57b43a16bc6fe --- /dev/null +++ b/crates/test_helpers/src/approxeq.rs @@ -0,0 +1,110 @@ +//! Compare numeric types approximately. + +use float_cmp::Ulps; + +pub trait ApproxEq { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool; + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result; +} + +impl ApproxEq for bool { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + +macro_rules! impl_integer_approxeq { + { $($type:ty),* } => { + $( + impl ApproxEq for $type { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?} ({:x})", self, self) + } + } + )* + }; +} + +impl_integer_approxeq! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize } + +macro_rules! impl_float_approxeq { + { $($type:ty),* } => { + $( + impl ApproxEq for $type { + fn approxeq(&self, other: &Self, ulps: i64) -> bool { + if self.is_nan() && other.is_nan() { + true + } else { + (self.ulps(other) as i64).abs() <= ulps + } + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?} ({:x})", self, self.to_bits()) + } + } + )* + }; +} + +impl_float_approxeq! { f32, f64 } + +impl ApproxEq for [T; N] { + fn approxeq(&self, other: &Self, ulps: i64) -> bool { + self.iter() + .zip(other.iter()) + .fold(true, |value, (left, right)| { + value && left.approxeq(right, ulps) + }) + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + #[repr(transparent)] + struct Wrapper<'a, T: ApproxEq>(&'a T); + + impl core::fmt::Debug for Wrapper<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } + } + + f.debug_list() + .entries(self.iter().map(|x| Wrapper(x))) + .finish() + } +} + +#[doc(hidden)] +pub struct ApproxEqWrapper<'a, T>(pub &'a T, pub i64); + +impl PartialEq for ApproxEqWrapper<'_, T> { + fn eq(&self, other: &T) -> bool { + self.0.approxeq(other, self.1) + } +} + +impl core::fmt::Debug for ApproxEqWrapper<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } +} + +#[macro_export] +macro_rules! prop_assert_approxeq { + { $a:expr, $b:expr, $ulps:expr $(,)? } => { + { + use $crate::approxeq::ApproxEqWrapper; + let a = $a; + let b = $b; + proptest::prop_assert_eq!(ApproxEqWrapper(&a, $ulps), b); + } + }; +} diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index 197c920e11eac..35401a9ddb40f 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -12,6 +12,9 @@ pub mod wasm; #[macro_use] pub mod biteq; +#[macro_use] +pub mod approxeq; + pub mod subnormals; use subnormals::FlushSubnormals; @@ -185,6 +188,41 @@ pub fn test_unary_elementwise( + fv: &dyn Fn(Vector) -> VectorResult, + fs: &dyn Fn(Scalar) -> ScalarResult, + check: &dyn Fn([Scalar; LANES]) -> bool, + ulps: i64, +) where + Scalar: Copy + core::fmt::Debug + DefaultStrategy, + ScalarResult: Copy + approxeq::ApproxEq + core::fmt::Debug + DefaultStrategy, + Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, + VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, +{ + test_1(&|x: [Scalar; LANES]| { + proptest::prop_assume!(check(x)); + let result_1: [ScalarResult; LANES] = fv(x.into()).into(); + let result_2: [ScalarResult; LANES] = x + .iter() + .copied() + .map(fs) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_approxeq!(result_1, result_2, ulps); + Ok(()) + }); +} + /// Test a unary vector function against a unary scalar function, applied elementwise. /// /// Where subnormals are flushed, use approximate equality. @@ -290,6 +328,44 @@ pub fn test_binary_elementwise< }); } +/// Test a binary vector function against a binary scalar function, applied elementwise. +pub fn test_binary_elementwise_approx< + Scalar1, + Scalar2, + ScalarResult, + Vector1, + Vector2, + VectorResult, + const LANES: usize, +>( + fv: &dyn Fn(Vector1, Vector2) -> VectorResult, + fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, + check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool, + ulps: i64, +) where + Scalar1: Copy + core::fmt::Debug + DefaultStrategy, + Scalar2: Copy + core::fmt::Debug + DefaultStrategy, + ScalarResult: Copy + approxeq::ApproxEq + core::fmt::Debug + DefaultStrategy, + Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, + Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, + VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, +{ + test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| { + proptest::prop_assume!(check(x, y)); + let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into(); + let result_2: [ScalarResult; LANES] = x + .iter() + .copied() + .zip(y.iter().copied()) + .map(|(x, y)| fs(x, y)) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_approxeq!(result_1, result_2, ulps); + Ok(()) + }); +} + /// Test a binary vector function against a binary scalar function, applied elementwise. /// /// Where subnormals are flushed, use approximate equality. From b902397bc20e1d0fa5ffe2939b3b9082f2f5b658 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 18 Aug 2025 01:33:28 -0400 Subject: [PATCH 16/43] Clippy isn't aware of repr(simd, packed) --- crates/core_simd/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 717b882b64ba1..9d7dfd2ab351d 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -41,7 +41,7 @@ clippy::undocumented_unsafe_blocks )] #![doc(test(attr(deny(warnings))))] -#![allow(internal_features)] +#![allow(internal_features, clippy::repr_packed_without_abi)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. From 1ce33dd37dd87dc566e0f9ea5b2e15e17c458ee5 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Mon, 18 Aug 2025 01:34:11 -0400 Subject: [PATCH 17/43] Revert "Add no-extra-rounding-error flag to Miri CI config" This reverts commit 323484c8278886ffe58e8d355107a5b59ffd85cd. --- .github/workflows/ci.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5344efe1db0e0..96881687af055 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -244,10 +244,5 @@ jobs: PROPTEST_CASES: 16 steps: - uses: actions/checkout@v4 - - name: Download and install nightly - run: | - rustup toolchain install nightly --component miri,rust-src - rustup override set nightly - cargo miri setup - name: Test (Miri) - run: MIRIFLAGS="-Zmiri-no-extra-rounding-error" cargo miri test + run: cargo miri test From 61c45c164a7a9ebf3107eb6d8735514ee3221053 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 8 Aug 2025 16:31:47 +0800 Subject: [PATCH 18/43] loongarch64: Use unified data types for SIMD intrinsics --- crates/core_simd/src/vendor/loongarch64.rs | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/core_simd/src/vendor/loongarch64.rs b/crates/core_simd/src/vendor/loongarch64.rs index 13dda4769aba8..1f84cdb971ecb 100644 --- a/crates/core_simd/src/vendor/loongarch64.rs +++ b/crates/core_simd/src/vendor/loongarch64.rs @@ -1,26 +1,26 @@ use crate::simd::*; use core::arch::loongarch64::*; -from_transmute! { unsafe u8x16 => v16u8 } -from_transmute! { unsafe u8x32 => v32u8 } -from_transmute! { unsafe i8x16 => v16i8 } -from_transmute! { unsafe i8x32 => v32i8 } +from_transmute! { unsafe u8x16 => m128i } +from_transmute! { unsafe u8x32 => m256i } +from_transmute! { unsafe i8x16 => m128i } +from_transmute! { unsafe i8x32 => m256i } -from_transmute! { unsafe u16x8 => v8u16 } -from_transmute! { unsafe u16x16 => v16u16 } -from_transmute! { unsafe i16x8 => v8i16 } -from_transmute! { unsafe i16x16 => v16i16 } +from_transmute! { unsafe u16x8 => m128i } +from_transmute! { unsafe u16x16 => m256i } +from_transmute! { unsafe i16x8 => m128i } +from_transmute! { unsafe i16x16 => m256i } -from_transmute! { unsafe u32x4 => v4u32 } -from_transmute! { unsafe u32x8 => v8u32 } -from_transmute! { unsafe i32x4 => v4i32 } -from_transmute! { unsafe i32x8 => v8i32 } -from_transmute! { unsafe f32x4 => v4f32 } -from_transmute! { unsafe f32x8 => v8f32 } +from_transmute! { unsafe u32x4 => m128i } +from_transmute! { unsafe u32x8 => m256i } +from_transmute! { unsafe i32x4 => m128i } +from_transmute! { unsafe i32x8 => m256i } +from_transmute! { unsafe f32x4 => m128 } +from_transmute! { unsafe f32x8 => m256 } -from_transmute! { unsafe u64x2 => v2u64 } -from_transmute! { unsafe u64x4 => v4u64 } -from_transmute! { unsafe i64x2 => v2i64 } -from_transmute! { unsafe i64x4 => v4i64 } -from_transmute! { unsafe f64x2 => v2f64 } -from_transmute! { unsafe f64x4 => v4f64 } +from_transmute! { unsafe u64x2 => m128i } +from_transmute! { unsafe u64x4 => m256i } +from_transmute! { unsafe i64x2 => m128i } +from_transmute! { unsafe i64x4 => m256i } +from_transmute! { unsafe f64x2 => m128d } +from_transmute! { unsafe f64x4 => m256d } From 402c045f7a8f2085dd6552e63d4b6ae26049e9f9 Mon Sep 17 00:00:00 2001 From: AudaciousAxiom <179637270+AudaciousAxiom@users.noreply.github.com> Date: Tue, 2 Sep 2025 21:34:04 +0200 Subject: [PATCH 19/43] docs(std): add missing closing code block fences in doc comments --- crates/core_simd/src/simd/num/int.rs | 1 + crates/core_simd/src/simd/num/uint.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/core_simd/src/simd/num/int.rs b/crates/core_simd/src/simd/num/int.rs index d25050c3e4b47..e7253313f036c 100644 --- a/crates/core_simd/src/simd/num/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -58,6 +58,7 @@ pub trait SimdInt: Copy + Sealed { /// let sat = x.saturating_sub(max); /// assert_eq!(unsat, Simd::from_array([1, MAX, MIN, 0])); /// assert_eq!(sat, Simd::from_array([MIN, MIN, MIN, 0])); + /// ``` fn saturating_sub(self, second: Self) -> Self; /// Lanewise absolute value, implemented in Rust. diff --git a/crates/core_simd/src/simd/num/uint.rs b/crates/core_simd/src/simd/num/uint.rs index 45d978068b664..e3ba8658bd803 100644 --- a/crates/core_simd/src/simd/num/uint.rs +++ b/crates/core_simd/src/simd/num/uint.rs @@ -55,6 +55,7 @@ pub trait SimdUint: Copy + Sealed { /// let sat = x.saturating_sub(max); /// assert_eq!(unsat, Simd::from_array([3, 2, 1, 0])); /// assert_eq!(sat, Simd::splat(0)); + /// ``` fn saturating_sub(self, second: Self) -> Self; /// Lanewise absolute difference. From 1e62d4c87cec532abf267cd5a7cb0342f8369ed1 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:29:26 -0400 Subject: [PATCH 20/43] Rename `Mask::to_int` to `Mask::to_simd` --- crates/core_simd/src/masks.rs | 8 ++++---- crates/core_simd/src/masks/bitmask.rs | 2 +- crates/core_simd/src/masks/full_masks.rs | 8 ++++---- crates/core_simd/src/select.rs | 2 +- crates/core_simd/src/simd/cmp/eq.rs | 4 ++-- crates/core_simd/src/simd/cmp/ord.rs | 8 ++++---- crates/core_simd/src/swizzle.rs | 22 +++++++++++----------- crates/core_simd/src/vector.rs | 8 ++++---- crates/core_simd/tests/masks.rs | 2 +- 9 files changed, 32 insertions(+), 32 deletions(-) diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index 2b6be3676279d..b42cca7c977d5 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -175,7 +175,7 @@ where // This would be hypothetically valid as an "in-place" transmute, // but these are "dependently-sized" types, so copy elision it is! unsafe { - let mut bytes: Simd = core::intrinsics::simd::simd_cast(self.to_int()); + let mut bytes: Simd = core::intrinsics::simd::simd_cast(self.to_simd()); bytes &= Simd::splat(1i8); mem::transmute_copy(&bytes) } @@ -214,8 +214,8 @@ where /// represents `true`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { - self.0.to_int() + pub fn to_simd(self) -> Simd { + self.0.to_simd() } /// Converts the mask to a mask of any other element size. @@ -352,7 +352,7 @@ where // Safety: the input and output are integer vectors let index: Simd = unsafe { core::intrinsics::simd::simd_cast(index) }; - let masked_index = self.select(index, Self::splat(true).to_int()); + let masked_index = self.select(index, Self::splat(true).to_simd()); // Safety: the input and output are integer vectors let masked_index: Simd = diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index 780162c5b4bbb..4211789da7085 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -105,7 +105,7 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub(crate) fn to_int(self) -> Simd { + pub(crate) fn to_simd(self) -> Simd { unsafe { core::intrinsics::simd::simd_select_bitmask( self.0, diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index ddd7fb69b1891..a5382f0ff739e 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -120,7 +120,7 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub(crate) fn to_int(self) -> Simd { + pub(crate) fn to_simd(self) -> Simd { self.0 } @@ -145,7 +145,7 @@ where where LaneCount: SupportedLaneCount, { - let resized = self.to_int().resize::(T::FALSE); + let resized = self.to_simd().resize::(T::FALSE); // Safety: `resized` is an integer vector with length M, which must match T let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized) }; @@ -223,14 +223,14 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] pub(crate) fn any(self) -> bool { // Safety: use `self` as an integer vector - unsafe { core::intrinsics::simd::simd_reduce_any(self.to_int()) } + unsafe { core::intrinsics::simd::simd_reduce_any(self.to_simd()) } } #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub(crate) fn all(self) -> bool { // Safety: use `self` as an integer vector - unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } + unsafe { core::intrinsics::simd::simd_reduce_all(self.to_simd()) } } } diff --git a/crates/core_simd/src/select.rs b/crates/core_simd/src/select.rs index f33aa261a928f..a2db455a52685 100644 --- a/crates/core_simd/src/select.rs +++ b/crates/core_simd/src/select.rs @@ -28,7 +28,7 @@ where { // Safety: The mask has been cast to a vector of integers, // and the operands to select between are vectors of the same type and length. - unsafe { core::intrinsics::simd::simd_select(self.to_int(), true_values, false_values) } + unsafe { core::intrinsics::simd::simd_select(self.to_simd(), true_values, false_values) } } /// Choose elements from two masks. diff --git a/crates/core_simd/src/simd/cmp/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs index 2312ba401fa78..3c483f3f94a33 100644 --- a/crates/core_simd/src/simd/cmp/eq.rs +++ b/crates/core_simd/src/simd/cmp/eq.rs @@ -59,14 +59,14 @@ macro_rules! impl_mask { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_eq(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_eq(self.to_simd(), other.to_simd())) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ne(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ne(self.to_simd(), other.to_simd())) } } } )* diff --git a/crates/core_simd/src/simd/cmp/ord.rs b/crates/core_simd/src/simd/cmp/ord.rs index e813e7613032c..6eb3e360bdeb6 100644 --- a/crates/core_simd/src/simd/cmp/ord.rs +++ b/crates/core_simd/src/simd/cmp/ord.rs @@ -163,28 +163,28 @@ macro_rules! impl_mask { fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_lt(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_lt(self.to_simd(), other.to_simd())) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_le(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_le(self.to_simd(), other.to_simd())) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_gt(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_gt(self.to_simd(), other.to_simd())) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ge(self.to_int(), other.to_int())) } + unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ge(self.to_simd(), other.to_simd())) } } } diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index dbdd6ef40eba7..b6af2ded1cce0 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -165,7 +165,7 @@ pub trait Swizzle { LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask - unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_int())) } + unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_simd())) } } /// Creates a new mask from the elements of `first` and `second`. @@ -181,7 +181,7 @@ pub trait Swizzle { LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask - unsafe { Mask::from_int_unchecked(Self::concat_swizzle(first.to_int(), second.to_int())) } + unsafe { Mask::from_int_unchecked(Self::concat_swizzle(first.to_simd(), second.to_simd())) } } } @@ -524,7 +524,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn reverse(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().reverse()) } + unsafe { Self::from_int_unchecked(self.to_simd().reverse()) } } /// Rotates the mask such that the first `OFFSET` elements of the slice move to the end @@ -534,7 +534,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_left(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::()) } + unsafe { Self::from_int_unchecked(self.to_simd().rotate_elements_left::()) } } /// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to @@ -544,7 +544,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_right(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::()) } + unsafe { Self::from_int_unchecked(self.to_simd().rotate_elements_right::()) } } /// Shifts the mask elements to the left by `OFFSET`, filling in with @@ -554,7 +554,7 @@ where pub fn shift_elements_left(self, padding: bool) -> Self { // Safety: swizzles are safe for masks unsafe { - Self::from_int_unchecked(self.to_int().shift_elements_left::(if padding { + Self::from_int_unchecked(self.to_simd().shift_elements_left::(if padding { T::TRUE } else { T::FALSE @@ -569,7 +569,7 @@ where pub fn shift_elements_right(self, padding: bool) -> Self { // Safety: swizzles are safe for masks unsafe { - Self::from_int_unchecked(self.to_int().shift_elements_right::(if padding { + Self::from_int_unchecked(self.to_simd().shift_elements_right::(if padding { T::TRUE } else { T::FALSE @@ -598,7 +598,7 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn interleave(self, other: Self) -> (Self, Self) { - let (lo, hi) = self.to_int().interleave(other.to_int()); + let (lo, hi) = self.to_simd().interleave(other.to_simd()); // Safety: swizzles are safe for masks unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } } @@ -627,7 +627,7 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn deinterleave(self, other: Self) -> (Self, Self) { - let (even, odd) = self.to_int().deinterleave(other.to_int()); + let (even, odd) = self.to_simd().deinterleave(other.to_simd()); // Safety: swizzles are safe for masks unsafe { ( @@ -659,7 +659,7 @@ where { // Safety: swizzles are safe for masks unsafe { - Mask::::from_int_unchecked(self.to_int().resize::(if value { + Mask::::from_int_unchecked(self.to_simd().resize::(if value { T::TRUE } else { T::FALSE @@ -684,6 +684,6 @@ where LaneCount: SupportedLaneCount, { // Safety: swizzles are safe for masks - unsafe { Mask::::from_int_unchecked(self.to_int().extract::()) } + unsafe { Mask::::from_int_unchecked(self.to_simd().extract::()) } } } diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 984a356c6552b..1f15c9f85c715 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -474,7 +474,7 @@ where or: Self, ) -> Self { // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. - unsafe { core::intrinsics::simd::simd_masked_load(enable.to_int(), ptr, or) } + unsafe { core::intrinsics::simd::simd_masked_load(enable.to_simd(), ptr, or) } } /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. @@ -652,7 +652,7 @@ where or: Self, ) -> Self { // Safety: The caller is responsible for upholding all invariants - unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) } + unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_simd()) } } /// Conditionally write contiguous elements to `slice`. The `enable` mask controls @@ -723,7 +723,7 @@ where #[inline] pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<::Mask, N>) { // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. - unsafe { core::intrinsics::simd::simd_masked_store(enable.to_int(), ptr, self) } + unsafe { core::intrinsics::simd::simd_masked_store(enable.to_simd(), ptr, self) } } /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. @@ -882,7 +882,7 @@ where #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask) { // Safety: The caller is responsible for upholding all invariants - unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_int()) } + unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_simd()) } } } diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index 48786d02440b3..be7c5a27e86f1 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -65,7 +65,7 @@ macro_rules! test_mask_api { fn roundtrip_int_conversion() { let values = [true, false, false, true, false, false, true, false]; let mask = Mask::<$type, 8>::from_array(values); - let int = mask.to_int(); + let int = mask.to_simd(); assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]); assert_eq!(Mask::<$type, 8>::from_int(int), mask); } From 42409f05bf162f4fac8e7010892c08539ccfa034 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:30:44 -0400 Subject: [PATCH 21/43] Rename `Mask::from_int` to `Mask::from_simd` --- crates/core_simd/src/masks.rs | 2 +- crates/core_simd/tests/masks.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index b42cca7c977d5..022003ef4dcc6 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -204,7 +204,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] #[track_caller] - pub fn from_int(value: Simd) -> Self { + pub fn from_simd(value: Simd) -> Self { assert!(T::valid(value), "all values must be either 0 or -1",); // Safety: the validity has been checked unsafe { Self::from_int_unchecked(value) } diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index be7c5a27e86f1..53fb2367b6055 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -67,7 +67,7 @@ macro_rules! test_mask_api { let mask = Mask::<$type, 8>::from_array(values); let int = mask.to_simd(); assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]); - assert_eq!(Mask::<$type, 8>::from_int(int), mask); + assert_eq!(Mask::<$type, 8>::from_simd(int), mask); } #[test] From 3a70dd6a69e0eb4b7d72e812cf32204ba2e7669c Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:32:40 -0400 Subject: [PATCH 22/43] Rename `Mask::from_int_unchecked` to `Mask::from_simd_unchecked` --- crates/core_simd/src/masks.rs | 8 ++++---- crates/core_simd/src/masks/bitmask.rs | 2 +- crates/core_simd/src/masks/full_masks.rs | 4 ++-- crates/core_simd/src/simd/cmp/eq.rs | 8 ++++---- crates/core_simd/src/simd/cmp/ord.rs | 24 +++++++++++----------- crates/core_simd/src/swizzle.rs | 26 +++++++++++++----------- crates/core_simd/src/vector.rs | 4 ++-- 7 files changed, 39 insertions(+), 37 deletions(-) diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index 022003ef4dcc6..ca1e3db8b46f0 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -157,7 +157,7 @@ where let bytes: [u8; N] = mem::transmute_copy(&array); let bools: Simd = core::intrinsics::simd::simd_ne(Simd::from_array(bytes), Simd::splat(0u8)); - Mask::from_int_unchecked(core::intrinsics::simd::simd_cast(bools)) + Mask::from_simd_unchecked(core::intrinsics::simd::simd_cast(bools)) } } @@ -188,11 +188,11 @@ where /// All elements must be either 0 or -1. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub unsafe fn from_simd_unchecked(value: Simd) -> Self { // Safety: the caller must confirm this invariant unsafe { core::intrinsics::assume(::valid(value)); - Self(mask_impl::Mask::from_int_unchecked(value)) + Self(mask_impl::Mask::from_simd_unchecked(value)) } } @@ -207,7 +207,7 @@ where pub fn from_simd(value: Simd) -> Self { assert!(T::valid(value), "all values must be either 0 or -1",); // Safety: the validity has been checked - unsafe { Self::from_int_unchecked(value) } + unsafe { Self::from_simd_unchecked(value) } } /// Converts the mask to a vector of integers, where 0 represents `false` and -1 diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index 4211789da7085..83ee88c372ab0 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -117,7 +117,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { + pub(crate) unsafe fn from_simd_unchecked(value: Simd) -> Self { unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } } diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index a5382f0ff739e..5ad2c1d1eaf6d 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -126,7 +126,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { + pub(crate) unsafe fn from_simd_unchecked(value: Simd) -> Self { Self(value) } @@ -180,7 +180,7 @@ where }; // SAFETY: `mask` only contains `T::TRUE` or `T::FALSE` - unsafe { Self::from_int_unchecked(mask.resize::(T::FALSE)) } + unsafe { Self::from_simd_unchecked(mask.resize::(T::FALSE)) } } #[inline] diff --git a/crates/core_simd/src/simd/cmp/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs index 3c483f3f94a33..789fc0bb94242 100644 --- a/crates/core_simd/src/simd/cmp/eq.rs +++ b/crates/core_simd/src/simd/cmp/eq.rs @@ -30,14 +30,14 @@ macro_rules! impl_number { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_eq(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_eq(self, other)) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ne(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ne(self, other)) } } } )* @@ -59,14 +59,14 @@ macro_rules! impl_mask { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_eq(self.to_simd(), other.to_simd())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_eq(self.to_simd(), other.to_simd())) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ne(self.to_simd(), other.to_simd())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_ne(self.to_simd(), other.to_simd())) } } } )* diff --git a/crates/core_simd/src/simd/cmp/ord.rs b/crates/core_simd/src/simd/cmp/ord.rs index 6eb3e360bdeb6..4b2d0b55feba3 100644 --- a/crates/core_simd/src/simd/cmp/ord.rs +++ b/crates/core_simd/src/simd/cmp/ord.rs @@ -56,28 +56,28 @@ macro_rules! impl_integer { fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_lt(self, other)) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_le(self, other)) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_gt(self, other)) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ge(self, other)) } } } @@ -122,28 +122,28 @@ macro_rules! impl_float { fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_lt(self, other)) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_le(self, other)) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_gt(self, other)) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ge(self, other)) } } } )* @@ -163,28 +163,28 @@ macro_rules! impl_mask { fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_lt(self.to_simd(), other.to_simd())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_lt(self.to_simd(), other.to_simd())) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_le(self.to_simd(), other.to_simd())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_le(self.to_simd(), other.to_simd())) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_gt(self.to_simd(), other.to_simd())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_gt(self.to_simd(), other.to_simd())) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ge(self.to_simd(), other.to_simd())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_ge(self.to_simd(), other.to_simd())) } } } diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index b6af2ded1cce0..81085a9ee4a3e 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -165,7 +165,7 @@ pub trait Swizzle { LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask - unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_simd())) } + unsafe { Mask::from_simd_unchecked(Self::swizzle(mask.to_simd())) } } /// Creates a new mask from the elements of `first` and `second`. @@ -181,7 +181,9 @@ pub trait Swizzle { LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask - unsafe { Mask::from_int_unchecked(Self::concat_swizzle(first.to_simd(), second.to_simd())) } + unsafe { + Mask::from_simd_unchecked(Self::concat_swizzle(first.to_simd(), second.to_simd())) + } } } @@ -524,7 +526,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn reverse(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_simd().reverse()) } + unsafe { Self::from_simd_unchecked(self.to_simd().reverse()) } } /// Rotates the mask such that the first `OFFSET` elements of the slice move to the end @@ -534,7 +536,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_left(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_simd().rotate_elements_left::()) } + unsafe { Self::from_simd_unchecked(self.to_simd().rotate_elements_left::()) } } /// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to @@ -544,7 +546,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_right(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_simd().rotate_elements_right::()) } + unsafe { Self::from_simd_unchecked(self.to_simd().rotate_elements_right::()) } } /// Shifts the mask elements to the left by `OFFSET`, filling in with @@ -554,7 +556,7 @@ where pub fn shift_elements_left(self, padding: bool) -> Self { // Safety: swizzles are safe for masks unsafe { - Self::from_int_unchecked(self.to_simd().shift_elements_left::(if padding { + Self::from_simd_unchecked(self.to_simd().shift_elements_left::(if padding { T::TRUE } else { T::FALSE @@ -569,7 +571,7 @@ where pub fn shift_elements_right(self, padding: bool) -> Self { // Safety: swizzles are safe for masks unsafe { - Self::from_int_unchecked(self.to_simd().shift_elements_right::(if padding { + Self::from_simd_unchecked(self.to_simd().shift_elements_right::(if padding { T::TRUE } else { T::FALSE @@ -600,7 +602,7 @@ where pub fn interleave(self, other: Self) -> (Self, Self) { let (lo, hi) = self.to_simd().interleave(other.to_simd()); // Safety: swizzles are safe for masks - unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } + unsafe { (Self::from_simd_unchecked(lo), Self::from_simd_unchecked(hi)) } } /// Deinterleave two masks. @@ -631,8 +633,8 @@ where // Safety: swizzles are safe for masks unsafe { ( - Self::from_int_unchecked(even), - Self::from_int_unchecked(odd), + Self::from_simd_unchecked(even), + Self::from_simd_unchecked(odd), ) } } @@ -659,7 +661,7 @@ where { // Safety: swizzles are safe for masks unsafe { - Mask::::from_int_unchecked(self.to_simd().resize::(if value { + Mask::::from_simd_unchecked(self.to_simd().resize::(if value { T::TRUE } else { T::FALSE @@ -684,6 +686,6 @@ where LaneCount: SupportedLaneCount, { // Safety: swizzles are safe for masks - unsafe { Mask::::from_int_unchecked(self.to_simd().extract::()) } + unsafe { Mask::::from_simd_unchecked(self.to_simd().extract::()) } } } diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 1f15c9f85c715..c00cfcdd41ffa 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -926,7 +926,7 @@ where let mask = unsafe { let tfvec: Simd<::Mask, N> = core::intrinsics::simd::simd_eq(*self, *other); - Mask::from_int_unchecked(tfvec) + Mask::from_simd_unchecked(tfvec) }; // Two vectors are equal if all elements are equal when compared elementwise @@ -940,7 +940,7 @@ where let mask = unsafe { let tfvec: Simd<::Mask, N> = core::intrinsics::simd::simd_ne(*self, *other); - Mask::from_int_unchecked(tfvec) + Mask::from_simd_unchecked(tfvec) }; // Two vectors are non-equal if any elements are non-equal when compared elementwise From 32ba8edf03c233640569c1f9a0bf4529c3a6608c Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Fri, 12 Sep 2025 22:03:51 -0400 Subject: [PATCH 23/43] Remove poor-performing bitmasks, add Select trait, and enable select on integer bitmasks (#482) --- crates/core_simd/src/masks.rs | 123 +++++++--- crates/core_simd/src/masks/bitmask.rs | 231 ------------------ crates/core_simd/src/masks/full_masks.rs | 297 ----------------------- crates/core_simd/src/mod.rs | 1 + crates/core_simd/src/ops.rs | 2 +- crates/core_simd/src/select.rs | 195 +++++++++++---- crates/core_simd/src/simd/cmp/ord.rs | 6 +- crates/core_simd/src/simd/num/float.rs | 2 +- crates/core_simd/src/simd/num/int.rs | 2 +- crates/core_simd/src/swizzle_dyn.rs | 4 +- 10 files changed, 246 insertions(+), 617 deletions(-) delete mode 100644 crates/core_simd/src/masks/bitmask.rs delete mode 100644 crates/core_simd/src/masks/full_masks.rs diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index ca1e3db8b46f0..7baa96475910a 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -2,20 +2,33 @@ //! Types representing #![allow(non_camel_case_types)] -#[cfg_attr( - not(all(target_arch = "x86_64", target_feature = "avx512f")), - path = "masks/full_masks.rs" -)] -#[cfg_attr( - all(target_arch = "x86_64", target_feature = "avx512f"), - path = "masks/bitmask.rs" -)] -mod mask_impl; - -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{LaneCount, Select, Simd, SimdCast, SimdElement, SupportedLaneCount}; use core::cmp::Ordering; use core::{fmt, mem}; +pub(crate) trait FixEndianness { + fn fix_endianness(self) -> Self; +} + +macro_rules! impl_fix_endianness { + { $($int:ty),* } => { + $( + impl FixEndianness for $int { + #[inline(always)] + fn fix_endianness(self) -> Self { + if cfg!(target_endian = "big") { + <$int>::reverse_bits(self) + } else { + self + } + } + } + )* + } +} + +impl_fix_endianness! { u8, u16, u32, u64 } + mod sealed { use super::*; @@ -109,7 +122,7 @@ impl_element! { isize, usize } /// and/or Rust versions, and code should not assume that it is equivalent to /// `[T; N]`. #[repr(transparent)] -pub struct Mask(mask_impl::Mask) +pub struct Mask(Simd) where T: MaskElement, LaneCount: SupportedLaneCount; @@ -141,7 +154,7 @@ where #[inline] #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] pub const fn splat(value: bool) -> Self { - Self(mask_impl::Mask::splat(value)) + Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) } /// Converts an array of bools to a SIMD mask. @@ -192,8 +205,8 @@ where // Safety: the caller must confirm this invariant unsafe { core::intrinsics::assume(::valid(value)); - Self(mask_impl::Mask::from_simd_unchecked(value)) } + Self(value) } /// Converts a vector of integers to a mask, where 0 represents `false` and -1 @@ -215,14 +228,15 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] pub fn to_simd(self) -> Simd { - self.0.to_simd() + self.0 } /// Converts the mask to a mask of any other element size. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn cast(self) -> Mask { - Mask(self.0.convert()) + // Safety: mask elements are integers + unsafe { Mask(core::intrinsics::simd::simd_as(self.0)) } } /// Tests the value of the specified element. @@ -233,7 +247,7 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] pub unsafe fn test_unchecked(&self, index: usize) -> bool { // Safety: the caller must confirm this invariant - unsafe { self.0.test_unchecked(index) } + unsafe { T::eq(*self.0.as_array().get_unchecked(index), T::TRUE) } } /// Tests the value of the specified element. @@ -244,9 +258,7 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] #[track_caller] pub fn test(&self, index: usize) -> bool { - assert!(index < N, "element index out of range"); - // Safety: the element index has been checked - unsafe { self.test_unchecked(index) } + T::eq(self.0[index], T::TRUE) } /// Sets the value of the specified element. @@ -257,7 +269,7 @@ where pub unsafe fn set_unchecked(&mut self, index: usize, value: bool) { // Safety: the caller must confirm this invariant unsafe { - self.0.set_unchecked(index, value); + *self.0.as_mut_array().get_unchecked_mut(index) = if value { T::TRUE } else { T::FALSE } } } @@ -268,35 +280,67 @@ where #[inline] #[track_caller] pub fn set(&mut self, index: usize, value: bool) { - assert!(index < N, "element index out of range"); - // Safety: the element index has been checked - unsafe { - self.set_unchecked(index, value); - } + self.0[index] = if value { T::TRUE } else { T::FALSE } } /// Returns true if any element is set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn any(self) -> bool { - self.0.any() + // Safety: `self` is a mask vector + unsafe { core::intrinsics::simd::simd_reduce_any(self.0) } } /// Returns true if all elements are set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn all(self) -> bool { - self.0.all() + // Safety: `self` is a mask vector + unsafe { core::intrinsics::simd::simd_reduce_all(self.0) } } /// Creates a bitmask from a mask. /// /// Each bit is set if the corresponding element in the mask is `true`. - /// If the mask contains more than 64 elements, the bitmask is truncated to the first 64. #[inline] #[must_use = "method returns a new integer and does not mutate the original value"] pub fn to_bitmask(self) -> u64 { - self.0.to_bitmask_integer() + const { + assert!(N <= 64, "number of elements can't be greater than 64"); + } + + #[inline] + unsafe fn to_bitmask_impl( + mask: Mask, + ) -> U + where + T: MaskElement, + LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, + { + let resized = mask.resize::(false); + + // Safety: `resized` is an integer vector with length M, which must match T + let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized.0) }; + + // LLVM assumes bit order should match endianness + bitmask.fix_endianness() + } + + // TODO modify simd_bitmask to zero-extend output, making this unnecessary + if N <= 8 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else if N <= 16 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else if N <= 32 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) } + } } /// Creates a mask from a bitmask. @@ -306,7 +350,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn from_bitmask(bitmask: u64) -> Self { - Self(mask_impl::Mask::from_bitmask_integer(bitmask)) + Self(bitmask.select(Simd::splat(T::TRUE), Simd::splat(T::FALSE))) } /// Finds the index of the first set element. @@ -450,7 +494,8 @@ where type Output = Self; #[inline] fn bitand(self, rhs: Self) -> Self { - Self(self.0 & rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) } } } @@ -486,7 +531,8 @@ where type Output = Self; #[inline] fn bitor(self, rhs: Self) -> Self { - Self(self.0 | rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) } } } @@ -522,7 +568,8 @@ where type Output = Self; #[inline] fn bitxor(self, rhs: Self) -> Self::Output { - Self(self.0 ^ rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) } } } @@ -558,7 +605,7 @@ where type Output = Mask; #[inline] fn not(self) -> Self::Output { - Self(!self.0) + Self::splat(true) ^ self } } @@ -569,7 +616,7 @@ where { #[inline] fn bitand_assign(&mut self, rhs: Self) { - self.0 = self.0 & rhs.0; + *self = *self & rhs; } } @@ -591,7 +638,7 @@ where { #[inline] fn bitor_assign(&mut self, rhs: Self) { - self.0 = self.0 | rhs.0; + *self = *self | rhs; } } @@ -613,7 +660,7 @@ where { #[inline] fn bitxor_assign(&mut self, rhs: Self) { - self.0 = self.0 ^ rhs.0; + *self = *self ^ rhs; } } diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs deleted file mode 100644 index 83ee88c372ab0..0000000000000 --- a/crates/core_simd/src/masks/bitmask.rs +++ /dev/null @@ -1,231 +0,0 @@ -#![allow(unused_imports)] -use super::MaskElement; -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; -use core::marker::PhantomData; - -/// A mask where each lane is represented by a single bit. -#[repr(transparent)] -pub(crate) struct Mask( - as SupportedLaneCount>::BitMask, - PhantomData, -) -where - T: MaskElement, - LaneCount: SupportedLaneCount; - -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Clone for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl PartialEq for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0.as_ref() == other.0.as_ref() - } -} - -impl PartialOrd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.as_ref().partial_cmp(other.0.as_ref()) - } -} - -impl Eq for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Ord for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.as_ref().cmp(other.0.as_ref()) - } -} - -impl Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] - pub(crate) const fn splat(value: bool) -> Self { - Self( - if value { - as SupportedLaneCount>::FULL_BIT_MASK - } else { - as SupportedLaneCount>::EMPTY_BIT_MASK - }, - PhantomData, - ) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { - (self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0 - } - - #[inline] - pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { - unsafe { - self.0.as_mut()[lane / 8] ^= ((value ^ self.test_unchecked(lane)) as u8) << (lane % 8) - } - } - - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub(crate) fn to_simd(self) -> Simd { - unsafe { - core::intrinsics::simd::simd_select_bitmask( - self.0, - Simd::splat(T::TRUE), - Simd::splat(T::FALSE), - ) - } - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) unsafe fn from_simd_unchecked(value: Simd) -> Self { - unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } - } - - #[inline] - pub(crate) fn to_bitmask_integer(self) -> u64 { - let mut bitmask = [0u8; 8]; - bitmask[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); - u64::from_ne_bytes(bitmask) - } - - #[inline] - pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { - let mut bytes = as SupportedLaneCount>::EMPTY_BIT_MASK; - let len = bytes.as_mut().len(); - bytes - .as_mut() - .copy_from_slice(&bitmask.to_ne_bytes()[..len]); - Self(bytes, PhantomData) - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn convert(self) -> Mask - where - U: MaskElement, - { - // Safety: bitmask layout does not depend on the element width - unsafe { core::mem::transmute_copy(&self) } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn any(self) -> bool { - self != Self::splat(false) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn all(self) -> bool { - self == Self::splat(true) - } -} - -impl core::ops::BitAnd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, - as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, -{ - type Output = Self; - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - fn bitand(mut self, rhs: Self) -> Self { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l &= r; - } - self - } -} - -impl core::ops::BitOr for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, - as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, -{ - type Output = Self; - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - fn bitor(mut self, rhs: Self) -> Self { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l |= r; - } - self - } -} - -impl core::ops::BitXor for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - fn bitxor(mut self, rhs: Self) -> Self::Output { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l ^= r; - } - self - } -} - -impl core::ops::Not for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - fn not(mut self) -> Self::Output { - for x in self.0.as_mut() { - *x = !*x; - } - if N % 8 > 0 { - *self.0.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8); - } - self - } -} diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs deleted file mode 100644 index 5ad2c1d1eaf6d..0000000000000 --- a/crates/core_simd/src/masks/full_masks.rs +++ /dev/null @@ -1,297 +0,0 @@ -//! Masks that take up full SIMD vector registers. - -use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount}; - -#[repr(transparent)] -pub(crate) struct Mask(Simd) -where - T: MaskElement, - LaneCount: SupportedLaneCount; - -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Clone for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl PartialEq for Mask -where - T: MaskElement + PartialEq, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -impl PartialOrd for Mask -where - T: MaskElement + PartialOrd, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Eq for Mask -where - T: MaskElement + Eq, - LaneCount: SupportedLaneCount, -{ -} - -impl Ord for Mask -where - T: MaskElement + Ord, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -// Used for bitmask bit order workaround -pub(crate) trait ReverseBits { - // Reverse the least significant `n` bits of `self`. - // (Remaining bits must be 0.) - fn reverse_bits(self, n: usize) -> Self; -} - -macro_rules! impl_reverse_bits { - { $($int:ty),* } => { - $( - impl ReverseBits for $int { - #[inline(always)] - fn reverse_bits(self, n: usize) -> Self { - let rev = <$int>::reverse_bits(self); - let bitsize = size_of::<$int>() * 8; - if n < bitsize { - // Shift things back to the right - rev >> (bitsize - n) - } else { - rev - } - } - } - )* - } -} - -impl_reverse_bits! { u8, u16, u32, u64 } - -impl Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] - pub(crate) const fn splat(value: bool) -> Self { - Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { - T::eq(self.0[lane], T::TRUE) - } - - #[inline] - pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { - self.0[lane] = if value { T::TRUE } else { T::FALSE } - } - - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub(crate) fn to_simd(self) -> Simd { - self.0 - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) unsafe fn from_simd_unchecked(value: Simd) -> Self { - Self(value) - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn convert(self) -> Mask - where - U: MaskElement, - { - // Safety: masks are simply integer vectors of 0 and -1, and we can cast the element type. - unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) } - } - - #[inline] - unsafe fn to_bitmask_impl(self) -> U - where - LaneCount: SupportedLaneCount, - { - let resized = self.to_simd().resize::(T::FALSE); - - // Safety: `resized` is an integer vector with length M, which must match T - let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized) }; - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - bitmask.reverse_bits(M) - } else { - bitmask - } - } - - #[inline] - unsafe fn from_bitmask_impl(bitmask: U) -> Self - where - LaneCount: SupportedLaneCount, - { - // LLVM assumes bit order should match endianness - let bitmask = if cfg!(target_endian = "big") { - bitmask.reverse_bits(M) - } else { - bitmask - }; - - // SAFETY: `mask` is the correct bitmask type for a u64 bitmask - let mask: Simd = unsafe { - core::intrinsics::simd::simd_select_bitmask( - bitmask, - Simd::::splat(T::TRUE), - Simd::::splat(T::FALSE), - ) - }; - - // SAFETY: `mask` only contains `T::TRUE` or `T::FALSE` - unsafe { Self::from_simd_unchecked(mask.resize::(T::FALSE)) } - } - - #[inline] - pub(crate) fn to_bitmask_integer(self) -> u64 { - // TODO modify simd_bitmask to zero-extend output, making this unnecessary - if N <= 8 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else if N <= 16 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else if N <= 32 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() } - } - } - - #[inline] - pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { - // TODO modify simd_bitmask_select to truncate input, making this unnecessary - if N <= 8 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u8) } - } else if N <= 16 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u16) } - } else if N <= 32 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u32) } - } else { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask) } - } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn any(self) -> bool { - // Safety: use `self` as an integer vector - unsafe { core::intrinsics::simd::simd_reduce_any(self.to_simd()) } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn all(self) -> bool { - // Safety: use `self` as an integer vector - unsafe { core::intrinsics::simd::simd_reduce_all(self.to_simd()) } - } -} - -impl From> for Simd -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn from(value: Mask) -> Self { - value.0 - } -} - -impl core::ops::BitAnd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitand(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) } - } -} - -impl core::ops::BitOr for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitor(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) } - } -} - -impl core::ops::BitXor for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitxor(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) } - } -} - -impl core::ops::Not for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn not(self) -> Self::Output { - Self::splat(true) ^ self - } -} diff --git a/crates/core_simd/src/mod.rs b/crates/core_simd/src/mod.rs index 45b1a0f975141..14fe70df4ed11 100644 --- a/crates/core_simd/src/mod.rs +++ b/crates/core_simd/src/mod.rs @@ -29,6 +29,7 @@ pub mod simd { pub use crate::core_simd::cast::*; pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount}; pub use crate::core_simd::masks::*; + pub use crate::core_simd::select::*; pub use crate::core_simd::swizzle::*; pub use crate::core_simd::to_bytes::ToBytes; pub use crate::core_simd::vector::*; diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index f36e8d01a73bb..f36e360fadf94 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount, cmp::SimdPartialEq}; +use crate::simd::{LaneCount, Select, Simd, SimdElement, SupportedLaneCount, cmp::SimdPartialEq}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; use core::ops::{Div, Rem, Sub}; diff --git a/crates/core_simd/src/select.rs b/crates/core_simd/src/select.rs index a2db455a52685..5240b9b0c7165 100644 --- a/crates/core_simd/src/select.rs +++ b/crates/core_simd/src/select.rs @@ -1,54 +1,163 @@ -use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{ + FixEndianness, LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount, +}; -impl Mask +/// Choose elements from two vectors using a mask. +/// +/// For each element in the mask, choose the corresponding element from `true_values` if +/// that element mask is true, and `false_values` if that element mask is false. +/// +/// If the mask is `u64`, it's treated as a bitmask with the least significant bit +/// corresponding to the first element. +/// +/// # Examples +/// +/// ## Selecting values from `Simd` +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Simd, Mask, Select}; +/// let a = Simd::from_array([0, 1, 2, 3]); +/// let b = Simd::from_array([4, 5, 6, 7]); +/// let mask = Mask::::from_array([true, false, false, true]); +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [0, 5, 6, 3]); +/// ``` +/// +/// ## Selecting values from `Mask` +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Mask, Select}; +/// let a = Mask::::from_array([true, true, false, false]); +/// let b = Mask::::from_array([false, false, true, true]); +/// let mask = Mask::::from_array([true, false, false, true]); +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [true, false, true, false]); +/// ``` +/// +/// ## Selecting with a bitmask +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Mask, Select}; +/// let a = Mask::::from_array([true, true, false, false]); +/// let b = Mask::::from_array([false, false, true, true]); +/// let mask = 0b1001; +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [true, false, true, false]); +/// ``` +pub trait Select { + /// Choose elements + fn select(self, true_values: T, false_values: T) -> T; +} + +impl Select> for Mask +where + T: SimdElement, + U: MaskElement, + LaneCount: SupportedLaneCount, +{ + #[inline] + fn select(self, true_values: Simd, false_values: Simd) -> Simd { + // Safety: + // simd_as between masks is always safe (they're vectors of ints). + // simd_select uses a mask that matches the width and number of elements + unsafe { + let mask: Simd = core::intrinsics::simd::simd_as(self.to_simd()); + core::intrinsics::simd::simd_select(mask, true_values, false_values) + } + } +} + +impl Select> for u64 +where + T: SimdElement, + LaneCount: SupportedLaneCount, +{ + #[inline] + fn select(self, true_values: Simd, false_values: Simd) -> Simd { + const { + assert!(N <= 64, "number of elements can't be greater than 64"); + } + + #[inline] + unsafe fn select_impl( + bitmask: U, + true_values: Simd, + false_values: Simd, + ) -> Simd + where + T: SimdElement, + LaneCount: SupportedLaneCount, + LaneCount: SupportedLaneCount, + { + let default = true_values[0]; + let true_values = true_values.resize::(default); + let false_values = false_values.resize::(default); + + // LLVM assumes bit order should match endianness + let bitmask = bitmask.fix_endianness(); + + // Safety: the caller guarantees that the size of U matches M + let selected = unsafe { + core::intrinsics::simd::simd_select_bitmask(bitmask, true_values, false_values) + }; + + selected.resize::(default) + } + + // TODO modify simd_bitmask_select to truncate input, making this unnecessary + if N <= 8 { + let bitmask = self as u8; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else if N <= 16 { + let bitmask = self as u16; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else if N <= 32 { + let bitmask = self as u32; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else { + let bitmask = self; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } + } +} + +impl Select> for Mask where T: MaskElement, + U: MaskElement, LaneCount: SupportedLaneCount, { - /// Choose elements from two vectors. - /// - /// For each element in the mask, choose the corresponding element from `true_values` if - /// that element mask is true, and `false_values` if that element mask is false. - /// - /// # Examples - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::{Simd, Mask}; - /// let a = Simd::from_array([0, 1, 2, 3]); - /// let b = Simd::from_array([4, 5, 6, 7]); - /// let mask = Mask::from_array([true, false, false, true]); - /// let c = mask.select(a, b); - /// assert_eq!(c.to_array(), [0, 5, 6, 3]); - /// ``` #[inline] - #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn select(self, true_values: Simd, false_values: Simd) -> Simd - where - U: SimdElement, - { - // Safety: The mask has been cast to a vector of integers, - // and the operands to select between are vectors of the same type and length. - unsafe { core::intrinsics::simd::simd_select(self.to_simd(), true_values, false_values) } + fn select(self, true_values: Mask, false_values: Mask) -> Mask { + let selected: Simd = + Select::select(self, true_values.to_simd(), false_values.to_simd()); + + // Safety: all values come from masks + unsafe { Mask::from_simd_unchecked(selected) } } +} - /// Choose elements from two masks. - /// - /// For each element in the mask, choose the corresponding element from `true_values` if - /// that element mask is true, and `false_values` if that element mask is false. - /// - /// # Examples - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::Mask; - /// let a = Mask::::from_array([true, true, false, false]); - /// let b = Mask::::from_array([false, false, true, true]); - /// let mask = Mask::::from_array([true, false, false, true]); - /// let c = mask.select_mask(a, b); - /// assert_eq!(c.to_array(), [true, false, true, false]); - /// ``` +impl Select> for u64 +where + T: MaskElement, + LaneCount: SupportedLaneCount, +{ #[inline] - #[must_use = "method returns a new mask and does not mutate the original inputs"] - pub fn select_mask(self, true_values: Self, false_values: Self) -> Self { - self & true_values | !self & false_values + fn select(self, true_values: Mask, false_values: Mask) -> Mask { + let selected: Simd = + Select::select(self, true_values.to_simd(), false_values.to_simd()); + + // Safety: all values come from masks + unsafe { Mask::from_simd_unchecked(selected) } } } diff --git a/crates/core_simd/src/simd/cmp/ord.rs b/crates/core_simd/src/simd/cmp/ord.rs index 4b2d0b55feba3..1b1c689ad4581 100644 --- a/crates/core_simd/src/simd/cmp/ord.rs +++ b/crates/core_simd/src/simd/cmp/ord.rs @@ -1,5 +1,5 @@ use crate::simd::{ - LaneCount, Mask, Simd, SupportedLaneCount, + LaneCount, Mask, Select, Simd, SupportedLaneCount, cmp::SimdPartialEq, ptr::{SimdConstPtr, SimdMutPtr}, }; @@ -194,12 +194,12 @@ macro_rules! impl_mask { { #[inline] fn simd_max(self, other: Self) -> Self { - self.simd_gt(other).select_mask(other, self) + self.simd_gt(other).select(other, self) } #[inline] fn simd_min(self, other: Self) -> Self { - self.simd_lt(other).select_mask(other, self) + self.simd_lt(other).select(other, self) } #[inline] diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index b5972c47373bb..76ab5748c6384 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, + LaneCount, Mask, Select, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::{SimdPartialEq, SimdPartialOrd}, }; diff --git a/crates/core_simd/src/simd/num/int.rs b/crates/core_simd/src/simd/num/int.rs index d25050c3e4b47..5a292407d0508 100644 --- a/crates/core_simd/src/simd/num/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd, + LaneCount, Mask, Select, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, }; diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index 773bd028bae09..73b18595d0a1c 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -139,7 +139,7 @@ unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd, idxs: Simd) -> S #[inline] #[allow(clippy::let_and_return)] unsafe fn avx2_pshufb(bytes: Simd, idxs: Simd) -> Simd { - use crate::simd::cmp::SimdPartialOrd; + use crate::simd::{Select, cmp::SimdPartialOrd}; #[cfg(target_arch = "x86")] use core::arch::x86; #[cfg(target_arch = "x86_64")] @@ -200,7 +200,7 @@ fn zeroing_idxs(idxs: Simd) -> Simd where LaneCount: SupportedLaneCount, { - use crate::simd::cmp::SimdPartialOrd; + use crate::simd::{Select, cmp::SimdPartialOrd}; idxs.simd_lt(Simd::splat(N as u8)) .select(idxs, Simd::splat(u8::MAX)) } From 8ce88bcb509e1f7d7b6b76724a6fcb827d9a78ac Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 7 Oct 2025 10:16:49 -0400 Subject: [PATCH 24/43] Remove LaneCount in favor of #[rustc_simd_monomorphize_lane_limit] attribute (#485) * Remove LaneCount in favor of #[rustc_simd_monomorphize_lane_limit] attribute * Document allowed vector lengths --- crates/core_simd/src/fmt.rs | 3 +- crates/core_simd/src/iter.rs | 10 +---- crates/core_simd/src/lane_count.rs | 52 ---------------------- crates/core_simd/src/lib.rs | 3 +- crates/core_simd/src/masks.rs | 46 +++---------------- crates/core_simd/src/mod.rs | 2 - crates/core_simd/src/ops.rs | 5 +-- crates/core_simd/src/ops/assign.rs | 1 - crates/core_simd/src/ops/deref.rs | 3 -- crates/core_simd/src/ops/shift_scalar.rs | 10 +---- crates/core_simd/src/ops/unary.rs | 4 +- crates/core_simd/src/select.rs | 10 +---- crates/core_simd/src/simd/cmp/eq.rs | 16 ++----- crates/core_simd/src/simd/cmp/ord.rs | 32 +++---------- crates/core_simd/src/simd/num/float.rs | 10 +---- crates/core_simd/src/simd/num/int.rs | 14 ++---- crates/core_simd/src/simd/num/uint.rs | 10 +---- crates/core_simd/src/simd/ptr/const_ptr.rs | 9 ++-- crates/core_simd/src/simd/ptr/mut_ptr.rs | 9 ++-- crates/core_simd/src/swizzle.rs | 32 +++---------- crates/core_simd/src/swizzle_dyn.rs | 17 ++----- crates/core_simd/src/to_bytes.rs | 4 +- crates/core_simd/src/vector.rs | 43 +++--------------- crates/std_float/src/lib.rs | 10 ++--- crates/test_helpers/src/lib.rs | 4 -- rust-toolchain.toml | 2 +- 26 files changed, 57 insertions(+), 304 deletions(-) delete mode 100644 crates/core_simd/src/lane_count.rs diff --git a/crates/core_simd/src/fmt.rs b/crates/core_simd/src/fmt.rs index 3a540f5a04908..90c520e75bb39 100644 --- a/crates/core_simd/src/fmt.rs +++ b/crates/core_simd/src/fmt.rs @@ -1,9 +1,8 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Simd, SimdElement}; use core::fmt; impl fmt::Debug for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + fmt::Debug, { /// A `Simd` has a debug format like the one for `[T]`: diff --git a/crates/core_simd/src/iter.rs b/crates/core_simd/src/iter.rs index b3732fd74d5f6..fdc458efeda48 100644 --- a/crates/core_simd/src/iter.rs +++ b/crates/core_simd/src/iter.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; use core::{ iter::{Product, Sum}, ops::{Add, Mul}, @@ -7,8 +7,6 @@ use core::{ macro_rules! impl_traits { { $type:ty } => { impl Sum for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn sum>(iter: I) -> Self { @@ -17,8 +15,6 @@ macro_rules! impl_traits { } impl Product for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn product>(iter: I) -> Self { @@ -27,8 +23,6 @@ macro_rules! impl_traits { } impl<'a, const N: usize> Sum<&'a Self> for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn sum>(iter: I) -> Self { @@ -37,8 +31,6 @@ macro_rules! impl_traits { } impl<'a, const N: usize> Product<&'a Self> for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn product>(iter: I) -> Self { diff --git a/crates/core_simd/src/lane_count.rs b/crates/core_simd/src/lane_count.rs deleted file mode 100644 index 839195c38eda7..0000000000000 --- a/crates/core_simd/src/lane_count.rs +++ /dev/null @@ -1,52 +0,0 @@ -mod sealed { - pub trait Sealed {} -} -use sealed::Sealed; - -/// Specifies the number of lanes in a SIMD vector as a type. -pub struct LaneCount; - -impl LaneCount { - /// The number of bytes in a bitmask with this many lanes. - pub const BITMASK_LEN: usize = N.div_ceil(8); -} - -/// Statically guarantees that a lane count is marked as supported. -/// -/// This trait is *sealed*: the list of implementors below is total. -/// Users do not have the ability to mark additional `LaneCount` values as supported. -/// Only SIMD vectors with supported lane counts are constructable. -pub trait SupportedLaneCount: Sealed { - #[doc(hidden)] - type BitMask: Copy + AsRef<[u8]> + AsMut<[u8]>; - #[doc(hidden)] - const EMPTY_BIT_MASK: Self::BitMask; - #[doc(hidden)] - const FULL_BIT_MASK: Self::BitMask; -} - -impl Sealed for LaneCount {} - -macro_rules! supported_lane_count { - ($($lanes:literal),+) => { - $( - impl SupportedLaneCount for LaneCount<$lanes> { - type BitMask = [u8; Self::BITMASK_LEN]; - const EMPTY_BIT_MASK: Self::BitMask = [0; Self::BITMASK_LEN]; - const FULL_BIT_MASK: Self::BitMask = { - let mut array = [!0u8; Self::BITMASK_LEN]; - if $lanes % 8 > 0 { - array[Self::BITMASK_LEN - 1] = (!0) >> (8 - $lanes % 8); - } - array - }; - } - )+ - }; -} - -supported_lane_count!( - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 -); diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 9d7dfd2ab351d..3e5ebe19e4dbc 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -9,7 +9,8 @@ simd_ffi, staged_api, prelude_import, - ptr_metadata + ptr_metadata, + rustc_attrs )] #![cfg_attr( all( diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index 7baa96475910a..3e2209556b66b 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -2,7 +2,7 @@ //! Types representing #![allow(non_camel_case_types)] -use crate::simd::{LaneCount, Select, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{Select, Simd, SimdCast, SimdElement}; use core::cmp::Ordering; use core::{fmt, mem}; @@ -41,7 +41,6 @@ mod sealed { pub trait Sealed { fn valid(values: Simd) -> bool where - LaneCount: SupportedLaneCount, Self: SimdElement; fn eq(self, other: Self) -> bool; @@ -69,8 +68,6 @@ macro_rules! impl_element { impl Sealed for $ty { #[inline] fn valid(value: Simd) -> bool - where - LaneCount: SupportedLaneCount, { // We can't use `Simd` directly, because `Simd`'s functions call this function and // we will end up with an infinite loop. @@ -121,23 +118,19 @@ impl_element! { isize, usize } /// The layout of this type is unspecified, and may change between platforms /// and/or Rust versions, and code should not assume that it is equivalent to /// `[T; N]`. +/// +/// `N` cannot be 0 and may be at most 64. This limit may be increased in +/// the future. #[repr(transparent)] pub struct Mask(Simd) where - T: MaskElement, - LaneCount: SupportedLaneCount; + T: MaskElement; -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} +impl Copy for Mask where T: MaskElement {} impl Clone for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn clone(&self) -> Self { @@ -148,7 +141,6 @@ where impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { /// Constructs a mask by setting all elements to the given value. #[inline] @@ -315,8 +307,6 @@ where ) -> U where T: MaskElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { let resized = mask.resize::(false); @@ -421,7 +411,6 @@ where impl From<[bool; N]> for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn from(array: [bool; N]) -> Self { @@ -432,7 +421,6 @@ where impl From> for [bool; N] where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn from(vector: Mask) -> Self { @@ -443,7 +431,6 @@ where impl Default for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn default() -> Self { @@ -454,7 +441,6 @@ where impl PartialEq for Mask where T: MaskElement + PartialEq, - LaneCount: SupportedLaneCount, { #[inline] fn eq(&self, other: &Self) -> bool { @@ -465,7 +451,6 @@ where impl PartialOrd for Mask where T: MaskElement + PartialOrd, - LaneCount: SupportedLaneCount, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -476,7 +461,6 @@ where impl fmt::Debug for Mask where T: MaskElement + fmt::Debug, - LaneCount: SupportedLaneCount, { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -489,7 +473,6 @@ where impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -502,7 +485,6 @@ where impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -514,7 +496,6 @@ where impl core::ops::BitAnd> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -526,7 +507,6 @@ where impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -539,7 +519,6 @@ where impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -551,7 +530,6 @@ where impl core::ops::BitOr> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -563,7 +541,6 @@ where impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -576,7 +553,6 @@ where impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -588,7 +564,6 @@ where impl core::ops::BitXor> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -600,7 +575,6 @@ where impl core::ops::Not for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -612,7 +586,6 @@ where impl core::ops::BitAndAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitand_assign(&mut self, rhs: Self) { @@ -623,7 +596,6 @@ where impl core::ops::BitAndAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitand_assign(&mut self, rhs: bool) { @@ -634,7 +606,6 @@ where impl core::ops::BitOrAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitor_assign(&mut self, rhs: Self) { @@ -645,7 +616,6 @@ where impl core::ops::BitOrAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitor_assign(&mut self, rhs: bool) { @@ -656,7 +626,6 @@ where impl core::ops::BitXorAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitxor_assign(&mut self, rhs: Self) { @@ -667,7 +636,6 @@ where impl core::ops::BitXorAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitxor_assign(&mut self, rhs: bool) { @@ -679,8 +647,6 @@ macro_rules! impl_from { { $from:ty => $($to:ty),* } => { $( impl From> for Mask<$to, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn from(value: Mask<$from, N>) -> Self { diff --git a/crates/core_simd/src/mod.rs b/crates/core_simd/src/mod.rs index 14fe70df4ed11..5f635d80a178f 100644 --- a/crates/core_simd/src/mod.rs +++ b/crates/core_simd/src/mod.rs @@ -5,7 +5,6 @@ mod alias; mod cast; mod fmt; mod iter; -mod lane_count; mod masks; mod ops; mod select; @@ -27,7 +26,6 @@ pub mod simd { pub use crate::core_simd::alias::*; pub use crate::core_simd::cast::*; - pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount}; pub use crate::core_simd::masks::*; pub use crate::core_simd::select::*; pub use crate::core_simd::swizzle::*; diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index f36e360fadf94..eb6601f734831 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Select, Simd, SimdElement, SupportedLaneCount, cmp::SimdPartialEq}; +use crate::simd::{Select, Simd, SimdElement, cmp::SimdPartialEq}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; use core::ops::{Div, Rem, Sub}; @@ -12,7 +12,6 @@ mod unary; impl core::ops::Index for Simd where T: SimdElement, - LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { type Output = I::Output; @@ -25,7 +24,6 @@ where impl core::ops::IndexMut for Simd where T: SimdElement, - LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { #[inline] @@ -130,7 +128,6 @@ macro_rules! for_base_types { impl $op for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = $out; diff --git a/crates/core_simd/src/ops/assign.rs b/crates/core_simd/src/ops/assign.rs index d21d867de26d6..c1830c35df778 100644 --- a/crates/core_simd/src/ops/assign.rs +++ b/crates/core_simd/src/ops/assign.rs @@ -21,7 +21,6 @@ macro_rules! assign_ops { where Self: $trait, T: SimdElement, - LaneCount: SupportedLaneCount, { #[inline] fn $assign_call(&mut self, rhs: U) { diff --git a/crates/core_simd/src/ops/deref.rs b/crates/core_simd/src/ops/deref.rs index 913cbbe977c46..360b83c403468 100644 --- a/crates/core_simd/src/ops/deref.rs +++ b/crates/core_simd/src/ops/deref.rs @@ -13,7 +13,6 @@ macro_rules! deref_lhs { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = Simd; @@ -33,7 +32,6 @@ macro_rules! deref_rhs { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = Simd; @@ -64,7 +62,6 @@ macro_rules! deref_ops { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = $simd; diff --git a/crates/core_simd/src/ops/shift_scalar.rs b/crates/core_simd/src/ops/shift_scalar.rs index f5115a5a5e935..7ca83dc40f617 100644 --- a/crates/core_simd/src/ops/shift_scalar.rs +++ b/crates/core_simd/src/ops/shift_scalar.rs @@ -1,13 +1,11 @@ // Shift operations uniquely typically only have a scalar on the right-hand side. // Here, we implement shifts for scalar RHS arguments. -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; macro_rules! impl_splatted_shifts { { impl $trait:ident :: $trait_fn:ident for $ty:ty } => { impl core::ops::$trait<$ty> for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -17,8 +15,6 @@ macro_rules! impl_splatted_shifts { } impl core::ops::$trait<&$ty> for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -28,8 +24,6 @@ macro_rules! impl_splatted_shifts { } impl<'lhs, const N: usize> core::ops::$trait<$ty> for &'lhs Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Simd<$ty, N>; #[inline] @@ -39,8 +33,6 @@ macro_rules! impl_splatted_shifts { } impl<'lhs, const N: usize> core::ops::$trait<&$ty> for &'lhs Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Simd<$ty, N>; #[inline] diff --git a/crates/core_simd/src/ops/unary.rs b/crates/core_simd/src/ops/unary.rs index 412a5b801171b..e1c06167f9790 100644 --- a/crates/core_simd/src/ops/unary.rs +++ b/crates/core_simd/src/ops/unary.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Simd, SimdElement}; use core::ops::{Neg, Not}; // unary ops macro_rules! neg { @@ -6,7 +6,6 @@ macro_rules! neg { $(impl Neg for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = Self; @@ -40,7 +39,6 @@ macro_rules! not { $(impl Not for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = Self; diff --git a/crates/core_simd/src/select.rs b/crates/core_simd/src/select.rs index 5240b9b0c7165..404f54d8f3827 100644 --- a/crates/core_simd/src/select.rs +++ b/crates/core_simd/src/select.rs @@ -1,6 +1,4 @@ -use crate::simd::{ - FixEndianness, LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount, -}; +use crate::simd::{FixEndianness, Mask, MaskElement, Simd, SimdElement}; /// Choose elements from two vectors using a mask. /// @@ -59,7 +57,6 @@ impl Select> for Mask where T: SimdElement, U: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn select(self, true_values: Simd, false_values: Simd) -> Simd { @@ -76,7 +73,6 @@ where impl Select> for u64 where T: SimdElement, - LaneCount: SupportedLaneCount, { #[inline] fn select(self, true_values: Simd, false_values: Simd) -> Simd { @@ -92,8 +88,6 @@ where ) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { let default = true_values[0]; let true_values = true_values.resize::(default); @@ -135,7 +129,6 @@ impl Select> for Mask where T: MaskElement, U: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn select(self, true_values: Mask, false_values: Mask) -> Mask { @@ -150,7 +143,6 @@ where impl Select> for u64 where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn select(self, true_values: Mask, false_values: Mask) -> Mask { diff --git a/crates/core_simd/src/simd/cmp/eq.rs b/crates/core_simd/src/simd/cmp/eq.rs index 789fc0bb94242..d553d6c040c91 100644 --- a/crates/core_simd/src/simd/cmp/eq.rs +++ b/crates/core_simd/src/simd/cmp/eq.rs @@ -1,5 +1,5 @@ use crate::simd::{ - LaneCount, Mask, Simd, SimdElement, SupportedLaneCount, + Mask, Simd, SimdElement, ptr::{SimdConstPtr, SimdMutPtr}, }; @@ -21,8 +21,6 @@ macro_rules! impl_number { { $($number:ty),* } => { $( impl SimdPartialEq for Simd<$number, N> - where - LaneCount: SupportedLaneCount, { type Mask = Mask<<$number as SimdElement>::Mask, N>; @@ -50,8 +48,6 @@ macro_rules! impl_mask { { $($integer:ty),* } => { $( impl SimdPartialEq for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { type Mask = Self; @@ -75,10 +71,7 @@ macro_rules! impl_mask { impl_mask! { i8, i16, i32, i64, isize } -impl SimdPartialEq for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialEq for Simd<*const T, N> { type Mask = Mask; #[inline] @@ -92,10 +85,7 @@ where } } -impl SimdPartialEq for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialEq for Simd<*mut T, N> { type Mask = Mask; #[inline] diff --git a/crates/core_simd/src/simd/cmp/ord.rs b/crates/core_simd/src/simd/cmp/ord.rs index 1b1c689ad4581..5672fbbf54caa 100644 --- a/crates/core_simd/src/simd/cmp/ord.rs +++ b/crates/core_simd/src/simd/cmp/ord.rs @@ -1,5 +1,5 @@ use crate::simd::{ - LaneCount, Mask, Select, Simd, SupportedLaneCount, + Mask, Select, Simd, cmp::SimdPartialEq, ptr::{SimdConstPtr, SimdMutPtr}, }; @@ -49,8 +49,6 @@ macro_rules! impl_integer { { $($integer:ty),* } => { $( impl SimdPartialOrd for Simd<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { @@ -82,8 +80,6 @@ macro_rules! impl_integer { } impl SimdOrd for Simd<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { @@ -115,8 +111,6 @@ macro_rules! impl_float { { $($float:ty),* } => { $( impl SimdPartialOrd for Simd<$float, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { @@ -156,8 +150,6 @@ macro_rules! impl_mask { { $($integer:ty),* } => { $( impl SimdPartialOrd for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { @@ -189,8 +181,6 @@ macro_rules! impl_mask { } impl SimdOrd for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { @@ -218,10 +208,7 @@ macro_rules! impl_mask { impl_mask! { i8, i16, i32, i64, isize } -impl SimdPartialOrd for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialOrd for Simd<*const T, N> { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { self.addr().simd_lt(other.addr()) @@ -243,10 +230,7 @@ where } } -impl SimdOrd for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdOrd for Simd<*const T, N> { #[inline] fn simd_max(self, other: Self) -> Self { self.simd_lt(other).select(other, self) @@ -268,10 +252,7 @@ where } } -impl SimdPartialOrd for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialOrd for Simd<*mut T, N> { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { self.addr().simd_lt(other.addr()) @@ -293,10 +274,7 @@ where } } -impl SimdOrd for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdOrd for Simd<*mut T, N> { #[inline] fn simd_max(self, other: Self) -> Self { self.simd_lt(other).select(other, self) diff --git a/crates/core_simd/src/simd/num/float.rs b/crates/core_simd/src/simd/num/float.rs index 76ab5748c6384..efd7c2469512b 100644 --- a/crates/core_simd/src/simd/num/float.rs +++ b/crates/core_simd/src/simd/num/float.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - LaneCount, Mask, Select, Simd, SimdCast, SimdElement, SupportedLaneCount, + Mask, Select, Simd, SimdCast, SimdElement, cmp::{SimdPartialEq, SimdPartialOrd}, }; @@ -240,15 +240,9 @@ pub trait SimdFloat: Copy + Sealed { macro_rules! impl_trait { { $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} impl SimdFloat for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Mask = Mask<<$mask_ty as SimdElement>::Mask, N>; type Scalar = $ty; diff --git a/crates/core_simd/src/simd/num/int.rs b/crates/core_simd/src/simd/num/int.rs index 5a292407d0508..6ebf0ba397c64 100644 --- a/crates/core_simd/src/simd/num/int.rs +++ b/crates/core_simd/src/simd/num/int.rs @@ -1,7 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - LaneCount, Mask, Select, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd, - cmp::SimdPartialOrd, num::SimdUint, + Mask, Select, Simd, SimdCast, SimdElement, cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, }; /// Operations on SIMD vectors of signed integers. @@ -241,16 +240,9 @@ pub trait SimdInt: Copy + Sealed { macro_rules! impl_trait { { $($ty:ident ($unsigned:ident)),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} - impl SimdInt for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { + impl SimdInt for Simd<$ty, N> { type Mask = Mask<<$ty as SimdElement>::Mask, N>; type Scalar = $ty; type Unsigned = Simd<$unsigned, N>; diff --git a/crates/core_simd/src/simd/num/uint.rs b/crates/core_simd/src/simd/num/uint.rs index 45d978068b664..f8a40f8ec5651 100644 --- a/crates/core_simd/src/simd/num/uint.rs +++ b/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd}; +use crate::simd::{Simd, SimdCast, SimdElement, cmp::SimdOrd}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { @@ -123,15 +123,9 @@ pub trait SimdUint: Copy + Sealed { macro_rules! impl_trait { { $($ty:ident ($signed:ident)),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} impl SimdUint for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Scalar = $ty; type Cast = Simd; diff --git a/crates/core_simd/src/simd/ptr/const_ptr.rs b/crates/core_simd/src/simd/ptr/const_ptr.rs index 36452e7ae920d..7ef9dc21373ec 100644 --- a/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; +use crate::simd::{Mask, Simd, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { @@ -88,12 +88,9 @@ pub trait SimdConstPtr: Copy + Sealed { fn wrapping_sub(self, count: Self::Usize) -> Self; } -impl Sealed for Simd<*const T, N> where LaneCount: SupportedLaneCount {} +impl Sealed for Simd<*const T, N> {} -impl SimdConstPtr for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdConstPtr for Simd<*const T, N> { type Usize = Simd; type Isize = Simd; type CastPtr = Simd<*const U, N>; diff --git a/crates/core_simd/src/simd/ptr/mut_ptr.rs b/crates/core_simd/src/simd/ptr/mut_ptr.rs index c644f390c20a5..3b9b75ddf5660 100644 --- a/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; +use crate::simd::{Mask, Simd, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { @@ -85,12 +85,9 @@ pub trait SimdMutPtr: Copy + Sealed { fn wrapping_sub(self, count: Self::Usize) -> Self; } -impl Sealed for Simd<*mut T, N> where LaneCount: SupportedLaneCount {} +impl Sealed for Simd<*mut T, N> {} -impl SimdMutPtr for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdMutPtr for Simd<*mut T, N> { type Usize = Simd; type Isize = Simd; type CastPtr = Simd<*mut U, N>; diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index 81085a9ee4a3e..02dcd71356dd6 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Mask, MaskElement, Simd, SimdElement}; /// Constructs a new SIMD vector by copying elements from selected elements in other vectors. /// @@ -82,8 +82,6 @@ pub trait Swizzle { fn swizzle(vector: Simd) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // Safety: `vector` is a vector, and the index is a const vector of u32. unsafe { @@ -122,8 +120,6 @@ pub trait Swizzle { fn concat_swizzle(first: Simd, second: Simd) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // Safety: `first` and `second` are vectors, and the index is a const vector of u32. unsafe { @@ -161,8 +157,6 @@ pub trait Swizzle { fn swizzle_mask(mask: Mask) -> Mask where T: MaskElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask unsafe { Mask::from_simd_unchecked(Self::swizzle(mask.to_simd())) } @@ -177,8 +171,6 @@ pub trait Swizzle { fn concat_swizzle_mask(first: Mask, second: Mask) -> Mask where T: MaskElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask unsafe { @@ -190,7 +182,6 @@ pub trait Swizzle { impl Simd where T: SimdElement, - LaneCount: SupportedLaneCount, { /// Reverse the order of the elements in the vector. #[inline] @@ -464,10 +455,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn resize(self, value: T) -> Simd - where - LaneCount: SupportedLaneCount, - { + pub fn resize(self, value: T) -> Simd { struct Resize; impl Swizzle for Resize { const INDEX: [usize; M] = const { @@ -495,10 +483,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn extract(self) -> Simd - where - LaneCount: SupportedLaneCount, - { + pub fn extract(self) -> Simd { struct Extract; impl Swizzle for Extract { const INDEX: [usize; LEN] = const { @@ -519,7 +504,6 @@ where impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { /// Reverse the order of the elements in the mask. #[inline] @@ -655,10 +639,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn resize(self, value: bool) -> Mask - where - LaneCount: SupportedLaneCount, - { + pub fn resize(self, value: bool) -> Mask { // Safety: swizzles are safe for masks unsafe { Mask::::from_simd_unchecked(self.to_simd().resize::(if value { @@ -681,10 +662,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn extract(self) -> Mask - where - LaneCount: SupportedLaneCount, - { + pub fn extract(self) -> Mask { // Safety: swizzles are safe for masks unsafe { Mask::::from_simd_unchecked(self.to_simd().extract::()) } } diff --git a/crates/core_simd/src/swizzle_dyn.rs b/crates/core_simd/src/swizzle_dyn.rs index 73b18595d0a1c..ae0b174973da7 100644 --- a/crates/core_simd/src/swizzle_dyn.rs +++ b/crates/core_simd/src/swizzle_dyn.rs @@ -1,10 +1,7 @@ -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; use core::mem; -impl Simd -where - LaneCount: SupportedLaneCount, -{ +impl Simd { /// Swizzle a vector of bytes according to the index vector. /// Indices within range select the appropriate byte. /// Indices "out of bounds" instead select 0. @@ -184,10 +181,7 @@ unsafe fn transize( f: unsafe fn(T, T) -> T, a: Simd, b: Simd, -) -> Simd -where - LaneCount: SupportedLaneCount, -{ +) -> Simd { // SAFETY: Same obligation to use this function as to use mem::transmute_copy. unsafe { mem::transmute_copy(&f(mem::transmute_copy(&a), mem::transmute_copy(&b))) } } @@ -196,10 +190,7 @@ where #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[allow(unused)] #[inline(always)] -fn zeroing_idxs(idxs: Simd) -> Simd -where - LaneCount: SupportedLaneCount, -{ +fn zeroing_idxs(idxs: Simd) -> Simd { use crate::simd::{Select, cmp::SimdPartialOrd}; idxs.simd_lt(Simd::splat(N as u8)) .select(idxs, Simd::splat(u8::MAX)) diff --git a/crates/core_simd/src/to_bytes.rs b/crates/core_simd/src/to_bytes.rs index fee2cc06c5b09..1fd285e457db8 100644 --- a/crates/core_simd/src/to_bytes.rs +++ b/crates/core_simd/src/to_bytes.rs @@ -1,12 +1,12 @@ use crate::simd::{ - LaneCount, Simd, SimdElement, SupportedLaneCount, + Simd, SimdElement, num::{SimdFloat, SimdInt, SimdUint}, }; mod sealed { use super::*; pub trait Sealed {} - impl Sealed for Simd where LaneCount: SupportedLaneCount {} + impl Sealed for Simd {} } use sealed::Sealed; diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index c00cfcdd41ffa..2dba5c83e1122 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -1,5 +1,5 @@ use crate::simd::{ - LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, + Mask, MaskElement, Swizzle, cmp::SimdPartialOrd, num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, @@ -51,6 +51,8 @@ use crate::simd::{ /// Thus it is sound to [`transmute`] `Simd` to `[T; N]` and should optimize to "zero cost", /// but the reverse transmutation may require a copy the compiler cannot simply elide. /// +/// `N` cannot be 0 and may be at most 64. This limit may be increased in the future. +/// /// # ABI "Features" /// Due to Rust's safety guarantees, `Simd` is currently passed and returned via memory, /// not SIMD registers, except as an optimization. Using `#[inline]` on functions that accept @@ -100,14 +102,13 @@ use crate::simd::{ // avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also // causes rustc to emit illegal LLVM IR in some cases. #[repr(simd, packed)] +#[rustc_simd_monomorphize_lane_limit = "64"] pub struct Simd([T; N]) where - LaneCount: SupportedLaneCount, T: SimdElement; impl Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { /// Number of elements in this vector. @@ -149,7 +150,6 @@ where const fn splat_const(value: T) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, { Simd::from_array([value; N]) } @@ -157,7 +157,6 @@ where fn splat_rt(value: T) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, { // This is preferred over `[value; N]`, since it's explicitly a splat: // https://github.com/rust-lang/rust/issues/97804 @@ -886,16 +885,10 @@ where } } -impl Copy for Simd -where - LaneCount: SupportedLaneCount, - T: SimdElement, -{ -} +impl Copy for Simd where T: SimdElement {} impl Clone for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -906,7 +899,6 @@ where impl Default for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + Default, { #[inline] @@ -917,7 +909,6 @@ where impl PartialEq for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + PartialEq, { #[inline] @@ -951,7 +942,6 @@ where /// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl PartialOrd for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + PartialOrd, { #[inline] @@ -961,17 +951,11 @@ where } } -impl Eq for Simd -where - LaneCount: SupportedLaneCount, - T: SimdElement + Eq, -{ -} +impl Eq for Simd where T: SimdElement + Eq {} /// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl Ord for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + Ord, { #[inline] @@ -983,7 +967,6 @@ where impl core::hash::Hash for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + core::hash::Hash, { #[inline] @@ -998,7 +981,6 @@ where // array references impl AsRef<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1009,7 +991,6 @@ where impl AsMut<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1021,7 +1002,6 @@ where // slice references impl AsRef<[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1032,7 +1012,6 @@ where impl AsMut<[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1044,7 +1023,6 @@ where // vector/array conversion impl From<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1055,7 +1033,6 @@ where impl From> for [T; N] where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1066,7 +1043,6 @@ where impl TryFrom<&[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { type Error = core::array::TryFromSliceError; @@ -1079,7 +1055,6 @@ where impl TryFrom<&mut [T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { type Error = core::array::TryFromSliceError; @@ -1217,10 +1192,7 @@ where } #[inline] -fn lane_indices() -> Simd -where - LaneCount: SupportedLaneCount, -{ +fn lane_indices() -> Simd { #![allow(clippy::needless_range_loop)] let mut index = [0; N]; for i in 0..N { @@ -1232,7 +1204,6 @@ where #[inline] fn mask_up_to(len: usize) -> Mask where - LaneCount: SupportedLaneCount, M: MaskElement, { let index = lane_indices::(); diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 148aa5f9f1771..c3c9b76e50b8a 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -11,7 +11,7 @@ use core_simd::simd; use core::intrinsics::simd as intrinsics; -use simd::{LaneCount, Simd, SupportedLaneCount}; +use simd::Simd; #[cfg(feature = "as_crate")] mod experimental { @@ -140,16 +140,14 @@ pub trait StdFloat: Sealed + Sized { fn fract(self) -> Self; } -impl Sealed for Simd where LaneCount: SupportedLaneCount {} -impl Sealed for Simd where LaneCount: SupportedLaneCount {} +impl Sealed for Simd {} +impl Sealed for Simd {} macro_rules! impl_float { { $($fn:ident: $intrinsic:ident,)* } => { impl StdFloat for Simd - where - LaneCount: SupportedLaneCount, { #[inline] fn fract(self) -> Self { @@ -165,8 +163,6 @@ macro_rules! impl_float { } impl StdFloat for Simd - where - LaneCount: SupportedLaneCount, { #[inline] fn fract(self) -> Self { diff --git a/crates/test_helpers/src/lib.rs b/crates/test_helpers/src/lib.rs index 35401a9ddb40f..eb3d3f68bc2ea 100644 --- a/crates/test_helpers/src/lib.rs +++ b/crates/test_helpers/src/lib.rs @@ -604,8 +604,6 @@ macro_rules! test_lanes { use super::*; fn implementation() - where - core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body #[cfg(target_arch = "wasm32")] @@ -704,8 +702,6 @@ macro_rules! test_lanes_panic { use super::*; fn implementation() - where - core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body // test some odd and even non-power-of-2 lengths on miri diff --git a/rust-toolchain.toml b/rust-toolchain.toml index df4bd75ecfe2f..ed4d7e8a801e9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-08-17" +channel = "nightly-2025-09-30" components = ["rustfmt", "clippy", "miri", "rust-src"] From 061617bdbcaa49bb5c4456c0c5264d0849afe4b1 Mon Sep 17 00:00:00 2001 From: sayantn Date: Wed, 8 Oct 2025 08:12:09 +0530 Subject: [PATCH 25/43] Add alignment parameter to `simd_masked_{load,store}` --- crates/core_simd/src/vector.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index d76a6cd52bfc5..f40031f8c4da7 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -474,7 +474,14 @@ where or: Self, ) -> Self { // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. - unsafe { core::intrinsics::simd::simd_masked_load(enable.to_int(), ptr, or) } + unsafe { + core::intrinsics::simd::simd_masked_load::< + _, + _, + _, + { core::intrinsics::simd::SimdAlign::Element }, + >(enable.to_int(), ptr, or) + } } /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. @@ -723,7 +730,14 @@ where #[inline] pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<::Mask, N>) { // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. - unsafe { core::intrinsics::simd::simd_masked_store(enable.to_int(), ptr, self) } + unsafe { + core::intrinsics::simd::simd_masked_store::< + _, + _, + _, + { core::intrinsics::simd::SimdAlign::Element }, + >(enable.to_int(), ptr, self) + } } /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. From 242b4b5dc96472858438338642635091dfd3ec99 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Nov 2025 13:25:22 -0800 Subject: [PATCH 26/43] Remove more #[must_use] from portable-simd warning: `#[must_use]` attribute cannot be used on trait methods in impl blocks --> crates/core_simd/src/masks/bitmask.rs:173:5 | 173 | #[must_use = "method returns a new mask and does not mutate the original value"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, traits, and unions = note: `#[warn(unused_attributes)]` (part of `#[warn(unused)]`) on by default warning: `#[must_use]` attribute cannot be used on trait methods in impl blocks --> crates/core_simd/src/masks/bitmask.rs:190:5 | 190 | #[must_use = "method returns a new mask and does not mutate the original value"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, traits, and unions warning: `#[must_use]` attribute cannot be used on trait methods in impl blocks --> crates/core_simd/src/masks/bitmask.rs:206:5 | 206 | #[must_use = "method returns a new mask and does not mutate the original value"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, traits, and unions warning: `#[must_use]` attribute cannot be used on trait methods in impl blocks --> crates/core_simd/src/masks/bitmask.rs:222:5 | 222 | #[must_use = "method returns a new mask and does not mutate the original value"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, traits, and unions --- crates/core_simd/src/masks/bitmask.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index 8221d8f17e90e..32d37b5533926 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -170,7 +170,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitand(mut self, rhs: Self) -> Self { for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { *l &= r; @@ -187,7 +186,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitor(mut self, rhs: Self) -> Self { for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { *l |= r; @@ -203,7 +201,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitxor(mut self, rhs: Self) -> Self::Output { for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { *l ^= r; @@ -219,7 +216,6 @@ where { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn not(mut self) -> Self::Output { for x in self.0.as_mut() { *x = !*x; From 4215b7228d28cb84016e8f0e41c0a5e0935a222f Mon Sep 17 00:00:00 2001 From: Andrew Cherry Date: Thu, 11 Dec 2025 13:58:40 +0000 Subject: [PATCH 27/43] removal of aarch64 special-casing (#497) --- crates/std_float/src/lib.rs | 98 ++++++++++++++----------------------- 1 file changed, 37 insertions(+), 61 deletions(-) diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index c3c9b76e50b8a..b269efc9b1d76 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -66,28 +66,43 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the sine of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn sin(self) -> Self; + fn sin(self) -> Self { + unsafe { intrinsics::simd_fsin(self) } + } /// Produces a vector where every element has the cosine of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn cos(self) -> Self; + fn cos(self) -> Self { + unsafe { intrinsics::simd_fcos(self) } + } /// Produces a vector where every element has the exponential (base e) of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp(self) -> Self; + fn exp(self) -> Self { + unsafe { intrinsics::simd_fexp(self) } + } /// Produces a vector where every element has the exponential (base 2) of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp2(self) -> Self; + fn exp2(self) -> Self { + unsafe { intrinsics::simd_fexp2(self) } + } /// Produces a vector where every element has the natural logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn ln(self) -> Self; + fn ln(self) -> Self { + unsafe { intrinsics::simd_flog(self) } + } /// Produces a vector where every element has the logarithm with respect to an arbitrary /// in the equivalently-indexed elements in `self` and `base`. @@ -99,13 +114,19 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the base-2 logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log2(self) -> Self; + fn log2(self) -> Self { + unsafe { intrinsics::simd_flog2(self) } + } /// Produces a vector where every element has the base-10 logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log10(self) -> Self; + fn log10(self) -> Self { + unsafe { intrinsics::simd_flog10(self) } + } /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] @@ -143,61 +164,16 @@ pub trait StdFloat: Sealed + Sized { impl Sealed for Simd {} impl Sealed for Simd {} -macro_rules! impl_float { - { - $($fn:ident: $intrinsic:ident,)* - } => { - impl StdFloat for Simd - { - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } - - $( - #[inline] - fn $fn(self) -> Self { - unsafe { intrinsics::$intrinsic(self) } - } - )* - } - - impl StdFloat for Simd - { - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } - - $( - #[inline] - fn $fn(self) -> Self { - // https://github.com/llvm/llvm-project/issues/83729 - #[cfg(target_arch = "aarch64")] - { - let mut ln = Self::splat(0f64); - for i in 0..N { - ln[i] = self[i].$fn() - } - ln - } - - #[cfg(not(target_arch = "aarch64"))] - { - unsafe { intrinsics::$intrinsic(self) } - } - } - )* - } +impl StdFloat for Simd { + #[inline] + fn fract(self) -> Self { + self - self.trunc() } } -impl_float! { - sin: simd_fsin, - cos: simd_fcos, - exp: simd_fexp, - exp2: simd_fexp2, - ln: simd_flog, - log2: simd_flog2, - log10: simd_flog10, +impl StdFloat for Simd { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } } From e86cfa0577af318b408fc786fa2fcac0a9961371 Mon Sep 17 00:00:00 2001 From: KaiTomotake Date: Thu, 22 Jan 2026 11:57:45 +0900 Subject: [PATCH 28/43] add foregin type tests add tests/ui/rfcs/rfc-1861-extern-types/comparison.rs --- .../rfcs/rfc-1861-extern-types/comparison.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/ui/rfcs/rfc-1861-extern-types/comparison.rs diff --git a/tests/ui/rfcs/rfc-1861-extern-types/comparison.rs b/tests/ui/rfcs/rfc-1861-extern-types/comparison.rs new file mode 100644 index 0000000000000..6504fd64f64da --- /dev/null +++ b/tests/ui/rfcs/rfc-1861-extern-types/comparison.rs @@ -0,0 +1,35 @@ +// Foreign type tests not covering all operations +//@ only-nightly +//@ build-pass + +#![feature(extern_types)] + +#![allow(ambiguous_wide_pointer_comparisons)] + +extern "C" { + type ForeignType; +} + +#[repr(C)] +struct Example { + field: ForeignType, +} + +fn main() { + // pointer comparison + let a = std::ptr::null::(); + let b = std::ptr::null::(); + + assert!(a == b); + + // field address computation + let p = std::ptr::null::(); + unsafe { + let _ = &(*p).field; + } + + // pointer casts involving extern types + let raw = std::ptr::null::<()>(); + let ext = raw as *const ForeignType; + let _ = ext as *const (); +} From 7b6f82eea6d4a34cf61e85516899b58551b58193 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 26 Jan 2026 18:23:05 +0100 Subject: [PATCH 29/43] use `intrinsics::simd::simd_splat` --- crates/core_simd/src/vector.rs | 46 +++++++++++++++------------------- rust-toolchain.toml | 2 +- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 2dba5c83e1122..5b3a689f3611b 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -1,5 +1,7 @@ +use core::intrinsics::simd::SimdAlign; + use crate::simd::{ - Mask, MaskElement, Swizzle, + Mask, MaskElement, cmp::SimdPartialOrd, num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, @@ -147,28 +149,8 @@ where #[inline] #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] pub const fn splat(value: T) -> Self { - const fn splat_const(value: T) -> Simd - where - T: SimdElement, - { - Simd::from_array([value; N]) - } - - fn splat_rt(value: T) -> Simd - where - T: SimdElement, - { - // This is preferred over `[value; N]`, since it's explicitly a splat: - // https://github.com/rust-lang/rust/issues/97804 - struct Splat; - impl Swizzle for Splat { - const INDEX: [usize; N] = [0; N]; - } - - Splat::swizzle::(Simd::::from([value])) - } - - core::intrinsics::const_eval_select((value,), splat_const, splat_rt) + // SAFETY: T is a SimdElement, and the item type of Self. + unsafe { core::intrinsics::simd::simd_splat(value) } } /// Returns an array reference containing the entire SIMD vector. @@ -464,7 +446,7 @@ where /// value from `or` is passed through. /// /// # Safety - /// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`. + /// Enabled `ptr` elements must be safe to read as if by `core::ptr::read`. #[must_use] #[inline] pub unsafe fn load_select_ptr( @@ -473,7 +455,13 @@ where or: Self, ) -> Self { // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. - unsafe { core::intrinsics::simd::simd_masked_load(enable.to_simd(), ptr, or) } + unsafe { + core::intrinsics::simd::simd_masked_load::<_, _, _, { SimdAlign::Element }>( + enable.to_simd(), + ptr, + or, + ) + } } /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. @@ -722,7 +710,13 @@ where #[inline] pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<::Mask, N>) { // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. - unsafe { core::intrinsics::simd::simd_masked_store(enable.to_simd(), ptr, self) } + unsafe { + core::intrinsics::simd::simd_masked_store::<_, _, _, { SimdAlign::Element }>( + enable.to_simd(), + ptr, + self, + ) + } } /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ed4d7e8a801e9..639d07df73374 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-09-30" +channel = "nightly-2026-01-26" components = ["rustfmt", "clippy", "miri", "rust-src"] From b3e93fc242cb08f3f18a295a574fdbb360ae1863 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 26 Jan 2026 21:58:18 +0100 Subject: [PATCH 30/43] use `cargo miri nextest` to parallelize miri tests --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96881687af055..05020108b5b7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -244,5 +244,7 @@ jobs: PROPTEST_CASES: 16 steps: - uses: actions/checkout@v4 + - name: Install cargo-nextest + run: cargo install cargo-nextest --locked - name: Test (Miri) - run: cargo miri test + run: cargo miri nextest run -j4 From 808d349fff4a32e22b8bae3df7aec4d9e38ce30a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 27 Jan 2026 00:18:16 +0100 Subject: [PATCH 31/43] experiment with cargo nextest partitions --- .github/workflows/ci.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05020108b5b7a..de7efa3552836 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -240,11 +240,18 @@ jobs: miri: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4] env: PROPTEST_CASES: 16 steps: - uses: actions/checkout@v4 + - name: Install cargo-nextest - run: cargo install cargo-nextest --locked - - name: Test (Miri) - run: cargo miri nextest run -j4 + uses: taiki-e/install-action@nextest + + - name: Test (Miri) (partition ${{ matrix.shard }}/4) + run: | + cargo miri nextest run --partition count:${{ matrix.shard }}/4 From 324e366b686dbbad63120ee613e07e2cdcc6579c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 28 Jan 2026 00:58:05 +0100 Subject: [PATCH 32/43] remove usage of `stdarch_x86_avx512` which is now stable --- crates/core_simd/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 3e5ebe19e4dbc..fe26d99b9194c 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -31,10 +31,6 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) )] -#![cfg_attr( - all(target_arch = "x86_64", target_feature = "avx512f"), - feature(stdarch_x86_avx512) -)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny( unsafe_op_in_unsafe_fn, From fb995ef5b3731fb627653a93cf54cea5ad1783ed Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Dec 2025 14:56:56 -0600 Subject: [PATCH 33/43] test(frontmatter): Show behavior for straw cr --- tests/ui/frontmatter/content-cr.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/ui/frontmatter/content-cr.rs diff --git a/tests/ui/frontmatter/content-cr.rs b/tests/ui/frontmatter/content-cr.rs new file mode 100644 index 0000000000000..768282fc2b937 --- /dev/null +++ b/tests/ui/frontmatter/content-cr.rs @@ -0,0 +1,11 @@ +--- +package.name = " " +package.description = "é" +--- + +// ignore-tidy-cr +//@ check-pass + +#![feature(frontmatter)] + +pub fn main() {} From e9122481a4e8a98b76b9c8668d3973d867f1bf6b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Dec 2025 15:56:10 -0600 Subject: [PATCH 34/43] refactor(parse): Be consistent in naming --- compiler/rustc_parse/src/lexer/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 7c969dd7f9f48..6c9ecd75cdbc0 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -598,9 +598,9 @@ impl<'psess, 'src> Lexer<'psess, 'src> { let s = self.str_from(start); let real_start = s.find("---").unwrap(); let frontmatter_opening_pos = BytePos(real_start as u32) + start; - let s_new = &s[real_start..]; - let within = s_new.trim_start_matches('-'); - let len_opening = s_new.len() - within.len(); + let real_s = &s[real_start..]; + let within = real_s.trim_start_matches('-'); + let len_opening = real_s.len() - within.len(); let frontmatter_opening_end_pos = frontmatter_opening_pos + BytePos(len_opening as u32); if has_invalid_preceding_whitespace { From 053b947aa910c500c5da3ada8b11bd50d3457ffb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Dec 2025 16:02:32 -0600 Subject: [PATCH 35/43] refactor(parse): Consistently use real_s --- compiler/rustc_parse/src/lexer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 6c9ecd75cdbc0..4fbc8f3938add 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -615,7 +615,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } if invalid_infostring { - let line_end = s[real_start..].find('\n').unwrap_or(s[real_start..].len()); + let line_end = real_s.find('\n').unwrap_or(real_s.len()); let span = self.mk_sp( frontmatter_opening_end_pos, frontmatter_opening_pos + BytePos(line_end as u32), From dd5539251c3a5eb6ad33b036d79b3f47238cb1ab Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Dec 2025 16:11:43 -0600 Subject: [PATCH 36/43] refactor(parse): Use a common frame of reference --- compiler/rustc_parse/src/lexer/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 4fbc8f3938add..4d0139e179f4a 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -623,10 +623,10 @@ impl<'psess, 'src> Lexer<'psess, 'src> { self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span }); } - let last_line_start = within.rfind('\n').map_or(0, |i| i + 1); - let last_line = &within[last_line_start..]; + let last_line_start = real_s.rfind('\n').map_or(0, |i| i + 1); + let last_line = &real_s[last_line_start..]; let last_line_trimmed = last_line.trim_start_matches(is_horizontal_whitespace); - let last_line_start_pos = frontmatter_opening_end_pos + BytePos(last_line_start as u32); + let last_line_start_pos = frontmatter_opening_pos + BytePos(last_line_start as u32); let frontmatter_span = self.mk_sp(frontmatter_opening_pos, self.pos); self.psess.gated_spans.gate(sym::frontmatter, frontmatter_span); From 52d4ef12a88ce0ead3cc5ac96b6778e3a33b3583 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 9 Dec 2025 15:54:26 -0600 Subject: [PATCH 37/43] fix(parser): Disallow CR in frontmatter T-lang came back on the stabilization PR asking for CR to be disallowed to leave room for all stray CRs to be rejected in the future. At that point, the test can remain but the implementation can be removed. If that plan does not go through, we'll need to re-evaluate - whether this is more lint-like and should defer to the calling tool that is managing the frontmatter - how much Rust should treat the frontmatter as Rust and apply the same grammar restrictions of "no stray CR" (like raw string literals) --- compiler/rustc_parse/messages.ftl | 3 ++- compiler/rustc_parse/src/errors.rs | 7 +++++++ compiler/rustc_parse/src/lexer/mod.rs | 10 +++++++++- tests/ui/frontmatter/content-cr.rs | 3 +-- tests/ui/frontmatter/content-cr.stderr | 8 ++++++++ 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 tests/ui/frontmatter/content-cr.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 449d0b964fd48..3f33001174602 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -98,6 +98,8 @@ parse_bare_cr = {$double_quotes -> } .escape = escape the character +parse_bare_cr_in_frontmatter = bare CR not allowed in frontmatter + parse_bare_cr_in_raw_string = bare CR not allowed in raw string parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier @@ -352,7 +354,6 @@ parse_frontmatter_length_mismatch = frontmatter close does not match the opening parse_frontmatter_too_many_dashes = too many `-` symbols: frontmatter openings may be delimited by up to 255 `-` symbols, but found {$len_opening} parse_frontmatter_unclosed = unclosed frontmatter .note = frontmatter opening here was not closed - parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 42327c7e343d1..4e789a321649e 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -829,6 +829,13 @@ pub(crate) struct FrontmatterTooManyDashes { pub len_opening: usize, } +#[derive(Diagnostic)] +#[diag(parse_bare_cr_in_frontmatter)] +pub(crate) struct BareCrFrontmatter { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_leading_plus_not_supported)] pub(crate) struct LeadingPlusNotSupported { diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 4d0139e179f4a..f9bf50de091a6 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -614,8 +614,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> { }); } + let line_end = real_s.find('\n').unwrap_or(real_s.len()); if invalid_infostring { - let line_end = real_s.find('\n').unwrap_or(real_s.len()); let span = self.mk_sp( frontmatter_opening_end_pos, frontmatter_opening_pos + BytePos(line_end as u32), @@ -624,6 +624,14 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } let last_line_start = real_s.rfind('\n').map_or(0, |i| i + 1); + + let content = &real_s[line_end..last_line_start]; + if let Some(cr_offset) = content.find('\r') { + let cr_pos = start + BytePos((real_start + line_end + cr_offset) as u32); + let span = self.mk_sp(cr_pos, cr_pos + BytePos(1 as u32)); + self.dcx().emit_err(errors::BareCrFrontmatter { span }); + } + let last_line = &real_s[last_line_start..]; let last_line_trimmed = last_line.trim_start_matches(is_horizontal_whitespace); let last_line_start_pos = frontmatter_opening_pos + BytePos(last_line_start as u32); diff --git a/tests/ui/frontmatter/content-cr.rs b/tests/ui/frontmatter/content-cr.rs index 768282fc2b937..71bede928f377 100644 --- a/tests/ui/frontmatter/content-cr.rs +++ b/tests/ui/frontmatter/content-cr.rs @@ -1,10 +1,9 @@ --- -package.name = " " +package.name = " " # //~ ERROR bare CR not allowed in frontmatter package.description = "é" --- // ignore-tidy-cr -//@ check-pass #![feature(frontmatter)] diff --git a/tests/ui/frontmatter/content-cr.stderr b/tests/ui/frontmatter/content-cr.stderr new file mode 100644 index 0000000000000..d59b899cc4c39 --- /dev/null +++ b/tests/ui/frontmatter/content-cr.stderr @@ -0,0 +1,8 @@ +error: bare CR not allowed in frontmatter + --> $DIR/content-cr.rs:2:17 + | +LL | package.name = "␍" # + | ^ + +error: aborting due to 1 previous error + From 9e61014a8a0bb1f1d7911511c303a7ae2a9c2a7d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Wed, 28 Jan 2026 08:59:16 +0100 Subject: [PATCH 38/43] Convert `parse_nested_meta` to `parse_args_with` for `#[diagnostic]` --- .../src/diagnostics/diagnostic_builder.rs | 76 +++---- .../session-diagnostic/diagnostic-derive.rs | 10 +- .../diagnostic-derive.stderr | 192 +++++++----------- 3 files changed, 124 insertions(+), 154 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index cbc70b55d7ee2..e71c84c805a7b 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -2,6 +2,7 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; +use syn::parse::ParseStream; use syn::spanned::Spanned; use syn::{Attribute, Meta, Path, Token, Type, parse_quote}; use synstructure::{BindingInfo, Structure, VariantInfo}; @@ -42,7 +43,7 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that /// has the actual diagnostic message. - pub slug: SpannedOption, + pub slug: Option, /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. @@ -111,7 +112,7 @@ impl DiagnosticDeriveKind { impl DiagnosticDeriveVariantBuilder { pub(crate) fn primary_message(&self) -> Option<&Path> { - match self.slug.value_ref() { + match self.slug.as_ref() { None => { span_err(self.span, "diagnostic slug not specified") .help( @@ -209,47 +210,54 @@ impl DiagnosticDeriveVariantBuilder { let name = attr.path().segments.last().unwrap().ident.to_string(); let name = name.as_str(); - let mut first = true; - if name == "diag" { let mut tokens = TokenStream::new(); - attr.parse_nested_meta(|nested| { - let path = &nested.path; + attr.parse_args_with(|input: ParseStream<'_>| { + let mut input = &*input; + let slug_recovery_point = input.fork(); - if first && (nested.input.is_empty() || nested.input.peek(Token![,])) { - self.slug.set_once(path.clone(), path.span().unwrap()); - first = false; - return Ok(()); + let slug = input.parse::()?; + if input.is_empty() || input.peek(Token![,]) { + self.slug = Some(slug); + } else { + input = &slug_recovery_point; } - first = false; - - let Ok(nested) = nested.value() else { - span_err( - nested.input.span().unwrap(), - "diagnostic slug must be the first argument", - ) - .emit(); - return Ok(()); - }; - - if path.is_ident("code") { - self.code.set_once((), path.span().unwrap()); - - let code = nested.parse::()?; - tokens.extend(quote! { - diag.code(#code); - }); - } else { - span_err(path.span().unwrap(), "unknown argument") - .note("only the `code` parameter is valid after the slug") + while !input.is_empty() { + input.parse::()?; + // Allow trailing comma + if input.is_empty() { + break; + } + let arg_name: Path = input.parse::()?; + if input.peek(Token![,]) { + span_err( + arg_name.span().unwrap(), + "diagnostic slug must be the first argument", + ) .emit(); - - // consume the buffer so we don't have syntax errors from syn - let _ = nested.parse::(); + continue; + } + let arg_name = arg_name.require_ident()?; + input.parse::()?; + let arg_value = input.parse::()?; + match arg_name.to_string().as_str() { + "code" => { + self.code.set_once((), arg_name.span().unwrap()); + tokens.extend(quote! { + diag.code(#arg_value); + }); + } + _ => { + span_err(arg_name.span().unwrap(), "unknown argument") + .note("only the `code` parameter is valid after the slug") + .emit(); + } + } } Ok(()) })?; + return Ok(tokens); } diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index fcae379d982fc..506ec7f88a82a 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -80,20 +80,17 @@ struct InvalidNestedStructAttr {} #[derive(Diagnostic)] #[diag(nonsense("foo"), code = E0123, slug = "foo")] -//~^ ERROR diagnostic slug must be the first argument -//~| ERROR diagnostic slug not specified +//~^ ERROR derive(Diagnostic): diagnostic slug not specified struct InvalidNestedStructAttr1 {} #[derive(Diagnostic)] #[diag(nonsense = "...", code = E0123, slug = "foo")] -//~^ ERROR unknown argument -//~| ERROR diagnostic slug not specified +//~^ ERROR diagnostic slug not specified struct InvalidNestedStructAttr2 {} #[derive(Diagnostic)] #[diag(nonsense = 4, code = E0123, slug = "foo")] -//~^ ERROR unknown argument -//~| ERROR diagnostic slug not specified +//~^ ERROR diagnostic slug not specified struct InvalidNestedStructAttr3 {} #[derive(Diagnostic)] @@ -113,7 +110,6 @@ struct WrongPlaceField { #[diag(no_crate_example, code = E0123)] #[diag(no_crate_example, code = E0456)] //~^ ERROR specified multiple times -//~^^ ERROR specified multiple times struct DiagSpecifiedTwice {} #[derive(Diagnostic)] diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index cf5c0c2e6491f..29132b8325f30 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -48,12 +48,6 @@ LL | #[diag(code = E0123)] | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` -error: derive(Diagnostic): diagnostic slug must be the first argument - --> $DIR/diagnostic-derive.rs:82:16 - | -LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")] - | ^ - error: derive(Diagnostic): diagnostic slug not specified --> $DIR/diagnostic-derive.rs:82:1 | @@ -62,32 +56,16 @@ LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")] | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` -error: derive(Diagnostic): unknown argument - --> $DIR/diagnostic-derive.rs:88:8 - | -LL | #[diag(nonsense = "...", code = E0123, slug = "foo")] - | ^^^^^^^^ - | - = note: only the `code` parameter is valid after the slug - error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:88:1 + --> $DIR/diagnostic-derive.rs:87:1 | LL | #[diag(nonsense = "...", code = E0123, slug = "foo")] | ^ | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` -error: derive(Diagnostic): unknown argument - --> $DIR/diagnostic-derive.rs:94:8 - | -LL | #[diag(nonsense = 4, code = E0123, slug = "foo")] - | ^^^^^^^^ - | - = note: only the `code` parameter is valid after the slug - error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:94:1 + --> $DIR/diagnostic-derive.rs:92:1 | LL | #[diag(nonsense = 4, code = E0123, slug = "foo")] | ^ @@ -95,7 +73,7 @@ LL | #[diag(nonsense = 4, code = E0123, slug = "foo")] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): unknown argument - --> $DIR/diagnostic-derive.rs:100:40 + --> $DIR/diagnostic-derive.rs:97:40 | LL | #[diag(no_crate_example, code = E0123, slug = "foo")] | ^^^^ @@ -103,55 +81,43 @@ LL | #[diag(no_crate_example, code = E0123, slug = "foo")] = note: only the `code` parameter is valid after the slug error: derive(Diagnostic): `#[suggestion = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:107:5 + --> $DIR/diagnostic-derive.rs:104:5 | LL | #[suggestion = "bar"] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:114:8 - | -LL | #[diag(no_crate_example, code = E0456)] - | ^^^^^^^^^^^^^^^^ - | -note: previously specified here - --> $DIR/diagnostic-derive.rs:113:8 - | -LL | #[diag(no_crate_example, code = E0123)] - | ^^^^^^^^^^^^^^^^ - -error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:114:26 + --> $DIR/diagnostic-derive.rs:111:26 | LL | #[diag(no_crate_example, code = E0456)] | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:113:26 + --> $DIR/diagnostic-derive.rs:110:26 | LL | #[diag(no_crate_example, code = E0123)] | ^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:120:40 + --> $DIR/diagnostic-derive.rs:116:40 | LL | #[diag(no_crate_example, code = E0123, code = E0456)] | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:120:26 + --> $DIR/diagnostic-derive.rs:116:26 | LL | #[diag(no_crate_example, code = E0123, code = E0456)] | ^^^^ error: derive(Diagnostic): diagnostic slug must be the first argument - --> $DIR/diagnostic-derive.rs:125:43 + --> $DIR/diagnostic-derive.rs:121:26 | LL | #[diag(no_crate_example, no_crate::example, code = E0123)] - | ^ + | ^^^^^^^^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:130:1 + --> $DIR/diagnostic-derive.rs:126:1 | LL | struct KindNotProvided {} | ^^^^^^ @@ -159,7 +125,7 @@ LL | struct KindNotProvided {} = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:133:1 + --> $DIR/diagnostic-derive.rs:129:1 | LL | #[diag(code = E0123)] | ^ @@ -167,31 +133,31 @@ LL | #[diag(code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:144:5 + --> $DIR/diagnostic-derive.rs:140:5 | LL | #[primary_span] | ^ error: derive(Diagnostic): `#[nonsense]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:152:5 + --> $DIR/diagnostic-derive.rs:148:5 | LL | #[nonsense] | ^ error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:169:5 + --> $DIR/diagnostic-derive.rs:165:5 | LL | #[label(no_crate_label)] | ^ error: derive(Diagnostic): `name` doesn't refer to a field on this type - --> $DIR/diagnostic-derive.rs:177:46 + --> $DIR/diagnostic-derive.rs:173:46 | LL | #[suggestion(no_crate_suggestion, code = "{name}")] | ^^^^^^^^ error: invalid format string: expected `}` but string was terminated - --> $DIR/diagnostic-derive.rs:182:10 + --> $DIR/diagnostic-derive.rs:178:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ expected `}` in format string @@ -200,7 +166,7 @@ LL | #[derive(Diagnostic)] = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid format string: unmatched `}` found - --> $DIR/diagnostic-derive.rs:192:10 + --> $DIR/diagnostic-derive.rs:188:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ unmatched `}` in format string @@ -209,19 +175,19 @@ LL | #[derive(Diagnostic)] = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:212:5 + --> $DIR/diagnostic-derive.rs:208:5 | LL | #[label(no_crate_label)] | ^ error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:231:5 + --> $DIR/diagnostic-derive.rs:227:5 | LL | #[suggestion(no_crate_suggestion)] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/diagnostic-derive.rs:239:18 + --> $DIR/diagnostic-derive.rs:235:18 | LL | #[suggestion(nonsense = "bar")] | ^^^^^^^^ @@ -229,13 +195,13 @@ LL | #[suggestion(nonsense = "bar")] = help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:239:5 + --> $DIR/diagnostic-derive.rs:235:5 | LL | #[suggestion(nonsense = "bar")] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/diagnostic-derive.rs:248:18 + --> $DIR/diagnostic-derive.rs:244:18 | LL | #[suggestion(msg = "bar")] | ^^^ @@ -243,13 +209,13 @@ LL | #[suggestion(msg = "bar")] = help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:248:5 + --> $DIR/diagnostic-derive.rs:244:5 | LL | #[suggestion(msg = "bar")] | ^ error: derive(Diagnostic): wrong field type for suggestion - --> $DIR/diagnostic-derive.rs:271:5 + --> $DIR/diagnostic-derive.rs:267:5 | LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")] | ^ @@ -257,79 +223,79 @@ LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")] = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)` error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:287:24 + --> $DIR/diagnostic-derive.rs:283:24 | LL | suggestion: (Span, Span, Applicability), | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:287:18 + --> $DIR/diagnostic-derive.rs:283:18 | LL | suggestion: (Span, Span, Applicability), | ^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:295:33 + --> $DIR/diagnostic-derive.rs:291:33 | LL | suggestion: (Applicability, Applicability, Span), | ^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:295:18 + --> $DIR/diagnostic-derive.rs:291:18 | LL | suggestion: (Applicability, Applicability, Span), | ^^^^^^^^^^^^^ error: derive(Diagnostic): `#[label = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:302:5 + --> $DIR/diagnostic-derive.rs:298:5 | LL | #[label = "bar"] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:453:5 + --> $DIR/diagnostic-derive.rs:449:5 | LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "maybe-incorrect")] | ^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:455:24 + --> $DIR/diagnostic-derive.rs:451:24 | LL | suggestion: (Span, Applicability), | ^^^^^^^^^^^^^ error: derive(Diagnostic): invalid applicability - --> $DIR/diagnostic-derive.rs:461:69 + --> $DIR/diagnostic-derive.rs:457:69 | LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "batman")] | ^^^^^^^^ error: derive(Diagnostic): the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()` - --> $DIR/diagnostic-derive.rs:528:5 + --> $DIR/diagnostic-derive.rs:524:5 | LL | #[help(no_crate_help)] | ^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/diagnostic-derive.rs:537:32 + --> $DIR/diagnostic-derive.rs:533:32 | LL | #[label(no_crate_label, foo)] | ^ error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/diagnostic-derive.rs:545:29 + --> $DIR/diagnostic-derive.rs:541:29 | LL | #[label(no_crate_label, foo = "...")] | ^^^ error: derive(Diagnostic): only `no_span` is a valid nested attribute - --> $DIR/diagnostic-derive.rs:553:29 + --> $DIR/diagnostic-derive.rs:549:29 | LL | #[label(no_crate_label, foo("..."))] | ^^^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:565:5 + --> $DIR/diagnostic-derive.rs:561:5 | LL | #[primary_span] | ^ @@ -337,13 +303,13 @@ LL | #[primary_span] = help: the `primary_span` field attribute is not valid for lint diagnostics error: derive(Diagnostic): `#[error(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:585:1 + --> $DIR/diagnostic-derive.rs:581:1 | LL | #[error(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:585:1 + --> $DIR/diagnostic-derive.rs:581:1 | LL | #[error(no_crate_example, code = E0123)] | ^ @@ -351,13 +317,13 @@ LL | #[error(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): `#[warn_(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:592:1 + --> $DIR/diagnostic-derive.rs:588:1 | LL | #[warn_(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:592:1 + --> $DIR/diagnostic-derive.rs:588:1 | LL | #[warn_(no_crate_example, code = E0123)] | ^ @@ -365,13 +331,13 @@ LL | #[warn_(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:599:1 + --> $DIR/diagnostic-derive.rs:595:1 | LL | #[lint(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:599:1 + --> $DIR/diagnostic-derive.rs:595:1 | LL | #[lint(no_crate_example, code = E0123)] | ^ @@ -379,13 +345,13 @@ LL | #[lint(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:606:1 + --> $DIR/diagnostic-derive.rs:602:1 | LL | #[lint(no_crate_example, code = E0123)] | ^ error: derive(Diagnostic): diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:606:1 + --> $DIR/diagnostic-derive.rs:602:1 | LL | #[lint(no_crate_example, code = E0123)] | ^ @@ -393,19 +359,19 @@ LL | #[lint(no_crate_example, code = E0123)] = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive.rs:615:53 + --> $DIR/diagnostic-derive.rs:611:53 | LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")] | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:615:39 + --> $DIR/diagnostic-derive.rs:611:39 | LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")] | ^^^^ error: derive(Diagnostic): wrong types for suggestion - --> $DIR/diagnostic-derive.rs:624:24 + --> $DIR/diagnostic-derive.rs:620:24 | LL | suggestion: (Span, usize), | ^^^^^ @@ -413,7 +379,7 @@ LL | suggestion: (Span, usize), = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` error: derive(Diagnostic): wrong types for suggestion - --> $DIR/diagnostic-derive.rs:632:17 + --> $DIR/diagnostic-derive.rs:628:17 | LL | suggestion: (Span,), | ^^^^^^^ @@ -421,13 +387,13 @@ LL | suggestion: (Span,), = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:639:5 + --> $DIR/diagnostic-derive.rs:635:5 | LL | #[suggestion(no_crate_suggestion)] | ^ error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:646:1 + --> $DIR/diagnostic-derive.rs:642:1 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^ @@ -435,7 +401,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:649:1 + --> $DIR/diagnostic-derive.rs:645:1 | LL | #[multipart_suggestion()] | ^ @@ -443,7 +409,7 @@ LL | #[multipart_suggestion()] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:653:5 + --> $DIR/diagnostic-derive.rs:649:5 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^ @@ -451,7 +417,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:661:1 + --> $DIR/diagnostic-derive.rs:657:1 | LL | #[suggestion(no_crate_suggestion, code = "...")] | ^ @@ -459,7 +425,7 @@ LL | #[suggestion(no_crate_suggestion, code = "...")] = help: `#[label]` and `#[suggestion]` can only be applied to fields error: derive(Diagnostic): `#[label]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:670:1 + --> $DIR/diagnostic-derive.rs:666:1 | LL | #[label] | ^ @@ -467,73 +433,73 @@ LL | #[label] = help: `#[label]` and `#[suggestion]` can only be applied to fields error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:704:5 + --> $DIR/diagnostic-derive.rs:700:5 | LL | #[subdiagnostic(bad)] | ^ error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:712:5 + --> $DIR/diagnostic-derive.rs:708:5 | LL | #[subdiagnostic = "bad"] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:720:5 + --> $DIR/diagnostic-derive.rs:716:5 | LL | #[subdiagnostic(bad, bad)] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:728:5 + --> $DIR/diagnostic-derive.rs:724:5 | LL | #[subdiagnostic("bad")] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:736:5 + --> $DIR/diagnostic-derive.rs:732:5 | LL | #[subdiagnostic(eager)] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:744:5 + --> $DIR/diagnostic-derive.rs:740:5 | LL | #[subdiagnostic(eager)] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:765:5 + --> $DIR/diagnostic-derive.rs:761:5 | LL | #[subdiagnostic(eager)] | ^ error: derive(Diagnostic): expected at least one string literal for `code(...)` - --> $DIR/diagnostic-derive.rs:796:23 + --> $DIR/diagnostic-derive.rs:792:23 | LL | #[suggestion(code())] | ^ error: derive(Diagnostic): `code(...)` must contain only string literals - --> $DIR/diagnostic-derive.rs:804:23 + --> $DIR/diagnostic-derive.rs:800:23 | LL | #[suggestion(code(foo))] | ^^^ error: unexpected token, expected `)` - --> $DIR/diagnostic-derive.rs:804:23 + --> $DIR/diagnostic-derive.rs:800:23 | LL | #[suggestion(code(foo))] | ^^^ error: expected string literal - --> $DIR/diagnostic-derive.rs:813:25 + --> $DIR/diagnostic-derive.rs:809:25 | LL | #[suggestion(code = 3)] | ^ error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:828:5 + --> $DIR/diagnostic-derive.rs:824:5 | LL | #[suggestion(no_crate_suggestion, code = "")] | ^ @@ -549,13 +515,13 @@ LL | #[nonsense(no_crate_example, code = E0123)] | ^^^^^^^^ error: cannot find attribute `nonsense` in this scope - --> $DIR/diagnostic-derive.rs:152:7 + --> $DIR/diagnostic-derive.rs:148:7 | LL | #[nonsense] | ^^^^^^^^ error: cannot find attribute `error` in this scope - --> $DIR/diagnostic-derive.rs:585:3 + --> $DIR/diagnostic-derive.rs:581:3 | LL | #[error(no_crate_example, code = E0123)] | ^^^^^ @@ -567,7 +533,7 @@ LL | struct ErrorAttribute {} | error: cannot find attribute `warn_` in this scope - --> $DIR/diagnostic-derive.rs:592:3 + --> $DIR/diagnostic-derive.rs:588:3 | LL | #[warn_(no_crate_example, code = E0123)] | ^^^^^ @@ -579,7 +545,7 @@ LL + #[warn(no_crate_example, code = E0123)] | error: cannot find attribute `lint` in this scope - --> $DIR/diagnostic-derive.rs:599:3 + --> $DIR/diagnostic-derive.rs:595:3 | LL | #[lint(no_crate_example, code = E0123)] | ^^^^ @@ -591,7 +557,7 @@ LL + #[link(no_crate_example, code = E0123)] | error: cannot find attribute `lint` in this scope - --> $DIR/diagnostic-derive.rs:606:3 + --> $DIR/diagnostic-derive.rs:602:3 | LL | #[lint(no_crate_example, code = E0123)] | ^^^^ @@ -603,7 +569,7 @@ LL + #[link(no_crate_example, code = E0123)] | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:646:3 + --> $DIR/diagnostic-derive.rs:642:3 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^^^^^^^^^^^^^^^^^^^^ @@ -615,7 +581,7 @@ LL | struct MultipartSuggestion { | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:649:3 + --> $DIR/diagnostic-derive.rs:645:3 | LL | #[multipart_suggestion()] | ^^^^^^^^^^^^^^^^^^^^ @@ -627,7 +593,7 @@ LL | struct MultipartSuggestion { | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:653:7 + --> $DIR/diagnostic-derive.rs:649:7 | LL | #[multipart_suggestion(no_crate_suggestion)] | ^^^^^^^^^^^^^^^^^^^^ @@ -641,7 +607,7 @@ LL | #[diag(nonsense, code = E0123)] | ^^^^^^^^ not found in `crate::fluent_generated` error[E0425]: cannot find value `__code_34` in this scope - --> $DIR/diagnostic-derive.rs:810:10 + --> $DIR/diagnostic-derive.rs:806:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ not found in this scope @@ -649,7 +615,7 @@ LL | #[derive(Diagnostic)] = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied - --> $DIR/diagnostic-derive.rs:351:12 + --> $DIR/diagnostic-derive.rs:347:12 | LL | #[derive(Diagnostic)] | ---------- required by a bound introduced by this call @@ -670,7 +636,7 @@ note: required by a bound in `Diag::<'a, G>::arg` = note: in this macro invocation = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 85 previous errors +error: aborting due to 81 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. From 5d21a21695d56b74ea249f269ee10195251008b7 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Wed, 28 Jan 2026 22:18:44 +0100 Subject: [PATCH 39/43] Convert `parse_nested_meta` to `parse_args_with` for `#[subdiagnostic]` --- .../src/diagnostics/subdiagnostic.rs | 51 ++-- .../rustc_macros/src/diagnostics/utils.rs | 248 ++++++++---------- .../session-diagnostic/diagnostic-derive.rs | 2 +- .../diagnostic-derive.stderr | 14 +- .../subdiagnostic-derive.rs | 7 +- .../subdiagnostic-derive.stderr | 46 ++-- 6 files changed, 168 insertions(+), 200 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index dcd0116d804d3..189d83c421607 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -1,9 +1,10 @@ #![deny(unused_must_use)] -use proc_macro2::TokenStream; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; +use syn::parse::ParseStream; use syn::spanned::Spanned; -use syn::{Attribute, Meta, MetaList, Path}; +use syn::{Attribute, Meta, MetaList, Path, Token}; use synstructure::{BindingInfo, Structure, VariantInfo}; use super::utils::SubdiagnosticVariant; @@ -437,23 +438,35 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut code = None; - list.parse_nested_meta(|nested| { - if nested.path.is_ident("code") { - let code_field = new_code_ident(); - let span = nested.path.span().unwrap(); - let formatting_init = build_suggestion_code( - &code_field, - nested, - self, - AllowMultipleAlternatives::No, - ); - code.set_once((code_field, formatting_init), span); - } else { - span_err( - nested.path.span().unwrap(), - "`code` is the only valid nested attribute", - ) - .emit(); + list.parse_args_with(|input: ParseStream<'_>| { + while !input.is_empty() { + let arg_name = input.parse::()?; + match arg_name.to_string().as_str() { + "code" => { + let code_field = new_code_ident(); + let formatting_init = build_suggestion_code( + &code_field, + input, + self, + AllowMultipleAlternatives::No, + )?; + code.set_once( + (code_field, formatting_init), + arg_name.span().unwrap(), + ); + } + _ => { + span_err( + arg_name.span().unwrap(), + "`code` is the only valid nested attribute", + ) + .emit(); + } + } + if input.is_empty() { + break; + } + input.parse::()?; } Ok(()) })?; diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index c310b99d53513..6ca2c90abb900 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use proc_macro::Span; use proc_macro2::{Ident, TokenStream}; use quote::{ToTokens, format_ident, quote}; -use syn::meta::ParseNestedMeta; +use syn::parse::ParseStream; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{Attribute, Field, LitStr, Meta, Path, Token, Type, TypeTuple, parenthesized}; @@ -428,64 +428,51 @@ pub(super) enum AllowMultipleAlternatives { } fn parse_suggestion_values( - nested: ParseNestedMeta<'_>, + nested: ParseStream<'_>, allow_multiple: AllowMultipleAlternatives, ) -> syn::Result> { - let values = if let Ok(val) = nested.value() { - vec![val.parse()?] - } else { - let content; - parenthesized!(content in nested.input); + if nested.parse::().is_ok() { + return Ok(vec![nested.parse::()?]); + } + + let content; + parenthesized!(content in nested); + if let AllowMultipleAlternatives::No = allow_multiple { + span_err(content.span().unwrap(), "expected exactly one string literal for `code = ...`") + .emit(); + return Ok(vec![]); + } - if let AllowMultipleAlternatives::No = allow_multiple { + let literals = Punctuated::::parse_terminated(&content); + Ok(match literals { + Ok(p) if p.is_empty() => { span_err( - nested.input.span().unwrap(), - "expected exactly one string literal for `code = ...`", + content.span().unwrap(), + "expected at least one string literal for `code(...)`", ) .emit(); vec![] - } else { - let literals = Punctuated::::parse_terminated(&content); - - match literals { - Ok(p) if p.is_empty() => { - span_err( - content.span().unwrap(), - "expected at least one string literal for `code(...)`", - ) - .emit(); - vec![] - } - Ok(p) => p.into_iter().collect(), - Err(_) => { - span_err( - content.span().unwrap(), - "`code(...)` must contain only string literals", - ) - .emit(); - vec![] - } - } } - }; - - Ok(values) + Ok(p) => p.into_iter().collect(), + Err(_) => { + span_err(content.span().unwrap(), "`code(...)` must contain only string literals") + .emit(); + vec![] + } + }) } /// Constructs the `format!()` invocation(s) necessary for a `#[suggestion*(code = "foo")]` or /// `#[suggestion*(code("foo", "bar"))]` attribute field pub(super) fn build_suggestion_code( code_field: &Ident, - nested: ParseNestedMeta<'_>, + nested: ParseStream<'_>, fields: &impl HasFieldMap, allow_multiple: AllowMultipleAlternatives, -) -> TokenStream { - let values = match parse_suggestion_values(nested, allow_multiple) { - Ok(x) => x, - Err(e) => return e.into_compile_error(), - }; +) -> Result { + let values = parse_suggestion_values(nested, allow_multiple)?; - if let AllowMultipleAlternatives::Yes = allow_multiple { + Ok(if let AllowMultipleAlternatives::Yes = allow_multiple { let formatted_strings: Vec<_> = values .into_iter() .map(|value| fields.build_format(&value.value(), value.span())) @@ -497,7 +484,7 @@ pub(super) fn build_suggestion_code( } else { // error handled previously quote! { let #code_field = String::new(); } - } + }) } /// Possible styles for suggestion subdiagnostics. @@ -709,112 +696,95 @@ impl SubdiagnosticVariant { let mut code = None; let mut suggestion_kind = None; - let mut first = true; let mut slug = None; let mut no_span = false; - list.parse_nested_meta(|nested| { - if nested.input.is_empty() || nested.input.peek(Token![,]) { - if first { - slug = Some(nested.path); - } else if nested.path.is_ident("no_span") { - no_span = true; - } else { - span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit(); + list.parse_args_with(|input: ParseStream<'_>| { + let mut is_first = true; + while !input.is_empty() { + let arg_name: Path = input.parse::()?; + let arg_name_span = arg_name.span().unwrap(); + if input.is_empty() || input.parse::().is_ok() { + if is_first { + slug = Some(arg_name); + is_first = false; + } else { + span_err(arg_name_span, "a diagnostic slug must be the first argument to the attribute").emit(); + } + continue } + is_first = false; + + match (arg_name.require_ident()?.to_string().as_str(), &mut kind) { + // ("no_span", _) => no_span = true, + ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { + let code_init = build_suggestion_code( + &code_field, + &input, + fields, + AllowMultipleAlternatives::Yes, + )?; + code.set_once(code_init, arg_name_span); + } + ( + "applicability", + SubdiagnosticKind::Suggestion { applicability, .. } + | SubdiagnosticKind::MultipartSuggestion { applicability, .. }, + ) => { + input.parse::()?; + let value = input.parse::()?; + let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| { + span_err(value.span().unwrap(), "invalid applicability").emit(); + Applicability::Unspecified + }); + applicability.set_once(value, span); + } + ( + "style", + SubdiagnosticKind::Suggestion { .. } + | SubdiagnosticKind::MultipartSuggestion { .. }, + ) => { + input.parse::()?; + let value = input.parse::()?; + + let value = value.value().parse().unwrap_or_else(|()| { + span_err(value.span().unwrap(), "invalid suggestion style") + .help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`") + .emit(); + SuggestionKind::Normal + }); + + suggestion_kind.set_once(value, span); + } - first = false; - return Ok(()); - } - - first = false; - - let nested_name = nested.path.segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - - let path_span = nested.path.span().unwrap(); - let val_span = nested.input.span().unwrap(); - - macro_rules! get_string { - () => {{ - let Ok(value) = nested.value().and_then(|x| x.parse::()) else { - span_err(val_span, "expected `= \"xxx\"`").emit(); - return Ok(()); - }; - value - }}; - } - let mut has_errors = false; - let input = nested.input; - - match (nested_name, &mut kind) { - ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { - let code_init = build_suggestion_code( - code_field, - nested, - fields, - AllowMultipleAlternatives::Yes, - ); - code.set_once(code_init, path_span); - } - ( - "applicability", - SubdiagnosticKind::Suggestion { applicability, .. } - | SubdiagnosticKind::MultipartSuggestion { applicability, .. }, - ) => { - let value = get_string!(); - let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| { - span_err(value.span().unwrap(), "invalid applicability").emit(); - has_errors = true; - Applicability::Unspecified - }); - applicability.set_once(value, span); - } - ( - "style", - SubdiagnosticKind::Suggestion { .. } - | SubdiagnosticKind::MultipartSuggestion { .. }, - ) => { - let value = get_string!(); - - let value = value.value().parse().unwrap_or_else(|()| { - span_err(value.span().unwrap(), "invalid suggestion style") - .help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`") + // Invalid nested attribute + (_, SubdiagnosticKind::Suggestion { .. }) => { + span_err(arg_name_span, "invalid nested attribute") + .help( + "only `no_span`, `style`, `code` and `applicability` are valid nested attributes", + ) .emit(); - has_errors = true; - SuggestionKind::Normal - }); - - suggestion_kind.set_once(value, span); - } - - // Invalid nested attribute - (_, SubdiagnosticKind::Suggestion { .. }) => { - span_err(path_span, "invalid nested attribute") - .help( - "only `no_span`, `style`, `code` and `applicability` are valid nested attributes", - ) - .emit(); - has_errors = true; - } - (_, SubdiagnosticKind::MultipartSuggestion { .. }) => { - span_err(path_span, "invalid nested attribute") - .help("only `no_span`, `style` and `applicability` are valid nested attributes") - .emit(); - has_errors = true; - } - _ => { - span_err(path_span, "only `no_span` is a valid nested attribute").emit(); - has_errors = true; + // Consume the rest of the input to avoid spamming errors + let _ = input.parse::(); + } + (_, SubdiagnosticKind::MultipartSuggestion { .. }) => { + span_err(arg_name_span, "invalid nested attribute") + .help("only `no_span`, `style` and `applicability` are valid nested attributes") + .emit(); + // Consume the rest of the input to avoid spamming errors + let _ = input.parse::(); + } + _ => { + span_err(arg_name_span, "only `no_span` is a valid nested attribute").emit(); + // Consume the rest of the input to avoid spamming errors + let _ = input.parse::(); + } } - } - if has_errors { - // Consume the rest of the input to avoid spamming errors - let _ = input.parse::(); + if input.is_empty() { break } + input.parse::()?; } - Ok(()) })?; diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 506ec7f88a82a..72b414362c729 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -803,7 +803,7 @@ struct SuggestionsInvalidItem { sub: Span, } -#[derive(Diagnostic)] //~ ERROR cannot find value `__code_34` in this scope +#[derive(Diagnostic)] #[diag(no_crate_example)] struct SuggestionsInvalidLiteral { #[suggestion(code = 3)] diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 29132b8325f30..3ccf89cedec76 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -277,10 +277,10 @@ LL | #[help(no_crate_help)] | ^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/diagnostic-derive.rs:533:32 + --> $DIR/diagnostic-derive.rs:533:29 | LL | #[label(no_crate_label, foo)] - | ^ + | ^^^ error: derive(Diagnostic): only `no_span` is a valid nested attribute --> $DIR/diagnostic-derive.rs:541:29 @@ -606,14 +606,6 @@ error[E0425]: cannot find value `nonsense` in module `crate::fluent_generated` LL | #[diag(nonsense, code = E0123)] | ^^^^^^^^ not found in `crate::fluent_generated` -error[E0425]: cannot find value `__code_34` in this scope - --> $DIR/diagnostic-derive.rs:806:10 - | -LL | #[derive(Diagnostic)] - | ^^^^^^^^^^ not found in this scope - | - = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied --> $DIR/diagnostic-derive.rs:347:12 | @@ -636,7 +628,7 @@ note: required by a bound in `Diag::<'a, G>::arg` = note: in this macro invocation = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 81 previous errors +error: aborting due to 80 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 941668ad602e4..578fc728de53e 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -95,7 +95,7 @@ struct G { #[derive(Subdiagnostic)] #[label("...")] -//~^ ERROR unexpected literal in nested attribute, expected ident +//~^ ERROR expected identifier struct H { #[primary_span] span: Span, @@ -775,7 +775,7 @@ struct SuggestionStyleInvalid1 { #[derive(Subdiagnostic)] #[suggestion(no_crate_example, code = "", style = 42)] -//~^ ERROR expected `= "xxx"` +//~^ ERROR expected string literal struct SuggestionStyleInvalid2 { #[primary_span] sub: Span, @@ -791,8 +791,7 @@ struct SuggestionStyleInvalid3 { #[derive(Subdiagnostic)] #[suggestion(no_crate_example, code = "", style("foo"))] -//~^ ERROR expected `= "xxx"` -//~| ERROR expected `,` +//~^ ERROR expected `=` struct SuggestionStyleInvalid4 { #[primary_span] sub: Span, diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index c31da4421d255..568d75d838fe7 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -34,7 +34,7 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label( LL | #[label(bug = "...")] | ^ -error: unexpected literal in nested attribute, expected ident +error: expected identifier --> $DIR/subdiagnostic-derive.rs:97:9 | LL | #[label("...")] @@ -175,10 +175,10 @@ LL | | } | |_^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/subdiagnostic-derive.rs:317:44 + --> $DIR/subdiagnostic-derive.rs:317:27 | LL | #[label(no_crate_example, no_crate::example)] - | ^ + | ^^^^^^^^ error: derive(Diagnostic): attribute specified multiple times --> $DIR/subdiagnostic-derive.rs:330:5 @@ -381,10 +381,10 @@ LL | #[applicability] | ^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:663:34 + --> $DIR/subdiagnostic-derive.rs:663:28 | LL | #[suggestion_part(code("foo"))] - | ^ + | ^^^^^ error: unexpected token, expected `)` --> $DIR/subdiagnostic-derive.rs:663:28 @@ -393,10 +393,10 @@ LL | #[suggestion_part(code("foo"))] | ^^^^^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:673:41 + --> $DIR/subdiagnostic-derive.rs:673:28 | LL | #[suggestion_part(code("foo", "bar"))] - | ^ + | ^^^^^ error: unexpected token, expected `)` --> $DIR/subdiagnostic-derive.rs:673:28 @@ -405,10 +405,10 @@ LL | #[suggestion_part(code("foo", "bar"))] | ^^^^^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:683:30 + --> $DIR/subdiagnostic-derive.rs:683:28 | LL | #[suggestion_part(code(3))] - | ^ + | ^ error: unexpected token, expected `)` --> $DIR/subdiagnostic-derive.rs:683:28 @@ -417,10 +417,10 @@ LL | #[suggestion_part(code(3))] | ^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:693:29 + --> $DIR/subdiagnostic-derive.rs:693:28 | LL | #[suggestion_part(code())] - | ^ + | ^ error: expected string literal --> $DIR/subdiagnostic-derive.rs:702:30 @@ -464,32 +464,26 @@ LL | #[suggestion(no_crate_example, code = "", style = "foo")] | = help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only` -error: derive(Diagnostic): expected `= "xxx"` - --> $DIR/subdiagnostic-derive.rs:777:49 +error: expected string literal + --> $DIR/subdiagnostic-derive.rs:777:51 | LL | #[suggestion(no_crate_example, code = "", style = 42)] - | ^ + | ^^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/subdiagnostic-derive.rs:785:48 + --> $DIR/subdiagnostic-derive.rs:785:43 | LL | #[suggestion(no_crate_example, code = "", style)] - | ^ - -error: derive(Diagnostic): expected `= "xxx"` - --> $DIR/subdiagnostic-derive.rs:793:48 - | -LL | #[suggestion(no_crate_example, code = "", style("foo"))] - | ^ + | ^^^^^ -error: expected `,` +error: expected `=` --> $DIR/subdiagnostic-derive.rs:793:48 | LL | #[suggestion(no_crate_example, code = "", style("foo"))] | ^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:805:5 + --> $DIR/subdiagnostic-derive.rs:804:5 | LL | #[primary_span] | ^ @@ -498,7 +492,7 @@ LL | #[primary_span] = help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:802:1 + --> $DIR/subdiagnostic-derive.rs:801:1 | LL | #[suggestion(no_crate_example, code = "")] | ^ @@ -557,5 +551,5 @@ error: cannot find attribute `bar` in this scope LL | #[bar("...")] | ^^^ -error: aborting due to 84 previous errors +error: aborting due to 83 previous errors From 0bf3f5d51cb853884240792818d81e70daec6ab7 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 25 Jan 2026 22:41:56 +0100 Subject: [PATCH 40/43] Remove unused `no_span` option --- .../src/diagnostics/diagnostic_builder.rs | 2 +- .../src/diagnostics/subdiagnostic.rs | 23 ++++++------------- .../rustc_macros/src/diagnostics/utils.rs | 15 +++++------- .../session-diagnostic/diagnostic-derive.rs | 4 ++-- .../diagnostic-derive.stderr | 8 +++---- .../subdiagnostic-derive.rs | 12 +++++----- .../subdiagnostic-derive.stderr | 14 +++++------ 7 files changed, 33 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index e71c84c805a7b..748d1d3a60f36 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -192,7 +192,7 @@ impl DiagnosticDeriveVariantBuilder { SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), }); - Ok(Some((subdiag.kind, slug, subdiag.no_span))) + Ok(Some((subdiag.kind, slug, false))) } /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 189d83c421607..e00d175fc295f 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -188,13 +188,11 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics { } impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { - fn identify_kind( - &mut self, - ) -> Result, DiagnosticDeriveError> { + fn identify_kind(&mut self) -> Result, DiagnosticDeriveError> { let mut kind_slugs = vec![]; for attr in self.variant.ast().attrs { - let Some(SubdiagnosticVariant { kind, slug, no_span }) = + let Some(SubdiagnosticVariant { kind, slug }) = SubdiagnosticVariant::from_attr(attr, self)? else { // Some attributes aren't errors - like documentation comments - but also aren't @@ -214,7 +212,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { ); }; - kind_slugs.push((kind, slug, no_span)); + kind_slugs.push((kind, slug)); } Ok(kind_slugs) @@ -505,8 +503,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { pub(crate) fn into_tokens(&mut self) -> Result { let kind_slugs = self.identify_kind()?; - let kind_stats: KindsStatistics = - kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect(); + let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect(); let init = if kind_stats.has_multipart_suggestion { quote! { let mut suggestions = Vec::new(); } @@ -539,17 +536,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let diag = &self.parent.diag; let mut calls = TokenStream::new(); - for (kind, slug, no_span) in kind_slugs { + for (kind, slug) in kind_slugs { let message = format_ident!("__message"); calls.extend( quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); }, ); - let name = format_ident!( - "{}{}", - if span_field.is_some() && !no_span { "span_" } else { "" }, - kind - ); + let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let call = match kind { SubdiagnosticKind::Suggestion { suggestion_kind, @@ -601,9 +594,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } _ => { - if let Some(span) = span_field - && !no_span - { + if let Some(span) = span_field { quote! { #diag.#name(#span, #message); } } else { quote! { #diag.#name(#message); } diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 6ca2c90abb900..4c73e6d453e90 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -592,12 +592,11 @@ pub(super) enum SubdiagnosticKind { pub(super) struct SubdiagnosticVariant { pub(super) kind: SubdiagnosticKind, pub(super) slug: Option, - pub(super) no_span: bool, } impl SubdiagnosticVariant { /// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`, - /// `#[error(parser::add_paren, no_span)]` or `#[suggestion(code = "...")]`. Returns the + /// `#[error(parser::add_paren)]` or `#[suggestion(code = "...")]`. Returns the /// `SubdiagnosticKind` and the diagnostic slug, if specified. pub(super) fn from_attr( attr: &Attribute, @@ -681,7 +680,7 @@ impl SubdiagnosticVariant { | SubdiagnosticKind::HelpOnce | SubdiagnosticKind::Warn | SubdiagnosticKind::MultipartSuggestion { .. } => { - return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false })); + return Ok(Some(SubdiagnosticVariant { kind, slug: None })); } SubdiagnosticKind::Suggestion { .. } => { throw_span_err!(span, "suggestion without `code = \"...\"`") @@ -697,7 +696,6 @@ impl SubdiagnosticVariant { let mut suggestion_kind = None; let mut slug = None; - let mut no_span = false; list.parse_args_with(|input: ParseStream<'_>| { let mut is_first = true; @@ -716,7 +714,6 @@ impl SubdiagnosticVariant { is_first = false; match (arg_name.require_ident()?.to_string().as_str(), &mut kind) { - // ("no_span", _) => no_span = true, ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let code_init = build_suggestion_code( &code_field, @@ -762,7 +759,7 @@ impl SubdiagnosticVariant { (_, SubdiagnosticKind::Suggestion { .. }) => { span_err(arg_name_span, "invalid nested attribute") .help( - "only `no_span`, `style`, `code` and `applicability` are valid nested attributes", + "only `style`, `code` and `applicability` are valid nested attributes", ) .emit(); // Consume the rest of the input to avoid spamming errors @@ -770,13 +767,13 @@ impl SubdiagnosticVariant { } (_, SubdiagnosticKind::MultipartSuggestion { .. }) => { span_err(arg_name_span, "invalid nested attribute") - .help("only `no_span`, `style` and `applicability` are valid nested attributes") + .help("only `style` and `applicability` are valid nested attributes") .emit(); // Consume the rest of the input to avoid spamming errors let _ = input.parse::(); } _ => { - span_err(arg_name_span, "only `no_span` is a valid nested attribute").emit(); + span_err(arg_name_span, "no nested attribute expected here").emit(); // Consume the rest of the input to avoid spamming errors let _ = input.parse::(); } @@ -821,7 +818,7 @@ impl SubdiagnosticVariant { | SubdiagnosticKind::Warn => {} } - Ok(Some(SubdiagnosticVariant { kind, slug, no_span })) + Ok(Some(SubdiagnosticVariant { kind, slug })) } } diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 72b414362c729..798eca68b1c91 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -539,7 +539,7 @@ struct LabelWithTrailingPath { #[diag(no_crate_example, code = E0123)] struct LabelWithTrailingNameValue { #[label(no_crate_label, foo = "...")] - //~^ ERROR only `no_span` is a valid nested attribute + //~^ ERROR no nested attribute expected here span: Span, } @@ -547,7 +547,7 @@ struct LabelWithTrailingNameValue { #[diag(no_crate_example, code = E0123)] struct LabelWithTrailingList { #[label(no_crate_label, foo("..."))] - //~^ ERROR only `no_span` is a valid nested attribute + //~^ ERROR no nested attribute expected here span: Span, } diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 3ccf89cedec76..9ecb6c15767e9 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -192,7 +192,7 @@ error: derive(Diagnostic): invalid nested attribute LL | #[suggestion(nonsense = "bar")] | ^^^^^^^^ | - = help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes + = help: only `style`, `code` and `applicability` are valid nested attributes error: derive(Diagnostic): suggestion without `code = "..."` --> $DIR/diagnostic-derive.rs:235:5 @@ -206,7 +206,7 @@ error: derive(Diagnostic): invalid nested attribute LL | #[suggestion(msg = "bar")] | ^^^ | - = help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes + = help: only `style`, `code` and `applicability` are valid nested attributes error: derive(Diagnostic): suggestion without `code = "..."` --> $DIR/diagnostic-derive.rs:244:5 @@ -282,13 +282,13 @@ error: derive(Diagnostic): a diagnostic slug must be the first argument to the a LL | #[label(no_crate_label, foo)] | ^^^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute +error: derive(Diagnostic): no nested attribute expected here --> $DIR/diagnostic-derive.rs:541:29 | LL | #[label(no_crate_label, foo = "...")] | ^^^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute +error: derive(Diagnostic): no nested attribute expected here --> $DIR/diagnostic-derive.rs:549:29 | LL | #[label(no_crate_label, foo("..."))] diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 578fc728de53e..b2e7b4c61daa0 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -85,7 +85,7 @@ struct F { #[derive(Subdiagnostic)] #[label(bug = "...")] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here //~| ERROR diagnostic slug must be first argument struct G { #[primary_span] @@ -104,7 +104,7 @@ struct H { #[derive(Subdiagnostic)] #[label(slug = 4)] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here //~| ERROR diagnostic slug must be first argument struct J { #[primary_span] @@ -114,7 +114,7 @@ struct J { #[derive(Subdiagnostic)] #[label(slug("..."))] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here //~| ERROR diagnostic slug must be first argument struct K { #[primary_span] @@ -133,7 +133,7 @@ struct M { #[derive(Subdiagnostic)] #[label(no_crate_example, code = "...")] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here struct N { #[primary_span] span: Span, @@ -142,7 +142,7 @@ struct N { #[derive(Subdiagnostic)] #[label(no_crate_example, applicability = "machine-applicable")] -//~^ ERROR only `no_span` is a valid nested attribute +//~^ ERROR no nested attribute expected here struct O { #[primary_span] span: Span, @@ -214,7 +214,7 @@ enum T { enum U { #[label(code = "...")] //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute - //~| ERROR only `no_span` is a valid nested attribute + //~| ERROR no nested attribute expected here A { #[primary_span] span: Span, diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index 568d75d838fe7..63634741e9347 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -22,7 +22,7 @@ error: derive(Diagnostic): `#[label = ...]` is not a valid attribute LL | #[label = "..."] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute +error: derive(Diagnostic): no nested attribute expected here --> $DIR/subdiagnostic-derive.rs:87:9 | LL | #[label(bug = "...")] @@ -40,7 +40,7 @@ error: expected identifier LL | #[label("...")] | ^^^^^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute +error: derive(Diagnostic): no nested attribute expected here --> $DIR/subdiagnostic-derive.rs:106:9 | LL | #[label(slug = 4)] @@ -52,7 +52,7 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label( LL | #[label(slug = 4)] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute +error: derive(Diagnostic): no nested attribute expected here --> $DIR/subdiagnostic-derive.rs:116:9 | LL | #[label(slug("..."))] @@ -70,13 +70,13 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label( LL | #[label()] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute +error: derive(Diagnostic): no nested attribute expected here --> $DIR/subdiagnostic-derive.rs:135:27 | LL | #[label(no_crate_example, code = "...")] | ^^^^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute +error: derive(Diagnostic): no nested attribute expected here --> $DIR/subdiagnostic-derive.rs:144:27 | LL | #[label(no_crate_example, applicability = "machine-applicable")] @@ -112,7 +112,7 @@ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute LL | #[bar("...")] | ^ -error: derive(Diagnostic): only `no_span` is a valid nested attribute +error: derive(Diagnostic): no nested attribute expected here --> $DIR/subdiagnostic-derive.rs:215:13 | LL | #[label(code = "...")] @@ -292,7 +292,7 @@ error: derive(Diagnostic): invalid nested attribute LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")] | ^^^^ | - = help: only `no_span`, `style` and `applicability` are valid nested attributes + = help: only `style` and `applicability` are valid nested attributes error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields --> $DIR/subdiagnostic-derive.rs:530:1 From 2e8347abf4147d2bffe4d7989a21b17ae04cdb57 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Wed, 28 Jan 2026 22:30:29 +0100 Subject: [PATCH 41/43] Remove `HasFieldMap` trait in favour of passing `FieldMap` directly --- .../src/diagnostics/diagnostic_builder.rs | 10 +- .../src/diagnostics/subdiagnostic.rs | 18 +- .../rustc_macros/src/diagnostics/utils.rs | 186 +++++++++--------- 3 files changed, 99 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 748d1d3a60f36..25110fd4f908e 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -12,7 +12,7 @@ use crate::diagnostics::error::{ DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err, }; use crate::diagnostics::utils::{ - FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, + FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind, build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error, should_generate_arg, type_is_bool, type_is_unit, type_matches_path, }; @@ -50,12 +50,6 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { pub code: SpannedOption<()>, } -impl HasFieldMap for DiagnosticDeriveVariantBuilder { - fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { - self.field_map.get(field) - } -} - impl DiagnosticDeriveKind { /// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the /// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions @@ -170,7 +164,7 @@ impl DiagnosticDeriveVariantBuilder { &self, attr: &Attribute, ) -> Result, DiagnosticDeriveError> { - let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else { + let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, &self.field_map)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. return Ok(None); diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index e00d175fc295f..db2a19ab85ba4 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -12,10 +12,10 @@ use crate::diagnostics::error::{ DiagnosticDeriveError, invalid_attr, span_err, throw_invalid_attr, throw_span_err, }; use crate::diagnostics::utils::{ - AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, - SpannedOption, SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, - new_code_ident, report_error_if_not_applied_to_applicability, - report_error_if_not_applied_to_span, should_generate_arg, + AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, + SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident, + report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, + should_generate_arg, }; /// The central struct for constructing the `add_to_diag` method from an annotated struct. @@ -143,12 +143,6 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> { is_enum: bool, } -impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> { - fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { - self.fields.get(field) - } -} - /// Provides frequently-needed information about the diagnostic kinds being derived for this type. #[derive(Clone, Copy, Debug)] struct KindsStatistics { @@ -193,7 +187,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { for attr in self.variant.ast().attrs { let Some(SubdiagnosticVariant { kind, slug }) = - SubdiagnosticVariant::from_attr(attr, self)? + SubdiagnosticVariant::from_attr(attr, &self.fields)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. @@ -445,7 +439,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let formatting_init = build_suggestion_code( &code_field, input, - self, + &self.fields, AllowMultipleAlternatives::No, )?; code.set_once( diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 4c73e6d453e90..f084ba60ae3f4 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -261,108 +261,104 @@ impl SetOnce for SpannedOption { pub(super) type FieldMap = HashMap; -pub(crate) trait HasFieldMap { - /// Returns the binding for the field with the given name, if it exists on the type. - fn get_field_binding(&self, field: &String) -> Option<&TokenStream>; - - /// In the strings in the attributes supplied to this macro, we want callers to be able to - /// reference fields in the format string. For example: - /// - /// ```ignore (not-usage-example) - /// /// Suggest `==` when users wrote `===`. - /// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")] - /// struct NotJavaScriptEq { - /// #[primary_span] - /// span: Span, - /// lhs: Ident, - /// rhs: Ident, - /// } - /// ``` - /// - /// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to - /// `self.rhs`, then generate this call to `format!`: - /// - /// ```ignore (not-usage-example) - /// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs) - /// ``` - /// - /// This function builds the entire call to `format!`. - fn build_format(&self, input: &str, span: proc_macro2::Span) -> TokenStream { - // This set is used later to generate the final format string. To keep builds reproducible, - // the iteration order needs to be deterministic, hence why we use a `BTreeSet` here - // instead of a `HashSet`. - let mut referenced_fields: BTreeSet = BTreeSet::new(); - - // At this point, we can start parsing the format string. - let mut it = input.chars().peekable(); - - // Once the start of a format string has been found, process the format string and spit out - // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so - // the next call to `it.next()` retrieves the next character. - while let Some(c) = it.next() { - if c != '{' { - continue; +/// In the strings in the attributes supplied to this macro, we want callers to be able to +/// reference fields in the format string. For example: +/// +/// ```ignore (not-usage-example) +/// /// Suggest `==` when users wrote `===`. +/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")] +/// struct NotJavaScriptEq { +/// #[primary_span] +/// span: Span, +/// lhs: Ident, +/// rhs: Ident, +/// } +/// ``` +/// +/// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to +/// `self.rhs`, then generate this call to `format!`: +/// +/// ```ignore (not-usage-example) +/// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs) +/// ``` +/// +/// This function builds the entire call to `format!`. +pub(super) fn build_format( + field_map: &FieldMap, + input: &str, + span: proc_macro2::Span, +) -> TokenStream { + // This set is used later to generate the final format string. To keep builds reproducible, + // the iteration order needs to be deterministic, hence why we use a `BTreeSet` here + // instead of a `HashSet`. + let mut referenced_fields: BTreeSet = BTreeSet::new(); + + // At this point, we can start parsing the format string. + let mut it = input.chars().peekable(); + + // Once the start of a format string has been found, process the format string and spit out + // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so + // the next call to `it.next()` retrieves the next character. + while let Some(c) = it.next() { + if c != '{' { + continue; + } + if *it.peek().unwrap_or(&'\0') == '{' { + assert_eq!(it.next().unwrap(), '{'); + continue; + } + let mut eat_argument = || -> Option { + let mut result = String::new(); + // Format specifiers look like: + // + // format := '{' [ argument ] [ ':' format_spec ] '}' . + // + // Therefore, we only need to eat until ':' or '}' to find the argument. + while let Some(c) = it.next() { + result.push(c); + let next = *it.peek().unwrap_or(&'\0'); + if next == '}' { + break; + } else if next == ':' { + // Eat the ':' character. + assert_eq!(it.next().unwrap(), ':'); + break; + } } - if *it.peek().unwrap_or(&'\0') == '{' { - assert_eq!(it.next().unwrap(), '{'); + // Eat until (and including) the matching '}' + while it.next()? != '}' { continue; } - let mut eat_argument = || -> Option { - let mut result = String::new(); - // Format specifiers look like: - // - // format := '{' [ argument ] [ ':' format_spec ] '}' . - // - // Therefore, we only need to eat until ':' or '}' to find the argument. - while let Some(c) = it.next() { - result.push(c); - let next = *it.peek().unwrap_or(&'\0'); - if next == '}' { - break; - } else if next == ':' { - // Eat the ':' character. - assert_eq!(it.next().unwrap(), ':'); - break; - } - } - // Eat until (and including) the matching '}' - while it.next()? != '}' { - continue; - } - Some(result) - }; + Some(result) + }; - if let Some(referenced_field) = eat_argument() { - referenced_fields.insert(referenced_field); - } + if let Some(referenced_field) = eat_argument() { + referenced_fields.insert(referenced_field); } + } - // At this point, `referenced_fields` contains a set of the unique fields that were - // referenced in the format string. Generate the corresponding "x = self.x" format - // string parameters: - let args = referenced_fields.into_iter().map(|field: String| { - let field_ident = format_ident!("{}", field); - let value = match self.get_field_binding(&field) { - Some(value) => value.clone(), - // This field doesn't exist. Emit a diagnostic. - None => { - span_err( - span.unwrap(), - format!("`{field}` doesn't refer to a field on this type"), - ) + // At this point, `referenced_fields` contains a set of the unique fields that were + // referenced in the format string. Generate the corresponding "x = self.x" format + // string parameters: + let args = referenced_fields.into_iter().map(|field: String| { + let field_ident = format_ident!("{}", field); + let value = match field_map.get(&field) { + Some(value) => value.clone(), + // This field doesn't exist. Emit a diagnostic. + None => { + span_err(span.unwrap(), format!("`{field}` doesn't refer to a field on this type")) .emit(); - quote! { - "{#field}" - } + quote! { + "{#field}" } - }; - quote! { - #field_ident = #value } - }); + }; quote! { - format!(#input #(,#args)*) + #field_ident = #value } + }); + quote! { + format!(#input #(,#args)*) } } @@ -467,7 +463,7 @@ fn parse_suggestion_values( pub(super) fn build_suggestion_code( code_field: &Ident, nested: ParseStream<'_>, - fields: &impl HasFieldMap, + fields: &FieldMap, allow_multiple: AllowMultipleAlternatives, ) -> Result { let values = parse_suggestion_values(nested, allow_multiple)?; @@ -475,11 +471,11 @@ pub(super) fn build_suggestion_code( Ok(if let AllowMultipleAlternatives::Yes = allow_multiple { let formatted_strings: Vec<_> = values .into_iter() - .map(|value| fields.build_format(&value.value(), value.span())) + .map(|value| build_format(fields, &value.value(), value.span())) .collect(); quote! { let #code_field = [#(#formatted_strings),*].into_iter(); } } else if let [value] = values.as_slice() { - let formatted_str = fields.build_format(&value.value(), value.span()); + let formatted_str = build_format(fields, &value.value(), value.span()); quote! { let #code_field = #formatted_str; } } else { // error handled previously @@ -600,7 +596,7 @@ impl SubdiagnosticVariant { /// `SubdiagnosticKind` and the diagnostic slug, if specified. pub(super) fn from_attr( attr: &Attribute, - fields: &impl HasFieldMap, + fields: &FieldMap, ) -> Result, DiagnosticDeriveError> { // Always allow documentation comments. if is_doc_comment(attr) { From b71ff51277dbd5907a7a8089b5d926a02e53d44d Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 28 Jan 2026 00:58:13 -0500 Subject: [PATCH 42/43] Update std and tests to match std::simd API (remove LaneCount bound and rename to_int to to_simd) --- library/core/src/slice/mod.rs | 2 -- src/tools/miri/tests/pass/intrinsics/portable-simd.rs | 10 +++++----- tests/ui/simd/dont-invalid-bitcast-masks.rs | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 139d2a4b42729..429f90acec348 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -4443,7 +4443,6 @@ impl [T] { where Simd: AsRef<[T; LANES]>, T: simd::SimdElement, - simd::LaneCount: simd::SupportedLaneCount, { // These are expected to always match, as vector types are laid out like // arrays per , but we @@ -4479,7 +4478,6 @@ impl [T] { where Simd: AsMut<[T; LANES]>, T: simd::SimdElement, - simd::LaneCount: simd::SupportedLaneCount, { // These are expected to always match, as vector types are laid out like // arrays per , but we diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index 56c000633e586..6c5e06518f569 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -471,7 +471,7 @@ fn simd_ops_i32() { fn simd_mask() { use std::intrinsics::simd::*; - let intmask = Mask::from_int(i32x4::from_array([0, -1, 0, 0])); + let intmask = Mask::from_simd(i32x4::from_array([0, -1, 0, 0])); assert_eq!(intmask, Mask::from_array([false, true, false, false])); assert_eq!(intmask.to_array(), [false, true, false, false]); @@ -486,8 +486,8 @@ fn simd_mask() { // Also directly call intrinsic, to test both kinds of return types. unsafe { - let bitmask1: u16 = simd_bitmask(mask.to_int()); - let bitmask2: [u8; 2] = simd_bitmask(mask.to_int()); + let bitmask1: u16 = simd_bitmask(mask.to_simd()); + let bitmask2: [u8; 2] = simd_bitmask(mask.to_simd()); if cfg!(target_endian = "little") { assert_eq!(bitmask1, 0b1010001101001001); assert_eq!(bitmask2, [0b01001001, 0b10100011]); @@ -506,8 +506,8 @@ fn simd_mask() { assert_eq!(bitmask, 0b1000); assert_eq!(Mask::::from_bitmask(bitmask), mask); unsafe { - let bitmask1: u8 = simd_bitmask(mask.to_int()); - let bitmask2: [u8; 1] = simd_bitmask(mask.to_int()); + let bitmask1: u8 = simd_bitmask(mask.to_simd()); + let bitmask2: [u8; 1] = simd_bitmask(mask.to_simd()); if cfg!(target_endian = "little") { assert_eq!(bitmask1, 0b1000); assert_eq!(bitmask2, [0b1000]); diff --git a/tests/ui/simd/dont-invalid-bitcast-masks.rs b/tests/ui/simd/dont-invalid-bitcast-masks.rs index 3d8376207cd07..1e2d097198d03 100644 --- a/tests/ui/simd/dont-invalid-bitcast-masks.rs +++ b/tests/ui/simd/dont-invalid-bitcast-masks.rs @@ -12,6 +12,6 @@ use std::simd::num::*; pub unsafe fn mask_to_array(mask: u8) -> [i32; 8] { let mut output = [0; 8]; let m = masksizex8::from_bitmask(mask as _); - output.copy_from_slice(&m.to_int().cast::().to_array()); + output.copy_from_slice(&m.to_simd().cast::().to_array()); output } From 4b22ee9fc5a208658d1fbfd2492ea3105f822e5d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 22 Jan 2026 21:09:44 +0900 Subject: [PATCH 43/43] Tweak E0599 to consolidate unsatisfied trait bound messages --- .../rustc_hir_typeck/src/method/suggest.rs | 145 +++++++++++++++++- .../ui/proc-macro/quote/not-repeatable.stderr | 2 +- ...ssociated-item-unsatisfied-trait-bounds.rs | 9 ++ ...iated-item-unsatisfied-trait-bounds.stderr | 33 ++++ 4 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 tests/ui/traits/associated-item-unsatisfied-trait-bounds.rs create mode 100644 tests/ui/traits/associated-item-unsatisfied-trait-bounds.stderr diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 5673d044ad2ce..b318f5bb25d22 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -13,7 +13,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err}; +use rustc_errors::{ + Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err, +}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -50,6 +52,51 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem}; use crate::method::probe::UnsatisfiedPredicates; use crate::{Expectation, FnCtxt}; +/// Tracks trait bounds and detects duplicates between ref and non-ref versions of self types. +/// This is used to condense error messages when the same trait bound appears for both +/// `T` and `&T` (or `&mut T`). +struct TraitBoundDuplicateTracker { + trait_def_ids: FxIndexSet, + seen_ref: FxIndexSet, + seen_non_ref: FxIndexSet, + has_ref_dupes: bool, +} + +impl TraitBoundDuplicateTracker { + fn new() -> Self { + Self { + trait_def_ids: FxIndexSet::default(), + seen_ref: FxIndexSet::default(), + seen_non_ref: FxIndexSet::default(), + has_ref_dupes: false, + } + } + + /// Track a trait bound. `is_ref` indicates whether the self type is a reference. + fn track(&mut self, def_id: DefId, is_ref: bool) { + self.trait_def_ids.insert(def_id); + if is_ref { + if self.seen_non_ref.contains(&def_id) { + self.has_ref_dupes = true; + } + self.seen_ref.insert(def_id); + } else { + if self.seen_ref.contains(&def_id) { + self.has_ref_dupes = true; + } + self.seen_non_ref.insert(def_id); + } + } + + fn has_ref_dupes(&self) -> bool { + self.has_ref_dupes + } + + fn into_trait_def_ids(self) -> FxIndexSet { + self.trait_def_ids + } +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { self.autoderef(span, ty) @@ -1004,6 +1051,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_ident: Ident, item_kind: &str, bound_spans: SortedMap>, + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, ) { let mut ty_span = match rcvr_ty.kind() { ty::Param(param_type) => { @@ -1012,13 +1060,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(def, _) if def.did().is_local() => Some(self.tcx.def_span(def.did())), _ => None, }; + let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); + let mut tracker = TraitBoundDuplicateTracker::new(); + for (predicate, _parent_pred, _cause) in unsatisfied_predicates { + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + predicate.kind().skip_binder() + && let self_ty = pred.trait_ref.self_ty() + && self_ty.peel_refs() == rcvr_ty + { + let is_ref = matches!(self_ty.kind(), ty::Ref(..)); + tracker.track(pred.trait_ref.def_id, is_ref); + } + } + let has_ref_dupes = tracker.has_ref_dupes(); + let mut missing_trait_names = tracker + .into_trait_def_ids() + .into_iter() + .map(|def_id| format!("`{}`", self.tcx.def_path_str(def_id))) + .collect::>(); + missing_trait_names.sort(); + let should_condense = + has_ref_dupes && missing_trait_names.len() > 1 && matches!(rcvr_ty.kind(), ty::Adt(..)); + let missing_trait_list = if should_condense { + Some(match missing_trait_names.as_slice() { + [only] => only.clone(), + [first, second] => format!("{first} or {second}"), + [rest @ .., last] => format!("{} or {last}", rest.join(", ")), + [] => String::new(), + }) + } else { + None + }; for (span, mut bounds) in bound_spans { if !self.tcx.sess.source_map().is_span_accessible(span) { continue; } bounds.sort(); bounds.dedup(); - let pre = if Some(span) == ty_span { + let is_ty_span = Some(span) == ty_span; + if is_ty_span && should_condense { + ty_span.take(); + let label = if let Some(missing_trait_list) = &missing_trait_list { + format!( + "{item_kind} `{item_ident}` not found for this {} because `{rcvr_ty_str}` doesn't implement {missing_trait_list}", + rcvr_ty.prefix_string(self.tcx) + ) + } else { + format!( + "{item_kind} `{item_ident}` not found for this {}", + rcvr_ty.prefix_string(self.tcx) + ) + }; + err.span_label(span, label); + continue; + } + let pre = if is_ty_span { ty_span.take(); format!( "{item_kind} `{item_ident}` not found for this {} because it ", @@ -1248,6 +1344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_ident, item_kind, bound_spans, + unsatisfied_predicates, ); self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected); @@ -1507,6 +1604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_spans: &mut SortedMap>, ) { let tcx = self.tcx; + let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); let mut type_params = FxIndexMap::default(); // Pick out the list of unimplemented traits on the receiver. @@ -1798,6 +1896,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); spanned_predicates.sort_by_key(|(span, _)| *span); for (_, (primary_spans, span_labels, predicates)) in spanned_predicates { + let mut tracker = TraitBoundDuplicateTracker::new(); + let mut all_trait_bounds_for_rcvr = true; + for pred in &predicates { + match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + let self_ty = pred.trait_ref.self_ty(); + if self_ty.peel_refs() != rcvr_ty { + all_trait_bounds_for_rcvr = false; + break; + } + let is_ref = matches!(self_ty.kind(), ty::Ref(..)); + tracker.track(pred.trait_ref.def_id, is_ref); + } + _ => { + all_trait_bounds_for_rcvr = false; + break; + } + } + } + let has_ref_dupes = tracker.has_ref_dupes(); + let trait_def_ids = tracker.into_trait_def_ids(); let mut preds: Vec<_> = predicates .iter() .filter_map(|pred| format_pred(**pred)) @@ -1805,7 +1924,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); preds.sort(); preds.dedup(); - let msg = if let [pred] = &preds[..] { + let availability_note = if all_trait_bounds_for_rcvr + && has_ref_dupes + && trait_def_ids.len() > 1 + && matches!(rcvr_ty.kind(), ty::Adt(..)) + { + let mut trait_names = trait_def_ids + .into_iter() + .map(|def_id| format!("`{}`", tcx.def_path_str(def_id))) + .collect::>(); + trait_names.sort(); + listify(&trait_names, |name| name.to_string()).map(|traits| { + format!( + "for `{item_ident}` to be available, `{rcvr_ty_str}` must implement {traits}" + ) + }) + } else { + None + }; + let msg = if let Some(availability_note) = availability_note { + availability_note + } else if let [pred] = &preds[..] { format!("trait bound {pred} was not satisfied") } else { format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),) diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr index 5943111efd585..6a867350a3b36 100644 --- a/tests/ui/proc-macro/quote/not-repeatable.stderr +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -2,7 +2,7 @@ error[E0599]: the method `quote_into_iter` exists for struct `Ipv4Addr`, but its --> $DIR/not-repeatable.rs:11:13 | LL | struct Ipv4Addr; - | --------------- method `quote_into_iter` not found for this struct because it doesn't satisfy `Ipv4Addr: Iterator`, `Ipv4Addr: ToTokens`, `Ipv4Addr: proc_macro::ext::RepIteratorExt` or `Ipv4Addr: proc_macro::ext::RepToTokensExt` + | --------------- method `quote_into_iter` not found for this struct because `Ipv4Addr` doesn't implement `Iterator` or `ToTokens` ... LL | let _ = quote! { $($ip)* }; | ^^^^^^^^^^^^^^^^^^ method cannot be called on `Ipv4Addr` due to unsatisfied trait bounds diff --git a/tests/ui/traits/associated-item-unsatisfied-trait-bounds.rs b/tests/ui/traits/associated-item-unsatisfied-trait-bounds.rs new file mode 100644 index 0000000000000..4fb4cd61ac516 --- /dev/null +++ b/tests/ui/traits/associated-item-unsatisfied-trait-bounds.rs @@ -0,0 +1,9 @@ +struct Foo; +trait Bar {} +trait Baz {} +trait Bat { fn bat(&self); } +impl Bat for T where T: 'static + Bar + Baz { fn bat(&self) { println!("generic bat"); } } + +pub fn main() { + Foo::bat(()); //~ ERROR E0599 +} diff --git a/tests/ui/traits/associated-item-unsatisfied-trait-bounds.stderr b/tests/ui/traits/associated-item-unsatisfied-trait-bounds.stderr new file mode 100644 index 0000000000000..42969de715eb7 --- /dev/null +++ b/tests/ui/traits/associated-item-unsatisfied-trait-bounds.stderr @@ -0,0 +1,33 @@ +error[E0599]: the function or associated item `bat` exists for struct `Foo`, but its trait bounds were not satisfied + --> $DIR/associated-item-unsatisfied-trait-bounds.rs:8:10 + | +LL | struct Foo; + | ---------- function or associated item `bat` not found for this struct because `Foo` doesn't implement `Bar` or `Baz` +... +LL | Foo::bat(()); + | ^^^ function or associated item cannot be called on `Foo` due to unsatisfied trait bounds + | +note: for `bat` to be available, `Foo` must implement `Bar` and `Baz` + --> $DIR/associated-item-unsatisfied-trait-bounds.rs:5:38 + | +LL | impl Bat for T where T: 'static + Bar + Baz { fn bat(&self) { println!("generic bat"); } } + | --- - ^^^ ^^^ unsatisfied trait bound introduced here + | | + | unsatisfied trait bound introduced here +note: the traits `Bar` and `Baz` must be implemented + --> $DIR/associated-item-unsatisfied-trait-bounds.rs:2:1 + | +LL | trait Bar {} + | ^^^^^^^^^ +LL | trait Baz {} + | ^^^^^^^^^ + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bat` defines an item `bat`, perhaps you need to implement it + --> $DIR/associated-item-unsatisfied-trait-bounds.rs:4:1 + | +LL | trait Bat { fn bat(&self); } + | ^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`.