From 1982d871475ffb8c2c160a96916a76fdcb6eb11c Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sat, 18 Jan 2025 15:37:14 -0500 Subject: [PATCH 001/583] 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 b292be2d6f99..3984d8f0d8d9 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 9d1fa66ccb59..22c2cb3f67f1 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 ea8c4bf7f35f..9673e52dcadb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +git-subtree.sh diff --git a/Cargo.toml b/Cargo.toml index d1732aaec2f9..21d4584a9f4d 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 000000000000..d21e76b92dd1 --- /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 b4a8fd70f4c0..a7a6d43b11d3 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 4cd7265ed671..280b27bc9bc6 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 992a7705e3c5..7f57847c9c23 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 04de3a968276..b763a7c75a5a 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 96c553426ee7..db4312d5bf88 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 87f031a9f367..2d01946b5747 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 dd7303a97b19..d3bd14a34027 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 5b4615ce51d7..93989ce91b89 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 59e43851ea8d..79954b937b39 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 d7598d9ceaf9..3a51235ff954 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 53dd97f501c6..1ab2d8c7b731 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 be635ea640b8..47383809ffba 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 f6823a949e32..3f20eef21a31 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 d62642fb9061..42425ef37e50 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 3b6388d0f275..773bd028bae0 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 3e2391691496..9c4dd36c24fe 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 1a34a3a8de5c..57536e4fc77d 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 000000000000..1290bc166b2b --- /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 000000000000..24114c2d261e --- /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 fc6a3476b7c6..48786d02440b 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 aa565a137527..6de78f51e59d 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 522d71439b77..7001e5f6bf87 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 23dae7c93381..a5359b9abc84 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 51b860a86356..197c920e11ea 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 000000000000..d17c6d2e8894 --- /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 000000000000..18360077623b --- /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 002/583] 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 b763a7c75a5a..19d45f4d3b31 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 2d01946b5747..387b508c4b4e 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 d3bd14a34027..4ac64a253a3b 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 0ff76cfba39b..913cbbe977c4 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 bdae96332a3a..412a5b801171 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 79954b937b39..db705dfe2022 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 003/583] 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 17ade06ae80f..dc08d847ced5 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 a7a6d43b11d3..537ce459c07c 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 7f57847c9c23..717b882b64ba 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 db4312d5bf88..8221d8f17e90 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 387b508c4b4e..4e98db4070a9 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 4ac64a253a3b..f36e8d01a73b 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 93989ce91b89..2312ba401fa7 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 899f00a83164..e813e7613032 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 db705dfe2022..b5972c47373b 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 3a51235ff954..d25050c3e4b4 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 1ab2d8c7b731..45d978068b66 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 4b7c744c0132..e5d7a2aeb73d 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 47383809ffba..36452e7ae920 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 3f20eef21a31..c644f390c20a 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 42425ef37e50..dbdd6ef40eba 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 4833ea9e1136..fee2cc06c5b0 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 9c4dd36c24fe..d76a6cd52bfc 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 24114c2d261e..3b4666249b0d 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 d7db4e82b3ca..6e74c2d18b1e 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 847766ec41ed..4c1ac3c36f89 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 ec0f1fb24b93..b5f19ba47b81 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 004/583] 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 dc08d847ced5..4250a18315a6 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 005/583] 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 1290bc166b2b..13dda4769aba 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 ef3baf885b0f..1fdb2bc86d34 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 66aaf90eef59..eae42e6fd0d0 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 006/583] 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 d76a6cd52bfc..984a356c6552 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 007/583] 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 280b27bc9bc6..c62b9d3b7d9b 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 19d45f4d3b31..2b6be3676279 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 8221d8f17e90..ef03ec7f7159 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 4e98db4070a9..ddd7fb69b189 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 008/583] 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 c62b9d3b7d9b..839195c38eda 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 009/583] 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 280b27bc9bc6..bbdfd5f5f3ed 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 010/583] 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 ef03ec7f7159..780162c5b4bb 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 b8955c5656e0805847549ac6043d6ae89f7fc0cc Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 8 Jul 2025 14:09:34 -0500 Subject: [PATCH 011/583] core: add Option::get_or_try_insert_with Co-authored-by: kennytm --- library/core/src/option.rs | 45 +++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index ed070fbd2274..da753f36b16a 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -578,7 +578,7 @@ use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; -use crate::ops::{self, ControlFlow, Deref, DerefMut}; +use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; use crate::{cmp, convert, hint, mem, slice}; @@ -1807,6 +1807,49 @@ impl Option { unsafe { self.as_mut().unwrap_unchecked() } } + /// If the option is `None`, calls the closure and inserts its output if successful. + /// + /// If the closure returns a residual value such as `Err` or `None`, + /// that residual value is returned and nothing is inserted. + /// + /// If the option is `Some`, nothing is inserted. + /// + /// Unless a residual is returned, a mutable reference to the value + /// of the option will be output. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_get_or_try_insert_with)] + /// let mut o1: Option = None; + /// let mut o2: Option = None; + /// + /// let number = "12345"; + /// + /// assert_eq!(o1.get_or_try_insert_with(|| number.parse()).copied(), Ok(12345)); + /// assert!(o2.get_or_try_insert_with(|| number.parse()).is_err()); + /// assert_eq!(o1, Some(12345)); + /// assert_eq!(o2, None); + /// ``` + #[inline] + #[unstable(feature = "option_get_or_try_insert_with", issue = "143648")] + pub fn get_or_try_insert_with<'a, R, F>( + &'a mut self, + f: F, + ) -> >::TryType + where + F: FnOnce() -> R, + R: Try>, + { + if let None = self { + *self = Some(f()?); + } + // SAFETY: a `None` variant for `self` would have been replaced by a `Some` + // variant in the code above. + + Try::from_output(unsafe { self.as_mut().unwrap_unchecked() }) + } + ///////////////////////////////////////////////////////////////////////// // Misc ///////////////////////////////////////////////////////////////////////// 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 012/583] 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 75d152ae7f0e..4ef32bfa60b5 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 bad86414401d..ad2eea9153e0 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 d17c6d2e8894..df4bd75ecfe2 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 013/583] Update Cargo.lock --- Cargo.lock | 267 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 206 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1584c704fb22..d7accf71ab69 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 = "cc" +version = "1.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -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 014/583] 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 3984d8f0d8d9..96881687af05 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 015/583] 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 96881687af05..5344efe1db0e 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 016/583] 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 d7accf71ab69..5a5f0d8907ae 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 c66c968f8c66..c608ba49564e 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 a5359b9abc84..408bb04c7aa4 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 000000000000..57b43a16bc6f --- /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 197c920e11ea..35401a9ddb40 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 017/583] 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 717b882b64ba..9d7dfd2ab351 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 018/583] 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 5344efe1db0e..96881687af05 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 019/583] 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 13dda4769aba..1f84cdb971ec 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 020/583] 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 d25050c3e4b4..e7253313f036 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 45d978068b66..e3ba8658bd80 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 021/583] 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 2b6be3676279..b42cca7c977d 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 780162c5b4bb..4211789da708 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 ddd7fb69b189..a5382f0ff739 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 f33aa261a928..a2db455a5268 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 2312ba401fa7..3c483f3f94a3 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 e813e7613032..6eb3e360bdeb 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 dbdd6ef40eba..b6af2ded1cce 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 984a356c6552..1f15c9f85c71 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 48786d02440b..be7c5a27e86f 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 022/583] 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 b42cca7c977d..022003ef4dcc 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 be7c5a27e86f..53fb2367b605 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 023/583] 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 022003ef4dcc..ca1e3db8b46f 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 4211789da708..83ee88c372ab 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 a5382f0ff739..5ad2c1d1eaf6 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 3c483f3f94a3..789fc0bb9424 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 6eb3e360bdeb..4b2d0b55feba 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 b6af2ded1cce..81085a9ee4a3 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 1f15c9f85c71..c00cfcdd41ff 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 024/583] 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 | 199 +++++++++++---- 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, 248 insertions(+), 619 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 ca1e3db8b46f..7baa96475910 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 83ee88c372ab..000000000000 --- 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 5ad2c1d1eaf6..000000000000 --- 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 45b1a0f97514..14fe70df4ed1 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 f36e8d01a73b..f36e360fadf9 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 a2db455a5268..5240b9b0c716 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, +{ + #[inline] + 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) } + } +} + +impl Select> for u64 where T: 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()); - /// 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]); - /// ``` - #[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 + // 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 4b2d0b55feba..1b1c689ad458 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 b5972c47373b..76ab5748c638 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 d25050c3e4b4..5a292407d050 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 773bd028bae0..73b18595d0a1 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 b031c516641c53577d021da1a89d0c953acdc23a Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Tue, 7 Oct 2025 05:52:37 -0400 Subject: [PATCH 025/583] slice/ascii: Optimize `eq_ignore_ascii_case` with auto-vectorization Refactor the current functionality into a helper function Use `as_chunks` to encourage auto-vectorization in the optimized chunk processing function Add a codegen test Add benches for `eq_ignore_ascii_case` The optimized function is initially only enabled for x86_64 which has `sse2` as part of its baseline, but none of the code is platform specific. Other platforms with SIMD instructions may also benefit from this implementation. Performance improvements only manifest for slices of 16 bytes or longer, so the optimized path is gated behind a length check for greater than or equal to 16. --- library/core/src/slice/ascii.rs | 43 ++++++++++++++ library/coretests/benches/ascii.rs | 1 + .../benches/ascii/eq_ignore_ascii_case.rs | 56 +++++++++++++++++++ .../lib-optimizations/eq_ignore_ascii_case.rs | 14 +++++ 4 files changed, 114 insertions(+) create mode 100644 library/coretests/benches/ascii/eq_ignore_ascii_case.rs create mode 100644 tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index e17a2e03d2dc..1f9ca4bc6698 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -60,6 +60,18 @@ impl [u8] { return false; } + #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] + if self.len() >= 16 { + return self.eq_ignore_ascii_case_chunks(other); + } + + self.eq_ignore_ascii_case_simple(other) + } + + /// ASCII case-insensitive equality check without chunk-at-a-time + /// optimization. + #[inline] + const fn eq_ignore_ascii_case_simple(&self, other: &[u8]) -> bool { // FIXME(const-hack): This implementation can be reverted when // `core::iter::zip` is allowed in const. The original implementation: // self.len() == other.len() && iter::zip(self, other).all(|(a, b)| a.eq_ignore_ascii_case(b)) @@ -78,6 +90,37 @@ impl [u8] { true } + /// Optimized version of `eq_ignore_ascii_case` which processes chunks at a + /// time. + /// + /// Platforms that have SIMD instructions may benefit from this + /// implementation over `eq_ignore_ascii_case_simple`. + #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] + #[inline] + const fn eq_ignore_ascii_case_chunks(&self, other: &[u8]) -> bool { + const N: usize = 16; + let (a, a_rem) = self.as_chunks::(); + let (b, b_rem) = other.as_chunks::(); + + let mut i = 0; + while i < a.len() && i < b.len() { + let mut equal_ascii = true; + let mut j = 0; + while j < N { + equal_ascii &= a[i][j].eq_ignore_ascii_case(&b[i][j]); + j += 1; + } + + if !equal_ascii { + return false; + } + + i += 1; + } + + a_rem.eq_ignore_ascii_case_simple(b_rem) + } + /// Converts this slice to its ASCII upper case equivalent in-place. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', diff --git a/library/coretests/benches/ascii.rs b/library/coretests/benches/ascii.rs index 64bdc7fed118..17a520922bfa 100644 --- a/library/coretests/benches/ascii.rs +++ b/library/coretests/benches/ascii.rs @@ -1,3 +1,4 @@ +mod eq_ignore_ascii_case; mod is_ascii; // Lower-case ASCII 'a' is the first byte that has its highest bit set diff --git a/library/coretests/benches/ascii/eq_ignore_ascii_case.rs b/library/coretests/benches/ascii/eq_ignore_ascii_case.rs new file mode 100644 index 000000000000..a51acb1e8463 --- /dev/null +++ b/library/coretests/benches/ascii/eq_ignore_ascii_case.rs @@ -0,0 +1,56 @@ +use test::Bencher; + +#[bench] +fn bench_str_under_8_bytes_eq(b: &mut Bencher) { + let s = "foo"; + let other = "FOo"; + b.iter(|| { + assert!(s.eq_ignore_ascii_case(other)); + }) +} + +#[bench] +fn bench_str_of_8_bytes_eq(b: &mut Bencher) { + let s = "foobar78"; + let other = "FOObAr78"; + b.iter(|| { + assert!(s.eq_ignore_ascii_case(other)); + }) +} + +#[bench] +fn bench_str_17_bytes_eq(b: &mut Bencher) { + let s = "performance-criti"; + let other = "performANce-cRIti"; + b.iter(|| { + assert!(s.eq_ignore_ascii_case(other)); + }) +} + +#[bench] +fn bench_str_31_bytes_eq(b: &mut Bencher) { + let s = "foobarbazquux02foobarbazquux025"; + let other = "fooBARbazQuuX02fooBARbazQuuX025"; + b.iter(|| { + assert!(s.eq_ignore_ascii_case(other)); + }) +} + +#[bench] +fn bench_long_str_eq(b: &mut Bencher) { + let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud \ + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute \ + irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \ + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui \ + officia deserunt mollit anim id est laborum."; + let other = "Lorem ipsum dolor sit amet, CONSECTETUR adipisicing elit, sed do eiusmod tempor \ + incididunt ut labore et dolore MAGNA aliqua. Ut enim ad MINIM veniam, quis nostrud \ + exercitation ullamco LABORIS nisi ut aliquip ex ea commodo consequat. Duis aute \ + irure dolor in reprehenderit in voluptate velit esse cillum DOLORE eu fugiat nulla \ + pariatur. Excepteur sint occaecat CUPIDATAT non proident, sunt in culpa qui \ + officia deserunt mollit anim id est laborum."; + b.iter(|| { + assert!(s.eq_ignore_ascii_case(other)); + }) +} diff --git a/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs b/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs new file mode 100644 index 000000000000..b733f1812c92 --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs @@ -0,0 +1,14 @@ +//@ compile-flags: -Copt-level=3 +//@ only-x86_64 +#![crate_type = "lib"] + +// Ensure that the optimized variant of the function gets auto-vectorized. +// CHECK-LABEL: @eq_ignore_ascii_case_autovectorized +#[no_mangle] +pub fn eq_ignore_ascii_case_autovectorized(s: &str, other: &str) -> bool { + // CHECK: load <16 x i8> + // CHECK: load <16 x i8> + // CHECK: bitcast <16 x i1> + // CHECK-NOT: panic + s.eq_ignore_ascii_case(other) +} From 8ce88bcb509e1f7d7b6b76724a6fcb827d9a78ac Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Tue, 7 Oct 2025 10:16:49 -0400 Subject: [PATCH 026/583] 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 3a540f5a0490..90c520e75bb3 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 b3732fd74d5f..fdc458efeda4 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 839195c38eda..000000000000 --- 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 9d7dfd2ab351..3e5ebe19e4db 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 7baa96475910..3e2209556b66 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 14fe70df4ed1..5f635d80a178 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 f36e360fadf9..eb6601f73483 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 d21d867de26d..c1830c35df77 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 913cbbe977c4..360b83c40346 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 f5115a5a5e93..7ca83dc40f61 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 412a5b801171..e1c06167f979 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 5240b9b0c716..404f54d8f382 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 789fc0bb9424..d553d6c040c9 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 1b1c689ad458..5672fbbf54ca 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 76ab5748c638..efd7c2469512 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 5a292407d050..6ebf0ba397c6 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 45d978068b66..f8a40f8ec565 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 36452e7ae920..7ef9dc21373e 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 c644f390c20a..3b9b75ddf566 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 81085a9ee4a3..02dcd71356dd 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 73b18595d0a1..ae0b174973da 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 fee2cc06c5b0..1fd285e457db 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 c00cfcdd41ff..2dba5c83e112 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 148aa5f9f177..c3c9b76e50b8 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 35401a9ddb40..eb3d3f68bc2e 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 df4bd75ecfe2..ed4d7e8a801e 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 7b88f48b530f6610581a0d023199c9563e635730 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:35:02 -0400 Subject: [PATCH 027/583] Avoid scalar fallback in chunk remainder check Refactor the eq check into an inner function for reuse in tail checking Rather than fall back to the simple implementation for tail handling, load the last 16 bytes to take advantage of vectorization. This doesn't seem to negatively impact check time even when the remainder count is low. --- library/core/src/slice/ascii.rs | 35 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 1f9ca4bc6698..3f3f5a8c441d 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -90,8 +90,8 @@ impl [u8] { true } - /// Optimized version of `eq_ignore_ascii_case` which processes chunks at a - /// time. + /// Optimized version of `eq_ignore_ascii_case` for byte lengths of at least + /// 16 bytes, which processes chunks at a time. /// /// Platforms that have SIMD instructions may benefit from this /// implementation over `eq_ignore_ascii_case_simple`. @@ -99,26 +99,41 @@ impl [u8] { #[inline] const fn eq_ignore_ascii_case_chunks(&self, other: &[u8]) -> bool { const N: usize = 16; - let (a, a_rem) = self.as_chunks::(); - let (b, b_rem) = other.as_chunks::(); + let (self_chunks, self_rem) = self.as_chunks::(); + let (other_chunks, _) = other.as_chunks::(); - let mut i = 0; - while i < a.len() && i < b.len() { + // Branchless check to encourage auto-vectorization + const fn eq_ignore_ascii_inner(lhs: &[u8; N], rhs: &[u8; N]) -> bool { let mut equal_ascii = true; let mut j = 0; while j < N { - equal_ascii &= a[i][j].eq_ignore_ascii_case(&b[i][j]); + equal_ascii &= lhs[j].eq_ignore_ascii_case(&rhs[j]); j += 1; } - if !equal_ascii { + equal_ascii + } + + // Process the chunks, returning early if an inequality is found + let mut i = 0; + while i < self_chunks.len() && i < other_chunks.len() { + if !eq_ignore_ascii_inner(&self_chunks[i], &other_chunks[i]) { return false; } - i += 1; } - a_rem.eq_ignore_ascii_case_simple(b_rem) + // If there are remaining tails, load the last N bytes in the slices to + // avoid falling back to per-byte checking. + if !self_rem.is_empty() { + if let (Some(a_rem), Some(b_rem)) = (self.last_chunk::(), other.last_chunk::()) { + if !eq_ignore_ascii_inner(a_rem, b_rem) { + return false; + } + } + } + + true } /// Converts this slice to its ASCII upper case equivalent in-place. From a5ba24843d6e4ceda580e49344bef73baec14204 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Tue, 7 Oct 2025 19:04:32 -0400 Subject: [PATCH 028/583] Relocate bench and use str corpora for data Add #[inline(always)] to inner function and check not for filecheck test --- library/core/src/slice/ascii.rs | 1 + library/coretests/benches/ascii.rs | 1 - .../benches/ascii/eq_ignore_ascii_case.rs | 56 ------------------- library/coretests/benches/str.rs | 1 + .../benches/str/eq_ignore_ascii_case.rs | 45 +++++++++++++++ .../lib-optimizations/eq_ignore_ascii_case.rs | 4 +- 6 files changed, 50 insertions(+), 58 deletions(-) delete mode 100644 library/coretests/benches/ascii/eq_ignore_ascii_case.rs create mode 100644 library/coretests/benches/str/eq_ignore_ascii_case.rs diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 3f3f5a8c441d..8b713947cd9c 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -103,6 +103,7 @@ impl [u8] { let (other_chunks, _) = other.as_chunks::(); // Branchless check to encourage auto-vectorization + #[inline(always)] const fn eq_ignore_ascii_inner(lhs: &[u8; N], rhs: &[u8; N]) -> bool { let mut equal_ascii = true; let mut j = 0; diff --git a/library/coretests/benches/ascii.rs b/library/coretests/benches/ascii.rs index 17a520922bfa..64bdc7fed118 100644 --- a/library/coretests/benches/ascii.rs +++ b/library/coretests/benches/ascii.rs @@ -1,4 +1,3 @@ -mod eq_ignore_ascii_case; mod is_ascii; // Lower-case ASCII 'a' is the first byte that has its highest bit set diff --git a/library/coretests/benches/ascii/eq_ignore_ascii_case.rs b/library/coretests/benches/ascii/eq_ignore_ascii_case.rs deleted file mode 100644 index a51acb1e8463..000000000000 --- a/library/coretests/benches/ascii/eq_ignore_ascii_case.rs +++ /dev/null @@ -1,56 +0,0 @@ -use test::Bencher; - -#[bench] -fn bench_str_under_8_bytes_eq(b: &mut Bencher) { - let s = "foo"; - let other = "FOo"; - b.iter(|| { - assert!(s.eq_ignore_ascii_case(other)); - }) -} - -#[bench] -fn bench_str_of_8_bytes_eq(b: &mut Bencher) { - let s = "foobar78"; - let other = "FOObAr78"; - b.iter(|| { - assert!(s.eq_ignore_ascii_case(other)); - }) -} - -#[bench] -fn bench_str_17_bytes_eq(b: &mut Bencher) { - let s = "performance-criti"; - let other = "performANce-cRIti"; - b.iter(|| { - assert!(s.eq_ignore_ascii_case(other)); - }) -} - -#[bench] -fn bench_str_31_bytes_eq(b: &mut Bencher) { - let s = "foobarbazquux02foobarbazquux025"; - let other = "fooBARbazQuuX02fooBARbazQuuX025"; - b.iter(|| { - assert!(s.eq_ignore_ascii_case(other)); - }) -} - -#[bench] -fn bench_long_str_eq(b: &mut Bencher) { - let s = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor \ - incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud \ - exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute \ - irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \ - pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui \ - officia deserunt mollit anim id est laborum."; - let other = "Lorem ipsum dolor sit amet, CONSECTETUR adipisicing elit, sed do eiusmod tempor \ - incididunt ut labore et dolore MAGNA aliqua. Ut enim ad MINIM veniam, quis nostrud \ - exercitation ullamco LABORIS nisi ut aliquip ex ea commodo consequat. Duis aute \ - irure dolor in reprehenderit in voluptate velit esse cillum DOLORE eu fugiat nulla \ - pariatur. Excepteur sint occaecat CUPIDATAT non proident, sunt in culpa qui \ - officia deserunt mollit anim id est laborum."; - b.iter(|| { - assert!(s.eq_ignore_ascii_case(other)); - }) -} diff --git a/library/coretests/benches/str.rs b/library/coretests/benches/str.rs index 2f7d9d56a70b..bf45a8f0a79b 100644 --- a/library/coretests/benches/str.rs +++ b/library/coretests/benches/str.rs @@ -5,6 +5,7 @@ use test::{Bencher, black_box}; mod char_count; mod corpora; mod debug; +mod eq_ignore_ascii_case; mod iter; #[bench] diff --git a/library/coretests/benches/str/eq_ignore_ascii_case.rs b/library/coretests/benches/str/eq_ignore_ascii_case.rs new file mode 100644 index 000000000000..29129b933bc4 --- /dev/null +++ b/library/coretests/benches/str/eq_ignore_ascii_case.rs @@ -0,0 +1,45 @@ +use test::{Bencher, black_box}; + +use super::corpora::*; + +#[bench] +fn bench_str_under_8_bytes_eq(b: &mut Bencher) { + let s = black_box("foo"); + let other = black_box("foo"); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_of_8_bytes_eq(b: &mut Bencher) { + let s = black_box(en::TINY); + let other = black_box(en::TINY); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_17_bytes_eq(b: &mut Bencher) { + let s = black_box(&en::SMALL[..17]); + let other = black_box(&en::SMALL[..17]); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_31_bytes_eq(b: &mut Bencher) { + let s = black_box(&en::SMALL[..31]); + let other = black_box(&en::SMALL[..31]); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_medium_str_eq(b: &mut Bencher) { + let s = black_box(en::MEDIUM); + let other = black_box(en::MEDIUM); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_large_str_eq(b: &mut Bencher) { + let s = black_box(en::LARGE); + let other = black_box(en::LARGE); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} diff --git a/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs b/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs index b733f1812c92..d4ac5d64585d 100644 --- a/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs +++ b/tests/codegen-llvm/lib-optimizations/eq_ignore_ascii_case.rs @@ -2,13 +2,15 @@ //@ only-x86_64 #![crate_type = "lib"] -// Ensure that the optimized variant of the function gets auto-vectorized. +// Ensure that the optimized variant of the function gets auto-vectorized and +// that the inner helper function is inlined. // CHECK-LABEL: @eq_ignore_ascii_case_autovectorized #[no_mangle] pub fn eq_ignore_ascii_case_autovectorized(s: &str, other: &str) -> bool { // CHECK: load <16 x i8> // CHECK: load <16 x i8> // CHECK: bitcast <16 x i1> + // CHECK-NOT: call {{.*}}eq_ignore_ascii_inner // CHECK-NOT: panic s.eq_ignore_ascii_case(other) } From 31bf836c74e5b53e654bed70158cabfdfdca8acf Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Tue, 14 Oct 2025 02:23:44 -0400 Subject: [PATCH 029/583] Use a const generic parameter for the function chunk size Add comments for the optimized function invariants to the caller Add const-hack fixme for using while-loops Document the invariant for the `_chunks` function Add a debug assert for the tail handling invariant --- library/core/src/slice/ascii.rs | 35 +++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 8b713947cd9c..0f3cf243f164 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -61,8 +61,15 @@ impl [u8] { } #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] - if self.len() >= 16 { - return self.eq_ignore_ascii_case_chunks(other); + { + const CHUNK_SIZE: usize = 16; + // The following function has two invariants: + // 1. The slice lengths must be equal, which we checked above. + // 2. The slice lengths must greater than or equal to N, which this + // if-statement is checking. + if self.len() >= CHUNK_SIZE { + return self.eq_ignore_ascii_case_chunks::(other); + } } self.eq_ignore_ascii_case_simple(other) @@ -90,24 +97,30 @@ impl [u8] { true } - /// Optimized version of `eq_ignore_ascii_case` for byte lengths of at least - /// 16 bytes, which processes chunks at a time. + /// Optimized version of `eq_ignore_ascii_case` to process chunks at a time. /// /// Platforms that have SIMD instructions may benefit from this /// implementation over `eq_ignore_ascii_case_simple`. + /// + /// # Invariants + /// + /// The caller must guarantee that the slices are equal in length, and the + /// slice lengths are greater than or equal to `N` bytes. #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] #[inline] - const fn eq_ignore_ascii_case_chunks(&self, other: &[u8]) -> bool { - const N: usize = 16; + const fn eq_ignore_ascii_case_chunks(&self, other: &[u8]) -> bool { + // FIXME(const-hack): The while-loops that follow should be replaced by + // for-loops when available in const. + let (self_chunks, self_rem) = self.as_chunks::(); let (other_chunks, _) = other.as_chunks::(); // Branchless check to encourage auto-vectorization #[inline(always)] - const fn eq_ignore_ascii_inner(lhs: &[u8; N], rhs: &[u8; N]) -> bool { + const fn eq_ignore_ascii_inner(lhs: &[u8; L], rhs: &[u8; L]) -> bool { let mut equal_ascii = true; let mut j = 0; - while j < N { + while j < L { equal_ascii &= lhs[j].eq_ignore_ascii_case(&rhs[j]); j += 1; } @@ -124,6 +137,12 @@ impl [u8] { i += 1; } + // Check the length invariant which is necessary for the tail-handling + // logic to be correct. This should have been upheld by the caller, + // otherwise lengths less than N will compare as true without any + // checking. + debug_assert!(self.len() >= N); + // If there are remaining tails, load the last N bytes in the slices to // avoid falling back to per-byte checking. if !self_rem.is_empty() { From 061617bdbcaa49bb5c4456c0c5264d0849afe4b1 Mon Sep 17 00:00:00 2001 From: sayantn Date: Wed, 8 Oct 2025 08:12:09 +0530 Subject: [PATCH 030/583] 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 d76a6cd52bfc..f40031f8c4da 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 031/583] 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 8221d8f17e90..32d37b553392 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 055cd2717fe298a567c130204743cc0cc06a08e0 Mon Sep 17 00:00:00 2001 From: Voxell Paladynee Date: Wed, 19 Nov 2025 21:17:40 +0100 Subject: [PATCH 032/583] ptr_cast_slice: add new methods to raw pointers --- library/core/src/ptr/const_ptr.rs | 37 +++++++++++++++++++++++++ library/core/src/ptr/mut_ptr.rs | 45 +++++++++++++++++++++++++++++++ library/core/src/ptr/non_null.rs | 29 ++++++++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 84a6982d5680..e4d2f7436f13 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1386,6 +1386,43 @@ impl *const T { pub const fn cast_uninit(self) -> *const MaybeUninit { self as _ } + + /// Forms a raw slice from a pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// This function is safe, but actually using the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. + /// + /// [`slice::from_raw_parts`]: crate::slice::from_raw_parts + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// // create a slice pointer when starting out with a pointer to the first element + /// let x = [5, 6, 7]; + /// let raw_pointer = x.as_ptr(); + /// let slice = raw_pointer.cast_slice(3); + /// assert_eq!(unsafe { &*slice }[2], 7); + /// ``` + /// + /// You must ensure that the pointer is valid and not null before dereferencing + /// the raw slice. A slice reference must never have a null pointer, even if it's empty. + /// + /// ```rust,should_panic + /// #![feature(ptr_cast_slice)] + /// use std::ptr; + /// let danger: *const [u8] = ptr::null::().cast_slice(0); + /// unsafe { + /// danger.as_ref().expect("references must not be null"); + /// } + /// ``` + #[inline] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> *const [T] { + slice_from_raw_parts(self, len) + } } impl *const MaybeUninit { /// Casts from a maybe-uninitialized type to its initialized version. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 85d54b4d3b9b..edcd0cd2d932 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1655,6 +1655,51 @@ impl *mut T { pub const fn cast_uninit(self) -> *mut MaybeUninit { self as _ } + + /// Forms a raw mutable slice from a pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// Performs the same functionality as [`cast_slice`] on a `*const T`, except that a + /// raw mutable slice is returned, as opposed to a raw immutable slice. + /// + /// This function is safe, but actually using the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements. + /// + /// [`slice::from_raw_parts_mut`]: crate::slice::from_raw_parts_mut + /// [`cast_slice`]: pointer::cast_slice + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// + /// let x = &mut [5, 6, 7]; + /// let slice = x.as_mut_ptr().cast_slice(3); + /// + /// unsafe { + /// (*slice)[2] = 99; // assign a value at an index in the slice + /// }; + /// + /// assert_eq!(unsafe { &*slice }[2], 99); + /// ``` + /// + /// You must ensure that the pointer is valid and not null before dereferencing + /// the raw slice. A slice reference must never have a null pointer, even if it's empty. + /// + /// ```rust,should_panic + /// #![feature(ptr_cast_slice)] + /// use std::ptr; + /// let danger: *mut [u8] = ptr::null_mut::().cast_slice(0); + /// unsafe { + /// danger.as_mut().expect("references must not be null"); + /// } + /// ``` + #[inline] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> *mut [T] { + slice_from_raw_parts_mut(self, len) + } } impl *mut MaybeUninit { /// Casts from a maybe-uninitialized type to its initialized version. diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index aa3af2f18528..2cefc41017f4 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1377,6 +1377,35 @@ impl NonNull { pub const fn cast_uninit(self) -> NonNull> { self.cast() } + + /// Creates a non-null raw slice from a thin pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// This function is safe, but dereferencing the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// use std::ptr::NonNull; + /// + /// // create a slice pointer when starting out with a pointer to the first element + /// let mut x = [5, 6, 7]; + /// let nonnull_pointer = NonNull::new(x.as_mut_ptr()).unwrap(); + /// let slice = nonnull_pointer.cast_slice(3); + /// assert_eq!(unsafe { slice.as_ref()[2] }, 7); + /// ``` + /// + /// (Note that this example artificially demonstrates a use of this method, + /// but `let slice = NonNull::from(&x[..]);` would be a better way to write code like this.) + #[inline] + #[must_use] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> NonNull<[T]> { + NonNull::slice_from_raw_parts(self, len) + } } impl NonNull> { /// Casts from a maybe-uninitialized type to its initialized version. From 1deb487076519970fdba20742cc10c380962d2c3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 30 Nov 2025 14:21:08 +0100 Subject: [PATCH 033/583] thread::scope: document how join interacts with TLS destructors --- library/std/src/thread/join_handle.rs | 2 ++ library/std/src/thread/scoped.rs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/library/std/src/thread/join_handle.rs b/library/std/src/thread/join_handle.rs index 8714ceeb4f46..bb3759cd3a07 100644 --- a/library/std/src/thread/join_handle.rs +++ b/library/std/src/thread/join_handle.rs @@ -102,6 +102,8 @@ impl JoinHandle { /// Waits for the associated thread to finish. /// /// This function will return immediately if the associated thread has already finished. + /// Otherwise, it fully waits for the thread to finish, including all destructors + /// for thread-local variables that might be running after the main function of the thread. /// /// In terms of [atomic memory orderings], the completion of the associated /// thread synchronizes with this function returning. In other words, all diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 301f5e949cac..368fb819962b 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -80,6 +80,9 @@ impl ScopeData { /// /// All threads spawned within the scope that haven't been manually joined /// will be automatically joined before this function returns. +/// However, note that joining will only wait for the main function of these threads to finish; even +/// when this function returns, destructors of thread-local variables in these threads might still +/// be running. /// /// # Panics /// @@ -292,6 +295,8 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> { /// Waits for the associated thread to finish. /// /// This function will return immediately if the associated thread has already finished. + /// Otherwise, it fully waits for the thread to finish, including all destructors + /// for thread-local variables that might be running after the main function of the thread. /// /// In terms of [atomic memory orderings], the completion of the associated /// thread synchronizes with this function returning. From 4215b7228d28cb84016e8f0e41c0a5e0935a222f Mon Sep 17 00:00:00 2001 From: Andrew Cherry Date: Thu, 11 Dec 2025 13:58:40 +0000 Subject: [PATCH 034/583] 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 c3c9b76e50b8..b269efc9b1d7 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 31ce232fda9790c5025d6f89a23b8fd67e8b9369 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Tue, 21 Oct 2025 15:02:52 -0500 Subject: [PATCH 035/583] Stabilize ppc inline assembly --- compiler/rustc_ast_lowering/src/asm.rs | 2 + .../asm-experimental-arch.md | 41 +------------------ 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index d44faad017ee..afcbae9c51b4 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -51,6 +51,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | asm::InlineAsmArch::LoongArch32 | asm::InlineAsmArch::LoongArch64 | asm::InlineAsmArch::S390x + | asm::InlineAsmArch::PowerPC + | asm::InlineAsmArch::PowerPC64 ); if !is_stable && !self.tcx.features().asm_experimental_arch() { feature_err( diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index 77d43315a6d4..23ac46b72ea7 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -8,7 +8,6 @@ The tracking issue for this feature is: [#93335] This feature tracks `asm!` and `global_asm!` support for the following architectures: - NVPTX -- PowerPC - Hexagon - MIPS32r2 and MIPS64r2 - wasm32 @@ -31,16 +30,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg64` | None\* | `l` | | Hexagon | `reg` | `r[0-28]` | `r` | | Hexagon | `preg` | `p[0-3]` | Only clobbers | -| PowerPC | `reg` | `r0`, `r[3-12]`, `r[14-29]`\* | `r` | -| PowerPC | `reg_nonzero` | `r[3-12]`, `r[14-29]`\* | `b` | -| PowerPC | `freg` | `f[0-31]` | `f` | -| PowerPC | `vreg` | `v[0-31]` | `v` | -| PowerPC | `vsreg | `vs[0-63]` | `wa` | -| PowerPC | `cr` | `cr[0-7]`, `cr` | Only clobbers | -| PowerPC | `ctr` | `ctr` | Only clobbers | -| PowerPC | `lr` | `lr` | Only clobbers | -| PowerPC | `xer` | `xer` | Only clobbers | -| PowerPC | `spe_acc` | `spe_acc` | Only clobbers | | wasm32 | `local` | None\* | `r` | | BPF | `reg` | `r[0-10]` | `r` | | BPF | `wreg` | `w[0-10]` | `w` | @@ -62,10 +51,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect > - NVPTX doesn't have a fixed register set, so named registers are not supported. > > - WebAssembly doesn't have registers, so named registers are not supported. -> -> - r29 is reserved only on 32 bit PowerPC targets. -> -> - spe_acc is only available on PowerPC SPE targets. # Register class supported types @@ -80,17 +65,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | | Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | | Hexagon | `preg` | N/A | Only clobbers | -| PowerPC | `reg` | None | `i8`, `i16`, `i32`, `i64` (powerpc64 only) | -| PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32`, `i64` (powerpc64 only) | -| PowerPC | `freg` | None | `f32`, `f64` | -| PowerPC | `vreg` | `altivec` | `i8x16`, `i16x8`, `i32x4`, `f32x4` | -| PowerPC | `vreg` | `vsx` | `f32`, `f64`, `i64x2`, `f64x2` | -| PowerPC | `vsreg` | `vsx` | The union of vsx and altivec vreg types | -| PowerPC | `cr` | N/A | Only clobbers | -| PowerPC | `ctr` | N/A | Only clobbers | -| PowerPC | `lr` | N/A | Only clobbers | -| PowerPC | `xer` | N/A | Only clobbers | -| PowerPC | `spe_acc` | N/A | Only clobbers | | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | | BPF | `reg` | None | `i8` `i16` `i32` `i64` | | BPF | `wreg` | `alu32` | `i8` `i16` `i32` | @@ -111,10 +85,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | Hexagon | `r29` | `sp` | | Hexagon | `r30` | `fr` | | Hexagon | `r31` | `lr` | -| PowerPC | `r1` | `sp` | -| PowerPC | `r31` | `fp` | -| PowerPC | `r[0-31]` | `[0-31]` | -| PowerPC | `f[0-31]` | `fr[0-31]`| | BPF | `r[0-10]` | `w[0-10]` | | AVR | `XH` | `r27` | | AVR | `XL` | `r26` | @@ -153,16 +123,14 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | Architecture | Unsupported register | Reason | | ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | All | `sp`, `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. | -| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC) | The frame pointer cannot be used as an input or output. | -| All | `r19` (Hexagon), `r29` (PowerPC 32 bit only), `r30` (PowerPC) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. | +| All | `fr` (Hexagon) `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC) | The frame pointer cannot be used as an input or output. | +| All | `r19` (Hexagon) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. | | MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | | MIPS | `$1` or `$at` | Reserved for assembler. | | MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | | MIPS | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. | | MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | -| PowerPC | `r2`, `r13` | These are system reserved registers. | -| PowerPC | `vrsave` | The vrsave register cannot be used as an input or output. | | AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. | |MSP430 | `r0`, `r2`, `r3` | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to. | | M68k | `a4`, `a5` | Used internally by LLVM for the base pointer and global base pointer. | @@ -189,11 +157,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | NVPTX | `reg32` | None | `r0` | None | | NVPTX | `reg64` | None | `rd0` | None | | Hexagon | `reg` | None | `r0` | None | -| PowerPC | `reg` | None | `0` | None | -| PowerPC | `reg_nonzero` | None | `3` | None | -| PowerPC | `freg` | None | `0` | None | -| PowerPC | `vreg` | None | `0` | None | -| PowerPC | `vsreg` | None | `0` | None | | SPARC | `reg` | None | `%o0` | None | | CSKY | `reg` | None | `r0` | None | | CSKY | `freg` | None | `f0` | None | From df25df1af84487b9ce9e793834e91bbf1485cecf Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sat, 27 Dec 2025 12:29:19 +0800 Subject: [PATCH 036/583] Constify `fmt::from_fn` --- library/core/src/fmt/builders.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 197cddd3fa9d..7550dac45cd0 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1227,8 +1227,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[stable(feature = "fmt_from_fn", since = "1.93.0")] +#[rustc_const_stable(feature = "const_fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] #[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] -pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { +pub const fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } From 979704dacdbb0ba839de2c3f73acee891be17a63 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 26 Oct 2025 16:18:50 +0800 Subject: [PATCH 037/583] Fix not applicable on statement for convert_to_guarded_return Fix not applicable in statement when exist else block Example --- ```rust fn main() { some_statements(); if$0 let Ok(x) = Err(92) { foo(x); } else { return; } some_statements(); } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { some_statements(); let Ok(x) = Err(92) else { return; }; foo(x); some_statements(); } ``` --- .../src/handlers/convert_to_guarded_return.rs | 72 ++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 08b114072fd9..ea5c1637b760 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -95,7 +95,9 @@ fn if_expr_to_guarded_return( let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; - if parent_block.tail_expr()? != if_expr.clone().into() { + if parent_block.tail_expr() != Some(if_expr.clone().into()) + && !(else_block.is_some() && ast::ExprStmt::can_cast(if_expr.syntax().parent()?.kind())) + { return None; } @@ -502,6 +504,36 @@ fn main() { ); } + #[test] + fn convert_if_let_has_else_block_in_statement() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + if$0 let Ok(x) = Err(92) { + foo(x); + } else { + // needless comment + return; + } + some_statements(); +} +"#, + r#" +fn main() { + some_statements(); + let Ok(x) = Err(92) else { + // needless comment + return; + }; + foo(x); + some_statements(); +} +"#, + ); + } + #[test] fn convert_if_let_result_inside_let() { check_assist( @@ -1136,6 +1168,44 @@ fn main() { ); } + #[test] + fn ignore_else_if() { + check_assist_not_applicable( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + if cond { + () + } else if$0 let Ok(x) = Err(92) { + foo(x); + } else { + return; + } + some_statements(); +} +"#, + ); + } + + #[test] + fn ignore_if_inside_let() { + check_assist_not_applicable( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + let _ = if$0 let Ok(x) = Err(92) { + foo(x); + } else { + return; + } + some_statements(); +} +"#, + ); + } + #[test] fn ignore_let_else_branch() { check_assist_not_applicable( From 57e44f5046058d467b9f8731f4c49885a8664d8e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 2 Jan 2026 18:39:06 +0100 Subject: [PATCH 038/583] skip codegen for intrinsics with big fallback bodies if backend does not need them --- compiler/rustc_codegen_llvm/src/lib.rs | 6 +++++- compiler/rustc_codegen_ssa/src/traits/backend.rs | 6 ++++++ compiler/rustc_interface/src/interface.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 9 +++++---- compiler/rustc_session/src/session.rs | 7 ++++++- tests/codegen-llvm/intrinsics/carrying_mul_add.rs | 5 ++--- 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 095274744993..875b60ef6ddb 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -42,7 +42,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; -use rustc_span::Symbol; +use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TlsModel}; use crate::llvm::ToLlvmBool; @@ -333,6 +333,10 @@ impl CodegenBackend for LlvmCodegenBackend { target_config(sess) } + fn replaced_intrinsics(&self) -> Vec { + vec![sym::unchecked_funnel_shl, sym::unchecked_funnel_shr, sym::carrying_mul_add] + } + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { Box::new(rustc_codegen_ssa::base::codegen_crate( LlvmCodegenBackend(()), diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index cb74e2e46d65..2ca24836d046 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -78,6 +78,12 @@ pub trait CodegenBackend { fn print_version(&self) {} + /// Returns a list of all intrinsics that this backend definitely + /// replaces, which means their fallback bodies do not need to be monomorphized. + fn replaced_intrinsics(&self) -> Vec { + vec![] + } + /// Value printed by `--print=backend-has-zstd`. /// /// Used by compiletest to determine whether tests involving zstd compression diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index c0f8f33692e8..c60cf9b8992a 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -496,6 +496,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se ); codegen_backend.init(&sess); + sess.replaced_intrinsics = FxHashSet::from_iter(codegen_backend.replaced_intrinsics()); let cfg = parse_cfg(sess.dcx(), config.crate_cfg); let mut cfg = config::build_configuration(&sess, cfg); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 4b2f8e03afc1..622fdeb4e81c 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1001,11 +1001,12 @@ fn visit_instance_use<'tcx>( if tcx.should_codegen_locally(panic_instance) { output.push(create_fn_mono_item(tcx, panic_instance, source)); } - } else if !intrinsic.must_be_overridden { + } else if !intrinsic.must_be_overridden + && !tcx.sess.replaced_intrinsics.contains(&intrinsic.name) + { // Codegen the fallback body of intrinsics with fallback bodies. - // We explicitly skip this otherwise to ensure we get a linker error - // if anyone tries to call this intrinsic and the codegen backend did not - // override the implementation. + // We have to skip this otherwise as there's no body to codegen. + // We also skip intrinsics the backend handles, to reduce monomorphizations. let instance = ty::Instance::new_raw(instance.def_id(), instance.args); if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, source)); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 1a0ec600af47..b3027d902b4c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -8,7 +8,7 @@ use std::{env, io}; use rand::{RngCore, rng}; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::flock; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_data_structures::sync::{DynSend, DynSync, Lock, MappedReadGuard, ReadGuard, RwLock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; @@ -154,6 +154,10 @@ pub struct Session { /// preserved with a flag like `-C save-temps`, since these files may be /// hard linked. pub invocation_temp: Option, + + /// The names of intrinsics that the current codegen backend replaces + /// with its own implementations. + pub replaced_intrinsics: FxHashSet, } #[derive(Clone, Copy)] @@ -1091,6 +1095,7 @@ pub fn build_session( target_filesearch, host_filesearch, invocation_temp, + replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` }; validate_commandline_args_with_session_available(&sess); diff --git a/tests/codegen-llvm/intrinsics/carrying_mul_add.rs b/tests/codegen-llvm/intrinsics/carrying_mul_add.rs index 21fb49a3786a..844f4f6cff64 100644 --- a/tests/codegen-llvm/intrinsics/carrying_mul_add.rs +++ b/tests/codegen-llvm/intrinsics/carrying_mul_add.rs @@ -11,10 +11,9 @@ use std::intrinsics::{carrying_mul_add, fallback}; -// The fallbacks are emitted even when they're never used, but optimize out. +// The fallbacks should not be emitted. -// RAW: wide_mul_u128 -// OPT-NOT: wide_mul_u128 +// NOT: wide_mul_u128 // CHECK-LABEL: @cma_u8 #[no_mangle] From c0ecf1ad40a953f01d8ebf5c035eef268cc001f8 Mon Sep 17 00:00:00 2001 From: Till Adam Date: Fri, 2 Jan 2026 21:57:02 +0100 Subject: [PATCH 039/583] Implement Span::line() and Span::column() for proc-macro server Add proper line/column resolution for proc-macro spans via a callback mechanism. Previously these methods returned hardcoded 1 values. The implementation adds: - SubRequest::LineColumn and SubResponse::LineColumnResult to the bidirectional protocol - ProcMacroClientInterface::line_column() method - Callback handling in load-cargo using LineIndex - Server implementation in RaSpanServer that uses the callback - a test for Span::line() and Span::column() in proc-macro server Add fn_like_span_line_column test proc-macro that exercises the new line/column API, and a corresponding test with a mock callback. --- src/tools/rust-analyzer/Cargo.lock | 1 + .../crates/load-cargo/src/lib.rs | 42 +++++++++--- .../src/bidirectional_protocol/msg.rs | 18 ++++- .../proc-macro-srv-cli/src/main_loop.rs | 14 ++++ .../crates/proc-macro-srv/Cargo.toml | 1 + .../proc-macro-test/imp/src/lib.rs | 10 +++ .../crates/proc-macro-srv/src/lib.rs | 2 + .../src/server_impl/rust_analyzer_span.rs | 10 ++- .../crates/proc-macro-srv/src/tests/mod.rs | 15 +++++ .../crates/proc-macro-srv/src/tests/utils.rs | 66 ++++++++++++++++++- 10 files changed, 161 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 42eaeb01f1f2..8188fbf96064 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1864,6 +1864,7 @@ dependencies = [ "intern", "libc", "libloading", + "line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memmap2", "object", "paths", diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index e8d98b1ce661..33468a5003c3 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -554,14 +554,12 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::LocalFilePathResult { name }) } SubRequest::SourceText { file_id, ast_id, start, end } => { - let ast_id = span::ErasedFileAstId::from_raw(ast_id); - let editioned_file_id = span::EditionedFileId::from_raw(file_id); - let span = Span { - range: TextRange::new(TextSize::from(start), TextSize::from(end)), - anchor: SpanAnchor { file_id: editioned_file_id, ast_id }, - ctx: SyntaxContext::root(editioned_file_id.edition()), - }; - let range = db.resolve_span(span); + let range = resolve_sub_span( + db, + file_id, + ast_id, + TextRange::new(TextSize::from(start), TextSize::from(end)), + ); let source = db.file_text(range.file_id.file_id(db)).text(db); let text = source .get(usize::from(range.range.start())..usize::from(range.range.end())) @@ -569,6 +567,18 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SourceTextResult { text }) } + SubRequest::LineColumn { file_id, ast_id, offset } => { + let range = + resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); + let source = db.file_text(range.file_id.file_id(db)).text(db); + let line_index = ide_db::line_index::LineIndex::new(source); + let (line, column) = line_index + .try_line_col(range.range.start()) + .map(|lc| (lc.line + 1, lc.col + 1)) + .unwrap_or((1, 1)); + // proc_macro::Span line/column are 1-based + Ok(SubResponse::LineColumnResult { line, column }) + } SubRequest::FilePath { file_id } => { let file_id = FileId::from_raw(file_id); let source_root_id = db.file_source_root(file_id).source_root_id(db); @@ -603,6 +613,22 @@ impl ProcMacroExpander for Expander { } } +fn resolve_sub_span( + db: &dyn ExpandDatabase, + file_id: u32, + ast_id: u32, + range: TextRange, +) -> hir_expand::FileRange { + let ast_id = span::ErasedFileAstId::from_raw(ast_id); + let editioned_file_id = span::EditionedFileId::from_raw(file_id); + let span = Span { + range, + anchor: SpanAnchor { file_id: editioned_file_id, ast_id }, + ctx: SyntaxContext::root(editioned_file_id.edition()), + }; + db.resolve_span(span) +} + #[cfg(test)] mod tests { use ide_db::base_db::RootQueryDb; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index e41f8a5d7da7..0e3b700dcc5a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -13,13 +13,25 @@ pub enum SubRequest { FilePath { file_id: u32 }, SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, LocalFilePath { file_id: u32 }, + LineColumn { file_id: u32, ast_id: u32, offset: u32 }, } #[derive(Debug, Serialize, Deserialize)] pub enum SubResponse { - FilePathResult { name: String }, - SourceTextResult { text: Option }, - LocalFilePathResult { name: Option }, + FilePathResult { + name: String, + }, + SourceTextResult { + text: Option, + }, + LocalFilePathResult { + name: Option, + }, + /// Line and column are 1-based. + LineColumnResult { + line: u32, + column: u32, + }, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index b2f4b96bd255..22536a4e52b1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -220,6 +220,20 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl _ => None, } } + + fn line_column(&mut self, span: proc_macro_srv::span::Span) -> Option<(u32, u32)> { + let proc_macro_srv::span::Span { range, anchor, ctx: _ } = span; + match self.roundtrip(bidirectional::SubRequest::LineColumn { + file_id: anchor.file_id.as_u32(), + ast_id: anchor.ast_id.into_raw(), + offset: range.start().into(), + }) { + Some(bidirectional::BidirectionalMessage::SubResponse( + bidirectional::SubResponse::LineColumnResult { line, column }, + )) => Some((line, column)), + _ => None, + } + } } fn handle_expand_ra( diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 361017178409..8e5617f8a20e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -31,6 +31,7 @@ libc.workspace = true [dev-dependencies] expect-test.workspace = true +line-index.workspace = true # used as proc macro test targets proc-macro-test.path = "./proc-macro-test" diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index b4fac26d6e72..06c76b6d0381 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -79,6 +79,16 @@ pub fn fn_like_span_ops(args: TokenStream) -> TokenStream { TokenStream::from_iter(vec![first, second, third]) } +/// Returns the line and column of the first token's span as two integer literals. +#[proc_macro] +pub fn fn_like_span_line_column(args: TokenStream) -> TokenStream { + let first = args.into_iter().next().unwrap(); + let span = first.span(); + let line = Literal::usize_unsuffixed(span.line()); + let column = Literal::usize_unsuffixed(span.column()); + TokenStream::from_iter(vec![TokenTree::Literal(line), TokenTree::Literal(column)]) +} + #[proc_macro_attribute] pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream { item diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index f2d1dfbba4cc..c1ef49a7176b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -98,6 +98,8 @@ pub trait ProcMacroClientInterface { fn file(&mut self, file_id: span::FileId) -> String; fn source_text(&mut self, span: Span) -> Option; fn local_file(&mut self, file_id: span::FileId) -> Option; + /// Line and column are 1-based. + fn line_column(&mut self, span: Span) -> Option<(u32, u32)>; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 32725afc5527..3a25391b573b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -257,14 +257,12 @@ impl server::Span for RaSpanServer<'_> { Span { range: TextRange::empty(span.range.start()), ..span } } - fn line(&mut self, _span: Self::Span) -> usize { - // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 1 + fn line(&mut self, span: Self::Span) -> usize { + self.callback.as_mut().and_then(|cb| cb.line_column(span)).map_or(1, |(l, _)| l as usize) } - fn column(&mut self, _span: Self::Span) -> usize { - // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 1 + fn column(&mut self, span: Self::Span) -> usize { + self.callback.as_mut().and_then(|cb| cb.line_column(span)).map_or(1, |(_, c)| c as usize) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 20507a6def54..ebef9a9a519a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -703,6 +703,7 @@ fn list_test_macros() { fn_like_mk_idents [Bang] fn_like_span_join [Bang] fn_like_span_ops [Bang] + fn_like_span_line_column [Bang] attr_noop [Attr] attr_panic [Attr] attr_error [Attr] @@ -712,3 +713,17 @@ fn list_test_macros() { DeriveError [CustomDerive]"#]] .assert_eq(&res); } + +#[test] +fn test_fn_like_span_line_column() { + assert_expand_with_callback( + "fn_like_span_line_column", + // Input text with known position: "hello" starts at offset 1 (line 2, column 1 in 1-based) + " +hello", + expect![[r#" + LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 2 + LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 1 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 61fcd810b1d9..81ff1965d68b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -6,7 +6,8 @@ use span::{ }; use crate::{ - EnvSnapshot, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path, token_stream::TokenStream, + EnvSnapshot, ProcMacroClientInterface, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path, + token_stream::TokenStream, }; fn parse_string(call_site: SpanId, src: &str) -> TokenStream { @@ -109,3 +110,66 @@ pub(crate) fn list() -> Vec { let res = srv.list_macros(&dylib_path).unwrap(); res.into_iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect() } + +/// A mock callback for testing that computes line/column from the input text. +struct MockCallback<'a> { + text: &'a str, +} + +impl ProcMacroClientInterface for MockCallback<'_> { + fn source_text(&mut self, span: Span) -> Option { + self.text + .get(usize::from(span.range.start())..usize::from(span.range.end())) + .map(ToOwned::to_owned) + } + + fn file(&mut self, _file_id: FileId) -> String { + String::new() + } + + fn local_file(&mut self, _file_id: FileId) -> Option { + None + } + + fn line_column(&mut self, span: Span) -> Option<(u32, u32)> { + let line_index = line_index::LineIndex::new(self.text); + let line_col = line_index.try_line_col(span.range.start())?; + // proc_macro uses 1-based line/column + Some((line_col.line as u32 + 1, line_col.col as u32 + 1)) + } +} + +pub fn assert_expand_with_callback( + macro_name: &str, + #[rust_analyzer::rust_fixture] ra_fixture: &str, + expect_spanned: Expect, +) { + let path = proc_macro_test_dylib_path(); + let expander = dylib::Expander::new(&temp_dir::TempDir::new().unwrap(), &path).unwrap(); + + let def_site = Span { + range: TextRange::new(0.into(), 150.into()), + anchor: SpanAnchor { + file_id: EditionedFileId::current_edition(FileId::from_raw(41)), + ast_id: ROOT_ERASED_FILE_AST_ID, + }, + ctx: SyntaxContext::root(span::Edition::CURRENT), + }; + let call_site = Span { + range: TextRange::new(0.into(), 100.into()), + anchor: SpanAnchor { + file_id: EditionedFileId::current_edition(FileId::from_raw(42)), + ast_id: ROOT_ERASED_FILE_AST_ID, + }, + ctx: SyntaxContext::root(span::Edition::CURRENT), + }; + let mixed_site = call_site; + + let fixture = parse_string_spanned(call_site.anchor, call_site.ctx, ra_fixture); + + let mut callback = MockCallback { text: ra_fixture }; + let res = expander + .expand(macro_name, fixture, None, def_site, call_site, mixed_site, Some(&mut callback)) + .unwrap(); + expect_spanned.assert_eq(&format!("{res:?}")); +} From 4fbc52085fda746d8128a79b878a5008e94a81fe Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Jan 2026 11:30:51 +0100 Subject: [PATCH 040/583] perf: Re-use scratch allocations for `try_evaluate_obligations` --- .../crates/hir-ty/src/next_solver/fulfill.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 0fe073297279..a8bff44a0258 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -48,6 +48,7 @@ pub struct FulfillmentCtxt<'db> { /// use the context in exactly this snapshot. #[expect(unused)] usable_in_snapshot: usize, + try_evaluate_obligations_scratch: PendingObligations<'db>, } #[derive(Default, Debug, Clone)] @@ -115,6 +116,7 @@ impl<'db> FulfillmentCtxt<'db> { FulfillmentCtxt { obligations: Default::default(), usable_in_snapshot: infcx.num_open_snapshots(), + try_evaluate_obligations_scratch: Default::default(), } } } @@ -162,12 +164,12 @@ impl<'db> FulfillmentCtxt<'db> { // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able // to not put the obligations queue in `InferenceTable`'s snapshots. // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + self.try_evaluate_obligations_scratch.clear(); let mut errors = Vec::new(); - let mut obligations = Vec::new(); loop { let mut any_changed = false; - obligations.extend(self.obligations.drain_pending(|_| true)); - for (mut obligation, stalled_on) in obligations.drain(..) { + self.try_evaluate_obligations_scratch.extend(self.obligations.drain_pending(|_| true)); + for (mut obligation, stalled_on) in self.try_evaluate_obligations_scratch.drain(..) { if obligation.recursion_depth >= infcx.interner.recursion_limit() { self.obligations.on_fulfillment_overflow(infcx); // Only return true errors that we have accumulated while processing. From 4699fdc2887bd9a28921ca9e73193aa38f94d0c4 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 6 Jan 2026 16:53:34 +0800 Subject: [PATCH 041/583] Fix loses exists guard for move_guard Example --- ```rust fn main() { let cond = true; match 92 { 3 => true, x if cond => if x $0> 10 { false } else if x > 5 { true } else if x > 4 || x < -2 { false } else { true }, } } ``` **Before this PR** ```rust fn main() { let cond = true; match 92 { 3 => true, x if x > 10 => false, x if x > 5 => true, x if x > 4 || x < -2 => false, x => true, } } ``` **After this PR** ```rust fn main() { let cond = true; match 92 { 3 => true, x if cond && x > 10 => false, x if cond && x > 5 => true, x if cond && (x > 4 || x < -2) => false, x if cond => true, } } ``` --- .../ide-assists/src/handlers/move_guard.rs | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 1c0c6e43d53b..31baa63372ff 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -3,7 +3,7 @@ use syntax::{ SyntaxKind::WHITESPACE, ast::{ AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make, - syntax_factory::SyntaxFactory, + prec::ExprPrecedence, syntax_factory::SyntaxFactory, }, syntax_editor::Element, }; @@ -109,6 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard( let match_arm: MatchArm = ctx.find_node_at_offset::()?; let match_pat = match_arm.pat()?; let arm_body = match_arm.expr()?; + let arm_guard = match_arm.guard().and_then(|it| it.condition()); let mut replace_node = None; let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { @@ -149,6 +150,25 @@ pub(crate) fn move_arm_cond_to_match_guard( 0 }; let indent_level = match_arm.indent_level(); + let make_guard = |cond: Option| { + let condition = match (arm_guard.clone(), cond) { + (None, None) => return None, + (None, Some(it)) | (Some(it), None) => it, + (Some(lhs), Some(rhs)) => { + let op_expr = |expr: Expr| { + if expr.precedence().needs_parentheses_in(ExprPrecedence::LAnd) { + make.expr_paren(expr).into() + } else { + expr + } + }; + let op = syntax::ast::BinaryOp::LogicOp(syntax::ast::LogicOp::And); + let expr_bin = make.expr_bin(op_expr(lhs), op, op_expr(rhs)); + expr_bin.into() + } + }; + Some(make.match_guard(condition)) + }; for (cond, block) in conds_blocks { let only_expr = block.statements().next().is_none(); @@ -156,8 +176,7 @@ pub(crate) fn move_arm_cond_to_match_guard( Some(then_expr) if only_expr => then_expr, _ => block.dedent(dedent.into()).into(), }; - let guard = make.match_guard(cond); - let new_arm = make.match_arm(match_pat.clone(), Some(guard), expr); + let new_arm = make.match_arm(match_pat.clone(), make_guard(Some(cond)), expr); replace_arms.push(new_arm); } if let Some(block) = tail { @@ -170,7 +189,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } _ => block.dedent(dedent.into()).into(), }; - let new_arm = make.match_arm(match_pat, None, expr); + let new_arm = make.match_arm(match_pat, make_guard(None), expr); replace_arms.push(new_arm); } else { // There's no else branch. Add a pattern without guard, unless the following match @@ -185,7 +204,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } _ => { let block_expr = make.expr_empty_block().into(); - replace_arms.push(make.match_arm(match_pat, None, block_expr)); + replace_arms.push(make.match_arm(match_pat, make_guard(None), block_expr)); } } } @@ -1081,6 +1100,42 @@ fn main() { x => {} } } +"#, + ) + } + + #[test] + fn move_arm_cond_to_match_guard_elseif_exist_guard() { + check_assist( + move_arm_cond_to_match_guard, + r#" +fn main() { + let cond = true; + match 92 { + 3 => true, + x if cond => if x $0> 10 { + false + } else if x > 5 { + true + } else if x > 4 || x < -2 { + false + } else { + true + }, + } +} +"#, + r#" +fn main() { + let cond = true; + match 92 { + 3 => true, + x if cond && x > 10 => false, + x if cond && x > 5 => true, + x if cond && (x > 4 || x < -2) => false, + x if cond => true, + } +} "#, ) } From a978fdcdace3c9bee4d11e03ee3ea2be5792025c Mon Sep 17 00:00:00 2001 From: Hendrik Lind Date: Tue, 6 Jan 2026 20:49:14 +0100 Subject: [PATCH 042/583] fix: use crates where ADT was defined in deref_chain of trait_applicable_items --- .../ide-completion/src/tests/flyimport.rs | 48 +++++++++++++++++++ .../ide-db/src/imports/import_assets.rs | 14 +++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 797df3f163da..d7db896679df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1976,3 +1976,51 @@ fn main() { "#]], ); } + +#[test] +fn trait_method_import_across_multiple_crates() { + let fixture = r#" + //- /lib.rs crate:test-trait + pub trait TestTrait { + fn test_function(&self) -> u32; + } + + //- /lib.rs crate:test-implementation deps:test-trait + pub struct TestStruct(pub usize); + + impl test_trait::TestTrait for TestStruct { + fn test_function(&self) -> u32 { + 1 + } + } + + //- /main.rs crate:main deps:test-implementation,test-trait + use test_implementation::TestStruct; + + fn main() { + let test = TestStruct(42); + test.test_f$0 + } + "#; + + check( + fixture, + expect![[r#" + me test_function() (use test_trait::TestTrait) fn(&self) -> u32 + "#]], + ); + + check_edit( + "test_function", + fixture, + r#" +use test_implementation::TestStruct; +use test_trait::TestTrait; + +fn main() { + let test = TestStruct(42); + test.test_function()$0 +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 90e3bb61f44d..35579eb2590d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -600,7 +600,19 @@ fn trait_applicable_items<'db>( } deref_chain .into_iter() - .filter_map(|ty| Some((ty.krate(db).into(), ty.fingerprint_for_trait_impl()?))) + .flat_map(|ty| { + let fingerprint = ty.fingerprint_for_trait_impl()?; + let mut crates = vec![]; + + if let Some(adt) = ty.as_adt() { + // Push crate where ADT was defined + crates.push((adt.krate(db).into(), fingerprint)); + } + // Always include environment crate + crates.push((ty.krate(db).into(), fingerprint)); + Some(crates) + }) + .flatten() .unique() .collect::>() }; From 6cc9e5ccd3728842f23502dc9d1f9de08056a340 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Jan 2026 08:15:27 +0100 Subject: [PATCH 043/583] Document `Query` --- .../crates/ide-db/src/symbol_index.rs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index eb0529d6b5e7..06e1f6bb4560 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -40,15 +40,61 @@ use salsa::Update; use crate::RootDatabase; +/// A query for searching symbols in the workspace or dependencies. +/// +/// This struct configures how symbol search is performed, including the search text, +/// matching strategy, and filtering options. It is used by [`world_symbols`] to find +/// symbols across the codebase. +/// +/// # Example +/// ```ignore +/// let mut query = Query::new("MyStruct".to_string()); +/// query.only_types(); // Only search for type definitions +/// query.libs(); // Include library dependencies +/// query.exact(); // Use exact matching instead of fuzzy +/// ``` #[derive(Debug, Clone)] pub struct Query { + /// The original search query string as provided by the user. + /// Used for the final matching check via [`SearchMode::check`]. query: String, + /// Lowercase version of [`Self::query`], pre-computed for efficiency. + /// Used to build FST automata for case-insensitive index lookups. lowercased: String, + /// The search strategy to use when matching symbols. + /// - [`SearchMode::Exact`]: Symbol name must exactly match the query. + /// - [`SearchMode::Fuzzy`]: Symbol name must contain all query characters in order (subsequence match). + /// - [`SearchMode::Prefix`]: Symbol name must start with the query string. + /// + /// Defaults to [`SearchMode::Fuzzy`]. mode: SearchMode, + /// Controls filtering of trait-associated items (methods, constants, types). + /// - [`AssocSearchMode::Include`]: Include both associated and non-associated items. + /// - [`AssocSearchMode::Exclude`]: Exclude trait-associated items from results. + /// - [`AssocSearchMode::AssocItemsOnly`]: Only return trait-associated items. + /// + /// Defaults to [`AssocSearchMode::Include`]. assoc_mode: AssocSearchMode, + /// Whether the final symbol name comparison should be case-sensitive. + /// When `false`, matching is case-insensitive (e.g., "foo" matches "Foo"). + /// + /// Defaults to `false`. case_sensitive: bool, + /// When `true`, only return type definitions: structs, enums, unions, + /// type aliases, built-in types, and traits. Functions, constants, statics, + /// and modules are excluded. + /// + /// Defaults to `false`. only_types: bool, + /// When `true`, search library dependency roots instead of local workspace crates. + /// This enables finding symbols in external dependencies including the standard library. + /// + /// Defaults to `false` (search local workspace only). libs: bool, + /// When `true`, exclude re-exported/imported symbols from results, + /// showing only the original definitions. + /// + /// Defaults to `false`. exclude_imports: bool, } From 1af7813baa02ec2f05acbaae5238b37bc0ebe5d7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Jan 2026 09:12:00 +0100 Subject: [PATCH 044/583] Document `WithFixture` --- .../crates/test-fixture/src/lib.rs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index d81f27d7c3b1..ca68edd88c05 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -37,7 +37,110 @@ use triomphe::Arc; pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); +/// A trait for setting up test databases from fixture strings. +/// +/// Fixtures are strings containing Rust source code with optional metadata that describe +/// a project setup. This is the primary way to write tests for rust-analyzer without +/// having to depend on the entire sysroot. +/// +/// # Fixture Syntax +/// +/// ## Basic Structure +/// +/// A fixture without metadata is parsed into a single source file (`/main.rs`). +/// Metadata is added after a `//-` comment prefix. +/// +/// ```text +/// //- /main.rs +/// fn main() { +/// println!("Hello"); +/// } +/// ``` +/// +/// Note that the fixture syntax is optional and can be omitted if the test only requires +/// a simple single file. +/// +/// ## File Metadata +/// +/// Each file can have the following metadata after `//-`: +/// +/// - **Path** (required): Must start with `/`, e.g., `/main.rs`, `/lib.rs`, `/foo/bar.rs` +/// - **`crate:`**: Defines a new crate with this file as its root +/// - Optional version: `crate:foo@0.1.0,https://example.com/repo.git` +/// - **`deps:,`**: Dependencies (requires `crate:`) +/// - **`extern-prelude:,`**: Limits extern prelude to specified crates +/// - **`edition:`**: Rust edition (2015, 2018, 2021, 2024). Defaults to current. +/// - **`cfg:=,`**: Configuration options, e.g., `cfg:test,feature="foo"` +/// - **`env:=`**: Environment variables +/// - **`crate-attr:`**: Crate-level attributes, e.g., `crate-attr:no_std` +/// - **`new_source_root:local|library`**: Starts a new source root +/// - **`library`**: Marks crate as external library (not workspace member) +/// +/// ## Global Meta (must appear at the top, in order) +/// +/// - **`//- toolchain: nightly|stable`**: Sets the Rust toolchain (default: stable) +/// - **`//- target_data_layout: `**: LLVM data layout string +/// - **`//- target_arch: `**: Target architecture (default: x86_64) +/// - **`//- proc_macros: ,`**: Enables predefined test proc macros +/// - **`//- minicore: , `**: Includes subset of libcore +/// +/// ## Cursor Markers +/// +/// Use `$0` to mark cursor position(s) in the fixture: +/// - Single `$0`: marks a position (use with [`with_position`](Self::with_position)) +/// - Two `$0` markers: marks a range (use with [`with_range`](Self::with_range)) +/// - Escape as `\$0` if you need a literal `$0` +/// +/// # Examples +/// +/// ## Single file with cursor position +/// ```text +/// r#" +/// fn main() { +/// let x$0 = 42; +/// } +/// "# +/// ``` +/// +/// ## Multiple crates with dependencies +/// ```text +/// r#" +/// //- /main.rs crate:main deps:helper +/// use helper::greet; +/// fn main() { greet(); } +/// +/// //- /lib.rs crate:helper +/// pub fn greet() {} +/// "# +/// ``` +/// +/// ## Using minicore for lang items +/// ```text +/// r#" +/// //- minicore: option, result, iterator +/// //- /main.rs +/// fn foo() -> Option { Some(42) } +/// "# +/// ``` +/// +/// The available minicore flags are listed at the top of crates\test-utils\src\minicore.rs. +/// +/// ## Using test proc macros +/// ```text +/// r#" +/// //- proc_macros: identity, mirror +/// //- /main.rs crate:main deps:proc_macros +/// use proc_macros::identity; +/// +/// #[identity] +/// fn foo() {} +/// "# +/// ``` +/// +/// Available proc macros: `identity` (attr), `DeriveIdentity` (derive), `input_replace` (attr), +/// `mirror` (bang), `shorten` (bang) pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_single_file( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -50,6 +153,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, file) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_many_files( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -66,6 +170,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, files) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self { let mut db = Self::default(); @@ -75,6 +180,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { db } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_files_extra_proc_macros( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -88,6 +194,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { db } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); @@ -95,6 +202,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, FilePosition { file_id, offset }) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); @@ -102,6 +210,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, FileRange { file_id, range }) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_range_or_offset( #[rust_analyzer::rust_fixture] ra_fixture: &str, From ad020937855df0af1dc7416be0b12d0b8c2a89a1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Jan 2026 08:46:47 +0100 Subject: [PATCH 045/583] feat: Allow rust paths in symbol search --- .../rust-analyzer/crates/hir/src/symbols.rs | 72 +- .../crates/ide-db/src/symbol_index.rs | 664 +++++++++++++++++- 2 files changed, 724 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 073142670d2a..544c759ed3a7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -9,6 +9,7 @@ use hir_def::{ ModuleDefId, ModuleId, TraitId, db::DefDatabase, item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob}, + nameres::crate_def_map, per_ns::Item, src::{HasChildSource, HasSource}, visibility::{Visibility, VisibilityExplicitness}, @@ -20,9 +21,12 @@ use hir_ty::{ }; use intern::Symbol; use rustc_hash::FxHashMap; -use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::HasName}; +use syntax::{ + AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, + ast::{HasModuleItem, HasName}, +}; -use crate::{HasCrate, Module, ModuleDef, Semantics}; +use crate::{Crate, HasCrate, Module, ModuleDef, Semantics}; /// The actual data that is stored in the index. It should be as compact as /// possible. @@ -57,6 +61,70 @@ impl DeclarationLocation { } } +impl<'db> FileSymbol<'db> { + /// Create a `FileSymbol` representing a crate's root module. + /// This is used for crate search queries like `::` or `::foo`. + pub fn for_crate_root(db: &'db dyn HirDatabase, krate: Crate) -> Option> { + let display_name = krate.display_name(db)?; + let crate_name = display_name.crate_name(); + let root_module = krate.root_module(db); + let def_map = crate_def_map(db, krate.into()); + let module_data = &def_map[root_module.into()]; + + // Get the definition source (the source file for crate roots) + let definition = module_data.origin.definition_source(db); + let hir_file_id = definition.file_id; + + // For a crate root, the "declaration" is the source file itself + // We use the entire file's syntax node as the location + let syntax_node = definition.value.node(); + let ptr = SyntaxNodePtr::new(&syntax_node); + + // For the name, we need to create a synthetic name pointer. + // We'll use the first token of the file as a placeholder since crate roots + // don't have an explicit name in the source. + // We create a name_ptr pointing to the start of the file. + let name_ptr = match &definition.value { + crate::ModuleSource::SourceFile(sf) => { + // Try to find the first item with a name as a reasonable location for focus + // This is a bit of a hack but works for navigation purposes + let first_item: Option = sf.items().next(); + if let Some(item) = first_item { + if let Some(name) = item.syntax().children().find_map(syntax::ast::Name::cast) { + AstPtr::new(&name).wrap_left() + } else { + // No name found, try to use a NameRef instead + if let Some(name_ref) = + item.syntax().descendants().find_map(syntax::ast::NameRef::cast) + { + AstPtr::new(&name_ref).wrap_right() + } else { + return None; + } + } + } else { + return None; + } + } + _ => return None, + }; + + let loc = DeclarationLocation { hir_file_id, ptr, name_ptr }; + + Some(FileSymbol { + name: Symbol::intern(crate_name.as_str()), + def: ModuleDef::Module(root_module), + loc, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Complete::Yes, + _marker: PhantomData, + }) + } +} + /// Represents an outstanding module that the symbol collector must collect symbols from. #[derive(Debug)] struct SymbolCollectorWork { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 06e1f6bb4560..ca0d5ec1e5e6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -55,12 +55,17 @@ use crate::RootDatabase; /// ``` #[derive(Debug, Clone)] pub struct Query { - /// The original search query string as provided by the user. - /// Used for the final matching check via [`SearchMode::check`]. + /// The item name to search for (last segment of the path, or full query if no path). + /// When empty with a non-empty `path_filter`, returns all items in that module. query: String, /// Lowercase version of [`Self::query`], pre-computed for efficiency. /// Used to build FST automata for case-insensitive index lookups. lowercased: String, + /// Path segments to filter by (all segments except the last). + /// Empty if no `::` in the original query. + path_filter: Vec, + /// If true, the first path segment must be a crate name (query started with `::`). + anchor_to_crate: bool, /// The search strategy to use when matching symbols. /// - [`SearchMode::Exact`]: Symbol name must exactly match the query. /// - [`SearchMode::Fuzzy`]: Symbol name must contain all query characters in order (subsequence match). @@ -100,10 +105,13 @@ pub struct Query { impl Query { pub fn new(query: String) -> Query { - let lowercased = query.to_lowercase(); + let (path_filter, item_query, anchor_to_crate) = Self::parse_path_query(&query); + let lowercased = item_query.to_lowercase(); Query { - query, + query: item_query, lowercased, + path_filter, + anchor_to_crate, only_types: false, libs: false, mode: SearchMode::Fuzzy, @@ -113,6 +121,74 @@ impl Query { } } + /// Parse a query string that may contain path segments. + /// + /// Returns (path_filter, item_query, anchor_to_crate) where: + /// - `path_filter`: Path segments to match (all but the last segment) + /// - `item_query`: The item name to search for (last segment) + /// - `anchor_to_crate`: Whether the first segment must be a crate name + fn parse_path_query(query: &str) -> (Vec, String, bool) { + // Check for leading :: (absolute path / crate search) + let anchor_to_crate = query.starts_with("::"); + let query = if anchor_to_crate { &query[2..] } else { query }; + + // Handle sole "::" - return all crates + if query.is_empty() && anchor_to_crate { + return (vec![], String::new(), true); + } + + // Check for trailing :: (module browsing - returns all items in module) + let return_all_in_module = query.ends_with("::"); + let query = if return_all_in_module { query.trim_end_matches("::") } else { query }; + + if !query.contains("::") { + // No path separator - single segment + if anchor_to_crate && !return_all_in_module { + // "::foo" - fuzzy search crate names only + return (vec![], query.to_string(), true); + } + if return_all_in_module { + // "foo::" - browse all items in module "foo" + // path_filter = ["foo"], query = "", anchor_to_crate = false/true + return (vec![query.to_string()], String::new(), anchor_to_crate); + } + // Plain "foo" - normal fuzzy search + return (vec![], query.to_string(), false); + } + + // Filter out empty segments (e.g., "foo::::bar" -> "foo::bar") + let segments: Vec<&str> = query.split("::").filter(|s| !s.is_empty()).collect(); + + if segments.is_empty() { + return (vec![], String::new(), anchor_to_crate); + } + + let path: Vec = + segments[..segments.len() - 1].iter().map(|s| s.to_string()).collect(); + let item = if return_all_in_module { + // All segments go to path, item is empty + let mut path = path; + path.push(segments.last().unwrap().to_string()); + return (path, String::new(), anchor_to_crate); + } else { + segments.last().unwrap_or(&"").to_string() + }; + + (path, item, anchor_to_crate) + } + + /// Returns true if this query should return all items in a module + /// (i.e., the original query ended with `::`) + fn is_module_browsing(&self) -> bool { + self.query.is_empty() && !self.path_filter.is_empty() + } + + /// Returns true if this query is searching for crates + /// (i.e., the query was "::" alone or "::foo" for fuzzy crate search) + fn is_crate_search(&self) -> bool { + self.anchor_to_crate && self.path_filter.is_empty() + } + pub fn only_types(&mut self) { self.only_types = true; } @@ -181,7 +257,28 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { let _p = tracing::info_span!("world_symbols", query = ?query.query).entered(); - let indices: Vec<_> = if query.libs { + // Handle special case: "::" alone or "::foo" for crate search + if query.is_crate_search() { + return search_crates(db, &query); + } + + // If we have a path filter, resolve it to target modules first + let indices: Vec<_> = if !query.path_filter.is_empty() { + let target_modules = resolve_path_to_modules( + db, + &query.path_filter, + query.anchor_to_crate, + query.case_sensitive, + ); + + if target_modules.is_empty() { + return vec![]; // Path doesn't resolve to any module + } + + // Get symbol indices only for the resolved modules + target_modules.iter().map(|&module| SymbolIndex::module_symbols(db, module)).collect() + } else if query.libs { + // Original behavior for non-path queries searching libs LibraryRoots::get(db) .roots(db) .par_iter() @@ -192,6 +289,7 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { .map(|&root| SymbolIndex::library_symbols(db, root)) .collect() } else { + // Original behavior for non-path queries searching local crates let mut crates = Vec::new(); for &root in LocalRoots::get(db).roots(db).iter() { @@ -204,13 +302,131 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { }; let mut res = vec![]; - query.search::<()>(&indices, |f| { - res.push(f.clone()); - ControlFlow::Continue(()) - }); + + // For module browsing (empty query, non-empty path_filter), return all symbols + if query.is_module_browsing() { + for index in &indices { + for symbol in index.symbols.iter() { + // Apply existing filters (only_types, assoc_mode, exclude_imports, etc.) + if query.matches_symbol_filters(symbol) { + res.push(symbol.clone()); + } + } + } + } else { + // Normal search: use FST to match item name + query.search::<()>(&indices, |f| { + res.push(f.clone()); + ControlFlow::Continue(()) + }); + } + res } +/// Search for crates by name (handles "::" and "::foo" queries) +fn search_crates<'db>(db: &'db RootDatabase, query: &Query) -> Vec> { + let mut res = vec![]; + + for krate in Crate::all(db) { + let Some(display_name) = krate.display_name(db) else { continue }; + let crate_name = display_name.crate_name().as_str(); + + // If query is empty (sole "::"), return all crates + // Otherwise, fuzzy match the crate name + let matches = if query.query.is_empty() { + true + } else { + query.mode.check(&query.query, query.case_sensitive, crate_name) + }; + + if matches { + // Create a FileSymbol for the crate's root module + if let Some(symbol) = hir::symbols::FileSymbol::for_crate_root(db, krate) { + res.push(symbol); + } + } + } + + res +} + +/// Resolve a path filter to the target module(s) it points to. +/// Returns the modules whose symbol indices should be searched. +/// +/// The path_filter contains segments like ["std", "vec"] for a query like "std::vec::Vec". +/// We resolve this by: +/// 1. Finding crates matching the first segment +/// 2. Walking down the module tree following subsequent segments +fn resolve_path_to_modules( + db: &dyn HirDatabase, + path_filter: &[String], + anchor_to_crate: bool, + case_sensitive: bool, +) -> Vec { + let [first_segment, rest_segments @ ..] = path_filter else { + return vec![]; + }; + + // Helper for name comparison + let names_match = |actual: &str, expected: &str| -> bool { + if case_sensitive { actual == expected } else { actual.eq_ignore_ascii_case(expected) } + }; + + // Find crates matching the first segment + let matching_crates: Vec = Crate::all(db) + .into_iter() + .filter(|krate| { + krate + .display_name(db) + .is_some_and(|name| names_match(name.crate_name().as_str(), first_segment)) + }) + .collect(); + + // If anchor_to_crate is true, first segment MUST be a crate name + // If anchor_to_crate is false, first segment could be a crate OR a module in local crates + let mut candidate_modules: Vec = vec![]; + + // Add crate root modules for matching crates + for krate in matching_crates { + candidate_modules.push(krate.root_module(db)); + } + + // If not anchored to crate, also search for modules matching first segment in local crates + if !anchor_to_crate { + for &root in LocalRoots::get(db).roots(db).iter() { + for &krate in db.source_root_crates(root).iter() { + let root_module = Crate::from(krate).root_module(db); + for child in root_module.children(db) { + if let Some(name) = child.name(db) { + if names_match(name.as_str(), first_segment) { + candidate_modules.push(child); + } + } + } + } + } + } + + // Walk down the module tree for remaining path segments + for segment in rest_segments { + candidate_modules = candidate_modules + .into_iter() + .flat_map(|module| { + module.children(db).filter(|child| { + child.name(db).is_some_and(|name| names_match(name.as_str(), segment)) + }) + }) + .collect(); + + if candidate_modules.is_empty() { + break; + } + } + + candidate_modules +} + #[derive(Default)] pub struct SymbolIndex<'db> { symbols: Box<[FileSymbol<'db>]>, @@ -382,12 +598,14 @@ impl<'db> SymbolIndex<'db> { } impl Query { + /// Search symbols in the given indices. pub(crate) fn search<'db, T>( - self, + &self, indices: &[&'db SymbolIndex<'db>], cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow, ) -> Option { let _p = tracing::info_span!("symbol_index::Query::search").entered(); + let mut op = fst::map::OpBuilder::new(); match self.mode { SearchMode::Exact => { @@ -466,6 +684,41 @@ impl Query { (true, AssocSearchMode::Exclude) | (false, AssocSearchMode::AssocItemsOnly) ) } + + /// Check if a symbol passes all filters except name matching. + /// Used for module browsing where we want all items in a module. + fn matches_symbol_filters(&self, symbol: &FileSymbol<'_>) -> bool { + // Check only_types filter + if self.only_types + && !matches!( + symbol.def, + hir::ModuleDef::Adt(..) + | hir::ModuleDef::TypeAlias(..) + | hir::ModuleDef::BuiltinType(..) + | hir::ModuleDef::Trait(..) + ) + { + return false; + } + + // Check assoc_mode filter + if !self.matches_assoc_mode(symbol.is_assoc) { + return false; + } + + // Check exclude_imports filter + if self.exclude_imports && symbol.is_import { + return false; + } + + // Check underscore prefix + let ignore_underscore_prefixed = !self.query.starts_with("__"); + if ignore_underscore_prefixed && symbol.name.as_str().starts_with("__") { + return false; + } + + true + } } #[cfg(test)] @@ -622,4 +875,395 @@ pub struct Foo; let symbols = world_symbols(&db, query); expect_file!["./test_data/test_symbols_exclude_imports.txt"].assert_debug_eq(&symbols); } + + #[test] + fn test_parse_path_query() { + // Plain query - no path + let (path, item, anchor) = Query::parse_path_query("Item"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Path with item + let (path, item, anchor) = Query::parse_path_query("foo::Item"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Multi-segment path + let (path, item, anchor) = Query::parse_path_query("foo::bar::Item"); + assert_eq!(path, vec!["foo", "bar"]); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Leading :: (anchor to crate) + let (path, item, anchor) = Query::parse_path_query("::std::vec::Vec"); + assert_eq!(path, vec!["std", "vec"]); + assert_eq!(item, "Vec"); + assert!(anchor); + + // Just "::" - return all crates + let (path, item, anchor) = Query::parse_path_query("::"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, ""); + assert!(anchor); + + // "::foo" - fuzzy search crate names + let (path, item, anchor) = Query::parse_path_query("::foo"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, "foo"); + assert!(anchor); + + // Trailing :: (module browsing) + let (path, item, anchor) = Query::parse_path_query("foo::"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, ""); + assert!(!anchor); + + // Full path with trailing :: + let (path, item, anchor) = Query::parse_path_query("foo::bar::"); + assert_eq!(path, vec!["foo", "bar"]); + assert_eq!(item, ""); + assert!(!anchor); + + // Absolute path with trailing :: + let (path, item, anchor) = Query::parse_path_query("::std::vec::"); + assert_eq!(path, vec!["std", "vec"]); + assert_eq!(item, ""); + assert!(anchor); + + // Empty segments should be filtered + let (path, item, anchor) = Query::parse_path_query("foo::::bar"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, "bar"); + assert!(!anchor); + } + + #[test] + fn test_query_modes() { + // Test is_module_browsing + let query = Query::new("foo::".to_owned()); + assert!(query.is_module_browsing()); + assert!(!query.is_crate_search()); + + // Test is_crate_search with sole :: + let query = Query::new("::".to_owned()); + assert!(!query.is_module_browsing()); + assert!(query.is_crate_search()); + + // Test is_crate_search with ::foo + let query = Query::new("::foo".to_owned()); + assert!(!query.is_module_browsing()); + assert!(query.is_crate_search()); + + // Normal query should be neither + let query = Query::new("foo".to_owned()); + assert!(!query.is_module_browsing()); + assert!(!query.is_crate_search()); + + // Path query should be neither + let query = Query::new("foo::bar".to_owned()); + assert!(!query.is_module_browsing()); + assert!(!query.is_crate_search()); + } + + #[test] + fn test_path_search() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod inner; +pub struct RootStruct; + +//- /inner.rs +pub struct InnerStruct; +pub mod nested { + pub struct NestedStruct; +} +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Search for item in specific module + let query = Query::new("inner::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"InnerStruct"), "Expected InnerStruct in {:?}", names); + + // Search for item in nested module + let query = Query::new("inner::nested::NestedStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"NestedStruct"), "Expected NestedStruct in {:?}", names); + + // Search with crate prefix + let query = Query::new("main::inner::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"InnerStruct"), "Expected InnerStruct in {:?}", names); + + // Wrong path should return empty + let query = Query::new("wrong::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong path"); + } + + #[test] + fn test_module_browsing() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod mymod; + +//- /mymod.rs +pub struct MyStruct; +pub fn my_func() {} +pub const MY_CONST: u32 = 1; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Browse all items in module + let query = Query::new("main::mymod::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"MyStruct"), "Expected MyStruct in {:?}", names); + assert!(names.contains(&"my_func"), "Expected my_func in {:?}", names); + assert!(names.contains(&"MY_CONST"), "Expected MY_CONST in {:?}", names); + } + + #[test] + fn test_fuzzy_item_with_path() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod mymod; + +//- /mymod.rs +pub struct MyLongStructName; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Fuzzy match on item name with exact path + let query = Query::new("main::mymod::MyLong".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"MyLongStructName"), + "Expected fuzzy match for MyLongStructName in {:?}", + names + ); + } + + #[test] + fn test_case_insensitive_path() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod MyMod; + +//- /MyMod.rs +pub struct MyStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Case insensitive path matching (default) + let query = Query::new("main::mymod::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"MyStruct"), "Expected case-insensitive match in {:?}", names); + } + + #[test] + fn test_absolute_path_search() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:mycrate +mod inner; +pub struct CrateRoot; + +//- /inner.rs +pub struct InnerItem; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Absolute path with leading :: + let query = Query::new("::mycrate::inner::InnerItem".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"InnerItem"), + "Expected InnerItem with absolute path in {:?}", + names + ); + + // Absolute path should NOT match if crate name is wrong + let query = Query::new("::wrongcrate::inner::InnerItem".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong crate name"); + } + + #[test] + fn test_wrong_path_returns_empty() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod existing; + +//- /existing.rs +pub struct MyStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Non-existent module path + let query = Query::new("nonexistent::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for non-existent path"); + + // Correct item, wrong module + let query = Query::new("wrongmod::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong module"); + } + + #[test] + fn test_root_module_items() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:mylib +pub struct RootItem; +pub fn root_fn() {} +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Items at crate root - path is just the crate name + let query = Query::new("mylib::RootItem".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"RootItem"), "Expected RootItem at crate root in {:?}", names); + + // Browse crate root + let query = Query::new("mylib::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"RootItem"), + "Expected RootItem when browsing crate root in {:?}", + names + ); + assert!( + names.contains(&"root_fn"), + "Expected root_fn when browsing crate root in {:?}", + names + ); + } + + #[test] + fn test_crate_search_all() { + // Test that sole "::" returns all crates + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:alpha +pub struct AlphaStruct; + +//- /beta.rs crate:beta +pub struct BetaStruct; + +//- /gamma.rs crate:gamma +pub struct GammaStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Sole "::" should return all crates (as module symbols) + let query = Query::new("::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"alpha"), "Expected alpha crate in {:?}", names); + assert!(names.contains(&"beta"), "Expected beta crate in {:?}", names); + assert!(names.contains(&"gamma"), "Expected gamma crate in {:?}", names); + assert_eq!(symbols.len(), 3, "Expected exactly 3 crates, got {:?}", names); + } + + #[test] + fn test_crate_search_fuzzy() { + // Test that "::foo" fuzzy-matches crate names + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:my_awesome_lib +pub struct AwesomeStruct; + +//- /other.rs crate:another_lib +pub struct OtherStruct; + +//- /foo.rs crate:foobar +pub struct FooStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // "::foo" should fuzzy-match crate names containing "foo" + let query = Query::new("::foo".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"foobar"), "Expected foobar crate in {:?}", names); + assert_eq!(symbols.len(), 1, "Expected only foobar crate, got {:?}", names); + + // "::awesome" should match my_awesome_lib + let query = Query::new("::awesome".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"my_awesome_lib"), "Expected my_awesome_lib crate in {:?}", names); + assert_eq!(symbols.len(), 1, "Expected only my_awesome_lib crate, got {:?}", names); + + // "::lib" should match multiple crates + let query = Query::new("::lib".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"my_awesome_lib"), "Expected my_awesome_lib in {:?}", names); + assert!(names.contains(&"another_lib"), "Expected another_lib in {:?}", names); + assert_eq!(symbols.len(), 2, "Expected 2 crates matching 'lib', got {:?}", names); + + // "::nonexistent" should return empty + let query = Query::new("::nonexistent".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for non-matching crate pattern"); + } } From dc64aef1ed2ea86e42fe83f10e00c2f362dec114 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 4 Jan 2026 19:10:10 +0900 Subject: [PATCH 046/583] fix: Properly lower `SelfOnly` predicates --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 369 +++++++++++++----- .../crates/hir-ty/src/lower/path.rs | 34 +- .../crates/hir-ty/src/next_solver/interner.rs | 7 +- .../crates/hir-ty/src/next_solver/util.rs | 15 + .../hir-ty/src/tests/regression/new_solver.rs | 60 +++ 5 files changed, 373 insertions(+), 112 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 46ec554e0a65..9da32464c86f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -77,6 +77,7 @@ pub struct ImplTraits { #[derive(PartialEq, Eq, Debug, Hash)] pub struct ImplTrait { pub(crate) predicates: StoredClauses, + pub(crate) assoc_ty_bounds_start: u32, } pub type ImplTraitIdx = Idx; @@ -166,6 +167,12 @@ impl<'db> LifetimeElisionKind<'db> { } } +#[derive(Clone, Copy, PartialEq, Debug)] +pub(crate) enum GenericPredicateSource { + SelfOnly, + AssocTyBound, +} + #[derive(Debug)] pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, @@ -465,10 +472,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = self - .impl_trait_mode - .opaque_type_data - .alloc(ImplTrait { predicates: Clauses::empty(interner).store() }); + let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { + predicates: Clauses::empty(interner).store(), + assoc_ty_bounds_start: 0, + }); let impl_trait_id = origin.either( |f| ImplTraitId::ReturnTypeImplTrait(f, idx), @@ -608,7 +615,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ignore_bindings: bool, generics: &Generics, predicate_filter: PredicateFilter, - ) -> impl Iterator> + use<'a, 'b, 'db> { + ) -> impl Iterator, GenericPredicateSource)> + use<'a, 'b, 'db> { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { @@ -634,8 +641,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let self_ty = self.lower_ty(*target); Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) } - &WherePredicate::Lifetime { bound, target } => { - Either::Right(iter::once(Clause(Predicate::new( + &WherePredicate::Lifetime { bound, target } => Either::Right(iter::once(( + Clause(Predicate::new( self.interner, Binder::dummy(rustc_type_ir::PredicateKind::Clause( rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate( @@ -643,8 +650,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.lower_lifetime(target), )), )), - )))) - } + )), + GenericPredicateSource::SelfOnly, + ))), } .into_iter() } @@ -654,7 +662,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { bound: &'b TypeBound, self_ty: Ty<'db>, ignore_bindings: bool, - ) -> impl Iterator> + use<'b, 'a, 'db> { + ) -> impl Iterator, GenericPredicateSource)> + use<'b, 'a, 'db> { let interner = self.interner; let meta_sized = self.lang_items.MetaSized; let pointee_sized = self.lang_items.PointeeSized; @@ -712,7 +720,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } TypeBound::Use(_) | TypeBound::Error => {} } - clause.into_iter().chain(assoc_bounds.into_iter().flatten()) + clause + .into_iter() + .map(|pred| (pred, GenericPredicateSource::SelfOnly)) + .chain(assoc_bounds.into_iter().flatten()) } fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { @@ -732,7 +743,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { for b in bounds { let db = ctx.db; - ctx.lower_type_bound(b, dummy_self_ty, false).for_each(|b| { + ctx.lower_type_bound(b, dummy_self_ty, false).for_each(|(b, _)| { match b.kind().skip_binder() { rustc_type_ir::ClauseKind::Trait(t) => { let id = t.def_id(); @@ -990,35 +1001,49 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { rustc_type_ir::AliasTyKind::Opaque, AliasTy::new_from_args(interner, def_id, args), ); - let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { - let mut predicates = Vec::new(); - for b in bounds { - predicates.extend(ctx.lower_type_bound(b, self_ty, false)); - } + let (predicates, assoc_ty_bounds_start) = + self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut predicates = Vec::new(); + let mut assoc_ty_bounds = Vec::new(); + for b in bounds { + for (pred, source) in ctx.lower_type_bound(b, self_ty, false) { + match source { + GenericPredicateSource::SelfOnly => predicates.push(pred), + GenericPredicateSource::AssocTyBound => assoc_ty_bounds.push(pred), + } + } + } - if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = self.lang_items.Sized; - let sized_clause = sized_trait.map(|trait_id| { - let trait_ref = TraitRef::new_from_args( - interner, - trait_id.into(), - GenericArgs::new_from_slice(&[self_ty.into()]), - ); - Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )) - }); - predicates.extend(sized_clause); - } - predicates - }); - ImplTrait { predicates: Clauses::new_from_slice(&predicates).store() } + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = self.lang_items.Sized; + let sized_clause = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_slice(&[self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + predicates.extend(sized_clause); + } + + let assoc_ty_bounds_start = predicates.len() as u32; + predicates.extend(assoc_ty_bounds); + (predicates, assoc_ty_bounds_start) + }); + + ImplTrait { + predicates: Clauses::new_from_slice(&predicates).store(), + assoc_ty_bounds_start, + } } pub(crate) fn lower_lifetime(&mut self, lifetime: LifetimeRefId) -> Region<'db> { @@ -1139,6 +1164,31 @@ impl ImplTraitId { .expect("owner should have opaque type") .get_with(|it| it.impl_traits[idx].predicates.as_ref().as_slice()) } + + #[inline] + pub fn self_predicates<'db>( + self, + db: &'db dyn HirDatabase, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + let (impl_traits, idx) = match self { + ImplTraitId::ReturnTypeImplTrait(owner, idx) => { + (ImplTraits::return_type_impl_traits(db, owner), idx) + } + ImplTraitId::TypeAliasImplTrait(owner, idx) => { + (ImplTraits::type_alias_impl_traits(db, owner), idx) + } + }; + let predicates = + impl_traits.as_deref().expect("owner should have opaque type").get_with(|it| { + let impl_trait = &it.impl_traits[idx]; + ( + impl_trait.predicates.as_ref().as_slice(), + impl_trait.assoc_ty_bounds_start as usize, + ) + }); + + predicates.map_bound(|(preds, len)| &preds[..len]) + } } impl InternedOpaqueTyId { @@ -1146,6 +1196,14 @@ impl InternedOpaqueTyId { pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { self.loc(db).predicates(db) } + + #[inline] + pub fn self_predicates<'db>( + self, + db: &'db dyn HirDatabase, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + self.loc(db).self_predicates(db) + } } #[salsa::tracked] @@ -1655,12 +1713,15 @@ pub(crate) fn generic_predicates_for_param<'db>( ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { if predicate(pred, &mut ctx) { - predicates.extend(ctx.lower_where_predicate( - pred, - true, - maybe_parent_generics, - PredicateFilter::All, - )); + predicates.extend( + ctx.lower_where_predicate( + pred, + true, + maybe_parent_generics, + PredicateFilter::All, + ) + .map(|(pred, _)| pred), + ); } } } @@ -1696,21 +1757,44 @@ pub(crate) fn type_alias_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - type_alias_bounds_with_diagnostics(db, type_alias).0.map_bound(|it| it.as_slice()) + type_alias_bounds_with_diagnostics(db, type_alias).0.predicates.map_bound(|it| it.as_slice()) } -pub(crate) fn type_alias_bounds_with_diagnostics<'db>( +#[inline] +pub(crate) fn type_alias_self_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, -) -> (EarlyBinder<'db, Clauses<'db>>, Diagnostics) { - let (bounds, diags) = type_alias_bounds_with_diagnostics_query(db, type_alias); - return (bounds.get(), diags.clone()); +) -> EarlyBinder<'db, &'db [Clause<'db>]> { + let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, _) = + type_alias_bounds_with_diagnostics(db, type_alias); + predicates.map_bound(|it| &it.as_slice()[..assoc_ty_bounds_start as usize]) +} + +#[derive(PartialEq, Eq, Debug, Hash)] +struct TypeAliasBounds { + predicates: T, + assoc_ty_bounds_start: u32, +} + +fn type_alias_bounds_with_diagnostics<'db>( + db: &'db dyn HirDatabase, + type_alias: TypeAliasId, +) -> (TypeAliasBounds>>, Diagnostics) { + let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, diags) = + type_alias_bounds_with_diagnostics_query(db, type_alias); + return ( + TypeAliasBounds { + predicates: predicates.get(), + assoc_ty_bounds_start: *assoc_ty_bounds_start, + }, + diags.clone(), + ); #[salsa::tracked(returns(ref))] pub fn type_alias_bounds_with_diagnostics_query<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, - ) -> (StoredEarlyBinder, Diagnostics) { + ) -> (TypeAliasBounds>, Diagnostics) { let type_alias_data = db.type_alias_signature(type_alias); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); let mut ctx = TyLoweringContext::new( @@ -1727,10 +1811,18 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args); let mut bounds = Vec::new(); + let mut assoc_ty_bounds = Vec::new(); for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| { - bounds.push(pred); - }); + ctx.lower_type_bound(bound, interner_ty, false).for_each( + |(pred, source)| match source { + GenericPredicateSource::SelfOnly => { + bounds.push(pred); + } + GenericPredicateSource::AssocTyBound => { + assoc_ty_bounds.push(pred); + } + }, + ); } if !ctx.unsized_types.contains(&interner_ty) { @@ -1745,8 +1837,14 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( }; } + let assoc_ty_bounds_start = bounds.len() as u32; + bounds.extend(assoc_ty_bounds); + ( - StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()), + TypeAliasBounds { + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()), + assoc_ty_bounds_start, + }, create_diagnostics(ctx.diagnostics), ) } @@ -1754,11 +1852,15 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericPredicates { - // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait predicate for the - // parent. Then come the explicit predicates for the parent, then the explicit trait predicate for the child, + // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait + // predicate for the parent. Then come the bounds of the associated types of the parents, + // then the explicit, self-only predicates for the parent, then the explicit, self-only trait + // predicate for the child, then the bounds of the associated types of the child, // then the implicit trait predicate for the child, if `is_trait` is `true`. predicates: StoredEarlyBinder, + parent_explicit_self_predicates_start: u32, own_predicates_start: u32, + own_assoc_ty_bounds_start: u32, is_trait: bool, parent_is_trait: bool, } @@ -1782,7 +1884,15 @@ impl GenericPredicates { pub(crate) fn from_explicit_own_predicates( predicates: StoredEarlyBinder, ) -> Self { - Self { predicates, own_predicates_start: 0, is_trait: false, parent_is_trait: false } + let len = predicates.get().skip_binder().len() as u32; + Self { + predicates, + parent_explicit_self_predicates_start: 0, + own_predicates_start: 0, + own_assoc_ty_bounds_start: len, + is_trait: false, + parent_is_trait: false, + } } #[inline] @@ -1814,6 +1924,14 @@ impl GenericPredicates { Self::query(db, def).explicit_predicates() } + #[inline] + pub fn query_explicit_implied<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + Self::query(db, def).explicit_implied_predicates() + } + #[inline] pub fn all_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { self.predicates.get().map_bound(|it| it.as_slice()) @@ -1824,9 +1942,18 @@ impl GenericPredicates { self.predicates.get().map_bound(|it| &it.as_slice()[self.own_predicates_start as usize..]) } - /// Returns the predicates, minus the implicit `Self: Trait` predicate for a trait. + /// Returns the predicates, minus the implicit `Self: Trait` predicate and bounds of the + /// associated types for a trait. #[inline] pub fn explicit_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { + self.predicates.get().map_bound(|it| { + &it.as_slice()[self.parent_explicit_self_predicates_start as usize + ..self.own_assoc_ty_bounds_start as usize] + }) + } + + #[inline] + pub fn explicit_implied_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { self.predicates.get().map_bound(|it| { &it.as_slice()[usize::from(self.parent_is_trait)..it.len() - usize::from(self.is_trait)] }) @@ -1902,26 +2029,22 @@ where ); let sized_trait = ctx.lang_items.Sized; - let mut predicates = Vec::new(); + // We need to lower parents and self separately - see the comment below lowering of implicit + // `Sized` predicates for why. + let mut own_predicates = Vec::new(); + let mut parent_predicates = Vec::new(); + let mut own_assoc_ty_bounds = Vec::new(); + let mut parent_assoc_ty_bounds = Vec::new(); let all_generics = std::iter::successors(Some(&generics), |generics| generics.parent_generics()) .collect::>(); - let mut is_trait = false; - let mut parent_is_trait = false; - if all_generics.len() > 1 { - add_implicit_trait_predicate( - interner, - all_generics.last().unwrap().def(), - predicate_filter, - &mut predicates, - &mut parent_is_trait, - ); - } - // We need to lower parent predicates first - see the comment below lowering of implicit `Sized` predicates - // for why. - let mut own_predicates_start = 0; + let own_implicit_trait_predicate = implicit_trait_predicate(interner, def, predicate_filter); + let parent_implicit_trait_predicate = if all_generics.len() > 1 { + implicit_trait_predicate(interner, all_generics.last().unwrap().def(), predicate_filter) + } else { + None + }; for &maybe_parent_generics in all_generics.iter().rev() { - let current_def_predicates_start = predicates.len(); // Collect only diagnostics from the child, not including parents. ctx.diagnostics.clear(); @@ -1929,15 +2052,37 @@ where ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { tracing::debug!(?pred); - predicates.extend(ctx.lower_where_predicate( - pred, - false, - maybe_parent_generics, - predicate_filter, - )); + for (pred, source) in + ctx.lower_where_predicate(pred, false, maybe_parent_generics, predicate_filter) + { + match source { + GenericPredicateSource::SelfOnly => { + if maybe_parent_generics.def() == def { + own_predicates.push(pred); + } else { + parent_predicates.push(pred); + } + } + GenericPredicateSource::AssocTyBound => { + if maybe_parent_generics.def() == def { + own_assoc_ty_bounds.push(pred); + } else { + parent_assoc_ty_bounds.push(pred); + } + } + } + } } - push_const_arg_has_type_predicates(db, &mut predicates, maybe_parent_generics); + if maybe_parent_generics.def() == def { + push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); + } else { + push_const_arg_has_type_predicates( + db, + &mut parent_predicates, + maybe_parent_generics, + ); + } if let Some(sized_trait) = sized_trait { let mut add_sized_clause = |param_idx, param_id, param_data| { @@ -1971,7 +2116,11 @@ where }), )), )); - predicates.push(clause); + if maybe_parent_generics.def() == def { + own_predicates.push(clause); + } else { + parent_predicates.push(clause); + } }; let parent_params_len = maybe_parent_generics.len_parent(); maybe_parent_generics.iter_self().enumerate().for_each( @@ -1990,30 +2139,55 @@ where // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. // But we do have to lower the parent first. } - - if maybe_parent_generics.def() == def { - own_predicates_start = current_def_predicates_start as u32; - } } - add_implicit_trait_predicate(interner, def, predicate_filter, &mut predicates, &mut is_trait); - let diagnostics = create_diagnostics(ctx.diagnostics); + + // The order is: + // + // 1. parent implicit trait pred + // 2. parent assoc bounds + // 3. parent self only preds + // 4. own self only preds + // 5. own assoc ty bounds + // 6. own implicit trait pred + // + // The purpose of this is to index the slice of the followings, without making extra `Vec`s or + // iterators: + // - explicit self only predicates, of own or own + self + // - explicit predicates, of own or own + self + let predicates = parent_implicit_trait_predicate + .iter() + .chain(parent_assoc_ty_bounds.iter()) + .chain(parent_predicates.iter()) + .chain(own_predicates.iter()) + .chain(own_assoc_ty_bounds.iter()) + .chain(own_implicit_trait_predicate.iter()) + .copied() + .collect::>(); + let parent_is_trait = parent_implicit_trait_predicate.is_some(); + let is_trait = own_implicit_trait_predicate.is_some(); + let parent_explicit_self_predicates_start = + parent_is_trait as u32 + parent_assoc_ty_bounds.len() as u32; + let own_predicates_start = + parent_explicit_self_predicates_start + parent_predicates.len() as u32; + let own_assoc_ty_bounds_start = own_predicates_start + own_predicates.len() as u32; + let predicates = GenericPredicates { + parent_explicit_self_predicates_start, own_predicates_start, + own_assoc_ty_bounds_start, is_trait, parent_is_trait, predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), }; return (predicates, diagnostics); - fn add_implicit_trait_predicate<'db>( + fn implicit_trait_predicate<'db>( interner: DbInterner<'db>, def: GenericDefId, predicate_filter: PredicateFilter, - predicates: &mut Vec>, - set_is_trait: &mut bool, - ) { + ) -> Option> { // For traits, add `Self: Trait` predicate. This is // not part of the predicates that a user writes, but it // is something that one must prove in order to invoke a @@ -2029,8 +2203,9 @@ where if let GenericDefId::TraitId(def_id) = def && predicate_filter == PredicateFilter::All { - *set_is_trait = true; - predicates.push(TraitRef::identity(interner, def_id.into()).upcast(interner)); + Some(TraitRef::identity(interner, def_id.into()).upcast(interner)) + } else { + None } } } @@ -2327,7 +2502,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( let mut bounds = Vec::new(); for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, self_ty, false).for_each(|pred| { + ctx.lower_type_bound(bound, self_ty, false).for_each(|(pred, _)| { if let Some(bound) = pred .kind() .map_bound(|c| match c { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index a79f547c2a44..b77aeab62d15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -32,7 +32,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, lower::{ - LifetimeElisionKind, PathDiagnosticCallbackData, named_associated_type_shorthand_candidates, + GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData, + named_associated_type_shorthand_candidates, }, next_solver::{ Binder, Clause, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Predicate, @@ -853,7 +854,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { pub(super) fn assoc_type_bindings_from_type_bound<'c>( mut self, trait_ref: TraitRef<'db>, - ) -> Option> + use<'a, 'b, 'c, 'db>> { + ) -> Option, GenericPredicateSource)> + use<'a, 'b, 'c, 'db>> + { let interner = self.ctx.interner; self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { @@ -921,21 +923,29 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ), )), )); - predicates.push(pred); + predicates.push((pred, GenericPredicateSource::SelfOnly)); } } }) } for bound in binding.bounds.iter() { - predicates.extend(self.ctx.lower_type_bound( - bound, - Ty::new_alias( - self.ctx.interner, - AliasTyKind::Projection, - AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), - ), - false, - )); + predicates.extend( + self.ctx + .lower_type_bound( + bound, + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args( + self.ctx.interner, + associated_ty.into(), + args, + ), + ), + false, + ) + .map(|(pred, _)| (pred, GenericPredicateSource::AssocTyBound)), + ); } predicates }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 2a3df1d32a30..e17bdac68cdd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -41,7 +41,8 @@ use crate::{ AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey, RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, - TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds, + TypeAliasIdWrapper, UnevaluatedConst, + util::{explicit_item_bounds, explicit_item_self_bounds}, }, }; @@ -1421,7 +1422,7 @@ impl<'db> Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { - explicit_item_bounds(self, def_id) + explicit_item_self_bounds(self, def_id) .map_bound(|bounds| elaborate(self, bounds).filter_only_self()) } @@ -1500,7 +1501,7 @@ impl<'db> Interner for DbInterner<'db> { } } - predicates_of(self.db, def_id).explicit_predicates().map_bound(|predicates| { + predicates_of(self.db, def_id).explicit_implied_predicates().map_bound(|predicates| { predicates .iter() .copied() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 34ecfed08f29..9a1b476976e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -455,6 +455,21 @@ pub fn explicit_item_bounds<'db>( clauses.map_bound(|clauses| clauses.iter().copied()) } +pub fn explicit_item_self_bounds<'db>( + interner: DbInterner<'db>, + def_id: SolverDefId, +) -> EarlyBinder<'db, impl DoubleEndedIterator> + ExactSizeIterator> { + let db = interner.db(); + let clauses = match def_id { + SolverDefId::TypeAliasId(type_alias) => { + crate::lower::type_alias_self_bounds(db, type_alias) + } + SolverDefId::InternedOpaqueTyId(id) => id.self_predicates(db), + _ => panic!("Unexpected GenericDefId"), + }; + clauses.map_bound(|clauses| clauses.iter().copied()) +} + pub struct ContainsTypeErrors; impl<'db> TypeVisitor> for ContainsTypeErrors { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index a4554673cdd5..be6ab23ad761 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -750,3 +750,63 @@ fn main() { "#]], ); } + +#[test] +fn regression_19339() { + check_infer( + r#" +trait Bar { + type Baz; + + fn baz(&self) -> Self::Baz; +} + +trait Foo { + type Bar; + + fn bar(&self) -> Self::Bar; +} + +trait FooFactory { + type Output: Foo>; + + fn foo(&self) -> Self::Output; + + fn foo_rpit(&self) -> impl Foo>; +} + +fn test1(foo: impl Foo>) { + let baz = foo.bar().baz(); +} + +fn test2(factory: T) { + let baz = factory.foo().bar().baz(); + let baz = factory.foo_rpit().bar().baz(); +} +"#, + expect![[r#" + 39..43 'self': &'? Self + 101..105 'self': &'? Self + 198..202 'self': &'? Self + 239..243 'self': &'? Self + 290..293 'foo': impl Foo + ?Sized + 325..359 '{ ...z(); }': () + 335..338 'baz': u8 + 341..344 'foo': impl Foo + ?Sized + 341..350 'foo.bar()': impl Bar + 341..356 'foo.bar().baz()': u8 + 385..392 'factory': T + 397..487 '{ ...z(); }': () + 407..410 'baz': u8 + 413..420 'factory': T + 413..426 'factory.foo()': ::Output + 413..432 'factor....bar()': <::Output as Foo>::Bar + 413..438 'factor....baz()': u8 + 448..451 'baz': u8 + 454..461 'factory': T + 454..472 'factor...rpit()': impl Foo + Bar + ?Sized + 454..478 'factor....bar()': + ?Sized as Foo>::Bar + 454..484 'factor....baz()': u8 + "#]], + ); +} From e2b507233c3224187dd5ef53a9044a430e4f9129 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Tue, 2 Dec 2025 13:05:47 +1100 Subject: [PATCH 047/583] Add ProjectJsonTargetSpec.project_root Needed to support flychecking in a later diff --- src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs | 1 + src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 9beab3c0e45c..81d60179cc94 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -833,6 +833,7 @@ impl GlobalStateSnapshot { label: build.label, target_kind: build.target_kind, shell_runnables: project.runnables().to_owned(), + project_root: project.project_root().to_owned(), })); } ProjectWorkspaceKind::DetachedFile { .. } => {} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index e0f95a7830ea..8452b6493e87 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -68,6 +68,7 @@ pub(crate) struct ProjectJsonTargetSpec { pub(crate) label: String, pub(crate) target_kind: TargetKind, pub(crate) shell_runnables: Vec, + pub(crate) project_root: AbsPathBuf, } impl ProjectJsonTargetSpec { From ff94498ef2a831c47d9237262f612a3bb530bd69 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Tue, 2 Dec 2025 14:02:07 +1100 Subject: [PATCH 048/583] project-model: Helpers for traversing dep graph in ProjectJson Needed for all_workspace_dependencies_for_package implementation. --- .../crates/project-model/src/project_json.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index b3478d2cfe03..adc9b1a49fd4 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -78,6 +78,13 @@ pub struct ProjectJson { runnables: Vec, } +impl std::ops::Index for ProjectJson { + type Output = Crate; + fn index(&self, index: CrateArrayIdx) -> &Self::Output { + &self.crates[index.0] + } +} + impl ProjectJson { /// Create a new ProjectJson instance. /// @@ -218,6 +225,14 @@ impl ProjectJson { .find(|build| build.build_file.as_std_path() == path) } + pub fn crate_by_label(&self, label: &str) -> Option<&Crate> { + // this is fast enough for now, but it's unfortunate that this is O(crates). + self.crates + .iter() + .filter(|krate| krate.is_workspace_member) + .find(|krate| krate.build.as_ref().is_some_and(|build| build.label == label)) + } + /// Returns the path to the project's manifest or root folder, if no manifest exists. pub fn manifest_or_root(&self) -> &AbsPath { self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref()) @@ -258,6 +273,12 @@ pub struct Crate { pub build: Option, } +impl Crate { + pub fn iter_deps(&self) -> impl ExactSizeIterator { + self.deps.iter().map(|dep| dep.krate) + } +} + /// Additional, build-specific data about a crate. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Build { From 3083bde73d2e7a1e2227ef77f1f248598005ba08 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Tue, 2 Dec 2025 14:02:25 +1100 Subject: [PATCH 049/583] project-model: Don't do O(n) clones as well as O(n) search --- .../rust-analyzer/crates/project-model/src/project_json.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index adc9b1a49fd4..8fe7885983a7 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -221,8 +221,9 @@ impl ProjectJson { self.crates .iter() .filter(|krate| krate.is_workspace_member) - .filter_map(|krate| krate.build.clone()) + .filter_map(|krate| krate.build.as_ref()) .find(|build| build.build_file.as_std_path() == path) + .cloned() } pub fn crate_by_label(&self, label: &str) -> Option<&Crate> { From ac641771a83d23f965b3dcb180bb854de21002fb Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 050/583] project-model: Return crate by reference --- .../rust-analyzer/crates/project-model/src/project_json.rs | 3 +-- .../rust-analyzer/crates/rust-analyzer/src/global_state.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 8fe7885983a7..a7fba6936244 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -202,12 +202,11 @@ impl ProjectJson { &self.project_root } - pub fn crate_by_root(&self, root: &AbsPath) -> Option { + pub fn crate_by_root(&self, root: &AbsPath) -> Option<&Crate> { self.crates .iter() .filter(|krate| krate.is_workspace_member) .find(|krate| krate.root_module == root) - .cloned() } /// Returns the path to the project's manifest, if it exists. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 81d60179cc94..99dc8bce062e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -825,7 +825,7 @@ impl GlobalStateSnapshot { let Some(krate) = project.crate_by_root(path) else { continue; }; - let Some(build) = krate.build else { + let Some(build) = krate.build.clone() else { continue; }; From 3b97d38702700a274b22dcd9cc21d836d4dc1ce0 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Tue, 2 Dec 2025 14:11:52 +1100 Subject: [PATCH 051/583] Fix misuse of ? This exited the whole loop instead of having continue semantics and continuing to find workspaces. So wrap in find_map. --- .../crates/rust-analyzer/src/global_state.rs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 99dc8bce062e..68d65cdee6f3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -847,21 +847,18 @@ impl GlobalStateSnapshot { &self, package: &Arc, ) -> Option>> { - for workspace in self.workspaces.iter() { - match &workspace.kind { - ProjectWorkspaceKind::Cargo { cargo, .. } - | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { - let package = cargo.packages().find(|p| cargo[*p].id == *package)?; + self.workspaces.iter().find_map(|workspace| match &workspace.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { + let package = cargo.packages().find(|p| cargo[*p].id == *package)?; - return cargo[package] - .all_member_deps - .as_ref() - .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect()); - } - _ => {} + return cargo[package] + .all_member_deps + .as_ref() + .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect()); } - } - None + _ => None, + }) } pub(crate) fn file_exists(&self, file_id: FileId) -> bool { From 327ea186f07053f70b4bf6af394e6f8ec80f303e Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 052/583] flycheck: Make the flycheckable unit a flycheck::PackageSpecifier enum You should be able to flycheck a ProjectJson crate based on its build label. This paves the way for that. Context: I don't think this has been working for some time. It used to be that we would use cargo to build ProjectJson crates. Support for ProjectJson seems to have been somewhat steamrolled in PR 18845 (e4bf6e1bc36e4cbc8a36d7911788176eb9fac76e). --- .../crates/rust-analyzer/src/diagnostics.rs | 16 +++-- .../crates/rust-analyzer/src/flycheck.rs | 69 +++++++++++++++---- .../crates/rust-analyzer/src/global_state.rs | 50 ++++++++++---- .../src/handlers/notification.rs | 42 +++++++---- 4 files changed, 130 insertions(+), 47 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 4a247800af9d..712960f13d7e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -3,7 +3,6 @@ pub(crate) mod flycheck_to_proto; use std::mem; -use cargo_metadata::PackageId; use ide::FileId; use ide_db::{FxHashMap, base_db::DbPanicContext}; use itertools::Itertools; @@ -12,10 +11,13 @@ use smallvec::SmallVec; use stdx::iter_eq_by; use triomphe::Arc; -use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind}; +use crate::{ + flycheck::PackageSpecifier, global_state::GlobalStateSnapshot, lsp, lsp_ext, + main_loop::DiagnosticsTaskKind, +}; pub(crate) type CheckFixes = - Arc>, FxHashMap>>>>; + Arc, FxHashMap>>>>; #[derive(Debug, Default, Clone)] pub struct DiagnosticsMapConfig { @@ -29,7 +31,7 @@ pub(crate) type DiagnosticsGeneration = usize; #[derive(Debug, Clone, Default)] pub(crate) struct WorkspaceFlycheckDiagnostic { - pub(crate) per_package: FxHashMap>, PackageFlycheckDiagnostic>, + pub(crate) per_package: FxHashMap, PackageFlycheckDiagnostic>, } #[derive(Debug, Clone)] @@ -85,7 +87,7 @@ impl DiagnosticCollection { pub(crate) fn clear_check_for_package( &mut self, flycheck_id: usize, - package_id: Arc, + package_id: PackageSpecifier, ) { let Some(check) = self.check.get_mut(flycheck_id) else { return; @@ -124,7 +126,7 @@ impl DiagnosticCollection { pub(crate) fn clear_check_older_than_for_package( &mut self, flycheck_id: usize, - package_id: Arc, + package_id: PackageSpecifier, generation: DiagnosticsGeneration, ) { let Some(check) = self.check.get_mut(flycheck_id) else { @@ -154,7 +156,7 @@ impl DiagnosticCollection { &mut self, flycheck_id: usize, generation: DiagnosticsGeneration, - package_id: &Option>, + package_id: &Option, file_id: FileId, diagnostic: lsp_types::Diagnostic, fix: Option>, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index b06264169188..2819ae98daaf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -195,9 +195,9 @@ impl FlycheckHandle { /// Schedule a re-start of the cargo check worker to do a package wide check. pub(crate) fn restart_for_package( &self, - package: Arc, + package: PackageSpecifier, target: Option, - workspace_deps: Option>>, + workspace_deps: Option>, ) { let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender @@ -233,7 +233,7 @@ pub(crate) enum ClearDiagnosticsKind { #[derive(Debug)] pub(crate) enum ClearScope { Workspace, - Package(Arc), + Package(PackageSpecifier), } pub(crate) enum FlycheckMessage { @@ -243,7 +243,7 @@ pub(crate) enum FlycheckMessage { generation: DiagnosticsGeneration, workspace_root: Arc, diagnostic: Diagnostic, - package_id: Option>, + package_id: Option, }, /// Request clearing all outdated diagnostics. @@ -295,7 +295,32 @@ pub(crate) enum Progress { enum FlycheckScope { Workspace, - Package { package: Arc, workspace_deps: Option>> }, + Package { + // Either a cargo package or a $label in rust-project.check.overrideCommand + package: PackageSpecifier, + workspace_deps: Option>, + }, +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +pub(crate) enum PackageSpecifier { + Cargo { + /// The one in Cargo.toml, assumed to work with `cargo check -p {}` etc + package_id: Arc, + }, + BuildInfo { + /// If a `build` field is present in rust-project.json, its label field + label: String, + }, +} + +impl PackageSpecifier { + pub(crate) fn as_str(&self) -> &str { + match self { + Self::Cargo { package_id } => &package_id.repr, + Self::BuildInfo { label } => label, + } + } } enum StateChange { @@ -331,7 +356,7 @@ struct FlycheckActor { command_handle: Option>, /// The receiver side of the channel mentioned above. command_receiver: Option>, - diagnostics_cleared_for: FxHashSet>, + diagnostics_cleared_for: FxHashSet, diagnostics_received: DiagnosticsReceived, } @@ -564,7 +589,10 @@ impl FlycheckActor { msg.target.kind.iter().format_with(", ", |kind, f| f(&kind)), ))); let package_id = Arc::new(msg.package_id); - if self.diagnostics_cleared_for.insert(package_id.clone()) { + if self + .diagnostics_cleared_for + .insert(PackageSpecifier::Cargo { package_id: package_id.clone() }) + { tracing::trace!( flycheck_id = self.id, package_id = package_id.repr, @@ -572,7 +600,9 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)), + kind: ClearDiagnosticsKind::All(ClearScope::Package( + PackageSpecifier::Cargo { package_id }, + )), }); } } @@ -580,7 +610,7 @@ impl FlycheckActor { tracing::trace!( flycheck_id = self.id, message = diagnostic.message, - package_id = package_id.as_ref().map(|it| &it.repr), + package_id = package_id.as_ref().map(|it| it.as_str()), "diagnostic received" ); if self.diagnostics_received == DiagnosticsReceived::No { @@ -590,7 +620,7 @@ impl FlycheckActor { if self.diagnostics_cleared_for.insert(package_id.clone()) { tracing::trace!( flycheck_id = self.id, - package_id = package_id.repr, + package_id = package_id.as_str(), "clearing diagnostics" ); self.send(FlycheckMessage::ClearDiagnostics { @@ -666,7 +696,18 @@ impl FlycheckActor { match scope { FlycheckScope::Workspace => cmd.arg("--workspace"), - FlycheckScope::Package { package, .. } => cmd.arg("-p").arg(&package.repr), + FlycheckScope::Package { + package: PackageSpecifier::Cargo { package_id }, + .. + } => cmd.arg("-p").arg(&package_id.repr), + FlycheckScope::Package { + package: PackageSpecifier::BuildInfo { .. }, .. + } => { + // No way to flycheck this single package. All we have is a build label. + // There's no way to really say whether this build label happens to be + // a cargo canonical name, so we won't try. + return None; + } }; if let Some(tgt) = target { @@ -748,7 +789,7 @@ impl FlycheckActor { #[allow(clippy::large_enum_variant)] enum CargoCheckMessage { CompilerArtifact(cargo_metadata::Artifact), - Diagnostic { diagnostic: Diagnostic, package_id: Option> }, + Diagnostic { diagnostic: Diagnostic, package_id: Option }, } struct CargoCheckParser; @@ -767,7 +808,9 @@ impl JsonLinesParser for CargoCheckParser { cargo_metadata::Message::CompilerMessage(msg) => { Some(CargoCheckMessage::Diagnostic { diagnostic: msg.message, - package_id: Some(Arc::new(msg.package_id)), + package_id: Some(PackageSpecifier::Cargo { + package_id: Arc::new(msg.package_id), + }), }) } _ => None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 68d65cdee6f3..0cfd0a141bae 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -9,7 +9,6 @@ use std::{ time::{Duration, Instant}, }; -use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, unbounded}; use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; @@ -36,7 +35,7 @@ use crate::{ config::{Config, ConfigChange, ConfigErrors, RatomlFileKind}, diagnostics::{CheckFixes, DiagnosticCollection}, discover, - flycheck::{FlycheckHandle, FlycheckMessage}, + flycheck::{FlycheckHandle, FlycheckMessage, PackageSpecifier}, line_index::{LineEndings, LineIndex}, lsp::{from_proto, to_proto::url_from_abs_path}, lsp_ext, @@ -845,20 +844,43 @@ impl GlobalStateSnapshot { pub(crate) fn all_workspace_dependencies_for_package( &self, - package: &Arc, - ) -> Option>> { - self.workspaces.iter().find_map(|workspace| match &workspace.kind { - ProjectWorkspaceKind::Cargo { cargo, .. } - | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { - let package = cargo.packages().find(|p| cargo[*p].id == *package)?; + package: &PackageSpecifier, + ) -> Option> { + match package { + PackageSpecifier::Cargo { package_id } => { + self.workspaces.iter().find_map(|workspace| match &workspace.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { + let package = cargo.packages().find(|p| cargo[*p].id == *package_id)?; - return cargo[package] - .all_member_deps - .as_ref() - .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect()); + cargo[package].all_member_deps.as_ref().map(|deps| { + deps.iter() + .map(|dep| cargo[*dep].id.clone()) + .map(|p| PackageSpecifier::Cargo { package_id: p }) + .collect() + }) + } + _ => None, + }) } - _ => None, - }) + PackageSpecifier::BuildInfo { label } => { + self.workspaces.iter().find_map(|workspace| match &workspace.kind { + ProjectWorkspaceKind::Json(p) => { + let krate = p.crate_by_label(label)?; + Some( + krate + .iter_deps() + .filter_map(|dep| p[dep].build.as_ref()) + .map(|build| PackageSpecifier::BuildInfo { + label: build.label.clone(), + }) + .collect(), + ) + } + _ => None, + }) + } + } } pub(crate) fn file_exists(&self, file_id: FileId) -> bool { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 4a6544508ff4..57adbbfe72a7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -18,7 +18,7 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ config::{Config, ConfigChange}, - flycheck::{InvocationStrategy, Target}, + flycheck::{InvocationStrategy, PackageSpecifier, Target}, global_state::{FetchWorkspaceRequest, GlobalState}, lsp::{from_proto, utils::apply_document_changes}, lsp_ext::{self, RunFlycheckParams}, @@ -328,22 +328,32 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } InvocationStrategy::PerWorkspace => { Box::new(move || { - let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { + let target = TargetSpec::for_file(&world, file_id)?.map(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { - TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package_id), - _ => return None, + TargetSpec::Cargo(c) => ( + Some(c.target), + c.workspace_root, + PackageSpecifier::Cargo { package_id: c.package_id }, + ), + TargetSpec::ProjectJson(p) => ( + None, + p.project_root, + PackageSpecifier::BuildInfo { label: p.label.clone() }, + ), }; - let tgt = match tgt_kind { - project_model::TargetKind::Bin => Target::Bin(tgt_name), - project_model::TargetKind::Example => Target::Example(tgt_name), - project_model::TargetKind::Test => Target::Test(tgt_name), - project_model::TargetKind::Bench => Target::Benchmark(tgt_name), - _ => return Some((None, root, package)), - }; + let tgt = tgt_name.and_then(|tgt_name| { + Some(match tgt_kind { + project_model::TargetKind::Bin => Target::Bin(tgt_name), + project_model::TargetKind::Example => Target::Example(tgt_name), + project_model::TargetKind::Test => Target::Test(tgt_name), + project_model::TargetKind::Bench => Target::Benchmark(tgt_name), + _ => return None, + }) + }); - Some((Some(tgt), root, package)) + (tgt, root, package) }); tracing::debug!(?target, "flycheck target"); // we have a specific non-library target, attempt to only check that target, nothing @@ -365,7 +375,13 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { cargo: Some((cargo, _, _)), .. } => *cargo.workspace_root() == root, - _ => false, + project_model::ProjectWorkspaceKind::Json(p) => { + *p.project_root() == root + } + project_model::ProjectWorkspaceKind::DetachedFile { + cargo: None, + .. + } => false, }); if let Some(idx) = package_workspace_idx { let workspace_deps = From 95a07dbfa06ad7c30d4e9fa2f85de2b991d610f3 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 053/583] project-model: Introduce RunnableKind::Flycheck We need to distinguish from RunnableKind::Check, which is human-readable. --- .../crates/project-model/src/project_json.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index a7fba6936244..536f170e1192 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -246,6 +246,10 @@ impl ProjectJson { pub fn runnables(&self) -> &[Runnable] { &self.runnables } + + pub fn runnable_template(&self, kind: RunnableKind) -> Option<&Runnable> { + self.runnables().iter().find(|r| r.kind == kind) + } } /// A crate points to the root module of a crate and lists the dependencies of the crate. This is @@ -349,6 +353,7 @@ pub struct Runnable { /// The kind of runnable. #[derive(Debug, Clone, PartialEq, Eq)] pub enum RunnableKind { + /// `cargo check`, basically, with human-readable output. Check, /// Can run a binary. @@ -356,6 +361,10 @@ pub enum RunnableKind { /// Run a single test. TestOne, + + /// Template for checking a target, emitting rustc JSON diagnostics. + /// May include {label} which will get the label from the `build` section of a crate. + Flycheck, } #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] @@ -462,6 +471,7 @@ pub struct RunnableData { #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum RunnableKindData { + Flycheck, Check, Run, TestOne, @@ -532,6 +542,7 @@ impl From for RunnableKind { RunnableKindData::Check => RunnableKind::Check, RunnableKindData::Run => RunnableKind::Run, RunnableKindData::TestOne => RunnableKind::TestOne, + RunnableKindData::Flycheck => RunnableKind::Flycheck, } } } From 2a899bb119bd9e33eadce3b7179cb1b8bc49fd78 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 054/583] flycheck: Use RunnableKind::Flycheck from ProjectJson to flycheck This adds a substitution helper to get the right behaviour re {label} and $saved_file. --- .../crates/rust-analyzer/src/flycheck.rs | 226 +++++++++++++++++- .../crates/rust-analyzer/src/reload.rs | 19 +- 2 files changed, 238 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 2819ae98daaf..1b1e3344e25f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -14,6 +14,7 @@ use ide_db::FxHashSet; use itertools::Itertools; use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use project_model::TargetDirectoryConfig; +use project_model::project_json; use rustc_hash::FxHashMap; use serde::Deserialize as _; use serde_derive::Deserialize; @@ -89,6 +90,24 @@ impl CargoOptions { } } +/// The flycheck config from a rust-project.json file or discoverConfig JSON output. +#[derive(Debug, Default)] +pub(crate) struct FlycheckConfigJson { + /// The template with [project_json::RunnableKind::Flycheck] + pub single_template: Option, +} + +impl FlycheckConfigJson { + pub(crate) fn any_configured(&self) -> bool { + // self.workspace_template.is_some() || + self.single_template.is_some() + } +} + +/// The flycheck config from rust-analyzer's own configuration. +/// +/// We rely on this when rust-project.json does not specify a flycheck runnable +/// #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum FlycheckConfig { CargoCommand { @@ -128,7 +147,7 @@ impl fmt::Display for FlycheckConfig { // in the IDE (e.g. in the VS Code status bar). let display_args = args .iter() - .map(|arg| if arg == SAVED_FILE_PLACEHOLDER { "..." } else { arg }) + .map(|arg| if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { "..." } else { arg }) .collect::>(); write!(f, "{command} {}", display_args.join(" ")) @@ -156,6 +175,7 @@ impl FlycheckHandle { generation: Arc, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, sysroot_root: Option, workspace_root: AbsPathBuf, manifest_path: Option, @@ -166,6 +186,7 @@ impl FlycheckHandle { generation.load(Ordering::Relaxed), sender, config, + config_json, sysroot_root, workspace_root, manifest_path, @@ -341,6 +362,8 @@ struct FlycheckActor { generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, + manifest_path: Option, ws_target_dir: Option, /// Either the workspace root of the workspace we are flychecking, @@ -373,7 +396,66 @@ enum Event { CheckEvent(Option), } -pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; +/// This is stable behaviour. Don't change. +const SAVED_FILE_PLACEHOLDER_DOLLAR: &str = "$saved_file"; +const LABEL_INLINE: &str = "{label}"; +const SAVED_FILE_INLINE: &str = "{saved_file}"; + +struct Substitutions<'a> { + label: Option<&'a str>, + saved_file: Option<&'a str>, +} + +impl<'a> Substitutions<'a> { + /// If you have a runnable, and it has {label} in it somewhere, treat it as a template that + /// may be unsatisfied if you do not provide a label to substitute into it. Returns None in + /// that situation. Otherwise performs the requested substitutions. + /// + /// Same for {saved_file}. + /// + #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ + fn substitute( + self, + template: &project_json::Runnable, + extra_env: &std::collections::HashMap, H>, + ) -> Option { + let mut cmd = toolchain::command(&template.program, &template.cwd, extra_env); + for arg in &template.args { + if let Some(ix) = arg.find(LABEL_INLINE) { + if let Some(label) = self.label { + let mut arg = arg.to_string(); + arg.replace_range(ix..ix + LABEL_INLINE.len(), label); + cmd.arg(arg); + continue; + } else { + return None; + } + } + if let Some(ix) = arg.find(SAVED_FILE_INLINE) { + if let Some(saved_file) = self.saved_file { + let mut arg = arg.to_string(); + arg.replace_range(ix..ix + SAVED_FILE_INLINE.len(), saved_file); + cmd.arg(arg); + continue; + } else { + return None; + } + } + // Legacy syntax: full argument match + if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { + if let Some(saved_file) = self.saved_file { + cmd.arg(saved_file); + continue; + } else { + return None; + } + } + cmd.arg(arg); + } + cmd.current_dir(&template.cwd); + Some(cmd) + } +} impl FlycheckActor { fn new( @@ -381,6 +463,7 @@ impl FlycheckActor { generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, sysroot_root: Option, workspace_root: AbsPathBuf, manifest_path: Option, @@ -392,6 +475,7 @@ impl FlycheckActor { generation, sender, config, + config_json, sysroot_root, root: Arc::new(workspace_root), scope: FlycheckScope::Workspace, @@ -672,6 +756,29 @@ impl FlycheckActor { self.diagnostics_received = DiagnosticsReceived::No; } + fn explicit_check_command( + &self, + scope: &FlycheckScope, + saved_file: Option<&AbsPath>, + ) -> Option { + let label = match scope { + // We could add a runnable like "RunnableKind::FlycheckWorkspace". But generally + // if you're not running cargo, it's because your workspace is too big to check + // all at once. You can always use `check_overrideCommand` with no {label}. + FlycheckScope::Workspace => return None, + FlycheckScope::Package { package: PackageSpecifier::BuildInfo { label }, .. } => { + label.as_str() + } + FlycheckScope::Package { + package: PackageSpecifier::Cargo { package_id: label }, + .. + } => &label.repr, + }; + let template = self.config_json.single_template.as_ref()?; + let subs = Substitutions { label: Some(label), saved_file: saved_file.map(|x| x.as_str()) }; + subs.substitute(template, &FxHashMap::default()) + } + /// Construct a `Command` object for checking the user's code. If the user /// has specified a custom command with placeholders that we cannot fill, /// return None. @@ -683,6 +790,20 @@ impl FlycheckActor { ) -> Option { match &self.config { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { + // Only use the rust-project.json's flycheck config when no check_overrideCommand + // is configured. In the FlycheckConcig::CustomCommand branch we will still do + // label substitution, but on the overrideCommand instead. + // + // There needs to be SOME way to override what your discoverConfig tool says, + // because to change the flycheck runnable there you may have to literally + // recompile the tool. + if self.config_json.any_configured() { + // Completely handle according to rust-project.json. + // We don't consider this to be "using cargo" so we will not apply any of the + // CargoOptions to the command. + return self.explicit_check_command(scope, saved_file); + } + let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root, &options.extra_env); if let Some(sysroot_root) = &self.sysroot_root @@ -757,7 +878,7 @@ impl FlycheckActor { // we're saving a file, replace the placeholder in the arguments. if let Some(saved_file) = saved_file { for arg in args { - if arg == SAVED_FILE_PLACEHOLDER { + if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { cmd.arg(saved_file); } else { cmd.arg(arg); @@ -765,7 +886,7 @@ impl FlycheckActor { } } else { for arg in args { - if arg == SAVED_FILE_PLACEHOLDER { + if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { // The custom command has a $saved_file placeholder, // but we had an IDE event that wasn't a file save. Do nothing. return None; @@ -837,3 +958,100 @@ enum JsonMessage { Cargo(cargo_metadata::Message), Rustc(Diagnostic), } + +#[cfg(test)] +mod tests { + use ide_db::FxHashMap; + use itertools::Itertools; + use paths::Utf8Path; + use project_model::project_json; + + use crate::flycheck::Substitutions; + + #[test] + fn test_substitutions() { + let label = ":label"; + let saved_file = "file.rs"; + + // Runnable says it needs both; you need both. + assert_eq!(test_substitute(None, None, "{label} {saved_file}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{label} {saved_file}").as_deref(), None); + assert_eq!( + test_substitute(None, Some(saved_file), "{label} {saved_file}").as_deref(), + None + ); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{label} {saved_file}").as_deref(), + Some("build :label file.rs") + ); + + // Only need label? only need label. + assert_eq!(test_substitute(None, None, "{label}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{label}").as_deref(), Some("build :label"),); + assert_eq!(test_substitute(None, Some(saved_file), "{label}").as_deref(), None,); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{label}").as_deref(), + Some("build :label"), + ); + + // Only need saved_file + assert_eq!(test_substitute(None, None, "{saved_file}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{saved_file}").as_deref(), None); + assert_eq!( + test_substitute(None, Some(saved_file), "{saved_file}").as_deref(), + Some("build file.rs") + ); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{saved_file}").as_deref(), + Some("build file.rs") + ); + + // Need neither + assert_eq!(test_substitute(None, None, "xxx").as_deref(), Some("build xxx")); + assert_eq!(test_substitute(Some(label), None, "xxx").as_deref(), Some("build xxx")); + assert_eq!(test_substitute(None, Some(saved_file), "xxx").as_deref(), Some("build xxx")); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "xxx").as_deref(), + Some("build xxx") + ); + + // {label} mid-argument substitution + assert_eq!( + test_substitute(Some(label), None, "--label={label}").as_deref(), + Some("build --label=:label") + ); + + // {saved_file} mid-argument substitution + assert_eq!( + test_substitute(None, Some(saved_file), "--saved={saved_file}").as_deref(), + Some("build --saved=file.rs") + ); + + // $saved_file legacy support (no mid-argument substitution, we never supported that) + assert_eq!( + test_substitute(None, Some(saved_file), "$saved_file").as_deref(), + Some("build file.rs") + ); + + fn test_substitute( + label: Option<&str>, + saved_file: Option<&str>, + args: &str, + ) -> Option { + Substitutions { label, saved_file } + .substitute( + &project_json::Runnable { + program: "build".to_owned(), + args: Vec::from_iter(args.split_whitespace().map(ToOwned::to_owned)), + cwd: Utf8Path::new("/path").to_owned(), + kind: project_json::RunnableKind::Flycheck, + }, + &FxHashMap::default(), + ) + .map(|command| { + command.get_args().map(|x| x.to_string_lossy()).collect_vec().join(" ") + }) + .map(|args| format!("build {}", args)) + } + } +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index e3a5ee221973..0a16b7a5614c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -25,7 +25,9 @@ use load_cargo::{ProjectFolders, load_proc_macro}; use lsp_types::FileSystemWatcher; use paths::Utf8Path; use proc_macro_api::ProcMacroClient; -use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; +use project_model::{ + ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts, project_json, +}; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, ChangeKind}; @@ -875,6 +877,7 @@ impl GlobalState { generation.clone(), sender.clone(), config, + crate::flycheck::FlycheckConfigJson::default(), None, self.config.root_path().clone(), None, @@ -894,16 +897,25 @@ impl GlobalState { cargo: Some((cargo, _, _)), .. } => ( + crate::flycheck::FlycheckConfigJson::default(), cargo.workspace_root(), Some(cargo.manifest_path()), Some(cargo.target_directory()), ), ProjectWorkspaceKind::Json(project) => { + let config_json = crate::flycheck::FlycheckConfigJson { + single_template: project + .runnable_template(project_json::RunnableKind::Flycheck) + .cloned(), + }; // Enable flychecks for json projects if a custom flycheck command was supplied // in the workspace configuration. match config { + _ if config_json.any_configured() => { + (config_json, project.path(), None, None) + } FlycheckConfig::CustomCommand { .. } => { - (project.path(), None, None) + (config_json, project.path(), None, None) } _ => return None, } @@ -913,12 +925,13 @@ impl GlobalState { ws.sysroot.root().map(ToOwned::to_owned), )) }) - .map(|(id, (root, manifest_path, target_dir), sysroot_root)| { + .map(|(id, (config_json, root, manifest_path, target_dir), sysroot_root)| { FlycheckHandle::spawn( id, generation.clone(), sender.clone(), config.clone(), + config_json, sysroot_root, root.to_path_buf(), manifest_path.map(|it| it.to_path_buf()), From 7f608da06af36c0c21b6119959e47d9480ccb18a Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 055/583] flycheck: Support {label} in check_overrideCommand as well as $saved_file --- .../crates/rust-analyzer/src/flycheck.rs | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 1b1e3344e25f..cf4ab29b8649 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -872,29 +872,23 @@ impl FlycheckActor { &*self.root } }; - let mut cmd = toolchain::command(command, root, extra_env); + let runnable = project_json::Runnable { + program: command.clone(), + cwd: Utf8Path::to_owned(root.as_ref()), + args: args.clone(), + kind: project_json::RunnableKind::Flycheck, + }; - // If the custom command has a $saved_file placeholder, and - // we're saving a file, replace the placeholder in the arguments. - if let Some(saved_file) = saved_file { - for arg in args { - if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { - cmd.arg(saved_file); - } else { - cmd.arg(arg); - } - } - } else { - for arg in args { - if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { - // The custom command has a $saved_file placeholder, - // but we had an IDE event that wasn't a file save. Do nothing. - return None; - } + let label = match scope { + FlycheckScope::Workspace => None, + // We support substituting both build labels (e.g. buck, bazel) and cargo package ids. + // With cargo package ids, you get `cargo check -p path+file:///path/to/rust-analyzer/crates/hir#0.0.0`. + // That does work! + FlycheckScope::Package { package, .. } => Some(package.as_str()), + }; - cmd.arg(arg); - } - } + let subs = Substitutions { label, saved_file: saved_file.map(|x| x.as_str()) }; + let cmd = subs.substitute(&runnable, extra_env)?; Some(cmd) } From 4e61c6052124c2e71403973cc3adb34c4ee5454d Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 056/583] flycheck: Always flycheck single crate if there is a build label from rust-project.json This requires us to add $saved_file / {saved_file} interpolation back to restart_for_package. Otherwise we break existing users of $saved_file. No grand reason why we can't delete saved_file later, although I would just leave it because sometimes a build system might really know better which target(s) to build, including multiple targets. --- .../rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 3 ++- .../crates/rust-analyzer/src/handlers/notification.rs | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index cf4ab29b8649..57ad774b1850 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -219,13 +219,14 @@ impl FlycheckHandle { package: PackageSpecifier, target: Option, workspace_deps: Option>, + saved_file: Option, ) { let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender .send(StateChange::Restart { generation, scope: FlycheckScope::Package { package, workspace_deps }, - saved_file: None, + saved_file, target, }) .unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 57adbbfe72a7..d95601043330 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -328,6 +328,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } InvocationStrategy::PerWorkspace => { Box::new(move || { + let saved_file = vfs_path.as_path().map(ToOwned::to_owned); let target = TargetSpec::for_file(&world, file_id)?.map(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { @@ -362,8 +363,10 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { if let Some((target, root, package)) = target { // trigger a package check if we have a non-library target as that can't affect // anything else in the workspace OR if we're not allowed to check the workspace as - // the user opted into package checks then - let package_check_allowed = target.is_some() || !may_flycheck_workspace; + // the user opted into package checks then OR if this is not cargo. + let package_check_allowed = target.is_some() + || !may_flycheck_workspace + || matches!(package, PackageSpecifier::BuildInfo { .. }); if package_check_allowed { package_workspace_idx = world.workspaces.iter().position(|ws| match &ws.kind { @@ -390,6 +393,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { package, target, workspace_deps, + saved_file.clone(), ); } } @@ -460,7 +464,6 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { ws_contains_file && !is_pkg_ws }); - let saved_file = vfs_path.as_path().map(ToOwned::to_owned); let mut workspace_check_triggered = false; // Find and trigger corresponding flychecks 'flychecks: for flycheck in world.flycheck.iter() { From 778de45547f9a584894fad295c86539e7c57aa9d Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 057/583] flycheck: Add display_command to pretty-print flycheck command being run in a notification --- .../crates/rust-analyzer/src/flycheck.rs | 82 +++++++++++++++++++ .../rust-analyzer/crates/toolchain/src/lib.rs | 3 + 2 files changed, 85 insertions(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 57ad774b1850..7f814121e909 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -22,6 +22,7 @@ use serde_derive::Deserialize; pub(crate) use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, }; +use toolchain::DISPLAY_COMMAND_IGNORE_ENVS; use toolchain::Tool; use triomphe::Arc; @@ -954,6 +955,54 @@ enum JsonMessage { Rustc(Diagnostic), } +/// Not good enough to execute in a shell, but good enough to show the user without all the noisy +/// quotes +/// +/// Pass implicit_cwd if there is one regarded as the obvious by the user, so we can skip showing it. +/// Compactness is the aim of the game, the output typically gets truncated quite a lot. +fn display_command(c: &Command, implicit_cwd: Option<&std::path::Path>) -> String { + let mut o = String::new(); + use std::fmt::Write; + let lossy = std::ffi::OsStr::to_string_lossy; + if let Some(dir) = c.get_current_dir() { + if Some(dir) == implicit_cwd.map(std::path::Path::new) { + // pass + } else if dir.to_string_lossy().contains(" ") { + write!(o, "cd {:?} && ", dir).unwrap(); + } else { + write!(o, "cd {} && ", dir.display()).unwrap(); + } + } + for (env, val) in c.get_envs() { + let (env, val) = (lossy(env), val.map(lossy).unwrap_or(std::borrow::Cow::Borrowed(""))); + if DISPLAY_COMMAND_IGNORE_ENVS.contains(&env.as_ref()) { + continue; + } + if env.contains(" ") { + write!(o, "\"{}={}\" ", env, val).unwrap(); + } else if val.contains(" ") { + write!(o, "{}=\"{}\" ", env, val).unwrap(); + } else { + write!(o, "{}={} ", env, val).unwrap(); + } + } + let prog = lossy(c.get_program()); + if prog.contains(" ") { + write!(o, "{:?}", prog).unwrap(); + } else { + write!(o, "{}", prog).unwrap(); + } + for arg in c.get_args() { + let arg = lossy(arg); + if arg.contains(" ") { + write!(o, " \"{}\"", arg).unwrap(); + } else { + write!(o, " {}", arg).unwrap(); + } + } + o +} + #[cfg(test)] mod tests { use ide_db::FxHashMap; @@ -962,6 +1011,7 @@ mod tests { use project_model::project_json; use crate::flycheck::Substitutions; + use crate::flycheck::display_command; #[test] fn test_substitutions() { @@ -1049,4 +1099,36 @@ mod tests { .map(|args| format!("build {}", args)) } } + + #[test] + fn test_display_command() { + use std::path::Path; + let workdir = Path::new("workdir"); + let mut cmd = toolchain::command("command", workdir, &FxHashMap::default()); + assert_eq!(display_command(cmd.arg("--arg"), Some(workdir)), "command --arg"); + assert_eq!( + display_command(cmd.arg("spaced arg"), Some(workdir)), + "command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.env("ENVIRON", "yeah"), Some(workdir)), + "ENVIRON=yeah command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.env("OTHER", "spaced env"), Some(workdir)), + "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp"), Some(workdir)), + "cd /tmp && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp and/thing"), Some(workdir)), + "cd \"/tmp and/thing\" && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp and/thing"), Some(Path::new("/tmp and/thing"))), + "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + } } diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index 39319886cfe4..1a1726983870 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -74,6 +74,9 @@ impl Tool { // Prevent rustup from automatically installing toolchains, see https://github.com/rust-lang/rust-analyzer/issues/20719. pub const NO_RUSTUP_AUTO_INSTALL_ENV: (&str, &str) = ("RUSTUP_AUTO_INSTALL", "0"); +// These get ignored when displaying what command is running in LSP status messages. +pub const DISPLAY_COMMAND_IGNORE_ENVS: &[&str] = &[NO_RUSTUP_AUTO_INSTALL_ENV.0]; + #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ pub fn command( cmd: impl AsRef, From 53a371c505a00be220aca18dfba091ee2b2d8f31 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 058/583] flycheck: notifications show full command when configured in a rust-project.json runnable For JSON / override users, pretty-print the custom flycheck command with fewer quote characters Better debug logging in flycheck --- .../crates/rust-analyzer/src/flycheck.rs | 52 ++++++++++++++----- .../crates/rust-analyzer/src/global_state.rs | 2 + .../crates/rust-analyzer/src/main_loop.rs | 27 +++++++--- .../crates/rust-analyzer/src/reload.rs | 1 + 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 7f814121e909..6dcae76c9354 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -309,13 +309,18 @@ impl fmt::Debug for FlycheckMessage { #[derive(Debug)] pub(crate) enum Progress { - DidStart, + DidStart { + /// The user sees this in VSCode, etc. May be a shortened version of the command we actually + /// executed, otherwise it is way too long. + user_facing_command: String, + }, DidCheckCrate(String), DidFinish(io::Result<()>), DidCancel, DidFailToRestart(String), } +#[derive(Debug, Clone)] enum FlycheckScope { Workspace, Package { @@ -346,6 +351,16 @@ impl PackageSpecifier { } } +#[derive(Debug)] +enum FlycheckCommandOrigin { + /// Regular cargo invocation + Cargo, + /// Configured via check_overrideCommand + CheckOverrideCommand, + /// From a runnable with [project_json::RunnableKind::Flycheck] + ProjectJsonRunnable, +} + enum StateChange { Restart { generation: DiagnosticsGeneration, @@ -529,16 +544,28 @@ impl FlycheckActor { } let command = self.check_command(&scope, saved_file.as_deref(), target); - self.scope = scope; + self.scope = scope.clone(); self.generation = generation; - let Some(command) = command else { + let Some((command, origin)) = command else { + tracing::debug!(?scope, "failed to build flycheck command"); continue; }; - let formatted_command = format!("{command:?}"); + let debug_command = format!("{command:?}"); + let user_facing_command = match origin { + // Don't show all the --format=json-with-blah-blah args, just the simple + // version + FlycheckCommandOrigin::Cargo => self.config.to_string(), + // show them the full command but pretty printed. advanced user + FlycheckCommandOrigin::ProjectJsonRunnable + | FlycheckCommandOrigin::CheckOverrideCommand => display_command( + &command, + Some(std::path::Path::new(self.root.as_path())), + ), + }; - tracing::debug!(?command, "will restart flycheck"); + tracing::debug!(?origin, ?command, "will restart flycheck"); let (sender, receiver) = unbounded(); match CommandHandle::spawn( command, @@ -575,14 +602,14 @@ impl FlycheckActor { }, ) { Ok(command_handle) => { - tracing::debug!(command = formatted_command, "did restart flycheck"); + tracing::debug!(?origin, command = %debug_command, "did restart flycheck"); self.command_handle = Some(command_handle); self.command_receiver = Some(receiver); - self.report_progress(Progress::DidStart); + self.report_progress(Progress::DidStart { user_facing_command }); } Err(error) => { self.report_progress(Progress::DidFailToRestart(format!( - "Failed to run the following command: {formatted_command} error={error}" + "Failed to run the following command: {debug_command} origin={origin:?} error={error}" ))); } } @@ -789,7 +816,7 @@ impl FlycheckActor { scope: &FlycheckScope, saved_file: Option<&AbsPath>, target: Option, - ) -> Option { + ) -> Option<(Command, FlycheckCommandOrigin)> { match &self.config { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { // Only use the rust-project.json's flycheck config when no check_overrideCommand @@ -803,7 +830,8 @@ impl FlycheckActor { // Completely handle according to rust-project.json. // We don't consider this to be "using cargo" so we will not apply any of the // CargoOptions to the command. - return self.explicit_check_command(scope, saved_file); + let cmd = self.explicit_check_command(scope, saved_file)?; + return Some((cmd, FlycheckCommandOrigin::ProjectJsonRunnable)); } let mut cmd = @@ -864,7 +892,7 @@ impl FlycheckActor { self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), ); cmd.args(&options.extra_args); - Some(cmd) + Some((cmd, FlycheckCommandOrigin::Cargo)) } FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { let root = match invocation_strategy { @@ -892,7 +920,7 @@ impl FlycheckActor { let subs = Substitutions { label, saved_file: saved_file.map(|x| x.as_str()) }; let cmd = subs.substitute(&runnable, extra_env)?; - Some(cmd) + Some((cmd, FlycheckCommandOrigin::CheckOverrideCommand)) } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 0cfd0a141bae..39b4aaa64738 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -112,6 +112,7 @@ pub(crate) struct GlobalState { pub(crate) flycheck_sender: Sender, pub(crate) flycheck_receiver: Receiver, pub(crate) last_flycheck_error: Option, + pub(crate) flycheck_formatted_commands: Vec, // Test explorer pub(crate) test_run_session: Option>, @@ -288,6 +289,7 @@ impl GlobalState { flycheck_sender, flycheck_receiver, last_flycheck_error: None, + flycheck_formatted_commands: vec![], test_run_session: None, test_run_sender, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index dd0813c14454..62a3b3a17bdf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -1179,8 +1179,24 @@ impl GlobalState { kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Package(package_id)), } => self.diagnostics.clear_check_older_than_for_package(id, package_id, generation), FlycheckMessage::Progress { id, progress } => { + let format_with_id = |user_facing_command: String| { + if self.flycheck.len() == 1 { + user_facing_command + } else { + format!("{user_facing_command} (#{})", id + 1) + } + }; + + self.flycheck_formatted_commands + .resize_with(self.flycheck.len().max(id + 1), || { + format_with_id(self.config.flycheck(None).to_string()) + }); + let (state, message) = match progress { - flycheck::Progress::DidStart => (Progress::Begin, None), + flycheck::Progress::DidStart { user_facing_command } => { + self.flycheck_formatted_commands[id] = format_with_id(user_facing_command); + (Progress::Begin, None) + } flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), flycheck::Progress::DidCancel => { self.last_flycheck_error = None; @@ -1200,13 +1216,8 @@ impl GlobalState { } }; - // When we're running multiple flychecks, we have to include a disambiguator in - // the title, or the editor complains. Note that this is a user-facing string. - let title = if self.flycheck.len() == 1 { - format!("{}", self.config.flycheck(None)) - } else { - format!("{} (#{})", self.config.flycheck(None), id + 1) - }; + // Clone because we &mut self for report_progress + let title = self.flycheck_formatted_commands[id].clone(); self.report_progress( &title, state, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 0a16b7a5614c..ccafbd7b30b9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -942,6 +942,7 @@ impl GlobalState { } } .into(); + self.flycheck_formatted_commands = vec![]; } } From 3fdb78cba695824cba5be894a032779c9b4a4ac3 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 11:34:58 +1100 Subject: [PATCH 059/583] flycheck: Rename FlycheckConfig::CargoCommand to Automatic Because (1) it is what we use when there is no relevant config (2) we automatically use either rust-project.json's flycheck, or cargo This also puts check_command config into CargoOptions. It's a cargo option, after all. --- .../crates/rust-analyzer/src/config.rs | 20 ++++++----- .../crates/rust-analyzer/src/flycheck.rs | 36 ++++++++++++------- .../crates/rust-analyzer/src/test_runner.rs | 2 +- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e39569e108de..c2f7ada8c8ca 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -2431,6 +2431,8 @@ impl Config { pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions { CargoOptions { + // Might be nice to allow users to specify test_command = "nextest" + subcommand: "test".into(), target_tuples: self.cargo_target(source_root).clone().into_iter().collect(), all_targets: false, no_default_features: *self.cargo_noDefaultFeatures(source_root), @@ -2464,9 +2466,9 @@ impl Config { }, } } - Some(_) | None => FlycheckConfig::CargoCommand { - command: self.check_command(source_root).clone(), - options: CargoOptions { + Some(_) | None => FlycheckConfig::Automatic { + cargo_options: CargoOptions { + subcommand: self.check_command(source_root).clone(), target_tuples: self .check_targets(source_root) .clone() @@ -4171,8 +4173,8 @@ mod tests { assert_eq!(config.cargo_targetDir(None), &None); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. }, .. } )); @@ -4195,8 +4197,8 @@ mod tests { Utf8PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned())); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config, .. }, .. } if target_dir_config.target_dir(Some(&ws_target_dir)).map(Cow::into_owned) == Some(ws_target_dir.join("rust-analyzer")) @@ -4221,8 +4223,8 @@ mod tests { ); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config, .. }, .. } if target_dir_config.target_dir(None).map(Cow::into_owned) == Some(Utf8PathBuf::from("other_folder")) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 6dcae76c9354..512c231990cb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -38,8 +38,11 @@ pub(crate) enum InvocationStrategy { PerWorkspace, } +/// Data needed to construct a `cargo` command invocation, e.g. for flycheck or running a test. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct CargoOptions { + /// The cargo subcommand to run, e.g. "check" or "clippy" + pub(crate) subcommand: String, pub(crate) target_tuples: Vec, pub(crate) all_targets: bool, pub(crate) set_test: bool, @@ -111,11 +114,16 @@ impl FlycheckConfigJson { /// #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum FlycheckConfig { - CargoCommand { - command: String, - options: CargoOptions, + /// Automatically use rust-project.json's flycheck runnable or just use cargo (the common case) + /// + /// We can't have a variant for ProjectJson because that is configured on the fly during + /// discoverConfig. We only know what we can read at config time. + Automatic { + /// If we do use cargo, how to build the check command + cargo_options: CargoOptions, ansi_color_output: bool, }, + /// check_overrideCommand. This overrides both cargo and rust-project.json's flycheck runnable. CustomCommand { command: String, args: Vec, @@ -127,7 +135,7 @@ pub(crate) enum FlycheckConfig { impl FlycheckConfig { pub(crate) fn invocation_strategy(&self) -> InvocationStrategy { match self { - FlycheckConfig::CargoCommand { .. } => InvocationStrategy::PerWorkspace, + FlycheckConfig::Automatic { .. } => InvocationStrategy::PerWorkspace, FlycheckConfig::CustomCommand { invocation_strategy, .. } => { invocation_strategy.clone() } @@ -138,7 +146,9 @@ impl FlycheckConfig { impl fmt::Display for FlycheckConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {command}"), + FlycheckConfig::Automatic { cargo_options, .. } => { + write!(f, "cargo {}", cargo_options.subcommand) + } FlycheckConfig::CustomCommand { command, args, .. } => { // Don't show `my_custom_check --foo $saved_file` literally to the user, as it // looks like we've forgotten to substitute $saved_file. @@ -572,11 +582,11 @@ impl FlycheckActor { CargoCheckParser, sender, match &self.config { - FlycheckConfig::CargoCommand { options, .. } => { + FlycheckConfig::Automatic { cargo_options, .. } => { let ws_target_dir = self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path); let target_dir = - options.target_dir_config.target_dir(ws_target_dir); + cargo_options.target_dir_config.target_dir(ws_target_dir); // If `"rust-analyzer.cargo.targetDir": null`, we should use // workspace's target dir instead of hard-coded fallback. @@ -818,7 +828,7 @@ impl FlycheckActor { target: Option, ) -> Option<(Command, FlycheckCommandOrigin)> { match &self.config { - FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { + FlycheckConfig::Automatic { cargo_options, ansi_color_output } => { // Only use the rust-project.json's flycheck config when no check_overrideCommand // is configured. In the FlycheckConcig::CustomCommand branch we will still do // label substitution, but on the overrideCommand instead. @@ -835,15 +845,15 @@ impl FlycheckActor { } let mut cmd = - toolchain::command(Tool::Cargo.path(), &*self.root, &options.extra_env); + toolchain::command(Tool::Cargo.path(), &*self.root, &cargo_options.extra_env); if let Some(sysroot_root) = &self.sysroot_root - && !options.extra_env.contains_key("RUSTUP_TOOLCHAIN") + && !cargo_options.extra_env.contains_key("RUSTUP_TOOLCHAIN") && std::env::var_os("RUSTUP_TOOLCHAIN").is_none() { cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); } cmd.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); - cmd.arg(command); + cmd.arg(&cargo_options.subcommand); match scope { FlycheckScope::Workspace => cmd.arg("--workspace"), @@ -887,11 +897,11 @@ impl FlycheckActor { cmd.arg("--keep-going"); - options.apply_on_command( + cargo_options.apply_on_command( &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), ); - cmd.args(&options.extra_args); + cmd.args(&cargo_options.extra_args); Some((cmd, FlycheckCommandOrigin::Cargo)) } FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 7111a15d0246..f0020f9088e3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -105,7 +105,7 @@ impl CargoTestHandle { let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("--color=always"); - cmd.arg("test"); + cmd.arg(&options.subcommand); // test, usually cmd.arg("--package"); cmd.arg(&test_target.package); From 2d581773fed793f9d62b190e56374065d37291d7 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 12:03:05 +1100 Subject: [PATCH 060/583] Fix RunnableKind::Run label interpolation It was pretty useless without this. Previously: Parsing target pattern `{label}` Caused by: Invalid target name `{label}`. (...) Build ID: 6dab5942-d81c-4430-83b0-5ba523999050 Network: Up: 0B Down: 0B Command: run. Time elapsed: 0.3s BUILD FAILED * The terminal process "buck2 'run', '{label}'" terminated with exit code: 3. --- .../crates/rust-analyzer/src/target_spec.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 8452b6493e87..b8d9acc02a32 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -77,7 +77,16 @@ impl ProjectJsonTargetSpec { RunnableKind::Bin => { for runnable in &self.shell_runnables { if matches!(runnable.kind, project_model::project_json::RunnableKind::Run) { - return Some(runnable.clone()); + let mut runnable = runnable.clone(); + + let replaced_args: Vec<_> = runnable + .args + .iter() + .map(|arg| arg.replace("{label}", &self.label)) + .collect(); + runnable.args = replaced_args; + + return Some(runnable); } } From 71e2ded9fb196bc9b4a1736759f9465f5d4d9619 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 13:32:03 +1100 Subject: [PATCH 061/583] doc: Update docs for runnables to include run/flycheck --- .../crates/project-model/src/project_json.rs | 3 +++ .../docs/book/src/non_cargo_based_projects.md | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 536f170e1192..6938010cbd70 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -357,9 +357,12 @@ pub enum RunnableKind { Check, /// Can run a binary. + /// May include {label} which will get the label from the `build` section of a crate. Run, /// Run a single test. + /// May include {label} which will get the label from the `build` section of a crate. + /// May include {test_id} which will get the test clicked on by the user. TestOne, /// Template for checking a target, emitting rustc JSON diagnostics. diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md index e7df4a5d7668..d8be9a82d0c9 100644 --- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -204,16 +204,25 @@ interface Runnable { args: string[]; /// The current working directory of the runnable. cwd: string; - /// Used to decide what code lens to offer. + /// Maps a runnable to a piece of rust-analyzer functionality. /// - /// `testOne`: This runnable will be used when the user clicks the 'Run Test' - /// CodeLens above a test. + /// - `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// - `run`: This runnable will be used when the user clicks the 'Run' CodeLens + /// above a main function or triggers a run command. + /// - `flycheck`: This is run to provide check-on-save diagnostics when the user + /// saves a file. It must emit rustc JSON diagnostics that rust-analyzer can + /// parse. If this runnable is not specified, we may try to use `cargo check -p`. + /// This is only run for a single crate that the user saved a file in. The + /// {label} syntax is replaced with `BuildInfo::label`. + /// Alternatively, you may use `{saved_file}` and figure out which crate + /// to produce diagnostics for based on that. /// /// The args for testOne can contain two template strings: /// `{label}` and `{test_id}`. `{label}` will be replaced - /// with the `Build::label` and `{test_id}` will be replaced + /// with the `BuildInfo::label` and `{test_id}` will be replaced /// with the test name. - kind: 'testOne' | string; + kind: 'testOne' | 'run' | 'flycheck' | string; } ``` From 422597f76395060d968fcb5c9e7de311bf1dc9a4 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 14:44:40 +1100 Subject: [PATCH 062/583] doc: make example for workspace.discoverConfig actually work rust-project requires {arg} these days. No good giving people bad information even if it's not crucial to documenting this. --- .../rust-analyzer/crates/rust-analyzer/src/config.rs | 9 +++++---- .../docs/book/src/configuration_generated.md | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index c2f7ada8c8ca..2b7ade6c26ef 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -480,8 +480,8 @@ config_data! { /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. /// - /// [`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. - /// `progress_label` is used for the title in progress indicators, whereas `files_to_watch` + /// [`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`. + /// `progressLabel` is used for the title in progress indicators, whereas `filesToWatch` /// is used to determine which build system-specific files should be watched in order to /// reload rust-analyzer. /// @@ -490,9 +490,10 @@ config_data! { /// "rust-analyzer.workspace.discoverConfig": { /// "command": [ /// "rust-project", - /// "develop-json" + /// "develop-json", + /// "{arg}" /// ], - /// "progressLabel": "rust-analyzer", + /// "progressLabel": "buck2/rust-project", /// "filesToWatch": [ /// "BUCK" /// ] diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 58b636334527..a0738ca0e179 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1623,9 +1623,10 @@ Below is an example of a valid configuration: "rust-analyzer.workspace.discoverConfig": { "command": [ "rust-project", - "develop-json" + "develop-json", + "{arg}" ], - "progressLabel": "rust-analyzer", + "progressLabel": "buck2/rust-project", "filesToWatch": [ "BUCK" ] From f06a6b9fdcb9db492a364b48a08f70afa98da182 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 17:11:16 +1100 Subject: [PATCH 063/583] doc: overhaul non-cargo build system docs --- .../crates/rust-analyzer/src/config.rs | 18 ++- .../docs/book/src/configuration_generated.md | 22 ++-- .../docs/book/src/non_cargo_based_projects.md | 105 +++++++++++++++--- .../rust-analyzer/editors/code/package.json | 4 +- 4 files changed, 120 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 2b7ade6c26ef..28ac94e4deb6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -500,7 +500,7 @@ config_data! { /// } /// ``` /// - /// ## On `DiscoverWorkspaceConfig::command` + /// ## Workspace Discovery Protocol /// /// **Warning**: This format is provisional and subject to change. /// @@ -871,10 +871,18 @@ config_data! { /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten /// by changing `#rust-analyzer.check.invocationStrategy#`. /// - /// If `$saved_file` is part of the command, rust-analyzer will pass - /// the absolute path of the saved file to the provided command. This is - /// intended to be used with non-Cargo build systems. - /// Note that `$saved_file` is experimental and may be removed in the future. + /// It supports two interpolation syntaxes, both mainly intended to be used with + /// [non-Cargo build systems](./non_cargo_based_projects.md): + /// + /// - If `{saved_file}` is part of the command, rust-analyzer will pass + /// the absolute path of the saved file to the provided command. + /// (A previous version, `$saved_file`, also works.) + /// - If `{label}` is part of the command, rust-analyzer will pass the + /// Cargo package ID, which can be used with `cargo check -p`, or a build label from + /// `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like + /// [`"rust-analyzer.check.workspace": false`](#check.workspace). + /// + /// /// /// An example command would be: /// diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index a0738ca0e179..c4124aaae075 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -323,10 +323,18 @@ each of them, with the working directory being the workspace root (i.e., the folder containing the `Cargo.toml`). This can be overwritten by changing `#rust-analyzer.check.invocationStrategy#`. -If `$saved_file` is part of the command, rust-analyzer will pass -the absolute path of the saved file to the provided command. This is -intended to be used with non-Cargo build systems. -Note that `$saved_file` is experimental and may be removed in the future. +It supports two interpolation syntaxes, both mainly intended to be used with +[non-Cargo build systems](./non_cargo_based_projects.md): + +- If `{saved_file}` is part of the command, rust-analyzer will pass + the absolute path of the saved file to the provided command. + (A previous version, `$saved_file`, also works.) +- If `{label}` is part of the command, rust-analyzer will pass the + Cargo package ID, which can be used with `cargo check -p`, or a build label from + `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like + [`"rust-analyzer.check.workspace": false`](#check.workspace). + + An example command would be: @@ -1613,8 +1621,8 @@ Default: `null` Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. -[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. -`progress_label` is used for the title in progress indicators, whereas `files_to_watch` +[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`. +`progressLabel` is used for the title in progress indicators, whereas `filesToWatch` is used to determine which build system-specific files should be watched in order to reload rust-analyzer. @@ -1633,7 +1641,7 @@ Below is an example of a valid configuration: } ``` -## On `DiscoverWorkspaceConfig::command` +## Workspace Discovery Protocol **Warning**: This format is provisional and subject to change. diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md index d8be9a82d0c9..a48b025c7b3a 100644 --- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -229,7 +229,15 @@ interface Runnable { This format is provisional and subject to change. Specifically, the `roots` setup will be different eventually. -There are three ways to feed `rust-project.json` to rust-analyzer: +### Providing a JSON project to rust-analyzer + +There are four ways to feed `rust-project.json` to rust-analyzer: + +- Use + [`"rust-analyzer.workspace.discoverConfig": … }`](./configuration.md#workspace.discoverConfig) + to specify a workspace discovery command to generate project descriptions + on-the-fly. Please note that the command output is message-oriented and must + follow [the discovery protocol](./configuration.md#workspace-discovery-protocol). - Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it. @@ -249,19 +257,86 @@ location or (for inline JSON) relative to `rootUri`. You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. -Note that calls to `cargo check` are disabled when using -`rust-project.json` by default, so compilation errors and warnings will -no longer be sent to your LSP client. To enable these compilation errors -you will need to specify explicitly what command rust-analyzer should -run to perform the checks using the -`rust-analyzer.check.overrideCommand` configuration. As an example, the -following configuration explicitly sets `cargo check` as the `check` -command. +### Flycheck support - { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } +Rust-analyzer has functionality to run an actual build of a crate when the user saves a file, to +fill in diagnostics it does not implement natively. This is known as "flycheck". -`check.overrideCommand` requires the command specified to output json -error messages for rust-analyzer to consume. The `--message-format=json` -flag does this for `cargo check` so whichever command you use must also -output errors in this format. See the [Configuration](#_configuration) -section for more information. +**Flycheck is disabled when using `rust-project.json` unless explicitly configured**, so compilation +errors and warnings will no longer be sent to your LSP client by default. To enable these +compilation errors you will need to specify explicitly what command rust-analyzer should run to +perform the checks. There are two ways to do this: + +- `rust-project.json` may contain a `runnables` field. The `flycheck` runnable may be used to + configure a check command. See above for documentation. + +- Using the [`rust-analyzer.check.overrideCommand`](./configuration.md#check.overrideCommand) + configuration. This will also override anything in `rust-project.json`. As an example, the + following configuration explicitly sets `cargo check` as the `check` command. + + ```json + { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } + ``` + + Note also that this works with cargo projects. + +Either option requires the command specified to output JSON error messages for rust-analyzer to +consume. The `--message-format=json` flag does this for `cargo check` so whichever command you use +must also output errors in this format. + +Either option also supports two syntaxes within each argument: + +- `{label}` will be replaced with the `BuildInfo::label` of the crate + containing a saved file, if `BuildInfo` is provided. In the case of `check.overrideCommand` being + used in a Cargo project, this will be the cargo package ID, which can be used with `cargo check -p`. +- `{saved_file}` will be replaced with an absolute path to the saved file. This can be queried against a + build system to find targets that include the file. + +For example: + +```json +{ "rust-analyzer.check.overrideCommand": ["custom_crate_checker", "{label}"] } +``` + +If you do use `{label}` or `{saved_file}`, the command will not be run unless the relevant value can +be substituted. + + +#### Flycheck considerations + +##### Diagnostic output on error + +A flycheck command using a complex build orchestrator like `"bazel", "build", "{label}"`, even with +a tweak to return JSON messages, is often insufficient. Such a command will typically succeed if +there are warnings, but if there are errors, it might "fail to compile" the diagnostics and not +produce any output. You must build a package in such a way that the build succeeds even if `rustc` +exits with an error, and prints the JSON build messages in every case. + +##### Diagnostics for upstream crates + +`cargo check -p` re-prints any errors and warnings in crates higher up in the dependency graph +than the one requested. We do clear all diagnostics when flychecking, so if you manage to +replicate this behaviour, diagnostics for crates other than the one being checked will show up in +the editor. If you do not, then users may be confused that diagnostics are "stuck" or disappear +entirely when there is a build error in an upstream crate. + +##### Compiler options + +`cargo check` invokes rustc differently from `cargo build`. It turns off codegen (with `rustc +--emit=metadata`), which results in lower latency to get to diagnostics. If your build system can +configure this, it is recommended. + +If your build tool can configure rustc for incremental compiles, this is also recommended. + +##### Locking and pre-emption + +In any good build system, including Cargo, build commands sometimes block each other. Running a +flycheck will (by default) frequently block you from running other build commands. Generally this is +undesirable. Users will have to (unintuitively) press save again in the editor to cancel a +flycheck, so that some other command may proceed. + +If your build system has the ability to isolate any rust-analyzer-driven flychecks and prevent lock +contention, for example a separate build output directory and/or daemon instance, this is +recommended. Alternatively, consider using a feature if available that can set the priority of +various build invocations and automatically cancel lower-priority ones when needed. Flychecks should +be set to a lower priority than general direct build invocations. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 2157cbd48653..0d91378706a4 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1213,7 +1213,7 @@ "title": "Check", "properties": { "rust-analyzer.check.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIt supports two interpolation syntaxes, both mainly intended to be used with\n[non-Cargo build systems](./non_cargo_based_projects.md):\n\n- If `{saved_file}` is part of the command, rust-analyzer will pass\n the absolute path of the saved file to the provided command.\n (A previous version, `$saved_file`, also works.)\n- If `{label}` is part of the command, rust-analyzer will pass the\n Cargo package ID, which can be used with `cargo check -p`, or a build label from\n `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like\n [`\"rust-analyzer.check.workspace\": false`](#check.workspace).\n\n\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", "default": null, "type": [ "null", @@ -3135,7 +3135,7 @@ "title": "Workspace", "properties": { "rust-analyzer.workspace.discoverConfig": { - "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.\n`progress_label` is used for the title in progress indicators, whereas `files_to_watch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\"\n ],\n \"progressLabel\": \"rust-analyzer\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## On `DiscoverWorkspaceConfig::command`\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: .", + "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.\n`progressLabel` is used for the title in progress indicators, whereas `filesToWatch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\",\n \"{arg}\"\n ],\n \"progressLabel\": \"buck2/rust-project\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## Workspace Discovery Protocol\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: .", "default": null, "anyOf": [ { From b02e9756f2d0b0367cdedcba16016fc5e867999c Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Thu, 8 Jan 2026 09:53:00 +1100 Subject: [PATCH 064/583] Fix hir-ty clippy issue I am not familiar with this code at allso just doing what I can to unblock. --- .../crates/hir-ty/src/next_solver/infer/traits.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 14df42dc2aeb..dde623483642 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -55,6 +55,13 @@ impl ObligationCause { } } +impl Default for ObligationCause { + #[inline] + fn default() -> Self { + Self::new() + } +} + /// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for /// which the "impl_source" must be found. The process of finding an "impl_source" is /// called "resolving" the `Obligation`. This process consists of From 6a9de224c463f54dc66dec6e3dbe3e244d2d7014 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 8 Jan 2026 04:20:55 +0000 Subject: [PATCH 065/583] Prepare for merging from rust-lang/rust This updates the rust-version file to 548e586795f6b6fe089d8329aa5edbf0f5202646. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 5ffe95a0b54f..4b08b0884ca8 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -e7d44143a12a526488e4f0c0d7ea8e62a4fe9354 +548e586795f6b6fe089d8329aa5edbf0f5202646 From 5d8a7daf2ab5d13330030880af021cf1cf418a7e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 8 Jan 2026 09:25:28 +0200 Subject: [PATCH 066/583] Fixes for builtin derive expansions - Do not store the `MacroCallId` of the "real" expansion anywhere, so that the IDE layer could not expand it by mistake - Fix a stupid bug where we used the directive of the `derive` itself instead of of the macro, leading us to re-expand it again and again. --- .../crates/hir-def/src/builtin_derive.rs | 22 ++++- .../crates/hir-def/src/dyn_map.rs | 10 ++- .../crates/hir-def/src/item_scope.rs | 19 ++-- .../crates/hir-def/src/lang_item.rs | 54 ++++++++++-- .../crates/hir-def/src/nameres.rs | 10 ++- .../crates/hir-def/src/nameres/collector.rs | 88 ++++++++++++------- .../rust-analyzer/crates/hir/src/semantics.rs | 31 +++++-- .../crates/hir/src/semantics/source_to_def.rs | 17 ++-- .../crates/ide/src/expand_macro.rs | 60 +++++-------- .../crates/intern/src/symbol/symbols.rs | 1 + 10 files changed, 211 insertions(+), 101 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs index 32385516ab58..946f08ec3682 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs @@ -8,7 +8,8 @@ use intern::{Symbol, sym}; use tt::TextRange; use crate::{ - AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, db::DefDatabase, + AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, MacroId, + db::DefDatabase, lang_item::LangItems, }; macro_rules! declare_enum { @@ -86,6 +87,25 @@ declare_enum!( DispatchFromDyn => [], ); +impl BuiltinDeriveImplTrait { + pub fn derive_macro(self, lang_items: &LangItems) -> Option { + match self { + BuiltinDeriveImplTrait::Copy => lang_items.CopyDerive, + BuiltinDeriveImplTrait::Clone => lang_items.CloneDerive, + BuiltinDeriveImplTrait::Default => lang_items.DefaultDerive, + BuiltinDeriveImplTrait::Debug => lang_items.DebugDerive, + BuiltinDeriveImplTrait::Hash => lang_items.HashDerive, + BuiltinDeriveImplTrait::Ord => lang_items.OrdDerive, + BuiltinDeriveImplTrait::PartialOrd => lang_items.PartialOrdDerive, + BuiltinDeriveImplTrait::Eq => lang_items.EqDerive, + BuiltinDeriveImplTrait::PartialEq => lang_items.PartialEqDerive, + BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { + lang_items.CoercePointeeDerive + } + } + } +} + impl BuiltinDeriveImplMethod { pub fn trait_method( self, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 7d3a94b03833..4308d0ef1c29 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -27,14 +27,15 @@ pub mod keys { use std::marker::PhantomData; + use either::Either; use hir_expand::{MacroCallId, attrs::AttrId}; use rustc_hash::FxHashMap; use syntax::{AstNode, AstPtr, ast}; use crate::{ - BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, - ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, - TypeAliasId, TypeOrConstParamId, UnionId, UseId, + BlockId, BuiltinDeriveImplId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, + FieldId, FunctionId, ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, + StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId, dyn_map::{DynMap, Policy}, }; @@ -71,7 +72,8 @@ pub mod keys { ( AttrId, /* derive() */ MacroCallId, - /* actual derive macros */ Box<[Option]>, + /* actual derive macros */ + Box<[Option>]>, ), > = Key::new(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index a3278dd76c86..9e1efb977786 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -4,6 +4,7 @@ use std::{fmt, sync::LazyLock}; use base_db::Crate; +use either::Either; use hir_expand::{AstId, MacroCallId, attrs::AttrId, name::Name}; use indexmap::map::Entry; use itertools::Itertools; @@ -199,7 +200,7 @@ struct DeriveMacroInvocation { attr_id: AttrId, /// The `#[derive]` call attr_call_id: MacroCallId, - derive_call_ids: SmallVec<[Option; 4]>, + derive_call_ids: SmallVec<[Option>; 4]>, } pub(crate) static BUILTIN_SCOPE: LazyLock> = LazyLock::new(|| { @@ -345,7 +346,9 @@ impl ItemScope { pub fn all_macro_calls(&self) -> impl Iterator + '_ { self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain( self.derive_macros.values().flat_map(|it| { - it.iter().flat_map(|it| it.derive_call_ids.iter().copied().flatten()) + it.iter().flat_map(|it| { + it.derive_call_ids.iter().copied().flatten().flat_map(|it| it.left()) + }) }), ) } @@ -379,6 +382,10 @@ impl ItemScope { self.types.get(name).map(|item| (item.def, item.vis)) } + pub(crate) fn makro(&self, name: &Name) -> Option { + self.macros.get(name).map(|item| item.def) + } + /// XXX: this is O(N) rather than O(1), try to not introduce new usages. pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility, /*declared*/ bool)> { match item { @@ -519,7 +526,7 @@ impl ItemScope { pub(crate) fn set_derive_macro_invoc( &mut self, adt: AstId, - call: MacroCallId, + call: Either, id: AttrId, idx: usize, ) { @@ -539,7 +546,7 @@ impl ItemScope { adt: AstId, attr_id: AttrId, attr_call_id: MacroCallId, - mut derive_call_ids: SmallVec<[Option; 4]>, + mut derive_call_ids: SmallVec<[Option>; 4]>, ) { derive_call_ids.shrink_to_fit(); self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation { @@ -554,7 +561,9 @@ impl ItemScope { ) -> impl Iterator< Item = ( AstId, - impl Iterator])>, + impl Iterator< + Item = (AttrId, MacroCallId, &[Option>]), + >, ), > + '_ { self.derive_macros.iter().map(|(k, v)| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index eba4d87ec9f8..092ff6e48671 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -7,8 +7,8 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId, - StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, MacroId, + ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map}, @@ -99,7 +99,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option Option { + let mut current = &core_def_map[core_def_map.root]; + for module in modules { + let Some((ModuleDefId::ModuleId(cur), _)) = + current.scope.type_(&Name::new_symbol_root(module.clone())) + else { + return None; + }; + if cur.krate(db) != core_def_map.krate() || cur.block(db) != core_def_map.block_id() { + return None; + } + current = &core_def_map[cur]; + } + current.scope.makro(&Name::new_symbol_root(name)) +} + #[salsa::tracked(returns(as_deref))] pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option> { let mut traits = Vec::new(); @@ -195,7 +216,11 @@ macro_rules! language_item_table { @non_lang_core_traits: - $( core::$($non_lang_module:ident)::*, $non_lang_trait:ident; )* + $( core::$($non_lang_trait_module:ident)::*, $non_lang_trait:ident; )* + + @non_lang_core_macros: + + $( core::$($non_lang_macro_module:ident)::*, $non_lang_macro:ident, $non_lang_macro_field:ident; )* ) => { #[allow(non_snake_case)] // FIXME: Should we remove this? #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -207,6 +232,9 @@ macro_rules! language_item_table { $( pub $non_lang_trait: Option, )* + $( + pub $non_lang_macro_field: Option, + )* } impl LangItems { @@ -218,6 +246,7 @@ macro_rules! language_item_table { fn merge_prefer_self(&mut self, other: &Self) { $( self.$lang_item = self.$lang_item.or(other.$lang_item); )* $( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )* + $( self.$non_lang_macro_field = self.$non_lang_macro_field.or(other.$non_lang_macro_field); )* } fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) { @@ -233,8 +262,9 @@ macro_rules! language_item_table { } } - fn fill_non_lang_core_traits(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) { - $( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_module),* ], sym::$non_lang_trait); )* + fn fill_non_lang_core_items(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) { + $( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_trait_module),* ], sym::$non_lang_trait); )* + $( self.$non_lang_macro_field = resolve_core_macro(db, core_def_map, &[ $(sym::$non_lang_macro_module),* ], sym::$non_lang_macro); )* } } @@ -479,4 +509,16 @@ language_item_table! { LangItems => core::hash, Hash; core::cmp, Ord; core::cmp, Eq; + + @non_lang_core_macros: + core::default, Default, DefaultDerive; + core::fmt, Debug, DebugDerive; + core::hash, Hash, HashDerive; + core::cmp, PartialOrd, PartialOrdDerive; + core::cmp, Ord, OrdDerive; + core::cmp, PartialEq, PartialEqDerive; + core::cmp, Eq, EqDerive; + core::marker, CoercePointee, CoercePointeeDerive; + core::marker, Copy, CopyDerive; + core::clone, Clone, CloneDerive; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 5f05cdb1e2ba..150372f1a0d9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -61,6 +61,7 @@ mod tests; use std::ops::{Deref, DerefMut, Index, IndexMut}; use base_db::Crate; +use either::Either; use hir_expand::{ EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name, proc_macro::ProcMacroKind, @@ -75,8 +76,8 @@ use triomphe::Arc; use tt::TextRange; use crate::{ - AstId, BlockId, BlockLoc, ExternCrateId, FunctionId, FxIndexMap, Lookup, MacroCallStyles, - MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId, + AstId, BlockId, BlockLoc, BuiltinDeriveImplId, ExternCrateId, FunctionId, FxIndexMap, Lookup, + MacroCallStyles, MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId, db::DefDatabase, item_scope::{BuiltinShadowMode, ItemScope}, item_tree::TreeId, @@ -192,7 +193,8 @@ pub struct DefMap { /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper /// attributes. // FIXME: Figure out a better way for the IDE layer to resolve these? - derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroId, MacroCallId)>>, + derive_helpers_in_scope: + FxHashMap, Vec<(Name, MacroId, Either)>>, /// A mapping from [`hir_expand::MacroDefId`] to [`crate::MacroId`]. pub macro_def_to_macro_id: FxHashMap, @@ -540,7 +542,7 @@ impl DefMap { pub fn derive_helpers_in_scope( &self, id: AstId, - ) -> Option<&[(Name, MacroId, MacroCallId)]> { + ) -> Option<&[(Name, MacroId, Either)]> { self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 87ade0651762..323060f61d15 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -3,7 +3,7 @@ //! `DefCollector::collect` contains the fixed-point iteration loop which //! resolves imports and expands macros. -use std::{iter, mem}; +use std::{iter, mem, ops::Range}; use base_db::{BuiltDependency, Crate, CrateOrigin, LangCrateOrigin}; use cfg::{CfgAtom, CfgExpr, CfgOptions}; @@ -226,6 +226,7 @@ struct DeferredBuiltinDerive { container: ItemContainerId, derive_attr_id: AttrId, derive_index: u32, + helpers_range: Range, } /// Walks the tree of module recursively @@ -1354,7 +1355,7 @@ impl<'db> DefCollector<'db> { if let Ok((macro_id, def_id, call_id)) = id { self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc( ast_id.ast_id, - call_id, + Either::Left(call_id), *derive_attr, *derive_pos, ); @@ -1369,7 +1370,7 @@ impl<'db> DefCollector<'db> { .extend(izip!( helpers.iter().cloned(), iter::repeat(macro_id), - iter::repeat(call_id), + iter::repeat(Either::Left(call_id)), )); } } @@ -1492,6 +1493,8 @@ impl<'db> DefCollector<'db> { Interned::new(path), ); + derive_call_ids.push(None); + // Try to resolve the derive immediately. If we succeed, we can also use the fast path // for builtin derives. If not, we cannot use it, as it can cause the ADT to become // interned while the derive is still unresolved, which will cause it to get forgotten. @@ -1506,23 +1509,42 @@ impl<'db> DefCollector<'db> { call_id, ); + let ast_id_without_path = ast_id.ast_id; + let directive = MacroDirective { + module_id: directive.module_id, + depth: directive.depth + 1, + kind: MacroDirectiveKind::Derive { + ast_id, + derive_attr: *attr_id, + derive_pos: idx, + ctxt: call_site.ctx, + derive_macro_id: call_id, + }, + container: directive.container, + }; + if let Ok((macro_id, def_id, call_id)) = id { - derive_call_ids.push(Some(call_id)); + let (mut helpers_start, mut helpers_end) = (0, 0); // Record its helper attributes. if def_id.krate != self.def_map.krate { let def_map = crate_def_map(self.db, def_id.krate); if let Some(helpers) = def_map.data.exported_derives.get(¯o_id) { - self.def_map + let derive_helpers = self + .def_map .derive_helpers_in_scope - .entry(ast_id.ast_id.map(|it| it.upcast())) - .or_default() - .extend(izip!( - helpers.iter().cloned(), - iter::repeat(macro_id), - iter::repeat(call_id), - )); + .entry( + ast_id_without_path.map(|it| it.upcast()), + ) + .or_default(); + helpers_start = derive_helpers.len(); + derive_helpers.extend(izip!( + helpers.iter().cloned(), + iter::repeat(macro_id), + iter::repeat(Either::Left(call_id)), + )); + helpers_end = derive_helpers.len(); } } @@ -1531,7 +1553,7 @@ impl<'db> DefCollector<'db> { def_id.kind { self.deferred_builtin_derives - .entry(ast_id.ast_id.upcast()) + .entry(ast_id_without_path.upcast()) .or_default() .push(DeferredBuiltinDerive { call_id, @@ -1541,24 +1563,15 @@ impl<'db> DefCollector<'db> { depth: directive.depth, derive_attr_id: *attr_id, derive_index: idx as u32, + helpers_range: helpers_start..helpers_end, }); } else { - push_resolved(&mut resolved, directive, call_id); + push_resolved(&mut resolved, &directive, call_id); + *derive_call_ids.last_mut().unwrap() = + Some(Either::Left(call_id)); } } else { - derive_call_ids.push(None); - self.unresolved_macros.push(MacroDirective { - module_id: directive.module_id, - depth: directive.depth + 1, - kind: MacroDirectiveKind::Derive { - ast_id, - derive_attr: *attr_id, - derive_pos: idx, - ctxt: call_site.ctx, - derive_macro_id: call_id, - }, - container: directive.container, - }); + self.unresolved_macros.push(directive); } } @@ -1858,9 +1871,8 @@ impl ModCollector<'_, '_> { ast_id: FileAstId, id: AdtId, def_map: &mut DefMap| { - let Some(deferred_derives) = - deferred_derives.remove(&InFile::new(file_id, ast_id.upcast())) - else { + let ast_id = InFile::new(file_id, ast_id.upcast()); + let Some(deferred_derives) = deferred_derives.remove(&ast_id.upcast()) else { return; }; let module = &mut def_map.modules[module_id]; @@ -1876,6 +1888,22 @@ impl ModCollector<'_, '_> { }, ); module.scope.define_builtin_derive_impl(impl_id); + module.scope.set_derive_macro_invoc( + ast_id, + Either::Right(impl_id), + deferred_derive.derive_attr_id, + deferred_derive.derive_index as usize, + ); + // Change its helper attributes to the new id. + if let Some(derive_helpers) = + def_map.derive_helpers_in_scope.get_mut(&ast_id.map(|it| it.upcast())) + { + for (_, _, call_id) in + &mut derive_helpers[deferred_derive.helpers_range.clone()] + { + *call_id = Either::Right(impl_id); + } + } }); } }; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index f4c42537de93..e55b693ef018 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -13,7 +13,7 @@ use std::{ use base_db::FxIndexSet; use either::Either; use hir_def::{ - DefWithBodyId, MacroId, StructId, TraitId, VariantId, + BuiltinDeriveImplId, DefWithBodyId, HasModule, MacroId, StructId, TraitId, VariantId, attrs::parse_extra_crate_attrs, expr_store::{Body, ExprOrPatSource, HygieneId, path::Path}, hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, @@ -622,7 +622,20 @@ impl<'db> SemanticsImpl<'db> { Some( calls .into_iter() - .map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id })) + .map(|call| { + let call = call?; + match call { + Either::Left(call) => { + macro_call_to_macro_id(ctx, call).map(|id| Macro { id }) + } + Either::Right(call) => { + let call = call.loc(self.db); + let krate = call.krate(self.db); + let lang_items = hir_def::lang_item::lang_items(self.db, krate); + call.trait_.derive_macro(lang_items).map(|id| Macro { id }) + } + } + }) .collect(), ) }) @@ -633,7 +646,7 @@ impl<'db> SemanticsImpl<'db> { .derive_macro_calls(attr)? .into_iter() .flat_map(|call| { - let file_id = call?; + let file_id = call?.left()?; let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id); let root_node = value.0.syntax_node(); self.cache(root_node.clone(), file_id.into()); @@ -643,7 +656,10 @@ impl<'db> SemanticsImpl<'db> { Some(res) } - fn derive_macro_calls(&self, attr: &ast::Attr) -> Option>> { + fn derive_macro_calls( + &self, + attr: &ast::Attr, + ) -> Option>>> { let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; let file_id = self.find_file(adt.syntax()).file_id; let adt = InFile::new(file_id, &adt); @@ -690,8 +706,9 @@ impl<'db> SemanticsImpl<'db> { .derive_helpers_in_scope(InFile::new(sa.file_id, id))? .iter() .filter(|&(name, _, _)| *name == attr_name) - .map(|&(_, macro_, call)| (macro_.into(), call)) + .filter_map(|&(_, macro_, call)| Some((macro_.into(), call.left()?))) .collect(); + // FIXME: We filter our builtin derive "fake" expansions, is this correct? Should we still expose them somehow? res.is_empty().not().then_some(res) } @@ -1338,6 +1355,7 @@ impl<'db> SemanticsImpl<'db> { // FIXME: We need to call `f` for all of them as well though! process_expansion_for_token(ctx, &mut stack, derive_attr); for derive in derives.into_iter().flatten() { + let Either::Left(derive) = derive else { continue }; process_expansion_for_token(ctx, &mut stack, derive); } } @@ -1467,11 +1485,12 @@ impl<'db> SemanticsImpl<'db> { for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) { + let Either::Left(derive) = *derive else { continue }; // as there may be multiple derives registering the same helper // name, we gotta make sure to call this for all of them! // FIXME: We need to call `f` for all of them as well though! res = res - .or(process_expansion_for_token(ctx, &mut stack, *derive)); + .or(process_expansion_for_token(ctx, &mut stack, derive)); } res }) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 257405992731..d222c3dc7ed1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -87,10 +87,10 @@ use either::Either; use hir_def::{ - AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, - ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, - Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, - UseId, VariantId, + AdtId, BlockId, BuiltinDeriveImplId, ConstId, ConstParamId, DefWithBodyId, EnumId, + EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, + ImplId, LifetimeParamId, Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, + TypeParamId, UnionId, UseId, VariantId, dyn_map::{ DynMap, keys::{self, Key}, @@ -394,7 +394,7 @@ impl SourceToDefCtx<'_, '_> { &mut self, item: InFile<&ast::Adt>, src: InFile, - ) -> Option<(AttrId, MacroCallId, &[Option])> { + ) -> Option<(AttrId, MacroCallId, &[Option>])> { let map = self.dyn_map(item)?; map[keys::DERIVE_MACRO_CALL] .get(&AstPtr::new(&src.value)) @@ -409,8 +409,11 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn derive_macro_calls<'slf>( &'slf mut self, adt: InFile<&ast::Adt>, - ) -> Option])> + use<'slf>> - { + ) -> Option< + impl Iterator< + Item = (AttrId, MacroCallId, &'slf [Option>]), + > + use<'slf>, + > { self.dyn_map(adt).as_ref().map(|&map| { let dyn_map = &map[keys::DERIVE_MACRO_CALL]; adt.value diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 7d02b8091890..ba8b3aa9cafe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -583,26 +583,16 @@ fn main() { fn macro_expand_derive() { check( r#" -//- proc_macros: identity -//- minicore: clone, derive +//- proc_macros: identity, derive_identity +//- minicore: derive #[proc_macros::identity] -#[derive(C$0lone)] +#[derive(proc_macros::DeriveIde$0ntity)] struct Foo {} "#, expect![[r#" - Clone - impl <>core::clone::Clone for Foo< >where { - fn clone(&self) -> Self { - match self { - Foo{} - => Foo{} - , - - } - } - - }"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); } @@ -610,15 +600,17 @@ struct Foo {} fn macro_expand_derive2() { check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Cop$0y)] -#[derive(Clone)] +#[derive(proc_macros::$0DeriveIdentity)] +#[derive(proc_macros::DeriveIdentity)] struct Foo {} "#, expect![[r#" - Copy - impl <>core::marker::Copy for Foo< >where{}"#]], + proc_macros::DeriveIdentity + #[derive(proc_macros::DeriveIdentity)] + struct Foo{}"#]], ); } @@ -626,35 +618,27 @@ struct Foo {} fn macro_expand_derive_multi() { check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Cop$0y, Clone)] +#[derive(proc_macros::DeriveIdent$0ity, proc_macros::DeriveIdentity)] struct Foo {} "#, expect![[r#" - Copy - impl <>core::marker::Copy for Foo< >where{}"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Copy, Cl$0one)] +#[derive(proc_macros::DeriveIdentity, proc_macros::De$0riveIdentity)] struct Foo {} "#, expect![[r#" - Clone - impl <>core::clone::Clone for Foo< >where { - fn clone(&self) -> Self { - match self { - Foo{} - => Foo{} - , - - } - } - - }"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index b6efc599f181..3fadca29d118 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -532,4 +532,5 @@ define_symbols! { CoerceUnsized, DispatchFromDyn, define_opaque, + marker, } From 459d77e863f0607f4f0fdc25be19db6f49fdc9c0 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 8 Jan 2026 22:23:16 +0200 Subject: [PATCH 067/583] Publish smol_str v0.3.5 --- src/tools/rust-analyzer/Cargo.lock | 2 +- src/tools/rust-analyzer/lib/smol_str/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 8188fbf96064..5bdde7c7c3e6 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2629,7 +2629,7 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.3.4" +version = "0.3.5" dependencies = [ "arbitrary", "borsh", diff --git a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml index 118b25993ffe..4e7844b49e19 100644 --- a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml +++ b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smol_str" -version = "0.3.4" +version = "0.3.5" description = "small-string optimized string type with O(1) clone" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/smol_str" From 26be33ae18b2aaa376eba9e755b54bc11e104a7b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 8 Jan 2026 15:07:24 +0800 Subject: [PATCH 068/583] Fix not disable string escape highlights Example --- with config `strings: false` ```rust fn main() { format_args!("foo\nbar\invalid"); } ``` **Before this PR** ```rust fn main() { format_args!("foo\nbar\invalid"); // ^^ EscapeSequence // ^^ InvalidEscapeSequence } ``` **After this PR** ```rust fn main() { format_args!("foo\nbar\invalid"); } ``` --- .../crates/ide/src/syntax_highlighting.rs | 16 ++++--- .../ide/src/syntax_highlighting/escape.rs | 43 ++++++++++++----- .../ide/src/syntax_highlighting/highlights.rs | 8 +++- .../test_data/highlight_strings_disabled.html | 47 +++++++++++++++++++ .../ide/src/syntax_highlighting/tests.rs | 17 +++++++ 5 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index e7c5f95a250e..e64fd6488f2a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -513,21 +513,21 @@ fn string_injections( ); if !string.is_raw() { - highlight_escape_string(hl, &string); + highlight_escape_string(hl, config, &string); } } } else if let Some(byte_string) = ast::ByteString::cast(token.clone()) { if !byte_string.is_raw() { - highlight_escape_string(hl, &byte_string); + highlight_escape_string(hl, config, &byte_string); } } else if let Some(c_string) = ast::CString::cast(token.clone()) { if !c_string.is_raw() { - highlight_escape_string(hl, &c_string); + highlight_escape_string(hl, config, &c_string); } } else if let Some(char) = ast::Char::cast(token.clone()) { - highlight_escape_char(hl, &char) + highlight_escape_char(hl, config, &char) } else if let Some(byte) = ast::Byte::cast(token) { - highlight_escape_byte(hl, &byte) + highlight_escape_byte(hl, config, &byte) } ControlFlow::Continue(()) } @@ -586,7 +586,11 @@ fn descend_token( fn filter_by_config(highlight: &mut Highlight, config: &HighlightConfig<'_>) -> bool { match &mut highlight.tag { - HlTag::StringLiteral if !config.strings => return false, + HlTag::StringLiteral | HlTag::EscapeSequence | HlTag::InvalidEscapeSequence + if !config.strings => + { + return false; + } HlTag::Comment if !config.comments => return false, // If punctuation is disabled, make the macro bang part of the macro call again. tag @ HlTag::Punctuation(HlPunct::MacroBang) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs index 094f88f3a864..4da69cc43d9e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs @@ -1,10 +1,14 @@ //! Syntax highlighting for escape sequences use crate::syntax_highlighting::highlights::Highlights; -use crate::{HlRange, HlTag}; +use crate::{HighlightConfig, HlRange, HlTag}; use syntax::ast::{Byte, Char, IsString}; use syntax::{AstToken, TextRange, TextSize}; -pub(super) fn highlight_escape_string(stack: &mut Highlights, string: &T) { +pub(super) fn highlight_escape_string( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + string: &T, +) { let text = string.text(); let start = string.syntax().text_range().start(); string.escaped_char_ranges(&mut |piece_range, char| { @@ -13,16 +17,23 @@ pub(super) fn highlight_escape_string(stack: &mut Highlights, strin Ok(_) => HlTag::EscapeSequence, Err(_) => HlTag::InvalidEscapeSequence, }; - stack.add(HlRange { - range: piece_range + start, - highlight: highlight.into(), - binding_hash: None, - }); + stack.add_with( + config, + HlRange { + range: piece_range + start, + highlight: highlight.into(), + binding_hash: None, + }, + ); } }); } -pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char) { +pub(super) fn highlight_escape_char( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + char: &Char, +) { if char.value().is_err() { // We do not emit invalid escapes highlighting here. The lexer would likely be in a bad // state and this token contains junk, since `'` is not a reliable delimiter (consider @@ -43,10 +54,17 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char) { char.syntax().text_range().start() + TextSize::from(1), TextSize::from(text.len() as u32), ); - stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) + stack.add_with( + config, + HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }, + ) } -pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte) { +pub(super) fn highlight_escape_byte( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + byte: &Byte, +) { if byte.value().is_err() { // See `highlight_escape_char` for why no error highlighting here. return; @@ -65,5 +83,8 @@ pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte) { byte.syntax().text_range().start() + TextSize::from(2), TextSize::from(text.len() as u32), ); - stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) + stack.add_with( + config, + HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }, + ) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs index 340290eafedb..6fe4d0844338 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs @@ -4,7 +4,7 @@ use std::iter; use stdx::equal_range_by; use syntax::TextRange; -use crate::{HlRange, HlTag}; +use crate::{HighlightConfig, HlRange, HlTag}; pub(super) struct Highlights { root: Node, @@ -26,6 +26,12 @@ impl Highlights { self.root.add(hl_range); } + pub(super) fn add_with(&mut self, config: &HighlightConfig<'_>, mut hl_range: HlRange) { + if super::filter_by_config(&mut hl_range.highlight, config) { + self.root.add(hl_range); + } + } + pub(super) fn to_vec(&self) -> Vec { let mut res = Vec::new(); self.root.flatten(&mut res); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html new file mode 100644 index 000000000000..344d0c2ff03b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html @@ -0,0 +1,47 @@ + + +
fn main() {
+    format_args!("foo\nbar");
+    format_args!("foo\invalid");
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 89a5e434f90c..8b529cf10f7f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -1498,6 +1498,23 @@ fn main() { ); } +#[test] +fn test_strings_highlighting_disabled() { + // Test that comments are not highlighted when disabled + check_highlighting_with_config( + r#" +//- minicore: fmt +fn main() { + format_args!("foo\nbar"); + format_args!("foo\invalid"); +} +"#, + HighlightConfig { strings: false, ..HL_CONFIG }, + expect_file!["./test_data/highlight_strings_disabled.html"], + false, + ); +} + #[test] fn regression_20952() { check_highlighting( From e80fbd4bca604211f810fc207f33089730a3e9e1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 9 Jan 2026 13:47:13 +0200 Subject: [PATCH 069/583] Fix lifetimes len diagnostics for fn pointers --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 51 ++++++++++--------- .../crates/hir-ty/src/lower/path.rs | 8 +-- .../src/handlers/missing_lifetime.rs | 15 ++++++ 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index a97d7687162e..9befca11b3e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -27,8 +27,8 @@ use hir_def::{ resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs}, signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, type_ref::{ - ConstRef, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, - TypeRef, TypeRefId, + ConstRef, FnType, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, + TypeBound, TypeRef, TypeRefId, }, }; use hir_expand::name::Name; @@ -98,7 +98,7 @@ impl ImplTraitLoweringState { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum LifetimeElisionKind<'db> { /// Create a new anonymous lifetime parameter and reference it. /// @@ -437,26 +437,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) } TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), - TypeRef::Fn(fn_) => { - let substs = self.with_shifted_in( - DebruijnIndex::from_u32(1), - |ctx: &mut TyLoweringContext<'_, '_>| { - Tys::new_from_iter( - interner, - fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), - ) - }, - ); - Ty::new_fn_ptr( - interner, - Binder::dummy(FnSig { - abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), - safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, - c_variadic: fn_.is_varargs, - inputs_and_output: substs, - }), - ) - } + TypeRef::Fn(fn_) => self.lower_fn_ptr(fn_), TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), TypeRef::ImplTrait(bounds) => { match self.impl_trait_mode.mode { @@ -517,6 +498,30 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { (ty, res) } + fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> { + let interner = self.interner; + let (params, ret_ty) = fn_.split_params_and_ret(); + let old_lifetime_elision = self.lifetime_elision; + let mut args = Vec::with_capacity(fn_.params.len()); + self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx: &mut TyLoweringContext<'_, '_>| { + ctx.lifetime_elision = + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; + args.extend(params.iter().map(|&(_, tr)| ctx.lower_ty(tr))); + ctx.lifetime_elision = LifetimeElisionKind::for_fn_ret(interner); + args.push(ctx.lower_ty(ret_ty)); + }); + self.lifetime_elision = old_lifetime_elision; + Ty::new_fn_ptr( + interner, + Binder::dummy(FnSig { + abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), + safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, + c_variadic: fn_.is_varargs, + inputs_and_output: Tys::new_from_slice(&args), + }), + ) + } + /// This is only for `generic_predicates_for_param`, where we can't just /// lower the self types of the predicates since that could lead to cycles. /// So we just check here if the `type_ref` resolves to a generic param, and which. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index b77aeab62d15..f3d0de12275e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -599,7 +599,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { explicit_self_ty: Option>, lowering_assoc_type_generics: bool, ) -> GenericArgs<'db> { - let old_lifetime_elision = self.ctx.lifetime_elision.clone(); + let old_lifetime_elision = self.ctx.lifetime_elision; if let Some(args) = self.current_or_prev_segment.args_and_bindings && args.parenthesized != GenericArgsParentheses::No @@ -640,7 +640,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { explicit_self_ty, PathGenericsSource::Segment(self.current_segment_u32()), lowering_assoc_type_generics, - self.ctx.lifetime_elision.clone(), + self.ctx.lifetime_elision, ); self.ctx.lifetime_elision = old_lifetime_elision; result @@ -884,7 +884,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { assoc_type: binding_idx as u32, }, false, - this.ctx.lifetime_elision.clone(), + this.ctx.lifetime_elision, ) }); let args = GenericArgs::new_from_iter( @@ -902,7 +902,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def). LifetimeElisionKind::for_fn_ret(self.ctx.interner) } else { - self.ctx.lifetime_elision.clone() + self.ctx.lifetime_elision }; self.with_lifetime_elision(lifetime_elision, |this| { match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs index b07f9e68f634..5cb710b66b5f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs @@ -100,4 +100,19 @@ fn foo WithLifetime>() {} "#, ); } + + #[test] + fn regression_21430() { + check_diagnostics( + r#" +struct S { + f: fn(A<()>), +} + +struct A<'a, T> { + a: &'a T, +} + "#, + ); + } } From e52695c3fca6f9b973c98103407d83d1c963cbdf Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Fri, 9 Jan 2026 11:58:16 +0000 Subject: [PATCH 070/583] internal: Include private definitions in generated rustdoc rust-analyzer has handy prebuilt `cargo doc` output at https://rust-lang.github.io/rust-analyzer/ide/ However, it doesn't include private definitions, which makes it less useful when trying to learn unfamiliar parts of the codebase. Instead, pass `--document-private-items` so the HTML includes information on private types and modules too. rustdoc renders these with a padlock icon, so it's still clear that they're private. This change also exposes some more rustdoc warnings, which I've fixed. --- src/tools/rust-analyzer/.github/workflows/rustdoc.yaml | 2 +- src/tools/rust-analyzer/crates/hir-def/src/nameres.rs | 2 +- .../crates/hir-expand/src/builtin/attr_macro.rs | 2 +- .../rust-analyzer/crates/hir-expand/src/cfg_process.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer/closure.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/method_resolution.rs | 4 ++-- .../crates/hir-ty/src/method_resolution/probe.rs | 2 +- .../crates/hir-ty/src/next_solver/infer/mod.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/term_search.rs | 2 +- .../crates/ide-assists/src/handlers/inline_type_alias.rs | 6 +++++- .../rust-analyzer/crates/ide-completion/src/context.rs | 2 +- src/tools/rust-analyzer/crates/parser/src/grammar.rs | 2 +- .../crates/project-model/src/cargo_workspace.rs | 2 +- .../crates/rust-analyzer/src/config/patch_old_style.rs | 2 +- .../rust-analyzer/crates/rust-analyzer/src/discover.rs | 4 ++-- .../crates/rust-analyzer/src/global_state.rs | 2 +- .../crates/rust-analyzer/src/handlers/dispatch.rs | 6 +++--- .../rust-analyzer/crates/rust-analyzer/src/task_pool.rs | 2 +- src/tools/rust-analyzer/crates/span/src/hygiene.rs | 8 ++++---- .../crates/syntax/src/syntax_editor/mapping.rs | 2 +- src/tools/rust-analyzer/lib/line-index/src/lib.rs | 2 +- 21 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml index 9cc18fc69ede..0cc7ce77ddb6 100644 --- a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml +++ b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml @@ -24,7 +24,7 @@ jobs: run: rustup update --no-self-update stable - name: Build Documentation - run: cargo doc --all --no-deps + run: cargo doc --all --no-deps --document-private-items - name: Deploy Docs uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 150372f1a0d9..1e3ea50c5a0f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -216,7 +216,7 @@ struct DefMapCrateData { registered_tools: Vec, /// Unstable features of Rust enabled with `#![feature(A, B)]`. unstable_features: FxHashSet, - /// #[rustc_coherence_is_core] + /// `#[rustc_coherence_is_core]` rustc_coherence_is_core: bool, no_core: bool, no_std: bool, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs index 06b9b5418e37..c94663ca0cbc 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs @@ -115,7 +115,7 @@ fn dummy_gate_test_expand( /// wasting a lot of memory, and it would also require some way to use a path in a way that makes it /// always resolve as a derive without nameres recollecting them. /// So this hacky approach is a lot more friendly for us, though it does require a bit of support in -/// [`hir::Semantics`] to make this work. +/// hir::Semantics to make this work. fn derive_expand( db: &dyn ExpandDatabase, id: MacroCallId, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index a0de36548e9f..ccef9168ac3a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -1,4 +1,4 @@ -//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro +//! Processes out `#[cfg]` and `#[cfg_attr]` attributes from the input for the derive macro use std::{cell::OnceCell, ops::ControlFlow}; use ::tt::TextRange; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index d1391ad24e4d..ce99016470c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -466,7 +466,7 @@ impl<'db> InferenceContext<'_, 'db> { } /// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args - /// and return type to infer a [`ty::PolyFnSig`] for the closure. + /// and return type to infer a `PolyFnSig` for the closure. fn extract_sig_from_projection( &self, projection: PolyProjectionPredicate<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index e4681b464fec..ad4d79e68a9f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -206,11 +206,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> { } } -/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`. +/// Used by `FnCtxt::lookup_method_for_operator` with `-Znext-solver`. /// /// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while /// `AsInfer` just treats it as ambiguous and succeeds. This is necessary -/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque +/// as we want `FnCtxt::check_expr_call` to treat not-yet-defined opaque /// types as rigid to support `impl Deref` and /// `Box`. /// diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 4a7c7d93539e..42a590e8b4cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -1740,7 +1740,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { /// We want to only accept trait methods if they were hold even if the /// opaque types were rigid. To handle this, we both check that for trait /// candidates the goal were to hold even when treating opaques as rigid, - /// see [OpaqueTypesJank](rustc_trait_selection::solve::OpaqueTypesJank). + /// see `rustc_trait_selection::solve::OpaqueTypesJank`. /// /// We also check that all opaque types encountered as self types in the /// autoderef chain don't get constrained when applying the candidate. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 7d291f7ddbed..21baacb11693 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -140,7 +140,7 @@ pub struct InferCtxtInner<'db> { /// /// Before running `resolve_regions_and_report_errors`, the creator /// of the inference context is expected to invoke - /// [`InferCtxt::process_registered_region_obligations`] + /// `InferCtxt::process_registered_region_obligations` /// for each body-id in this map, which will process the /// obligations within. This is expected to be done 'late enough' /// that all type inference variables have been bound and so forth. diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index e4089218305c..f2dc1ce798ad 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -172,7 +172,7 @@ impl<'db> LookupTable<'db> { /// Insert new type trees for type /// /// Note that the types have to be the same, unification is not enough as unification is not - /// transitive. For example Vec and FxHashSet both unify with Iterator, + /// transitive. For example `Vec` and `FxHashSet` both unify with `Iterator`, /// but they clearly do not unify themselves. fn insert(&mut self, ty: Type<'db>, exprs: impl Iterator>) { match self.data.get_mut(&ty) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index ae8d130df23c..c7a48f3261a9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -290,19 +290,23 @@ impl ConstAndTypeMap { /// ^ alias generic params /// let a: A<100>; /// ^ instance generic args -/// ``` /// /// generic['a] = '_ due to omission /// generic[N] = 100 due to the instance arg /// generic[T] = u64 due to the default param +/// ``` /// /// 2. Copy the concrete type and substitute in each found mapping: /// +/// ```ignore /// &'_ [u64; 100] +/// ``` /// /// 3. Remove wildcard lifetimes entirely: /// +/// ```ignore /// &[u64; 100] +/// ``` fn create_replacement( lifetime_map: &LifetimeMap, const_and_type_map: &ConstAndTypeMap, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index d116f665adbd..cab8bced88df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -628,7 +628,7 @@ impl CompletionContext<'_> { } /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and - /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`]. + /// passes all doc-aliases along, to funnel it into `Completions::add_path_resolution`. pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec)) { let _p = tracing::info_span!("CompletionContext::process_all_names").entered(); self.scope.process_all_names(&mut |name, def| { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index bf8430294110..e481bbe9bc4a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -6,7 +6,7 @@ //! each submodule starts with `use super::*` import and exports //! "public" productions via `pub(super)`. //! -//! See docs for [`Parser`](super::parser::Parser) to learn about API, +//! See docs for [`Parser`] to learn about API, //! available to the grammar, and see docs for [`Event`](super::event::Event) //! to learn how this actually manages to produce parse trees. //! diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 6e1a3f37ff1c..483ab2845045 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -640,7 +640,7 @@ impl FetchMetadata { /// Builds a command to fetch metadata for the given `cargo_toml` manifest. /// /// Performs a lightweight pre-fetch using the `--no-deps` option, - /// available via [`FetchMetadata::no_deps_metadata`], to gather basic + /// available via `FetchMetadata::no_deps_metadata`, to gather basic /// information such as the `target-dir`. /// /// The provided sysroot is used to set the `RUSTUP_TOOLCHAIN` diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs index 389bb7848c01..5dc463eccce4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs @@ -3,7 +3,7 @@ use serde_json::{Value, json}; /// This function patches the json config to the new expected keys. /// That is we try to load old known config keys here and convert them to the new ones. -/// See https://github.com/rust-lang/rust-analyzer/pull/12010 +/// See /// /// We already have an alias system for simple cases, but if we make structural changes /// the alias infra fails down. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 4aef5b0b7f3d..f129f156a030 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -42,7 +42,7 @@ impl DiscoverCommand { Self { sender, command } } - /// Spawn the command inside [Discover] and report progress, if any. + /// Spawn the command inside `DiscoverCommand` and report progress, if any. pub(crate) fn spawn( &self, discover_arg: DiscoverArgument, @@ -73,7 +73,7 @@ impl DiscoverCommand { } } -/// A handle to a spawned [Discover]. +/// A handle to a spawned `DiscoverCommand`. #[derive(Debug)] pub(crate) struct DiscoverHandle { pub(crate) handle: CommandHandle, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 39b4aaa64738..afd4162de622 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -188,7 +188,7 @@ pub(crate) struct GlobalState { /// been called. pub(crate) deferred_task_queue: DeferredTaskQueue, - /// HACK: Workaround for https://github.com/rust-lang/rust-analyzer/issues/19709 + /// HACK: Workaround for /// This is marked true if we failed to load a crate root file at crate graph creation, /// which will usually end up causing a bunch of incorrect diagnostics on startup. pub(crate) incomplete_crate_graph: bool, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index 10bbb0bb31d9..90deae2d902e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -101,7 +101,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a default constructed [`R::Result`]. + /// ready this will return a default constructed `R::Result`. pub(crate) fn on( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, @@ -128,7 +128,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a `default` constructed [`R::Result`]. + /// ready this will return a `default` constructed `R::Result`. pub(crate) fn on_with_vfs_default( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, @@ -176,7 +176,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a default constructed [`R::Result`]. + /// ready this will return a default constructed `R::Result`. pub(crate) fn on_latency_sensitive( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs index 8b8876b801cf..104cd3d2eae9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs @@ -52,7 +52,7 @@ impl TaskPool { /// `DeferredTaskQueue` holds deferred tasks. /// /// These are tasks that must be run after -/// [`GlobalState::process_changes`] has been called. +/// `GlobalState::process_changes` has been called. pub(crate) struct DeferredTaskQueue { pub(crate) sender: crossbeam_channel::Sender, pub(crate) receiver: crossbeam_channel::Receiver, diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index ea4f4c5efb42..92bf892ea529 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -8,9 +8,9 @@ //! //! # The Expansion Order Hierarchy //! -//! `ExpnData` in rustc, rust-analyzer's version is [`MacroCallLoc`]. Traversing the hierarchy -//! upwards can be achieved by walking up [`MacroCallLoc::kind`]'s contained file id, as -//! [`MacroFile`]s are interned [`MacroCallLoc`]s. +//! `ExpnData` in rustc, rust-analyzer's version is `MacroCallLoc`. Traversing the hierarchy +//! upwards can be achieved by walking up `MacroCallLoc::kind`'s contained file id, as +//! `MacroFile`s are interned `MacroCallLoc`s. //! //! # The Macro Definition Hierarchy //! @@ -18,7 +18,7 @@ //! //! # The Call-site Hierarchy //! -//! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. +//! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. use crate::Edition; use std::fmt; diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index 1eaef03197c5..6257bf4e572e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -1,6 +1,6 @@ //! Maps syntax elements through disjoint syntax nodes. //! -//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a [`SyntaxEditor`] +//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a `SyntaxEditor` use itertools::Itertools; use rustc_hash::FxHashMap; diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs index 905da330e64b..d5f0584d988f 100644 --- a/src/tools/rust-analyzer/lib/line-index/src/lib.rs +++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs @@ -207,7 +207,7 @@ impl LineIndex { } } -/// This is adapted from the rustc_span crate, https://github.com/rust-lang/rust/blob/de59844c98f7925242a798a72c59dc3610dd0e2c/compiler/rustc_span/src/analyze_source_file.rs +/// This is adapted from the rustc_span crate, fn analyze_source_file(src: &str) -> (Vec, IntMap>) { assert!(src.len() < !0u32 as usize); let mut lines = vec![]; From 998a5ac623d9879cfba667d6220a8f7d228845e8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 9 Jan 2026 15:55:45 +0200 Subject: [PATCH 071/583] Remove code made redundant by method resolution rewrite Its job is now done elsewhere, and it's also wrong (not accounting for autoderef) --- .../crates/hir-ty/src/infer/expr.rs | 12 ++-- .../crates/hir-ty/src/tests/regression.rs | 55 +++++++++++++--- .../crates/hir-ty/src/tests/traits.rs | 66 +++++++++++-------- 3 files changed, 88 insertions(+), 45 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 226e9f5cd667..62339779a562 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1704,7 +1704,7 @@ impl<'db> InferenceContext<'_, 'db> { }); match resolved { Ok((func, _is_visible)) => { - self.check_method_call(tgt_expr, &[], func.sig, receiver_ty, expected) + self.check_method_call(tgt_expr, &[], func.sig, expected) } Err(_) => self.err_ty(), } @@ -1844,7 +1844,7 @@ impl<'db> InferenceContext<'_, 'db> { item: func.def_id.into(), }) } - self.check_method_call(tgt_expr, args, func.sig, receiver_ty, expected) + self.check_method_call(tgt_expr, args, func.sig, expected) } // Failed to resolve, report diagnostic and try to resolve as call to field access or // assoc function @@ -1934,16 +1934,14 @@ impl<'db> InferenceContext<'_, 'db> { tgt_expr: ExprId, args: &[ExprId], sig: FnSig<'db>, - receiver_ty: Ty<'db>, expected: &Expectation<'db>, ) -> Ty<'db> { - let (formal_receiver_ty, param_tys) = if !sig.inputs_and_output.inputs().is_empty() { - (sig.inputs_and_output.as_slice()[0], &sig.inputs_and_output.inputs()[1..]) + let param_tys = if !sig.inputs_and_output.inputs().is_empty() { + &sig.inputs_and_output.inputs()[1..] } else { - (self.types.types.error, &[] as _) + &[] }; let ret_ty = sig.output(); - self.table.unify(formal_receiver_ty, receiver_ty); self.check_call_arguments(tgt_expr, param_tys, ret_ty, expected, args, &[], sig.c_variadic); ret_ty diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index c805f030446c..df49d7999fee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -891,13 +891,14 @@ use core::ops::Deref; struct BufWriter {} -struct Mutex {} -struct MutexGuard<'a, T> {} +struct Mutex(T); +struct MutexGuard<'a, T>(&'a T); impl Mutex { fn lock(&self) -> MutexGuard<'_, T> {} } impl<'a, T: 'a> Deref for MutexGuard<'a, T> { type Target = T; + fn deref(&self) -> &Self::Target { loop {} } } fn flush(&self) { let w: &Mutex; @@ -905,14 +906,18 @@ fn flush(&self) { } "#, expect![[r#" - 123..127 'self': &'? Mutex - 150..152 '{}': MutexGuard<'?, T> - 234..238 'self': &'? {unknown} - 240..290 '{ ...()); }': () - 250..251 'w': &'? Mutex - 276..287 '*(w.lock())': BufWriter - 278..279 'w': &'? Mutex - 278..286 'w.lock()': MutexGuard<'?, BufWriter> + 129..133 'self': &'? Mutex + 156..158 '{}': MutexGuard<'?, T> + 242..246 'self': &'? MutexGuard<'a, T> + 265..276 '{ loop {} }': &'? T + 267..274 'loop {}': ! + 272..274 '{}': () + 289..293 'self': &'? {unknown} + 295..345 '{ ...()); }': () + 305..306 'w': &'? Mutex + 331..342 '*(w.lock())': BufWriter + 333..334 'w': &'? Mutex + 333..341 'w.lock()': MutexGuard<'?, BufWriter> "#]], ); } @@ -2563,3 +2568,33 @@ fn main() { "#, ); } + +#[test] +fn regression_21429() { + check_no_mismatches( + r#" +trait DatabaseLike { + type ForeignKey: ForeignKeyLike; +} + +trait ForeignKeyLike { + type DB: DatabaseLike; + + fn host_columns(&self, database: &Self::DB); +} + +trait ColumnLike { + type DB: DatabaseLike; + + fn foo() -> &&<::DB as DatabaseLike>::ForeignKey { + loop {} + } + + fn foreign_keys(&self, database: &Self::DB) { + let fk = Self::foo(); + fk.host_columns(database); + } +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 38591f486e97..b825a0a8f0e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -429,7 +429,7 @@ fn associated_type_shorthand_from_method_bound() { trait Iterable { type Item; } -struct S; +struct S(T); impl S { fn foo(self) -> T::Item where T: Iterable { loop {} } } @@ -1103,40 +1103,50 @@ fn test() { fn argument_impl_trait_type_args_2() { check_infer_with_mismatches( r#" -//- minicore: sized +//- minicore: sized, phantom_data +use core::marker::PhantomData; + trait Trait {} struct S; impl Trait for S {} -struct F; +struct F(PhantomData); impl F { fn foo(self, x: impl Trait) -> (T, U) { loop {} } } fn test() { - F.foo(S); - F::.foo(S); - F::.foo::(S); - F::.foo::(S); // extraneous argument should be ignored + F(PhantomData).foo(S); + F::(PhantomData).foo(S); + F::(PhantomData).foo::(S); + F::(PhantomData).foo::(S); // extraneous argument should be ignored }"#, expect![[r#" - 87..91 'self': F - 93..94 'x': impl Trait - 118..129 '{ loop {} }': (T, U) - 120..127 'loop {}': ! - 125..127 '{}': () - 143..283 '{ ...ored }': () - 149..150 'F': F<{unknown}> - 149..157 'F.foo(S)': ({unknown}, {unknown}) - 155..156 'S': S - 163..171 'F::': F - 163..178 'F::.foo(S)': (u32, {unknown}) - 176..177 'S': S - 184..192 'F::': F - 184..206 'F::(S)': (u32, i32) - 204..205 'S': S - 212..220 'F::': F - 212..239 'F::(S)': (u32, i32) - 237..238 'S': S + 135..139 'self': F + 141..142 'x': impl Trait + 166..177 '{ loop {} }': (T, U) + 168..175 'loop {}': ! + 173..175 '{}': () + 191..383 '{ ...ored }': () + 197..198 'F': fn F<{unknown}>(PhantomData<{unknown}>) -> F<{unknown}> + 197..211 'F(PhantomData)': F<{unknown}> + 197..218 'F(Phan...foo(S)': ({unknown}, {unknown}) + 199..210 'PhantomData': PhantomData<{unknown}> + 216..217 'S': S + 224..232 'F::': fn F(PhantomData) -> F + 224..245 'F:: + 224..252 'F:: + 250..251 'S': S + 258..266 'F::': fn F(PhantomData) -> F + 258..279 'F:: + 258..293 'F::(S)': (u32, i32) + 267..278 'PhantomData': PhantomData + 291..292 'S': S + 299..307 'F::': fn F(PhantomData) -> F + 299..320 'F:: + 299..339 'F::(S)': (u32, i32) + 308..319 'PhantomData': PhantomData + 337..338 'S': S "#]], ); } @@ -4012,7 +4022,7 @@ fn f() { fn dyn_map() { check_types( r#" -pub struct Key {} +pub struct Key(K, V, P); pub trait Policy { type K; @@ -4024,7 +4034,7 @@ impl Policy for (K, V) { type V = V; } -pub struct KeyMap {} +pub struct KeyMap(KEY); impl KeyMap> { pub fn get(&self, key: &P::K) -> P::V { @@ -5023,7 +5033,7 @@ fn main() { 278..280 '{}': () 290..291 '_': Box + '?> 294..298 'iter': Box + 'static> - 294..310 'iter.i...iter()': Box + 'static> + 294..310 'iter.i...iter()': Box + '?> 152..156 'self': &'? mut Box 177..208 '{ ... }': Option<::Item> 191..198 'loop {}': ! From bcf059c81eb056c39cb03958feb6baea289690b4 Mon Sep 17 00:00:00 2001 From: cry-inc Date: Fri, 9 Jan 2026 18:23:37 +0100 Subject: [PATCH 072/583] Fix issue with ignore attribute for tests where the attribute has a value with the reason --- src/tools/rust-analyzer/crates/hir-def/src/attrs.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs index 83df11f2d2a4..0b8f65687218 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -135,6 +135,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow match name.text() { "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), + "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE), "lang" => attr_flags.insert(AttrFlags::LANG_ITEM), "path" => attr_flags.insert(AttrFlags::HAS_PATH), "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), From c825a504ab7d284610adf43d496fbfe899ed90aa Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jan 2026 09:51:42 +0100 Subject: [PATCH 073/583] Cleanup --- .../rust-analyzer/crates/hir/src/symbols.rs | 145 +++--- .../crates/ide-db/src/symbol_index.rs | 185 ++------ .../ide-db/src/test_data/test_doc_alias.txt | 112 +++-- .../test_symbol_index_collection.txt | 424 +++++++++++------- .../test_symbols_exclude_imports.txt | 12 +- .../test_data/test_symbols_with_imports.txt | 24 +- .../crates/ide/src/navigation_target.rs | 4 +- .../crates/mbe/src/expander/matcher.rs | 5 +- .../rust-analyzer/tests/slow-tests/main.rs | 44 +- .../rust-analyzer/crates/syntax/src/ptr.rs | 2 +- 10 files changed, 507 insertions(+), 450 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 544c759ed3a7..f9002f31fd15 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -21,12 +21,9 @@ use hir_ty::{ }; use intern::Symbol; use rustc_hash::FxHashMap; -use syntax::{ - AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, - ast::{HasModuleItem, HasName}, -}; +use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::HasName}; -use crate::{Crate, HasCrate, Module, ModuleDef, Semantics}; +use crate::{HasCrate, Module, ModuleDef, Semantics}; /// The actual data that is stored in the index. It should be as compact as /// possible. @@ -44,14 +41,14 @@ pub struct FileSymbol<'db> { _marker: PhantomData<&'db ()>, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct DeclarationLocation { /// The file id for both the `ptr` and `name_ptr`. pub hir_file_id: HirFileId, /// This points to the whole syntax node of the declaration. pub ptr: SyntaxNodePtr, /// This points to the [`syntax::ast::Name`] identifier of the declaration. - pub name_ptr: AstPtr>, + pub name_ptr: Option>>, } impl DeclarationLocation { @@ -61,70 +58,6 @@ impl DeclarationLocation { } } -impl<'db> FileSymbol<'db> { - /// Create a `FileSymbol` representing a crate's root module. - /// This is used for crate search queries like `::` or `::foo`. - pub fn for_crate_root(db: &'db dyn HirDatabase, krate: Crate) -> Option> { - let display_name = krate.display_name(db)?; - let crate_name = display_name.crate_name(); - let root_module = krate.root_module(db); - let def_map = crate_def_map(db, krate.into()); - let module_data = &def_map[root_module.into()]; - - // Get the definition source (the source file for crate roots) - let definition = module_data.origin.definition_source(db); - let hir_file_id = definition.file_id; - - // For a crate root, the "declaration" is the source file itself - // We use the entire file's syntax node as the location - let syntax_node = definition.value.node(); - let ptr = SyntaxNodePtr::new(&syntax_node); - - // For the name, we need to create a synthetic name pointer. - // We'll use the first token of the file as a placeholder since crate roots - // don't have an explicit name in the source. - // We create a name_ptr pointing to the start of the file. - let name_ptr = match &definition.value { - crate::ModuleSource::SourceFile(sf) => { - // Try to find the first item with a name as a reasonable location for focus - // This is a bit of a hack but works for navigation purposes - let first_item: Option = sf.items().next(); - if let Some(item) = first_item { - if let Some(name) = item.syntax().children().find_map(syntax::ast::Name::cast) { - AstPtr::new(&name).wrap_left() - } else { - // No name found, try to use a NameRef instead - if let Some(name_ref) = - item.syntax().descendants().find_map(syntax::ast::NameRef::cast) - { - AstPtr::new(&name_ref).wrap_right() - } else { - return None; - } - } - } else { - return None; - } - } - _ => return None, - }; - - let loc = DeclarationLocation { hir_file_id, ptr, name_ptr }; - - Some(FileSymbol { - name: Symbol::intern(crate_name.as_str()), - def: ModuleDef::Module(root_module), - loc, - container_name: None, - is_alias: false, - is_assoc: false, - is_import: false, - do_not_complete: Complete::Yes, - _marker: PhantomData, - }) - } -} - /// Represents an outstanding module that the symbol collector must collect symbols from. #[derive(Debug)] struct SymbolCollectorWork { @@ -167,6 +100,11 @@ impl<'a> SymbolCollector<'a> { let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered(); tracing::info!(?module, "SymbolCollector::collect"); + // If this is a crate root module, add a symbol for the crate itself + if module.is_crate_root(self.db) { + self.push_crate_root(module); + } + // The initial work is the root module we're collecting, additional work will // be populated as we traverse the module's definitions. self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None }); @@ -176,6 +114,51 @@ impl<'a> SymbolCollector<'a> { } } + /// Push a symbol for a crate's root module. + /// This allows crate roots to appear in the symbol index for queries like `::` or `::foo`. + fn push_crate_root(&mut self, module: Module) { + let krate = module.krate(self.db); + let Some(display_name) = krate.display_name(self.db) else { return }; + let crate_name = display_name.crate_name(); + let canonical_name = display_name.canonical_name(); + + let def_map = crate_def_map(self.db, krate.into()); + let module_data = &def_map[def_map.crate_root(self.db)]; + + let definition = module_data.origin.definition_source(self.db); + let hir_file_id = definition.file_id; + let syntax_node = definition.value.node(); + let ptr = SyntaxNodePtr::new(&syntax_node); + + let loc = DeclarationLocation { hir_file_id, ptr, name_ptr: None }; + + self.symbols.insert(FileSymbol { + name: crate_name.symbol().clone(), + def: ModuleDef::Module(module), + loc, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Complete::Yes, + _marker: PhantomData, + }); + + if canonical_name != crate_name.symbol() { + self.symbols.insert(FileSymbol { + name: canonical_name.clone(), + def: ModuleDef::Module(module), + loc, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Complete::Yes, + _marker: PhantomData, + }); + } + } + pub fn finish(self) -> Box<[FileSymbol<'a>]> { self.symbols.into_iter().collect() } @@ -277,7 +260,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr: AstPtr::new(&name_syntax), + name_ptr: Some(AstPtr::new(&name_syntax)), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -312,7 +295,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: AstPtr::new(&name_syntax), + name_ptr: Some(AstPtr::new(&name_syntax)), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -477,10 +460,10 @@ impl<'a> SymbolCollector<'a> { let source = loc.source(self.db); let Some(name_node) = source.value.name() else { return Complete::Yes }; let def = ModuleDef::from(id.into()); - let dec_loc = DeclarationLocation { + let loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: AstPtr::new(&name_node).wrap_left(), + name_ptr: Some(AstPtr::new(&name_node).wrap_left()), }; let mut do_not_complete = Complete::Yes; @@ -495,7 +478,7 @@ impl<'a> SymbolCollector<'a> { self.symbols.insert(FileSymbol { name: alias.clone(), def, - loc: dec_loc.clone(), + loc, container_name: self.current_container_name.clone(), is_alias: true, is_assoc, @@ -510,7 +493,7 @@ impl<'a> SymbolCollector<'a> { name: name.symbol().clone(), def, container_name: self.current_container_name.clone(), - loc: dec_loc, + loc, is_alias: false, is_assoc, is_import: false, @@ -527,10 +510,10 @@ impl<'a> SymbolCollector<'a> { let Some(declaration) = module_data.origin.declaration() else { return }; let module = declaration.to_node(self.db); let Some(name_node) = module.name() else { return }; - let dec_loc = DeclarationLocation { + let loc = DeclarationLocation { hir_file_id: declaration.file_id, ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: AstPtr::new(&name_node).wrap_left(), + name_ptr: Some(AstPtr::new(&name_node).wrap_left()), }; let def = ModuleDef::Module(module_id.into()); @@ -543,7 +526,7 @@ impl<'a> SymbolCollector<'a> { self.symbols.insert(FileSymbol { name: alias.clone(), def, - loc: dec_loc.clone(), + loc, container_name: self.current_container_name.clone(), is_alias: true, is_assoc: false, @@ -558,7 +541,7 @@ impl<'a> SymbolCollector<'a> { name: name.symbol().clone(), def: ModuleDef::Module(module_id.into()), container_name: self.current_container_name.clone(), - loc: dec_loc, + loc, is_alias: false, is_assoc: false, is_import: false, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index ca0d5ec1e5e6..05c3f360fa87 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -129,58 +129,19 @@ impl Query { /// - `anchor_to_crate`: Whether the first segment must be a crate name fn parse_path_query(query: &str) -> (Vec, String, bool) { // Check for leading :: (absolute path / crate search) - let anchor_to_crate = query.starts_with("::"); - let query = if anchor_to_crate { &query[2..] } else { query }; - - // Handle sole "::" - return all crates - if query.is_empty() && anchor_to_crate { - return (vec![], String::new(), true); - } - - // Check for trailing :: (module browsing - returns all items in module) - let return_all_in_module = query.ends_with("::"); - let query = if return_all_in_module { query.trim_end_matches("::") } else { query }; - - if !query.contains("::") { - // No path separator - single segment - if anchor_to_crate && !return_all_in_module { - // "::foo" - fuzzy search crate names only - return (vec![], query.to_string(), true); - } - if return_all_in_module { - // "foo::" - browse all items in module "foo" - // path_filter = ["foo"], query = "", anchor_to_crate = false/true - return (vec![query.to_string()], String::new(), anchor_to_crate); - } - // Plain "foo" - normal fuzzy search - return (vec![], query.to_string(), false); - } - - // Filter out empty segments (e.g., "foo::::bar" -> "foo::bar") - let segments: Vec<&str> = query.split("::").filter(|s| !s.is_empty()).collect(); - - if segments.is_empty() { - return (vec![], String::new(), anchor_to_crate); - } - - let path: Vec = - segments[..segments.len() - 1].iter().map(|s| s.to_string()).collect(); - let item = if return_all_in_module { - // All segments go to path, item is empty - let mut path = path; - path.push(segments.last().unwrap().to_string()); - return (path, String::new(), anchor_to_crate); - } else { - segments.last().unwrap_or(&"").to_string() + let (query, anchor_to_crate) = match query.strip_prefix("::") { + Some(q) => (q, true), + None => (query, false), }; - (path, item, anchor_to_crate) - } + let Some((prefix, query)) = query.rsplit_once("::") else { + return (vec![], query.to_owned(), anchor_to_crate); + }; - /// Returns true if this query should return all items in a module - /// (i.e., the original query ended with `::`) - fn is_module_browsing(&self) -> bool { - self.query.is_empty() && !self.path_filter.is_empty() + let prefix: Vec<_> = + prefix.split("::").filter(|s| !s.is_empty()).map(ToOwned::to_owned).collect(); + + (prefix, query.to_owned(), anchor_to_crate) } /// Returns true if this query is searching for crates @@ -245,11 +206,14 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_ // That is, `#` switches from "types" to all symbols, `*` switches from the current // workspace to dependencies. // -// Note that filtering does not currently work in VSCode due to the editor never -// sending the special symbols to the language server. Instead, you can configure -// the filtering via the `rust-analyzer.workspace.symbol.search.scope` and -// `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed -// with `__` are hidden from the search results unless configured otherwise. +// This also supports general Rust path syntax with the usual rules. +// +// Note that paths do not currently work in VSCode due to the editor never +// sending the special symbols to the language server. Some other editors might not support the # or +// * search either, instead, you can configure the filtering via the +// `rust-analyzer.workspace.symbol.search.scope` and `rust-analyzer.workspace.symbol.search.kind` +// settings. Symbols prefixed with `__` are hidden from the search results unless configured +// otherwise. // // | Editor | Shortcut | // |---------|-----------| @@ -257,12 +221,11 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { let _p = tracing::info_span!("world_symbols", query = ?query.query).entered(); - // Handle special case: "::" alone or "::foo" for crate search if query.is_crate_search() { return search_crates(db, &query); } - // If we have a path filter, resolve it to target modules first + // If we have a path filter, resolve it to target modules let indices: Vec<_> = if !query.path_filter.is_empty() { let target_modules = resolve_path_to_modules( db, @@ -272,13 +235,11 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { ); if target_modules.is_empty() { - return vec![]; // Path doesn't resolve to any module + return vec![]; } - // Get symbol indices only for the resolved modules target_modules.iter().map(|&module| SymbolIndex::module_symbols(db, module)).collect() } else if query.libs { - // Original behavior for non-path queries searching libs LibraryRoots::get(db) .roots(db) .par_iter() @@ -289,7 +250,6 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { .map(|&root| SymbolIndex::library_symbols(db, root)) .collect() } else { - // Original behavior for non-path queries searching local crates let mut crates = Vec::new(); for &root in LocalRoots::get(db).roots(db).iter() { @@ -303,23 +263,11 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { let mut res = vec![]; - // For module browsing (empty query, non-empty path_filter), return all symbols - if query.is_module_browsing() { - for index in &indices { - for symbol in index.symbols.iter() { - // Apply existing filters (only_types, assoc_mode, exclude_imports, etc.) - if query.matches_symbol_filters(symbol) { - res.push(symbol.clone()); - } - } - } - } else { - // Normal search: use FST to match item name - query.search::<()>(&indices, |f| { - res.push(f.clone()); - ControlFlow::Continue(()) - }); - } + // Normal search: use FST to match item name + query.search::<()>(&indices, |f| { + res.push(f.clone()); + ControlFlow::Continue(()) + }); res } @@ -341,9 +289,15 @@ fn search_crates<'db>(db: &'db RootDatabase, query: &Query) -> Vec) -> bool { - // Check only_types filter - if self.only_types - && !matches!( - symbol.def, - hir::ModuleDef::Adt(..) - | hir::ModuleDef::TypeAlias(..) - | hir::ModuleDef::BuiltinType(..) - | hir::ModuleDef::Trait(..) - ) - { - return false; - } - - // Check assoc_mode filter - if !self.matches_assoc_mode(symbol.is_assoc) { - return false; - } - - // Check exclude_imports filter - if self.exclude_imports && symbol.is_import { - return false; - } - - // Check underscore prefix - let ignore_underscore_prefixed = !self.query.starts_with("__"); - if ignore_underscore_prefixed && symbol.name.as_str().starts_with("__") { - return false; - } - - true - } } #[cfg(test)] @@ -939,34 +858,6 @@ pub struct Foo; assert!(!anchor); } - #[test] - fn test_query_modes() { - // Test is_module_browsing - let query = Query::new("foo::".to_owned()); - assert!(query.is_module_browsing()); - assert!(!query.is_crate_search()); - - // Test is_crate_search with sole :: - let query = Query::new("::".to_owned()); - assert!(!query.is_module_browsing()); - assert!(query.is_crate_search()); - - // Test is_crate_search with ::foo - let query = Query::new("::foo".to_owned()); - assert!(!query.is_module_browsing()); - assert!(query.is_crate_search()); - - // Normal query should be neither - let query = Query::new("foo".to_owned()); - assert!(!query.is_module_browsing()); - assert!(!query.is_crate_search()); - - // Path query should be neither - let query = Query::new("foo::bar".to_owned()); - assert!(!query.is_module_browsing()); - assert!(!query.is_crate_search()); - } - #[test] fn test_path_search() { let (mut db, _) = RootDatabase::with_many_files( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt index 5783d97564d0..71680699b739 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt @@ -27,11 +27,13 @@ kind: STRUCT, range: 83..119, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), ), }, container_name: None, @@ -62,11 +64,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -97,11 +101,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -132,11 +138,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -146,6 +154,34 @@ do_not_complete: Yes, _marker: PhantomData<&()>, }, + FileSymbol { + name: "ra_test_fixture", + def: Module( + Module { + id: ModuleIdLt { + [salsa id]: Id(3800), + }, + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + EditionedFileId( + Id(3000), + ), + ), + ptr: SyntaxNodePtr { + kind: SOURCE_FILE, + range: 0..128, + }, + name_ptr: None, + }, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Yes, + _marker: PhantomData<&()>, + }, FileSymbol { name: "s1", def: Adt( @@ -167,11 +203,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -202,11 +240,13 @@ kind: STRUCT, range: 83..119, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), ), }, container_name: None, @@ -237,11 +277,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 7692a7d61abf..2d62a56fe22d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -25,11 +25,13 @@ kind: VARIANT, range: 201..202, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 201..202, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 201..202, + }, + ), ), }, container_name: Some( @@ -60,11 +62,13 @@ kind: TYPE_ALIAS, range: 470..490, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 475..480, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 475..480, + }, + ), ), }, container_name: None, @@ -93,11 +97,13 @@ kind: VARIANT, range: 204..205, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 204..205, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 204..205, + }, + ), ), }, container_name: Some( @@ -128,11 +134,13 @@ kind: CONST, range: 413..434, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 419..424, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 419..424, + }, + ), ), }, container_name: None, @@ -161,11 +169,13 @@ kind: CONST, range: 593..665, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 599..615, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 599..615, + }, + ), ), }, container_name: None, @@ -196,11 +206,13 @@ kind: ENUM, range: 185..207, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 190..194, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 190..194, + }, + ), ), }, container_name: None, @@ -231,11 +243,13 @@ kind: USE_TREE, range: 727..749, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 736..749, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 736..749, + }, + ), ), }, container_name: None, @@ -266,11 +280,13 @@ kind: MACRO_DEF, range: 153..168, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 159..164, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 159..164, + }, + ), ), }, container_name: None, @@ -299,11 +315,13 @@ kind: STATIC, range: 435..469, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 442..448, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 442..448, + }, + ), ), }, container_name: None, @@ -334,11 +352,13 @@ kind: STRUCT, range: 170..184, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 177..183, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 177..183, + }, + ), ), }, container_name: None, @@ -369,11 +389,13 @@ kind: STRUCT, range: 0..22, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 6..21, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 6..21, + }, + ), ), }, container_name: None, @@ -404,11 +426,13 @@ kind: STRUCT, range: 391..409, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 398..408, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 398..408, + }, + ), ), }, container_name: Some( @@ -441,11 +465,13 @@ kind: STRUCT, range: 628..654, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 635..653, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 635..653, + }, + ), ), }, container_name: Some( @@ -478,11 +504,13 @@ kind: STRUCT, range: 552..580, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 559..579, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 559..579, + }, + ), ), }, container_name: None, @@ -513,11 +541,13 @@ kind: STRUCT, range: 261..279, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 268..275, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 268..275, + }, + ), ), }, container_name: None, @@ -546,11 +576,13 @@ kind: TRAIT, range: 334..373, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 340..345, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 340..345, + }, + ), ), }, container_name: None, @@ -581,11 +613,13 @@ kind: USE_TREE, range: 755..769, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 764..769, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 764..769, + }, + ), ), }, container_name: None, @@ -616,11 +650,13 @@ kind: UNION, range: 208..222, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 214..219, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 214..219, + }, + ), ), }, container_name: None, @@ -649,11 +685,13 @@ kind: MODULE, range: 492..530, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 496..501, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 496..501, + }, + ), ), }, container_name: None, @@ -682,11 +720,13 @@ kind: MODULE, range: 667..677, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 671..676, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 671..676, + }, + ), ), }, container_name: None, @@ -717,11 +757,13 @@ kind: MACRO_RULES, range: 51..131, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 64..77, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 64..77, + }, + ), ), }, container_name: None, @@ -750,11 +792,13 @@ kind: FN, range: 307..330, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 310..325, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 310..325, + }, + ), ), }, container_name: Some( @@ -785,11 +829,13 @@ kind: FN, range: 242..257, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 245..252, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 245..252, + }, + ), ), }, container_name: Some( @@ -822,11 +868,13 @@ kind: MACRO_RULES, range: 1..48, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 14..31, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 14..31, + }, + ), ), }, container_name: None, @@ -855,11 +903,13 @@ kind: FN, range: 375..411, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 378..382, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 378..382, + }, + ), ), }, container_name: None, @@ -869,6 +919,34 @@ do_not_complete: Yes, _marker: PhantomData<&()>, }, + FileSymbol { + name: "ra_test_fixture", + def: Module( + Module { + id: ModuleIdLt { + [salsa id]: Id(3800), + }, + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + EditionedFileId( + Id(3000), + ), + ), + ptr: SyntaxNodePtr { + kind: SOURCE_FILE, + range: 0..793, + }, + name_ptr: None, + }, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Yes, + _marker: PhantomData<&()>, + }, FileSymbol { name: "really_define_struct", def: Macro( @@ -890,11 +968,13 @@ kind: USE_TREE, range: 684..721, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 701..721, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 701..721, + }, + ), ), }, container_name: None, @@ -923,11 +1003,13 @@ kind: FN, range: 352..371, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 355..363, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 355..363, + }, + ), ), }, container_name: Some( @@ -969,11 +1051,13 @@ kind: STRUCT, range: 508..528, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 515..527, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 515..527, + }, + ), ), }, container_name: None, @@ -1011,11 +1095,13 @@ kind: USE_TREE, range: 141..173, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 157..173, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 157..173, + }, + ), ), }, container_name: None, @@ -1046,11 +1132,13 @@ kind: USE_TREE, range: 141..173, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 157..173, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 157..173, + }, + ), ), }, container_name: None, @@ -1081,11 +1169,13 @@ kind: STRUCT, range: 0..20, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 7..19, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 7..19, + }, + ), ), }, container_name: None, @@ -1116,11 +1206,13 @@ kind: USE_TREE, range: 35..69, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 51..69, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 51..69, + }, + ), ), }, container_name: None, @@ -1151,11 +1243,13 @@ kind: USE_TREE, range: 85..125, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 115..125, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 115..125, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt index 6f5f8f889c7d..87f0c7d9a817 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt @@ -20,11 +20,13 @@ kind: STRUCT, range: 0..15, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 11..14, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 11..14, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt index 5d3fe4d2658d..e96aa889ba06 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt @@ -20,11 +20,13 @@ kind: STRUCT, range: 0..15, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 11..14, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 11..14, + }, + ), ), }, container_name: None, @@ -55,11 +57,13 @@ kind: USE_TREE, range: 17..25, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME_REF, - range: 22..25, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME_REF, + range: 22..25, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index a271cac6fcd0..047df309eca6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -19,7 +19,7 @@ use ide_db::{ }; use stdx::never; use syntax::{ - AstNode, SyntaxNode, TextRange, + AstNode, AstPtr, SyntaxNode, TextRange, ast::{self, HasName}, }; @@ -253,7 +253,7 @@ impl<'db> TryToNav for FileSymbol<'db> { db, self.loc.hir_file_id, self.loc.ptr.text_range(), - Some(self.loc.name_ptr.text_range()), + self.loc.name_ptr.map(AstPtr::text_range), ) .map(|(FileRange { file_id, range: full_range }, focus_range)| { NavigationTarget { diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index 8f6627a60fe6..fe01fb1f1063 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -414,8 +414,9 @@ fn match_loop_inner<'t>( } // Check if we need a separator. - if item.sep.is_some() && !item.sep_matched { - let sep = item.sep.as_ref().unwrap(); + if let Some(sep) = &item.sep + && !item.sep_matched + { let mut fork = src.clone(); if expect_separator(&mut fork, sep) { // HACK: here we use `meta_result` to pass `TtIter` back to caller because diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index eb1b8c5dd0e6..9f3c6742d651 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -1447,7 +1447,27 @@ foo = { path = "../foo" } .server() .wait_until_workspace_is_loaded(); - server.request::(Default::default(), json!([])); + server.request::( + Default::default(), + json!([ + { + "name": "bar", + "kind": 2, + "location": { + "uri": "file:///[..]bar/src/lib.rs", + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + } + } + }]), + ); let server = Project::with_fixture( r#" @@ -1486,7 +1506,27 @@ version = "0.0.0" .server() .wait_until_workspace_is_loaded(); - server.request::(Default::default(), json!([])); + server.request::( + Default::default(), + json!([ + { + "name": "baz", + "kind": 2, + "location": { + "uri": "file:///[..]baz/src/lib.rs", + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + } + } + }]), + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs index 34c07598d200..c4979b8e3ae8 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs @@ -68,7 +68,7 @@ impl AstPtr { self.raw } - pub fn text_range(&self) -> TextRange { + pub fn text_range(self) -> TextRange { self.raw.text_range() } From 4e18f1dad2be32a53a43840a39cab9457262d1c1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jan 2026 14:53:22 +0100 Subject: [PATCH 074/583] Abstract proc-macro-srv input and output away --- .../proc-macro-api/src/legacy_protocol.rs | 4 +- .../proc-macro-api/src/legacy_protocol/msg.rs | 4 +- .../crates/proc-macro-api/src/lib.rs | 41 +++++- .../crates/proc-macro-api/src/process.rs | 138 +++++++++++++----- .../crates/proc-macro-srv-cli/src/main.rs | 12 +- .../proc-macro-srv-cli/src/main_loop.rs | 72 +++++---- 6 files changed, 193 insertions(+), 78 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs index 22a7d9868e21..4524d1b66bfe 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs @@ -162,11 +162,11 @@ fn send_request( req: Request, buf: &mut P::Buf, ) -> Result, ServerError> { - req.write::<_, P>(&mut writer).map_err(|err| ServerError { + req.write::

(&mut writer).map_err(|err| ServerError { message: "failed to write request".into(), io: Some(Arc::new(err)), })?; - let res = Response::read::<_, P>(&mut reader, buf).map_err(|err| ServerError { + let res = Response::read::

(&mut reader, buf).map_err(|err| ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)), })?; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index 4146b619ec0c..1b6590693354 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -155,13 +155,13 @@ impl ExpnGlobals { } pub trait Message: serde::Serialize + DeserializeOwned { - fn read(inp: &mut R, buf: &mut C::Buf) -> io::Result> { + fn read(inp: &mut dyn BufRead, buf: &mut C::Buf) -> io::Result> { Ok(match C::read(inp, buf)? { None => None, Some(buf) => Some(C::decode(buf)?), }) } - fn write(self, out: &mut W) -> io::Result<()> { + fn write(self, out: &mut dyn Write) -> io::Result<()> { let value = C::encode(&self)?; C::write(out, &value) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index f5fcc99f14a3..98ee6817c2d2 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -18,7 +18,7 @@ extern crate rustc_driver as _; pub mod bidirectional_protocol; pub mod legacy_protocol; -mod process; +pub mod process; pub mod transport; use paths::{AbsPath, AbsPathBuf}; @@ -44,6 +44,25 @@ pub mod version { pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; } +#[derive(Copy, Clone)] +pub enum ProtocolFormat { + JsonLegacy, + PostcardLegacy, + BidirectionalPostcardPrototype, +} + +impl fmt::Display for ProtocolFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ProtocolFormat::JsonLegacy => write!(f, "json-legacy"), + ProtocolFormat::PostcardLegacy => write!(f, "postcard-legacy"), + ProtocolFormat::BidirectionalPostcardPrototype => { + write!(f, "bidirectional-postcard-prototype") + } + } + } +} + /// Represents different kinds of procedural macros that can be expanded by the external server. #[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub enum ProcMacroKind { @@ -132,7 +151,25 @@ impl ProcMacroClient { > + Clone, version: Option<&Version>, ) -> io::Result { - let process = ProcMacroServerProcess::run(process_path, env, version)?; + let process = ProcMacroServerProcess::spawn(process_path, env, version)?; + Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) + } + + /// Invokes `spawn` and returns a client connected to the resulting read and write handles. + /// + /// The `process_path` is used for `Self::server_path`. This function is mainly used for testing. + pub fn with_io_channels( + process_path: &AbsPath, + spawn: impl Fn( + Option, + ) -> io::Result<( + Box, + Box, + Box, + )>, + version: Option<&Version>, + ) -> io::Result { + let process = ProcMacroServerProcess::run(spawn, version, || "".to_owned())?; Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index f6a656e3ce3a..4f8762158790 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -13,14 +13,13 @@ use span::Span; use stdx::JodChild; use crate::{ - Codec, ProcMacro, ProcMacroKind, ServerError, + Codec, ProcMacro, ProcMacroKind, ProtocolFormat, ServerError, bidirectional_protocol::{self, SubCallback, msg::BidirectionalMessage, reject_subrequests}, legacy_protocol::{self, SpanMode}, version, }; /// Represents a process handling proc-macro communication. -#[derive(Debug)] pub(crate) struct ProcMacroServerProcess { /// The state of the proc-macro server process, the protocol is currently strictly sequential /// hence the lock on the state. @@ -31,6 +30,16 @@ pub(crate) struct ProcMacroServerProcess { exited: OnceLock>, } +impl std::fmt::Debug for ProcMacroServerProcess { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ProcMacroServerProcess") + .field("version", &self.version) + .field("protocol", &self.protocol) + .field("exited", &self.exited) + .finish() + } +} + #[derive(Debug, Clone)] pub(crate) enum Protocol { LegacyJson { mode: SpanMode }, @@ -38,22 +47,83 @@ pub(crate) enum Protocol { BidirectionalPostcardPrototype { mode: SpanMode }, } +pub trait ProcessExit: Send + Sync { + fn exit_err(&mut self) -> Option; +} + +impl ProcessExit for Process { + fn exit_err(&mut self) -> Option { + match self.child.try_wait() { + Ok(None) | Err(_) => None, + Ok(Some(status)) => { + let mut msg = String::new(); + if !status.success() + && let Some(stderr) = self.child.stderr.as_mut() + { + _ = stderr.read_to_string(&mut msg); + } + Some(ServerError { + message: format!( + "proc-macro server exited with {status}{}{msg}", + if msg.is_empty() { "" } else { ": " } + ), + io: None, + }) + } + } + } +} + /// Maintains the state of the proc-macro server process. -#[derive(Debug)] struct ProcessSrvState { - process: Process, - stdin: ChildStdin, - stdout: BufReader, + process: Box, + stdin: Box, + stdout: Box, } impl ProcMacroServerProcess { /// Starts the proc-macro server and performs a version check - pub(crate) fn run<'a>( + pub(crate) fn spawn<'a>( process_path: &AbsPath, env: impl IntoIterator< Item = (impl AsRef, &'a Option>), > + Clone, version: Option<&Version>, + ) -> io::Result { + Self::run( + |format| { + let mut process = Process::run( + process_path, + env.clone(), + format.map(|format| format.to_string()).as_deref(), + )?; + let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); + + Ok((Box::new(process), Box::new(stdin), Box::new(stdout))) + }, + version, + || { + #[expect(clippy::disallowed_methods)] + Command::new(process_path) + .arg("--version") + .output() + .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned()) + .unwrap_or_else(|_| "unknown version".to_owned()) + }, + ) + } + + /// Invokes `spawn` and performs a version check. + pub(crate) fn run( + spawn: impl Fn( + Option, + ) -> io::Result<( + Box, + Box, + Box, + )>, + version: Option<&Version>, + binary_server_version: impl Fn() -> String, ) -> io::Result { const VERSION: Version = Version::new(1, 93, 0); // we do `>` for nightly as this started working in the middle of the 1.93 nightly release, so we dont want to break on half of the nightlies @@ -65,27 +135,33 @@ impl ProcMacroServerProcess { && has_working_format_flag { &[ - ( - Some("bidirectional-postcard-prototype"), - Protocol::BidirectionalPostcardPrototype { mode: SpanMode::Id }, - ), - (Some("postcard-legacy"), Protocol::LegacyPostcard { mode: SpanMode::Id }), - (Some("json-legacy"), Protocol::LegacyJson { mode: SpanMode::Id }), + Some(ProtocolFormat::BidirectionalPostcardPrototype), + Some(ProtocolFormat::PostcardLegacy), + Some(ProtocolFormat::JsonLegacy), ] } else { - &[(None, Protocol::LegacyJson { mode: SpanMode::Id })] + &[None] }; let mut err = None; - for &(format, ref protocol) in formats { + for &format in formats { let create_srv = || { - let mut process = Process::run(process_path, env.clone(), format)?; - let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); + let (process, stdin, stdout) = spawn(format)?; io::Result::Ok(ProcMacroServerProcess { state: Mutex::new(ProcessSrvState { process, stdin, stdout }), version: 0, - protocol: protocol.clone(), + protocol: match format { + Some(ProtocolFormat::BidirectionalPostcardPrototype) => { + Protocol::BidirectionalPostcardPrototype { mode: SpanMode::Id } + } + Some(ProtocolFormat::PostcardLegacy) => { + Protocol::LegacyPostcard { mode: SpanMode::Id } + } + Some(ProtocolFormat::JsonLegacy) | None => { + Protocol::LegacyJson { mode: SpanMode::Id } + } + }, exited: OnceLock::new(), }) }; @@ -93,12 +169,7 @@ impl ProcMacroServerProcess { tracing::info!("sending proc-macro server version check"); match srv.version_check(Some(&mut reject_subrequests)) { Ok(v) if v > version::CURRENT_API_VERSION => { - #[allow(clippy::disallowed_methods)] - let process_version = Command::new(process_path) - .arg("--version") - .output() - .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned()) - .unwrap_or_else(|_| "unknown version".to_owned()); + let process_version = binary_server_version(); err = Some(io::Error::other(format!( "Your installed proc-macro server is too new for your rust-analyzer. API version: {}, server version: {process_version}. \ This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain.", @@ -275,22 +346,9 @@ impl ProcMacroServerProcess { f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| { if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { - match state.process.child.try_wait() { - Ok(None) | Err(_) => e, - Ok(Some(status)) => { - let mut msg = String::new(); - if !status.success() - && let Some(stderr) = state.process.child.stderr.as_mut() - { - _ = stderr.read_to_string(&mut msg); - } - let server_error = ServerError { - message: format!( - "proc-macro server exited with {status}{}{msg}", - if msg.is_empty() { "" } else { ": " } - ), - io: None, - }; + match state.process.exit_err() { + None => e, + Some(server_error) => { self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone() } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index bdfdb50002e1..189a1eea5c19 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -45,7 +45,11 @@ fn main() -> std::io::Result<()> { } let &format = matches.get_one::("format").expect("format value should always be present"); - run(format) + + let mut stdin = std::io::BufReader::new(std::io::stdin()); + let mut stdout = std::io::stdout(); + + run(&mut stdin, &mut stdout, format) } #[derive(Copy, Clone)] @@ -88,7 +92,11 @@ impl ValueEnum for ProtocolFormat { } #[cfg(not(feature = "sysroot-abi"))] -fn run(_: ProtocolFormat) -> std::io::Result<()> { +fn run( + _: &mut std::io::BufReader, + _: &mut std::io::Stdout, + _: ProtocolFormat, +) -> std::io::Result<()> { Err(std::io::Error::new( std::io::ErrorKind::Unsupported, "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function" diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 22536a4e52b1..0c651d22b41b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -6,7 +6,7 @@ use proc_macro_api::{ transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version::CURRENT_API_VERSION, }; -use std::io; +use std::io::{self, BufRead, Write}; use legacy::Message; @@ -32,15 +32,24 @@ impl legacy::SpanTransformer for SpanTrans { } } -pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> { +pub(crate) fn run( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), + format: ProtocolFormat, +) -> io::Result<()> { match format { - ProtocolFormat::JsonLegacy => run_::(), - ProtocolFormat::PostcardLegacy => run_::(), - ProtocolFormat::BidirectionalPostcardPrototype => run_new::(), + ProtocolFormat::JsonLegacy => run_old::(stdin, stdout), + ProtocolFormat::PostcardLegacy => run_old::(stdin, stdout), + ProtocolFormat::BidirectionalPostcardPrototype => { + run_new::(stdin, stdout) + } } } -fn run_new() -> io::Result<()> { +fn run_new( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), +) -> io::Result<()> { fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { match kind { proc_macro_srv::ProcMacroKind::CustomDerive => { @@ -52,8 +61,6 @@ fn run_new() -> io::Result<()> { } let mut buf = C::Buf::default(); - let mut stdin = io::stdin(); - let mut stdout = io::stdout(); let env_snapshot = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env_snapshot); @@ -61,8 +68,7 @@ fn run_new() -> io::Result<()> { let mut span_mode = legacy::SpanMode::Id; 'outer: loop { - let req_opt = - bidirectional::BidirectionalMessage::read::<_, C>(&mut stdin.lock(), &mut buf)?; + let req_opt = bidirectional::BidirectionalMessage::read::(stdin, &mut buf)?; let Some(req) = req_opt else { break 'outer; }; @@ -77,22 +83,22 @@ fn run_new() -> io::Result<()> { .collect() }); - send_response::(&stdout, bidirectional::Response::ListMacros(res))?; + send_response::(stdout, bidirectional::Response::ListMacros(res))?; } bidirectional::Request::ApiVersionCheck {} => { send_response::( - &stdout, + stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), )?; } bidirectional::Request::SetConfig(config) => { span_mode = config.span_mode; - send_response::(&stdout, bidirectional::Response::SetConfig(config))?; + send_response::(stdout, bidirectional::Response::SetConfig(config))?; } bidirectional::Request::ExpandMacro(task) => { - handle_expand::(&srv, &mut stdin, &mut stdout, &mut buf, span_mode, *task)?; + handle_expand::(&srv, stdin, stdout, &mut buf, span_mode, *task)?; } }, _ => continue, @@ -104,8 +110,8 @@ fn run_new() -> io::Result<()> { fn handle_expand( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdin: &io::Stdin, - stdout: &io::Stdout, + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), buf: &mut C::Buf, span_mode: legacy::SpanMode, task: bidirectional::ExpandMacro, @@ -118,7 +124,7 @@ fn handle_expand( fn handle_expand_id( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdout: &io::Stdout, + stdout: &mut dyn Write, task: bidirectional::ExpandMacro, ) -> io::Result<()> { let bidirectional::ExpandMacro { lib, env, current_dir, data } = task; @@ -157,12 +163,12 @@ fn handle_expand_id( }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(&stdout, bidirectional::Response::ExpandMacro(res)) + send_response::(stdout, bidirectional::Response::ExpandMacro(res)) } struct ProcMacroClientHandle<'a, C: Codec> { - stdin: &'a io::Stdin, - stdout: &'a io::Stdout, + stdin: &'a mut (dyn BufRead + Send + Sync), + stdout: &'a mut (dyn Write + Send + Sync), buf: &'a mut C::Buf, } @@ -173,11 +179,11 @@ impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { ) -> Option { let msg = bidirectional::BidirectionalMessage::SubRequest(req); - if msg.write::<_, C>(&mut self.stdout.lock()).is_err() { + if msg.write::(&mut *self.stdout).is_err() { return None; } - match bidirectional::BidirectionalMessage::read::<_, C>(&mut self.stdin.lock(), self.buf) { + match bidirectional::BidirectionalMessage::read::(&mut *self.stdin, self.buf) { Ok(Some(msg)) => Some(msg), _ => None, } @@ -238,8 +244,8 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl fn handle_expand_ra( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdin: &io::Stdin, - stdout: &io::Stdout, + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), buf: &mut C::Buf, task: bidirectional::ExpandMacro, ) -> io::Result<()> { @@ -301,10 +307,13 @@ fn handle_expand_ra( .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(&stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response::(stdout, bidirectional::Response::ExpandMacroExtended(res)) } -fn run_() -> io::Result<()> { +fn run_old( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), +) -> io::Result<()> { fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { match kind { proc_macro_srv::ProcMacroKind::CustomDerive => { @@ -316,8 +325,8 @@ fn run_() -> io::Result<()> { } let mut buf = C::Buf::default(); - let mut read_request = || legacy::Request::read::<_, C>(&mut io::stdin().lock(), &mut buf); - let write_response = |msg: legacy::Response| msg.write::<_, C>(&mut io::stdout().lock()); + let mut read_request = || legacy::Request::read::(stdin, &mut buf); + let mut write_response = |msg: legacy::Response| msg.write::(stdout); let env = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env); @@ -446,7 +455,10 @@ fn run_() -> io::Result<()> { Ok(()) } -fn send_response(stdout: &io::Stdout, resp: bidirectional::Response) -> io::Result<()> { +fn send_response( + stdout: &mut dyn Write, + resp: bidirectional::Response, +) -> io::Result<()> { let resp = bidirectional::BidirectionalMessage::Response(resp); - resp.write::<_, C>(&mut stdout.lock()) + resp.write::(stdout) } From 27fef0ccbe56e9e4494d19780cdd7c41660d764d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jan 2026 16:01:09 +0100 Subject: [PATCH 075/583] internal: Landing integration test infra for proc-macro-srv-cli --- src/tools/rust-analyzer/Cargo.lock | 6 + .../crates/proc-macro-api/src/lib.rs | 6 +- .../crates/proc-macro-srv-cli/Cargo.toml | 14 ++ .../crates/proc-macro-srv-cli/src/lib.rs | 6 + .../crates/proc-macro-srv-cli/src/main.rs | 43 ++-- .../proc-macro-srv-cli/src/main_loop.rs | 5 +- .../proc-macro-srv-cli/tests/common/utils.rs | 213 +++++++++++++++++ .../proc-macro-srv-cli/tests/legacy_json.rs | 224 ++++++++++++++++++ 8 files changed, 494 insertions(+), 23 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 5bdde7c7c3e6..d6c6250e13dc 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1879,9 +1879,15 @@ name = "proc-macro-srv-cli" version = "0.0.0" dependencies = [ "clap", + "expect-test", + "intern", + "paths", "postcard", "proc-macro-api", "proc-macro-srv", + "proc-macro-test", + "span", + "tt", ] [[package]] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index 98ee6817c2d2..822809943a36 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -44,10 +44,14 @@ pub mod version { pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; } -#[derive(Copy, Clone)] +/// Protocol format for communication between client and server. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ProtocolFormat { + /// JSON-based legacy protocol (newline-delimited JSON). JsonLegacy, + /// Postcard-based legacy protocol (COBS-encoded postcard). PostcardLegacy, + /// Bidirectional postcard protocol with sub-request support. BidirectionalPostcardPrototype, } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 6b2db0b269d5..a25e3b64ad42 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -10,12 +10,26 @@ license.workspace = true rust-version.workspace = true publish = false +[lib] +doctest = false + [dependencies] proc-macro-srv.workspace = true proc-macro-api.workspace = true postcard.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} +[dev-dependencies] +expect-test.workspace = true +paths.workspace = true +# span = {workspace = true, default-features = false} does not work +span = { path = "../span", default-features = false} +tt.workspace = true +intern.workspace = true + +# used as proc macro test target +proc-macro-test.path = "../proc-macro-srv/proc-macro-test" + [features] default = [] # default = ["sysroot-abi"] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs new file mode 100644 index 000000000000..9e6f03bf4604 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -0,0 +1,6 @@ +//! Library interface for `proc-macro-srv-cli`. +//! +//! This module exposes the server main loop and protocol format for integration testing. + +#[cfg(feature = "sysroot-abi")] +pub mod main_loop; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 189a1eea5c19..a246d4d3f28f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -9,11 +9,11 @@ extern crate rustc_driver as _; mod version; -#[cfg(feature = "sysroot-abi")] -mod main_loop; use clap::{Command, ValueEnum}; +use proc_macro_api::ProtocolFormat; + #[cfg(feature = "sysroot-abi")] -use main_loop::run; +use proc_macro_srv_cli::main_loop::run; fn main() -> std::io::Result<()> { let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); @@ -32,7 +32,7 @@ fn main() -> std::io::Result<()> { .long("format") .action(clap::ArgAction::Set) .default_value("json-legacy") - .value_parser(clap::builder::EnumValueParser::::new()), + .value_parser(clap::builder::EnumValueParser::::new()), clap::Arg::new("version") .long("version") .action(clap::ArgAction::SetTrue) @@ -43,33 +43,37 @@ fn main() -> std::io::Result<()> { println!("rust-analyzer-proc-macro-srv {}", version::version()); return Ok(()); } - let &format = - matches.get_one::("format").expect("format value should always be present"); + let &format = matches + .get_one::("format") + .expect("format value should always be present"); let mut stdin = std::io::BufReader::new(std::io::stdin()); let mut stdout = std::io::stdout(); - run(&mut stdin, &mut stdout, format) + run(&mut stdin, &mut stdout, format.into()) } +/// Wrapper for CLI argument parsing that implements `ValueEnum`. #[derive(Copy, Clone)] -enum ProtocolFormat { - JsonLegacy, - PostcardLegacy, - BidirectionalPostcardPrototype, +struct ProtocolFormatArg(ProtocolFormat); + +impl From for ProtocolFormat { + fn from(arg: ProtocolFormatArg) -> Self { + arg.0 + } } -impl ValueEnum for ProtocolFormat { +impl ValueEnum for ProtocolFormatArg { fn value_variants<'a>() -> &'a [Self] { &[ - ProtocolFormat::JsonLegacy, - ProtocolFormat::PostcardLegacy, - ProtocolFormat::BidirectionalPostcardPrototype, + ProtocolFormatArg(ProtocolFormat::JsonLegacy), + ProtocolFormatArg(ProtocolFormat::PostcardLegacy), + ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype), ] } fn to_possible_value(&self) -> Option { - match self { + match self.0 { ProtocolFormat::JsonLegacy => Some(clap::builder::PossibleValue::new("json-legacy")), ProtocolFormat::PostcardLegacy => { Some(clap::builder::PossibleValue::new("postcard-legacy")) @@ -79,12 +83,13 @@ impl ValueEnum for ProtocolFormat { } } } + fn from_str(input: &str, _ignore_case: bool) -> Result { match input { - "json-legacy" => Ok(ProtocolFormat::JsonLegacy), - "postcard-legacy" => Ok(ProtocolFormat::PostcardLegacy), + "json-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::JsonLegacy)), + "postcard-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::PostcardLegacy)), "bidirectional-postcard-prototype" => { - Ok(ProtocolFormat::BidirectionalPostcardPrototype) + Ok(ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype)) } _ => Err(format!("unknown protocol format: {input}")), } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 0c651d22b41b..b927eea46b58 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,6 +1,6 @@ //! The main loop of the proc-macro server. use proc_macro_api::{ - Codec, + Codec, ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, @@ -12,7 +12,6 @@ use legacy::Message; use proc_macro_srv::{EnvSnapshot, SpanId}; -use crate::ProtocolFormat; struct SpanTrans; impl legacy::SpanTransformer for SpanTrans { @@ -32,7 +31,7 @@ impl legacy::SpanTransformer for SpanTrans { } } -pub(crate) fn run( +pub fn run( stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), format: ProtocolFormat, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs new file mode 100644 index 000000000000..722e92eec7e5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -0,0 +1,213 @@ +use std::{ + collections::VecDeque, + io::{self, BufRead, Read, Write}, + sync::{Arc, Condvar, Mutex}, + thread, +}; + +use paths::Utf8PathBuf; +use proc_macro_api::{ + legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap}, + transport::codec::json::JsonProtocol, +}; +use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange}; +use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder}; + +/// Shared state for an in-memory byte channel. +#[derive(Default)] +struct ChannelState { + buffer: VecDeque, + closed: bool, +} + +type InMemoryChannel = Arc<(Mutex, Condvar)>; + +/// Writer end of an in-memory channel. +pub(crate) struct ChannelWriter { + state: InMemoryChannel, +} + +impl Write for ChannelWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + if state.closed { + return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed")); + } + state.buffer.extend(buf); + cvar.notify_all(); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Drop for ChannelWriter { + fn drop(&mut self) { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + state.closed = true; + cvar.notify_all(); + } +} + +/// Reader end of an in-memory channel. +pub(crate) struct ChannelReader { + state: InMemoryChannel, + internal_buf: Vec, +} + +impl Read for ChannelReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + + while state.buffer.is_empty() && !state.closed { + state = cvar.wait(state).unwrap(); + } + + if state.buffer.is_empty() && state.closed { + return Ok(0); + } + + let to_read = buf.len().min(state.buffer.len()); + for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) { + *dst = src; + } + Ok(to_read) + } +} + +impl BufRead for ChannelReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + + while state.buffer.is_empty() && !state.closed { + state = cvar.wait(state).unwrap(); + } + + self.internal_buf.clear(); + self.internal_buf.extend(&state.buffer); + Ok(&self.internal_buf) + } + + fn consume(&mut self, amt: usize) { + let (lock, _) = &*self.state; + let mut state = lock.lock().unwrap(); + let to_drain = amt.min(state.buffer.len()); + drop(state.buffer.drain(..to_drain)); + } +} + +/// Creates a connected pair of channels for bidirectional communication. +fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) { + // Channel for client -> server communication + let client_to_server = Arc::new(( + Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), + Condvar::new(), + )); + let client_writer = ChannelWriter { state: client_to_server.clone() }; + let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() }; + + // Channel for server -> client communication + let server_to_client = Arc::new(( + Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), + Condvar::new(), + )); + + let server_writer = ChannelWriter { state: server_to_client.clone() }; + let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() }; + + (client_writer, client_reader, server_writer, server_reader) +} + +pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf { + let path = proc_macro_test::PROC_MACRO_TEST_LOCATION; + if path.is_empty() { + panic!("proc-macro-test dylib not available (requires nightly toolchain)"); + } + path.into() +} + +/// Runs a test with the server in a background thread. +pub(crate) fn with_server(test_fn: F) -> R +where + F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R, +{ + let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) = + create_channel_pair(); + + let server_handle = thread::spawn(move || { + proc_macro_srv_cli::main_loop::run( + &mut server_reader, + &mut server_writer, + proc_macro_api::ProtocolFormat::JsonLegacy, + ) + }); + + let result = test_fn(&mut client_writer, &mut client_reader); + + // Close the client writer to signal the server to stop + drop(client_writer); + + // Wait for server to finish + match server_handle.join() { + Ok(Ok(())) => {} + Ok(Err(e)) => { + // IO error from server is expected when client disconnects + if matches!( + e.kind(), + io::ErrorKind::BrokenPipe + | io::ErrorKind::UnexpectedEof + | io::ErrorKind::InvalidData + ) { + panic!("Server error: {e}"); + } + } + Err(e) => std::panic::resume_unwind(e), + } + + result +} + +/// Sends a request and reads the response using JSON protocol. +pub(crate) fn request( + writer: &mut dyn Write, + reader: &mut dyn BufRead, + request: Request, +) -> Response { + request.write::(writer).expect("failed to write request"); + + let mut buf = String::new(); + Response::read::(reader, &mut buf) + .expect("failed to read response") + .expect("no response received") +} + +/// Creates a simple empty token tree suitable for testing. +pub(crate) fn create_empty_token_tree( + version: u32, + span_data_table: &mut SpanDataIndexMap, +) -> FlatTree { + let anchor = SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), + ast_id: span::ROOT_ERASED_FILE_AST_ID, + }; + let span = Span { + range: TextRange::empty(0.into()), + anchor, + ctx: SyntaxContext::root(Edition::CURRENT), + }; + + let builder = TopSubtreeBuilder::new(Delimiter { + open: span, + close: span, + kind: DelimiterKind::Invisible, + }); + let tt = builder.build(); + + FlatTree::from_subtree(tt.view(), version, span_data_table) +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs new file mode 100644 index 000000000000..1fa886219a8a --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -0,0 +1,224 @@ +//! Integration tests for the proc-macro-srv-cli main loop. +//! +//! These tests exercise the full client-server RPC procedure using in-memory +//! channels without needing to spawn the actual server and client processes. + +#![cfg(feature = "sysroot-abi")] + +mod common { + pub(crate) mod utils; +} + +use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server}; +use expect_test::expect; +use proc_macro_api::{ + legacy_protocol::msg::{ + ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig, + SpanDataIndexMap, SpanMode, + }, + version::CURRENT_API_VERSION, +}; + +#[test] +fn test_version_check() { + with_server(|writer, reader| { + let response = request(writer, reader, Request::ApiVersionCheck {}); + + match response { + Response::ApiVersionCheck(version) => { + assert_eq!(version, CURRENT_API_VERSION); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_list_macros() { + with_server(|writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + let response = request(writer, reader, Request::ListMacros { dylib_path }); + + let Response::ListMacros(Ok(macros)) = response else { + panic!("expected successful ListMacros response"); + }; + + let mut macro_list: Vec<_> = + macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect(); + macro_list.sort(); + let macro_list_str = macro_list.join("\n"); + + expect![[r#" + DeriveEmpty [CustomDerive] + DeriveError [CustomDerive] + DerivePanic [CustomDerive] + DeriveReemit [CustomDerive] + attr_error [Attr] + attr_noop [Attr] + attr_panic [Attr] + fn_like_clone_tokens [Bang] + fn_like_error [Bang] + fn_like_mk_idents [Bang] + fn_like_mk_literals [Bang] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_span_join [Bang] + fn_like_span_line_column [Bang] + fn_like_span_ops [Bang]"#]] + .assert_eq(¯o_list_str); + }); +} + +#[test] +fn test_list_macros_invalid_path() { + with_server(|writer, reader| { + let response = request( + writer, + reader, + Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + ); + + match response { + Response::ListMacros(Err(e)) => assert!( + e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"), + "{e}" + ), + other => panic!("expected error response, got: {other:?}"), + } + }); +} + +#[test] +fn test_set_config() { + with_server(|writer, reader| { + let config = ServerConfig { span_mode: SpanMode::Id }; + let response = request(writer, reader, Request::SetConfig(config)); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::Id); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_set_config_rust_analyzer_mode() { + with_server(|writer, reader| { + let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; + let response = request(writer, reader, Request::SetConfig(config)); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_expand_macro_panic() { + with_server(|writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = request(writer, reader, Request::ApiVersionCheck {}); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "fn_like_panic".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: version >= 3, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: vec![], + }, + })); + + let response = request(writer, reader, expand_request); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + assert!(msg.contains("fn_like_panic"), "panic message should mention the macro"); + } + Response::ExpandMacro(Ok(_)) => { + panic!("expected panic, but macro succeeded"); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_basic_call_flow() { + with_server(|writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let response1 = request(writer, reader, Request::ApiVersionCheck {}); + assert!(matches!(response1, Response::ApiVersionCheck(_))); + + let response2 = + request(writer, reader, Request::SetConfig(ServerConfig { span_mode: SpanMode::Id })); + assert!(matches!(response2, Response::SetConfig(_))); + + let response3 = + request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }); + assert!(matches!(response3, Response::ListMacros(Ok(_)))); + }); +} + +#[test] +fn test_expand_nonexistent_macro() { + with_server(|writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = request(writer, reader, Request::ApiVersionCheck {}); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "NonexistentMacro".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: version >= 3, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: vec![], + }, + })); + + let response = request(writer, reader, expand_request); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg) + } + other => panic!("expected error for nonexistent macro, got: {other:?}"), + } + }); +} From e40bd1cf6ece4bf9a2fb05474a20cd399694be93 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 11 Jan 2026 15:06:38 +0800 Subject: [PATCH 076/583] Add inherit attributes for extract_function assist Example --- ```rust #[cfg(test)] fn foo() { foo($01 + 1$0); } ``` **Before this PR** ```rust #[cfg(test)] fn foo() { foo(fun_name()); } fn $0fun_name() -> i32 { 1 + 1 } ``` **After this PR** ```rust #[cfg(test)] fn foo() { foo(fun_name()); } #[cfg(test)] fn $0fun_name() -> i32 { 1 + 1 } ``` --- .../src/handlers/extract_function.rs | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 231df9b5b3e1..294e5f7da8b3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -25,7 +25,7 @@ use syntax::{ SyntaxKind::{self, COMMENT}, SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, ast::{ - self, AstNode, AstToken, HasGenericParams, HasName, edit::IndentLevel, + self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::IndentLevel, edit_in_place::Indent, }, match_ast, ted, @@ -375,6 +375,7 @@ struct ContainerInfo<'db> { ret_type: Option>, generic_param_lists: Vec, where_clauses: Vec, + attrs: Vec, edition: Edition, } @@ -911,6 +912,7 @@ impl FunctionBody { let parents = generic_parents(&parent); let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect(); let where_clauses = parents.iter().filter_map(|it| it.where_clause()).collect(); + let attrs = parents.iter().flat_map(|it| it.attrs()).filter(is_inherit_attr).collect(); Some(( ContainerInfo { @@ -919,6 +921,7 @@ impl FunctionBody { ret_type: ty, generic_param_lists, where_clauses, + attrs, edition, }, contains_tail_expr, @@ -1103,6 +1106,14 @@ impl GenericParent { GenericParent::Trait(trait_) => trait_.where_clause(), } } + + fn attrs(&self) -> impl Iterator { + match self { + GenericParent::Fn(fn_) => fn_.attrs(), + GenericParent::Impl(impl_) => impl_.attrs(), + GenericParent::Trait(trait_) => trait_.attrs(), + } + } } /// Search `parent`'s ancestors for items with potentially applicable generic parameters @@ -1578,7 +1589,7 @@ fn format_function( let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); make::fn_( - None, + fun.mods.attrs.clone(), None, fun_name, generic_params, @@ -1958,6 +1969,11 @@ fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned()) } +fn is_inherit_attr(attr: &ast::Attr) -> bool { + let Some(name) = attr.simple_name() else { return false }; + matches!(name.as_str(), "track_caller" | "cfg") +} + fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type { let ty_str = format_type(ty, ctx, module); make::ty(&ty_str) @@ -6372,6 +6388,55 @@ fn foo() { fn $0fun_name(mut a: i32, mut b: i32) { (a, b) = (b, a); } +"#, + ); + } + + #[test] + fn with_cfg_attr() { + check_assist( + extract_function, + r#" +//- /main.rs crate:main cfg:test +#[cfg(test)] +fn foo() { + foo($01 + 1$0); +} +"#, + r#" +#[cfg(test)] +fn foo() { + foo(fun_name()); +} + +#[cfg(test)] +fn $0fun_name() -> i32 { + 1 + 1 +} +"#, + ); + } + + #[test] + fn with_track_caller() { + check_assist( + extract_function, + r#" +#[track_caller] +fn foo() { + foo($01 + 1$0); +} +"#, + r#" +#[track_caller] +fn foo() { + foo(fun_name()); +} + +#[track_caller] +fn $0fun_name() -> i32 { + 1 + 1 +} "#, ); } From 8150413bf5c1327c0a58b544662b50e5ef4bedc2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 7 Jan 2026 17:19:33 +0530 Subject: [PATCH 077/583] add byte range subrequest/response --- .../crates/proc-macro-api/src/bidirectional_protocol/msg.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index 0e3b700dcc5a..57e7b1ee8f68 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -1,5 +1,7 @@ //! Bidirectional protocol messages +use std::ops::Range; + use paths::Utf8PathBuf; use serde::{Deserialize, Serialize}; @@ -14,6 +16,7 @@ pub enum SubRequest { SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, LocalFilePath { file_id: u32 }, LineColumn { file_id: u32, ast_id: u32, offset: u32 }, + ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 }, } #[derive(Debug, Serialize, Deserialize)] @@ -32,6 +35,9 @@ pub enum SubResponse { line: u32, column: u32, }, + ByteRangeResult { + range: Range, + }, } #[derive(Debug, Serialize, Deserialize)] From e909b4b28286d914c1d67f12f419cf1ed88daa8d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 7 Jan 2026 17:20:06 +0530 Subject: [PATCH 078/583] update proc-macro-srv to include byte-range --- src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 3 +++ .../proc-macro-srv/src/server_impl/rust_analyzer_span.rs | 5 ++++- .../rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index c1ef49a7176b..ac9f89352c2b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -41,6 +41,7 @@ use std::{ env, ffi::OsString, fs, + ops::Range, path::{Path, PathBuf}, sync::{Arc, Mutex, PoisonError}, thread, @@ -100,6 +101,8 @@ pub trait ProcMacroClientInterface { fn local_file(&mut self, file_id: span::FileId) -> Option; /// Line and column are 1-based. fn line_column(&mut self, span: Span) -> Option<(u32, u32)>; + + fn byte_range(&mut self, span: Span) -> Range; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 3a25391b573b..9946608247c3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -162,7 +162,10 @@ impl server::Span for RaSpanServer<'_> { span } fn byte_range(&mut self, span: Self::Span) -> Range { - // FIXME requires db to resolve the ast id, THIS IS NOT INCREMENTAL + if let Some(cb) = self.callback.as_mut() { + return cb.byte_range(span); + } + Range { start: span.range.start().into(), end: span.range.end().into() } } fn join(&mut self, first: Self::Span, second: Self::Span) -> Option { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 81ff1965d68b..b7c5c4fdd21f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -4,6 +4,7 @@ use expect_test::Expect; use span::{ EditionedFileId, FileId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, }; +use std::ops::Range; use crate::{ EnvSnapshot, ProcMacroClientInterface, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path, @@ -137,6 +138,10 @@ impl ProcMacroClientInterface for MockCallback<'_> { // proc_macro uses 1-based line/column Some((line_col.line as u32 + 1, line_col.col as u32 + 1)) } + + fn byte_range(&mut self, span: Span) -> Range { + Range { start: span.range.start().into(), end: span.range.end().into() } + } } pub fn assert_expand_with_callback( From e68a654dca8b43e4f430485249fdef00ed9d85a7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 7 Jan 2026 17:20:37 +0530 Subject: [PATCH 079/583] add byte range to main loop and direct the request via callback and define the callback on client side --- .../crates/load-cargo/src/lib.rs | 10 ++++++++++ .../proc-macro-srv-cli/src/main_loop.rs | 19 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 33468a5003c3..904f704221cf 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -591,6 +591,16 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::FilePathResult { name }) } + SubRequest::ByteRange { file_id, ast_id, start, end } => { + let range = resolve_sub_span( + db, + file_id, + ast_id, + TextRange::new(TextSize::from(start), TextSize::from(end)), + ); + + Ok(SubResponse::ByteRangeResult { range: range.range.into() }) + } }; match self.0.expand( subtree.view(), diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 0c651d22b41b..e35f832716dd 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -6,7 +6,7 @@ use proc_macro_api::{ transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version::CURRENT_API_VERSION, }; -use std::io::{self, BufRead, Write}; +use std::{io, ops::Range}; use legacy::Message; @@ -240,6 +240,23 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl _ => None, } } + + fn byte_range( + &mut self, + proc_macro_srv::span::Span { range, anchor, ctx: _ }: proc_macro_srv::span::Span, + ) -> Range { + match self.roundtrip(bidirectional::SubRequest::ByteRange { + file_id: anchor.file_id.as_u32(), + ast_id: anchor.ast_id.into_raw(), + start: range.start().into(), + end: range.end().into(), + }) { + Some(bidirectional::BidirectionalMessage::SubResponse( + bidirectional::SubResponse::ByteRangeResult { range }, + )) => range, + _ => Range { start: range.start().into(), end: range.end().into() }, + } + } } fn handle_expand_ra( From 78d243c751e420c38c3a92f707aae78bafc8ad1e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 8 Jan 2026 22:43:03 +0530 Subject: [PATCH 080/583] add comment on incrementality of subrequest --- src/tools/rust-analyzer/crates/load-cargo/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 904f704221cf..8342492a33a4 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -553,6 +553,7 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::LocalFilePathResult { name }) } + // Not incremental: requires full file text. SubRequest::SourceText { file_id, ast_id, start, end } => { let range = resolve_sub_span( db, @@ -567,6 +568,7 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SourceTextResult { text }) } + // Not incremental: requires building line index. SubRequest::LineColumn { file_id, ast_id, offset } => { let range = resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); @@ -591,6 +593,7 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::FilePathResult { name }) } + // Not incremental: requires global span resolution. SubRequest::ByteRange { file_id, ast_id, start, end } => { let range = resolve_sub_span( db, From d30f7c9f7c8c375c2b806b770dd11f3d481979e9 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 15:51:33 +0530 Subject: [PATCH 081/583] add write read imports --- .../rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index e35f832716dd..3beaeb0697ec 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -6,7 +6,10 @@ use proc_macro_api::{ transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version::CURRENT_API_VERSION, }; -use std::{io, ops::Range}; +use std::{ + io::{self, BufRead, Write}, + ops::Range, +}; use legacy::Message; From c7c5adbe6452c1c904fbe6b6bb3fdd0ed7671a55 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 11 Jan 2026 07:00:54 -0600 Subject: [PATCH 082/583] compiler-builtins: Remove the no-f16-f128 feature This option was used to gate `f16` and `f128` when support across backends and targets was inconsistent. We now have the rustc builtin cfg `target_has_reliable{f16,f128}` which has taken over this usecase. Remove no-f16-f128 since it is now unused and redundant. --- library/alloc/Cargo.toml | 1 - library/compiler-builtins/builtins-shim/Cargo.toml | 4 ---- library/compiler-builtins/builtins-test/Cargo.toml | 1 - library/compiler-builtins/ci/run.sh | 4 ---- library/compiler-builtins/compiler-builtins/Cargo.toml | 4 ---- library/compiler-builtins/compiler-builtins/configure.rs | 7 ++----- library/compiler-builtins/libm/configure.rs | 7 ++----- library/std/Cargo.toml | 1 - library/sysroot/Cargo.toml | 1 - 9 files changed, 4 insertions(+), 26 deletions(-) diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index fb1f8c86dbfd..541257b6cda6 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -21,7 +21,6 @@ compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features [features] compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] -compiler-builtins-no-f16-f128 = ["compiler_builtins/no-f16-f128"] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size"] diff --git a/library/compiler-builtins/builtins-shim/Cargo.toml b/library/compiler-builtins/builtins-shim/Cargo.toml index ac77224f5ce1..746d5b21dc3f 100644 --- a/library/compiler-builtins/builtins-shim/Cargo.toml +++ b/library/compiler-builtins/builtins-shim/Cargo.toml @@ -47,10 +47,6 @@ c = ["dep:cc"] # the generic versions on all platforms. no-asm = [] -# Workaround for codegen backends which haven't yet implemented `f16` and -# `f128` support. Disabled any intrinsics which use those types. -no-f16-f128 = [] - # Flag this library as the unstable compiler-builtins lib compiler-builtins = [] diff --git a/library/compiler-builtins/builtins-test/Cargo.toml b/library/compiler-builtins/builtins-test/Cargo.toml index 9346ea65420b..550f736a76db 100644 --- a/library/compiler-builtins/builtins-test/Cargo.toml +++ b/library/compiler-builtins/builtins-test/Cargo.toml @@ -33,7 +33,6 @@ utest-macros = { git = "https://github.com/japaric/utest" } default = ["mangled-names"] c = ["compiler_builtins/c"] no-asm = ["compiler_builtins/no-asm"] -no-f16-f128 = ["compiler_builtins/no-f16-f128"] mem = ["compiler_builtins/mem"] mangled-names = ["compiler_builtins/mangled-names"] # Skip tests that rely on f128 symbols being available on the system diff --git a/library/compiler-builtins/ci/run.sh b/library/compiler-builtins/ci/run.sh index bc94d42fe837..0c07b32c74b9 100755 --- a/library/compiler-builtins/ci/run.sh +++ b/library/compiler-builtins/ci/run.sh @@ -36,8 +36,6 @@ else "${test_builtins[@]}" --features c --release "${test_builtins[@]}" --features no-asm "${test_builtins[@]}" --features no-asm --release - "${test_builtins[@]}" --features no-f16-f128 - "${test_builtins[@]}" --features no-f16-f128 --release "${test_builtins[@]}" --benches "${test_builtins[@]}" --benches --release @@ -63,8 +61,6 @@ symcheck+=(-- build-and-check) "${symcheck[@]}" "$target" -- -p compiler_builtins --features c --release "${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm "${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm --release -"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-f16-f128 -"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-f16-f128 --release run_intrinsics_test() { build_args=(--verbose --manifest-path builtins-test-intrinsics/Cargo.toml) diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index 0845861dcfe3..496dde2d4cf2 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -45,10 +45,6 @@ c = ["dep:cc"] # the generic versions on all platforms. no-asm = [] -# Workaround for codegen backends which haven't yet implemented `f16` and -# `f128` support. Disabled any intrinsics which use those types. -no-f16-f128 = [] - # Flag this library as the unstable compiler-builtins lib compiler-builtins = [] diff --git a/library/compiler-builtins/compiler-builtins/configure.rs b/library/compiler-builtins/compiler-builtins/configure.rs index 79e238abc0f6..f16da6b58f81 100644 --- a/library/compiler-builtins/compiler-builtins/configure.rs +++ b/library/compiler-builtins/compiler-builtins/configure.rs @@ -95,16 +95,13 @@ pub fn configure_aliases(target: &Target) { * * https://github.com/rust-lang/rustc_codegen_cranelift/blob/c713ffab3c6e28ab4b4dd4e392330f786ea657ad/src/lib.rs#L196-L226 */ - // If the feature is set, disable both of these types. - let no_f16_f128 = target.cargo_features.iter().any(|s| s == "no-f16-f128"); - println!("cargo::rustc-check-cfg=cfg(f16_enabled)"); - if target.reliable_f16 && !no_f16_f128 { + if target.reliable_f16 { println!("cargo::rustc-cfg=f16_enabled"); } println!("cargo::rustc-check-cfg=cfg(f128_enabled)"); - if target.reliable_f128 && !no_f16_f128 { + if target.reliable_f128 { println!("cargo::rustc-cfg=f128_enabled"); } } diff --git a/library/compiler-builtins/libm/configure.rs b/library/compiler-builtins/libm/configure.rs index 857a30229716..ee65a3a8d624 100644 --- a/library/compiler-builtins/libm/configure.rs +++ b/library/compiler-builtins/libm/configure.rs @@ -143,16 +143,13 @@ fn emit_f16_f128_cfg(cfg: &Config) { /* See the compiler-builtins configure file for info about the meaning of these options */ - // If the feature is set, disable both of these types. - let no_f16_f128 = cfg.cargo_features.iter().any(|s| s == "no-f16-f128"); - println!("cargo:rustc-check-cfg=cfg(f16_enabled)"); - if cfg.reliable_f16 && !no_f16_f128 { + if cfg.reliable_f16 { println!("cargo:rustc-cfg=f16_enabled"); } println!("cargo:rustc-check-cfg=cfg(f128_enabled)"); - if cfg.reliable_f128 && !no_f16_f128 { + if cfg.reliable_f128 { println!("cargo:rustc-cfg=f128_enabled"); } } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 5c9ae52d9e6c..b6683a2ae9ec 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -115,7 +115,6 @@ backtrace-trace-only = [] panic-unwind = ["dep:panic_unwind"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] -compiler-builtins-no-f16-f128 = ["alloc/compiler-builtins-no-f16-f128"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index a18868082916..b2069ef6a613 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -25,7 +25,6 @@ backtrace = ["std/backtrace"] backtrace-trace-only = ["std/backtrace-trace-only"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] -compiler-builtins-no-f16-f128 = ["std/compiler-builtins-no-f16-f128"] debug_refcell = ["std/debug_refcell"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] From 33f0f80c1ac9dd1df22082cce77e7eedbb30b4f1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 21:33:57 +0530 Subject: [PATCH 083/583] add make corresponding constructor methods in SyntaxFactory --- .../src/ast/syntax_factory/constructors.rs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 7cf9e2bf14f9..5fe419ad4eb7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1578,6 +1578,44 @@ impl SyntaxFactory { pub fn ident(&self, text: &str) -> SyntaxToken { make::tokens::ident(text) } + + pub fn mut_self_param(&self) -> ast::SelfParam { + let ast = make::mut_self_param().clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn ret_type(&self, ty: ast::Type) -> ast::RetType { + let ast = make::ret_type(ty.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + ast + } + + pub fn ty_ref(&self, ty: ast::Type, is_mut: bool) -> ast::Type { + let ast = make::ty_ref(ty.clone(), is_mut).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + match &ast { + ast::Type::RefType(ref_ty) => { + builder.map_node(ty.syntax().clone(), ref_ty.ty().unwrap().syntax().clone()); + } + _ => unreachable!(), + } + builder.finish(&mut mapping); + } + ast + } } // `ext` constructors From 16d74e7b90fc5fe2aaa23377bf51093f0295910b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 21:34:44 +0530 Subject: [PATCH 084/583] migrate generate_mut_trait_impl to new SyntaxEditor --- .../src/handlers/generate_mut_trait_impl.rs | 191 +++++++++++------- 1 file changed, 121 insertions(+), 70 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index ae1ae24d1ec1..53f6f4883f4d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -1,8 +1,8 @@ use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait}; use syntax::{ - AstNode, T, - ast::{self, edit_in_place::Indent, make}, - ted, + AstNode, SyntaxElement, SyntaxNode, T, + ast::{self, edit::AstNodeEdit, edit_in_place::Indent, syntax_factory::SyntaxFactory}, + syntax_editor::{Element, Position, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists}; @@ -45,12 +45,13 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let impl_def = ctx.find_node_at_offset::()?.clone_for_update(); - let indent = impl_def.indent_level(); + let impl_def = ctx.find_node_at_offset::()?; + let indent = Indent::indent_level(&impl_def); let ast::Type::PathType(path) = impl_def.trait_()? else { return None; }; + let trait_name = path.path()?.segment()?.name_ref()?; let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?; @@ -59,75 +60,133 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; let trait_new = get_trait_mut(&trait_, famous)?; - // Index -> IndexMut - ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax()); - - // index -> index_mut - let (trait_method_name, new_trait_method_name) = impl_def - .syntax() - .descendants() - .filter_map(ast::Name::cast) - .find_map(process_method_name)?; - ted::replace( - trait_method_name.syntax(), - make::name(new_trait_method_name).clone_for_update().syntax(), - ); - - if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) { - ted::remove(type_alias.syntax()); - } - - // &self -> &mut self - let mut_self_param = make::mut_self_param(); - let self_param: ast::SelfParam = - impl_def.syntax().descendants().find_map(ast::SelfParam::cast)?; - ted::replace(self_param.syntax(), mut_self_param.clone_for_update().syntax()); - - // &Self::Output -> &mut Self::Output - let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?; - let new_ret_type = process_ret_type(&ret_type)?; - ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax()); - - let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it { - ast::AssocItem::Fn(f) => Some(f), - _ => None, - })?; - let _ = process_ref_mut(&fn_); - - let assoc_list = make::assoc_item_list(None).clone_for_update(); - ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax()); - impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_)); - let target = impl_def.syntax().text_range(); + acc.add( AssistId::generate("generate_mut_trait_impl"), format!("Generate `{trait_new}` impl from this `{trait_name}` trait"), target, |edit| { - edit.insert( - target.start(), - if ctx.config.snippet_cap.is_some() { - format!("$0{impl_def}\n\n{indent}") - } else { - format!("{impl_def}\n\n{indent}") - }, + let impl_clone = impl_def.reset_indent().clone_subtree(); + let mut editor = SyntaxEditor::new(impl_clone.syntax().clone()); + let factory = SyntaxFactory::without_mappings(); + + apply_generate_mut_impl(&mut editor, &factory, &impl_clone, trait_new); + + let new_root = editor.finish(); + let new_root = new_root.new_root(); + + let new_impl = ast::Impl::cast(new_root.clone()).unwrap(); + + Indent::indent(&new_impl, indent); + + let mut editor = edit.make_editor(impl_def.syntax()); + editor.insert_all( + Position::before(impl_def.syntax()), + vec![ + new_impl.syntax().syntax_element(), + factory.whitespace(&format!("\n\n{indent}")).syntax_element(), + ], ); + + if let Some(cap) = ctx.config.snippet_cap { + let tabstop_before = edit.make_tabstop_before(cap); + editor.add_annotation(new_impl.syntax(), tabstop_before); + } + + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn process_ref_mut(fn_: &ast::Fn) -> Option<()> { - let expr = fn_.body()?.tail_expr()?; - match &expr { - ast::Expr::RefExpr(ref_expr) if ref_expr.mut_token().is_none() => { - ted::insert_all_raw( - ted::Position::after(ref_expr.amp_token()?), - vec![make::token(T![mut]).into(), make::tokens::whitespace(" ").into()], - ); - } - _ => {} +fn delete_with_trivia(editor: &mut SyntaxEditor, node: &SyntaxNode) { + let mut end: SyntaxElement = node.clone().into(); + + if let Some(next) = node.next_sibling_or_token() + && let SyntaxElement::Token(tok) = &next + && tok.kind().is_trivia() + { + end = next.clone(); } - None + + editor.delete_all(node.clone().into()..=end); +} + +fn apply_generate_mut_impl( + editor: &mut SyntaxEditor, + factory: &SyntaxFactory, + impl_def: &ast::Impl, + trait_new: &str, +) -> Option<()> { + let path = + impl_def.trait_().and_then(|t| t.syntax().descendants().find_map(ast::Path::cast))?; + let seg = path.segment()?; + let name_ref = seg.name_ref()?; + + let new_name_ref = factory.name_ref(trait_new); + editor.replace(name_ref.syntax(), new_name_ref.syntax()); + + if let Some((name, new_name)) = + impl_def.syntax().descendants().filter_map(ast::Name::cast).find_map(process_method_name) + { + let new_name_node = factory.name(new_name); + editor.replace(name.syntax(), new_name_node.syntax()); + } + + if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) { + delete_with_trivia(editor, type_alias.syntax()); + } + + if let Some(self_param) = impl_def.syntax().descendants().find_map(ast::SelfParam::cast) { + let mut_self = factory.mut_self_param(); + editor.replace(self_param.syntax(), mut_self.syntax()); + } + + if let Some(ret_type) = impl_def.syntax().descendants().find_map(ast::RetType::cast) + && let Some(new_ty) = process_ret_type(factory, &ret_type) + { + let new_ret = factory.ret_type(new_ty); + editor.replace(ret_type.syntax(), new_ret.syntax()) + } + + if let Some(fn_) = impl_def.assoc_item_list().and_then(|l| { + l.assoc_items().find_map(|it| match it { + ast::AssocItem::Fn(f) => Some(f), + _ => None, + }) + }) { + process_ref_mut(editor, factory, &fn_); + } + + Some(()) +} + +fn process_ref_mut(editor: &mut SyntaxEditor, factory: &SyntaxFactory, fn_: &ast::Fn) { + let Some(expr) = fn_.body().and_then(|b| b.tail_expr()) else { return }; + + let ast::Expr::RefExpr(ref_expr) = expr else { return }; + + if ref_expr.mut_token().is_some() { + return; + } + + let Some(amp) = ref_expr.amp_token() else { return }; + + let mut_kw = factory.token(T![mut]); + let space = factory.whitespace(" "); + + editor.insert(Position::after(amp.clone()), space.syntax_element()); + editor.insert(Position::after(amp), mut_kw.syntax_element()); +} + +fn process_ret_type(factory: &SyntaxFactory, ref_ty: &ast::RetType) -> Option { + let ty = ref_ty.ty()?; + let ast::Type::RefType(ref_type) = ty else { + return None; + }; + + let inner = ref_type.ty()?; + Some(factory.ty_ref(inner, true)) } fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> { @@ -158,14 +217,6 @@ fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> { Some((name, new_name)) } -fn process_ret_type(ref_ty: &ast::RetType) -> Option { - let ty = ref_ty.ty()?; - let ast::Type::RefType(ref_type) = ty else { - return None; - }; - Some(make::ty_ref(ref_type.ty()?, true)) -} - #[cfg(test)] mod tests { use crate::{ From 594ca4b1bc47588a851265d51a5db05ed2f080b2 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 12 Jan 2026 04:26:31 +0000 Subject: [PATCH 085/583] Prepare for merging from rust-lang/rust This updates the rust-version file to 44a5b55557c26353f388400d7da95527256fe260. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 4b08b0884ca8..b53a66c66751 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -548e586795f6b6fe089d8329aa5edbf0f5202646 +44a5b55557c26353f388400d7da95527256fe260 From 80acef153ffa6ecc4c9458b8a1169b10d7d750c7 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 12 Jan 2026 04:26:43 +0000 Subject: [PATCH 086/583] Format code --- src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs | 4 +++- src/tools/rust-analyzer/crates/span/src/hygiene.rs | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index d10e122a5deb..172641227599 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -101,7 +101,9 @@ impl DeclarativeMacroExpander { match &*value { "transparent" => ControlFlow::Break(Transparency::Transparent), // "semitransparent" is for old rustc versions. - "semiopaque" | "semitransparent" => ControlFlow::Break(Transparency::SemiOpaque), + "semiopaque" | "semitransparent" => { + ControlFlow::Break(Transparency::SemiOpaque) + } "opaque" => ControlFlow::Break(Transparency::Opaque), _ => ControlFlow::Continue(()), } diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 9904c562a740..fe05ef946518 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -241,9 +241,7 @@ const _: () = { edition: zalsa_::interned::Lookup::into_owned(data.2), parent: zalsa_::interned::Lookup::into_owned(data.3), opaque: opaque(zalsa_::FromId::from_id(id)), - opaque_and_semiopaque: opaque_and_semiopaque( - zalsa_::FromId::from_id(id), - ), + opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)), }, ) } From 294a0afd655f22f98201c5e34e9f3b57e42a537c Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 12 Jan 2026 08:51:42 +0200 Subject: [PATCH 087/583] Disable `unused_variables` and `unused_mut` warnings They suffer from an unacceptable amount of false positives after #21209. Another option to disable them is to include them in `rust-analyzer.diagnostics.disable` by default, but that will mean users could override that. --- .../src/handlers/mutability_errors.rs | 24 ++++++----- .../src/handlers/unused_variables.rs | 42 ++++++++++--------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 2887a32825db..e3cfbdfb515f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1,3 +1,5 @@ +#![expect(unused, reason = "diagnostics is temporarily disabled due to too many false positives")] + use hir::db::ExpandDatabase; use ide_db::source_change::SourceChange; use ide_db::text_edit::TextEdit; @@ -88,16 +90,17 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - Some( - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_mut"), - "variable does not need to be mutable", - ast, - ) - // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. - .with_fixes(fixes), - ) + // Some( + // Diagnostic::new_with_syntax_node_ptr( + // ctx, + // DiagnosticCode::RustcLint("unused_mut"), + // "variable does not need to be mutable", + // ast, + // ) + // // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. + // .with_fixes(fixes), + // ) + None } pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { @@ -105,6 +108,7 @@ pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option Date: Mon, 12 Jan 2026 08:36:32 +0100 Subject: [PATCH 088/583] Create a new `SymbolKind::CrateRoot` --- .../src/completions/extern_crate.rs | 6 +++--- .../crates/ide-completion/src/item.rs | 1 + .../rust-analyzer/crates/ide-db/src/lib.rs | 8 ++++--- .../crates/ide/src/navigation_target.rs | 13 ++++-------- .../crates/ide/src/references.rs | 2 +- .../rust-analyzer/crates/ide/src/runnables.rs | 8 +++---- .../ide/src/syntax_highlighting/highlight.rs | 21 +++++++++---------- .../ide/src/syntax_highlighting/inject.rs | 12 +++++------ .../ide/src/syntax_highlighting/tags.rs | 1 + .../test_data/highlight_asm.html | 8 +++---- .../test_data/highlight_attributes.html | 2 +- .../test_data/highlight_crate_root.html | 16 +++++++------- .../test_data/highlight_default_library.html | 2 +- .../test_data/highlight_deprecated.html | 4 ++-- .../test_data/highlight_doctest.html | 6 +++--- .../test_data/highlight_extern_crate.html | 14 ++++++------- .../test_data/highlight_general.html | 12 +++++------ .../test_data/highlight_injection_2.html | 2 +- .../test_data/highlight_issue_18089.html | 2 +- .../test_data/highlight_keywords_2015.html | 8 +++---- .../test_data/highlight_keywords_2018.html | 8 +++---- .../test_data/highlight_keywords_2021.html | 8 +++---- .../test_data/highlight_keywords_2024.html | 8 +++---- .../test_data/highlight_keywords_macros.html | 4 ++-- .../test_data/highlight_macros.html | 2 +- .../test_data/highlight_strings.html | 2 +- .../test_data/highlight_unsafe.html | 4 ++-- .../crates/rust-analyzer/src/lsp/to_proto.rs | 8 ++++++- 28 files changed, 98 insertions(+), 94 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs index 71a3e4eb4ed6..91202e8b32fc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs @@ -17,7 +17,7 @@ pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionConte } let mut item = CompletionItem::new( - CompletionItemKind::SymbolKind(SymbolKind::Module), + CompletionItemKind::SymbolKind(SymbolKind::CrateRoot), ctx.source_range(), name.display_no_db(ctx.edition).to_smolstr(), ctx.edition, @@ -48,7 +48,7 @@ mod other_mod {} let completion_list = completion_list_no_kw(case); - assert_eq!("md other_crate_a\n".to_owned(), completion_list); + assert_eq!("cr other_crate_a\n".to_owned(), completion_list); } #[test] @@ -68,6 +68,6 @@ mod other_mod {} let completion_list = completion_list_no_kw(case); - assert_eq!("md other_crate_a\n".to_owned(), completion_list); + assert_eq!("cr other_crate_a\n".to_owned(), completion_list); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 71d32da74710..1a9139d8553b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -381,6 +381,7 @@ impl CompletionItemKind { SymbolKind::BuiltinAttr => "ba", SymbolKind::Const => "ct", SymbolKind::ConstParam => "cp", + SymbolKind::CrateRoot => "cr", SymbolKind::Derive => "de", SymbolKind::DeriveHelper => "dh", SymbolKind::Enum => "en", diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 413b58bf7980..023b32b36195 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -65,7 +65,7 @@ use base_db::{ }; use hir::{ FilePositionWrapper, FileRangeWrapper, - db::{DefDatabase, ExpandDatabase}, + db::{DefDatabase, ExpandDatabase, HirDatabase}, }; use triomphe::Arc; @@ -269,6 +269,7 @@ pub enum SymbolKind { BuiltinAttr, Const, ConstParam, + CrateRoot, Derive, DeriveHelper, Enum, @@ -307,14 +308,15 @@ impl From for SymbolKind { } } -impl From for SymbolKind { - fn from(it: hir::ModuleDef) -> Self { +impl SymbolKind { + pub fn from_module_def(db: &dyn HirDatabase, it: hir::ModuleDef) -> Self { match it { hir::ModuleDef::Const(..) => SymbolKind::Const, hir::ModuleDef::Variant(..) => SymbolKind::Variant, hir::ModuleDef::Function(..) => SymbolKind::Function, hir::ModuleDef::Macro(mac) if mac.is_proc_macro() => SymbolKind::ProcMacro, hir::ModuleDef::Macro(..) => SymbolKind::Macro, + hir::ModuleDef::Module(m) if m.is_crate_root(db) => SymbolKind::CrateRoot, hir::ModuleDef::Module(..) => SymbolKind::Module, hir::ModuleDef::Static(..) => SymbolKind::Static, hir::ModuleDef::Adt(hir::Adt::Struct(..)) => SymbolKind::Struct, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 047df309eca6..185df92e2d39 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -264,7 +264,7 @@ impl<'db> TryToNav for FileSymbol<'db> { .flatten() .map_or_else(|| self.name.clone(), |it| it.symbol().clone()), alias: self.is_alias.then(|| self.name.clone()), - kind: Some(self.def.into()), + kind: Some(SymbolKind::from_module_def(db, self.def)), full_range, focus_range, container_name: self.container_name.clone(), @@ -480,16 +480,11 @@ impl ToNav for hir::Module { ModuleSource::Module(node) => (node.syntax(), node.name()), ModuleSource::BlockExpr(node) => (node.syntax(), None), }; + let kind = if self.is_crate_root(db) { SymbolKind::CrateRoot } else { SymbolKind::Module }; orig_range_with_focus(db, file_id, syntax, focus).map( |(FileRange { file_id, range: full_range }, focus_range)| { - NavigationTarget::from_syntax( - file_id, - name.clone(), - focus_range, - full_range, - SymbolKind::Module, - ) + NavigationTarget::from_syntax(file_id, name.clone(), focus_range, full_range, kind) }, ) } @@ -549,7 +544,7 @@ impl TryToNav for hir::ExternCrateDecl { self.alias_or_name(db).unwrap_or_else(|| self.name(db)).symbol().clone(), focus_range, full_range, - SymbolKind::Module, + SymbolKind::CrateRoot, ); res.docs = self.docs(db).map(Documentation::into_owned); diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 4918fe4ff9a4..5443021988d4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -1079,7 +1079,7 @@ use self$0; use self$0; "#, expect![[r#" - _ Module FileId(0) 0..10 + _ CrateRoot FileId(0) 0..10 FileId(0) 4..8 import "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 6cec91250351..42efa7142b50 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -815,7 +815,7 @@ mod not_a_root { "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"_\", kind: CrateRoot })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 15..76, focus_range: 42..71, name: \"__cortex_m_rt_main_trampoline\", kind: Function })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 78..154, focus_range: 113..149, name: \"__cortex_m_rt_main_trampoline_unsafe\", kind: Function })", @@ -1136,7 +1136,7 @@ fn test_foo1() {} "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..51, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..51, name: \"_\", kind: CrateRoot })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 1..50, focus_range: 36..45, name: \"test_foo1\", kind: Function }, Atom(KeyValue { key: \"feature\", value: \"foo\" }))", ] "#]], @@ -1155,7 +1155,7 @@ fn test_foo1() {} "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..73, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..73, name: \"_\", kind: CrateRoot })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 1..72, focus_range: 58..67, name: \"test_foo1\", kind: Function }, All([Atom(KeyValue { key: \"feature\", value: \"foo\" }), Atom(KeyValue { key: \"feature\", value: \"bar\" })]))", ] "#]], @@ -1234,7 +1234,7 @@ generate_main!(); "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"_\", kind: CrateRoot })", "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 282..312, focus_range: 286..291, name: \"tests\", kind: Module, description: \"mod tests\" })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..307, name: \"foo_test\", kind: Function })", "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 044fd3f5ac9a..3795d3d4146d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -399,7 +399,7 @@ fn highlight_name_ref( highlight_def(sema, krate, field_ref.into(), edition, true) } NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => { - let mut h = HlTag::Symbol(SymbolKind::Module).into(); + let mut h = HlTag::Symbol(SymbolKind::CrateRoot).into(); if krate.as_ref().is_some_and(|krate| resolved_krate != *krate) { h |= HlMod::Library; @@ -417,7 +417,6 @@ fn highlight_name_ref( if is_deprecated { h |= HlMod::Deprecated; } - h |= HlMod::CrateRoot; h } }; @@ -496,15 +495,15 @@ pub(super) fn highlight_def( } Definition::TupleField(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Field)), None), Definition::Crate(krate) => ( - Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot, + Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)).into(), Some(krate.attrs(sema.db)), ), Definition::Module(module) => { - let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module)); - if module.is_crate_root(db) { - h |= HlMod::CrateRoot; - } - + let h = Highlight::new(HlTag::Symbol(if module.is_crate_root(db) { + SymbolKind::CrateRoot + } else { + SymbolKind::Module + })); (h, Some(module.attrs(sema.db))) } Definition::Function(func) => { @@ -662,8 +661,7 @@ pub(super) fn highlight_def( (h, None) } Definition::ExternCrateDecl(extern_crate) => { - let mut highlight = - Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot; + let mut highlight = Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)).into(); if extern_crate.alias(db).is_none() { highlight |= HlMod::Library; } @@ -805,6 +803,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { TYPE_PARAM => SymbolKind::TypeParam, RECORD_FIELD => SymbolKind::Field, MODULE => SymbolKind::Module, + EXTERN_CRATE => SymbolKind::CrateRoot, FN => SymbolKind::Function, CONST => SymbolKind::Const, STATIC => SymbolKind::Static, @@ -835,7 +834,7 @@ fn highlight_name_ref_by_syntax( }; match parent.kind() { - EXTERN_CRATE => HlTag::Symbol(SymbolKind::Module) | HlMod::CrateRoot, + EXTERN_CRATE => HlTag::Symbol(SymbolKind::CrateRoot).into(), METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) .and_then(|it| highlight_method_call(sema, krate, &it, is_unsafe_node)) .unwrap_or_else(|| SymbolKind::Method.into()), diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 26d2bb5e0288..291333f09cf8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -1,6 +1,6 @@ //! "Recursive" Syntax highlighting for code in doctests and fixtures. -use hir::{EditionedFileId, HirFileId, InFile, Semantics}; +use hir::{EditionedFileId, HirFileId, InFile, Semantics, db::HirDatabase}; use ide_db::{ SymbolKind, defs::Definition, documentation::Documentation, range_mapper::RangeMapper, rust_doc::is_rust_fence, @@ -109,7 +109,7 @@ pub(super) fn doc_comment( .for_each(|(range, def)| { hl.add(HlRange { range, - highlight: module_def_to_hl_tag(def) + highlight: module_def_to_hl_tag(sema.db, def) | HlMod::Documentation | HlMod::Injected | HlMod::IntraDocLink, @@ -200,11 +200,11 @@ pub(super) fn doc_comment( } } -fn module_def_to_hl_tag(def: Definition) -> HlTag { +fn module_def_to_hl_tag(db: &dyn HirDatabase, def: Definition) -> HlTag { let symbol = match def { - Definition::Module(_) | Definition::Crate(_) | Definition::ExternCrateDecl(_) => { - SymbolKind::Module - } + Definition::Crate(_) | Definition::ExternCrateDecl(_) => SymbolKind::CrateRoot, + Definition::Module(m) if m.is_crate_root(db) => SymbolKind::CrateRoot, + Definition::Module(_) => SymbolKind::Module, Definition::Function(_) => SymbolKind::Function, Definition::Adt(hir::Adt::Struct(_)) => SymbolKind::Struct, Definition::Adt(hir::Adt::Enum(_)) => SymbolKind::Enum, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index ca3c3e3aaace..0c64d3de1012 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -144,6 +144,7 @@ impl HlTag { SymbolKind::BuiltinAttr => "builtin_attr", SymbolKind::Const => "constant", SymbolKind::ConstParam => "const_param", + SymbolKind::CrateRoot => "crate_root", SymbolKind::Derive => "derive", SymbolKind::DeriveHelper => "derive_helper", SymbolKind::Enum => "enum", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index 100fdd2155a4..1228849c5bfd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd unsafe { let foo = 1; let mut o = 0; - core::arch::asm!( + core::arch::asm!( "%input = OpLoad _ {0}", concat!("%result = ", "bar", " _ %input"), "OpStore {1} %result", @@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd ); let thread_id: usize; - core::arch::asm!(" + core::arch::asm!(" mov {0}, gs:[0x30] mov {0}, [{0}+0x48] ", out(reg) thread_id, options(pure, readonly, nostack)); @@ -64,7 +64,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd static VirtualFree: usize; const OffPtr: usize; const OffFn: usize; - core::arch::asm!(" + core::arch::asm!(" push {free_type} push {free_size} push {base} @@ -97,7 +97,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd // Ensure thumb mode is set. let rv = (rv as u32) | 1; let msp = msp as u32; - core::arch::asm!( + core::arch::asm!( "mrs {tmp}, CONTROL", "bics {tmp}, {spsel}", "msr CONTROL, {tmp}", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index b151ff42fc39..fa7f7b1cbafb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -43,7 +43,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd

#[allow(dead_code)]
 #[rustfmt::skip]
-#[proc_macros::identity]
+#[proc_macros::identity]
 #[derive(Default)]
 /// This is a doc comment
 // This is a normal comment
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
index a6e6b16bead5..0b32cedca5d8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
@@ -41,25 +41,25 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate foo;
-use core::iter;
+
extern crate foo;
+use core::iter;
 
 pub const NINETY_TWO: u8 = 92;
 
-use foo as foooo;
+use foo as foooo;
 
-pub(crate) fn main() {
+pub(crate) fn main() {
     let baz = iter::repeat(92);
 }
 
 mod bar {
-    pub(in super) const FORTY_TWO: u8 = 42;
+    pub(in super) const FORTY_TWO: u8 = 42;
 
     mod baz {
-        use super::super::NINETY_TWO;
-        use crate::foooo::Point;
+        use super::super::NINETY_TWO;
+        use crate::foooo::Point;
 
-        pub(in super::super) const TWENTY_NINE: u8 = 29;
+        pub(in super::super) const TWENTY_NINE: u8 = 29;
     }
 }
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 2f4a2004f1de..29f78959a54f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -41,7 +41,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
use core::iter;
+
use core::iter;
 
 fn main() {
     let foo = Some(92);
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
index 41d3dff8ed9e..5287affbfc5c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
@@ -42,8 +42,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
#![deprecated]
-use crate as _;
-extern crate bar;
+use crate as _;
+extern crate bar;
 #[deprecated]
 macro_rules! macro_ {
     () => {};
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index b5c3df6ee447..ce9ec7431a97 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -48,9 +48,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 //! Syntactic name ref highlighting testing
 //! ```rust
-//! extern crate self;
-//! extern crate other as otter;
-//! extern crate core;
+//! extern crate self;
+//! extern crate other as otter;
+//! extern crate core;
 //! trait T { type Assoc; }
 //! fn f<Arg>() -> use<Arg> where (): T<Assoc = ()> {}
 //! ```
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 3a4518236883..8f7cbddd7ffb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self as this;
-extern crate std;
-extern crate alloc as abc;
-extern crate unresolved as definitely_unresolved;
+
extern crate self as this;
+extern crate std;
+extern crate alloc as abc;
+extern crate unresolved as definitely_unresolved;
 extern crate unresolved as _;
-extern crate test as opt_in_crate;
-extern crate test as _;
-extern crate proc_macro;
+extern crate test as opt_in_crate;
+extern crate test as _;
+extern crate proc_macro;
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index fd652f444ffd..c6dbc435c0e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -72,7 +72,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } } -use self::FooCopy::{self as BarCopy}; +use self::FooCopy::{self as BarCopy}; #[derive(Copy)] struct FooCopy { @@ -110,7 +110,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd FOO } -use core::ops::Fn; +use core::ops::Fn; fn baz<F: Fn() -> ()>(f: F) { f() } @@ -184,15 +184,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } fn use_foo_items() { - let bob = foo::Person { + let bob = foo::Person { name: "Bob", - age: foo::consts::NUMBER, + age: foo::consts::NUMBER, }; - let control_flow = foo::identity(foo::ControlFlow::Continue); + let control_flow = foo::identity(foo::ControlFlow::Continue); if control_flow.should_die() { - foo::die!(); + foo::die!(); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html index 5a5d9bd1f909..391a46f706c8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html @@ -47,7 +47,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fixture(r#" @@- /main.rs crate:main deps:other_crate fn test() { - let x = other_crate::foo::S::thing(); + let x = other_crate::foo::S::thing(); x; } //^ i128 diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html index b28818e679ff..fccf34083d7f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html @@ -45,5 +45,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd template!(template); } -#[proc_macros::issue_18089] +#[proc_macros::issue_18089] fn template() {}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html index d2a53b2ff9e1..6366cba1bd03 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html @@ -41,12 +41,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
index d309b4723238..a89e8190832e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
index d309b4723238..a89e8190832e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
index 575c9a6b0aca..aa1500b8f85b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
index caf66ace7a68..484afd81ead2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
@@ -41,6 +41,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
lib2015::void_2015!(try async await gen);
-lib2024::void_2024!(try async await gen);
+
lib2015::void_2015!(try async await gen);
+lib2024::void_2024!(try async await gen);
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index b63d5cedc825..59612634fda3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -41,7 +41,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
use proc_macros::{mirror, identity, DeriveIdentity};
+
use proc_macros::{mirror, identity, DeriveIdentity};
 
 mirror! {
     {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index e178782c79c4..4e3822c3d31f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -165,7 +165,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     toho!("{}fmt", 0);
     let i: u64 = 3;
     let o: u64;
-    core::arch::asm!(
+    core::arch::asm!(
         "mov {0}, {1}",
         "add {0}, 5",
         out(reg) o,
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 93513f5b575d..008987d409ad 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -91,7 +91,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
         // unsafe fn and method calls
         unsafe_fn();
-        self::unsafe_fn();
+        self::unsafe_fn();
         (unsafe_fn as unsafe fn())();
         Struct { field: 0 }.unsafe_method();
 
@@ -120,7 +120,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         &EXTERN_STATIC;
         &raw const EXTERN_STATIC;
 
-        core::arch::asm!(
+        core::arch::asm!(
             "push {base}",
             base = const 0
         );
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 6f0f57725fc7..e5b983dcbf85 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -70,6 +70,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         | SymbolKind::Attribute
         | SymbolKind::Derive
         | SymbolKind::DeriveHelper => lsp_types::SymbolKind::FUNCTION,
+        SymbolKind::CrateRoot => lsp_types::SymbolKind::PACKAGE,
         SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE,
         SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => {
             lsp_types::SymbolKind::TYPE_PARAMETER
@@ -141,6 +142,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::Method => lsp_types::CompletionItemKind::METHOD,
             SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT,
             SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
+            SymbolKind::CrateRoot => lsp_types::CompletionItemKind::MODULE,
             SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION,
             SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION,
             SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM,
@@ -803,11 +805,16 @@ fn semantic_token_type_and_modifiers(
 ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {
     use semantic_tokens::{modifiers as mods, types};
 
+    let mut mods = semantic_tokens::ModifierSet::default();
     let ty = match highlight.tag {
         HlTag::Symbol(symbol) => match symbol {
             SymbolKind::Attribute => types::DECORATOR,
             SymbolKind::Derive => types::DERIVE,
             SymbolKind::DeriveHelper => types::DERIVE_HELPER,
+            SymbolKind::CrateRoot => {
+                mods |= mods::CRATE_ROOT;
+                types::NAMESPACE
+            }
             SymbolKind::Module => types::NAMESPACE,
             SymbolKind::Impl => types::TYPE_ALIAS,
             SymbolKind::Field => types::PROPERTY,
@@ -870,7 +877,6 @@ fn semantic_token_type_and_modifiers(
         },
     };
 
-    let mut mods = semantic_tokens::ModifierSet::default();
     for modifier in highlight.mods.iter() {
         let modifier = match modifier {
             HlMod::Associated => mods::ASSOCIATED,

From 5f6d3852ee494e167eaa8128b1d5f81a0f03234d Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Mon, 12 Jan 2026 08:46:20 +0100
Subject: [PATCH 089/583] Fix crate root search in world symbols duplicating
 root entries

---
 .../rust-analyzer/crates/hir/src/symbols.rs   |  15 +--
 .../crates/ide-db/src/items_locator.rs        |   4 +-
 .../crates/ide-db/src/symbol_index.rs         | 102 +++++++++---------
 .../ide-db/src/test_data/test_doc_alias.txt   |  28 -----
 .../test_symbol_index_collection.txt          |  28 -----
 .../ide/src/syntax_highlighting/highlight.rs  |   9 +-
 .../rust-analyzer/tests/slow-tests/main.rs    |   4 +-
 7 files changed, 67 insertions(+), 123 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index f9002f31fd15..4461659f5c4e 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -23,7 +23,7 @@ use intern::Symbol;
 use rustc_hash::FxHashMap;
 use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::HasName};
 
-use crate::{HasCrate, Module, ModuleDef, Semantics};
+use crate::{Crate, HasCrate, Module, ModuleDef, Semantics};
 
 /// The actual data that is stored in the index. It should be as compact as
 /// possible.
@@ -100,11 +100,6 @@ impl<'a> SymbolCollector<'a> {
         let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered();
         tracing::info!(?module, "SymbolCollector::collect");
 
-        // If this is a crate root module, add a symbol for the crate itself
-        if module.is_crate_root(self.db) {
-            self.push_crate_root(module);
-        }
-
         // The initial work is the root module we're collecting, additional work will
         // be populated as we traverse the module's definitions.
         self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None });
@@ -116,8 +111,7 @@ impl<'a> SymbolCollector<'a> {
 
     /// Push a symbol for a crate's root module.
     /// This allows crate roots to appear in the symbol index for queries like `::` or `::foo`.
-    fn push_crate_root(&mut self, module: Module) {
-        let krate = module.krate(self.db);
+    pub fn push_crate_root(&mut self, krate: Crate) {
         let Some(display_name) = krate.display_name(self.db) else { return };
         let crate_name = display_name.crate_name();
         let canonical_name = display_name.canonical_name();
@@ -131,10 +125,11 @@ impl<'a> SymbolCollector<'a> {
         let ptr = SyntaxNodePtr::new(&syntax_node);
 
         let loc = DeclarationLocation { hir_file_id, ptr, name_ptr: None };
+        let root_module = krate.root_module(self.db);
 
         self.symbols.insert(FileSymbol {
             name: crate_name.symbol().clone(),
-            def: ModuleDef::Module(module),
+            def: ModuleDef::Module(root_module),
             loc,
             container_name: None,
             is_alias: false,
@@ -147,7 +142,7 @@ impl<'a> SymbolCollector<'a> {
         if canonical_name != crate_name.symbol() {
             self.symbols.insert(FileSymbol {
                 name: canonical_name.clone(),
-                def: ModuleDef::Module(module),
+                def: ModuleDef::Module(root_module),
                 loc,
                 container_name: None,
                 is_alias: false,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
index 0d305530d925..af0c69c6856d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
@@ -110,7 +110,7 @@ pub fn items_with_name_in_module(
             local_query
         }
     };
-    local_query.search(&[SymbolIndex::module_symbols(db, module)], |local_candidate| {
+    local_query.search(db, &[SymbolIndex::module_symbols(db, module)], |local_candidate| {
         cb(match local_candidate.def {
             hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
             def => ItemInNs::from(def),
@@ -140,7 +140,7 @@ fn find_items(
 
     // Query the local crate using the symbol index.
     let mut local_results = Vec::new();
-    local_query.search(&symbol_index::crate_symbols(db, krate), |local_candidate| {
+    local_query.search(db, &symbol_index::crate_symbols(db, krate), |local_candidate| {
         let def = match local_candidate.def {
             hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
             def => ItemInNs::from(def),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index 05c3f360fa87..c95b541748ec 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -218,15 +218,18 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_
 // | Editor  | Shortcut |
 // |---------|-----------|
 // | VS Code | Ctrl+T
-pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> {
+pub fn world_symbols(db: &RootDatabase, mut query: Query) -> Vec> {
     let _p = tracing::info_span!("world_symbols", query = ?query.query).entered();
 
-    if query.is_crate_search() {
-        return search_crates(db, &query);
-    }
-
-    // If we have a path filter, resolve it to target modules
-    let indices: Vec<_> = if !query.path_filter.is_empty() {
+    // Search for crates by name (handles "::" and "::foo" queries)
+    let indices: Vec<_> = if query.is_crate_search() {
+        query.only_types = false;
+        query.libs = true;
+        vec![SymbolIndex::extern_prelude_symbols(db)]
+        // If we have a path filter, resolve it to target modules
+    } else if !query.path_filter.is_empty() {
+        query.only_types = false;
+        query.libs = true;
         let target_modules = resolve_path_to_modules(
             db,
             &query.path_filter,
@@ -258,13 +261,17 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> {
         crates
             .par_iter()
             .for_each_with(db.clone(), |snap, &krate| _ = crate_symbols(snap, krate.into()));
-        crates.into_iter().flat_map(|krate| Vec::from(crate_symbols(db, krate.into()))).collect()
+        crates
+            .into_iter()
+            .flat_map(|krate| Vec::from(crate_symbols(db, krate.into())))
+            .chain(std::iter::once(SymbolIndex::extern_prelude_symbols(db)))
+            .collect()
     };
 
     let mut res = vec![];
 
     // Normal search: use FST to match item name
-    query.search::<()>(&indices, |f| {
+    query.search::<()>(db, &indices, |f| {
         res.push(f.clone());
         ControlFlow::Continue(())
     });
@@ -272,39 +279,6 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> {
     res
 }
 
-/// Search for crates by name (handles "::" and "::foo" queries)
-fn search_crates<'db>(db: &'db RootDatabase, query: &Query) -> Vec> {
-    let mut res = vec![];
-
-    for krate in Crate::all(db) {
-        let Some(display_name) = krate.display_name(db) else { continue };
-        let crate_name = display_name.crate_name().as_str();
-
-        // If query is empty (sole "::"), return all crates
-        // Otherwise, fuzzy match the crate name
-        let matches = if query.query.is_empty() {
-            true
-        } else {
-            query.mode.check(&query.query, query.case_sensitive, crate_name)
-        };
-
-        if matches {
-            // Get the crate root module's symbol index and find the root module symbol
-            let root_module = krate.root_module(db);
-            let index = SymbolIndex::module_symbols(db, root_module);
-            // Find the module symbol itself (representing the crate)
-            for symbol in index.symbols.iter() {
-                if matches!(symbol.def, hir::ModuleDef::Module(m) if m == root_module) {
-                    res.push(symbol.clone());
-                    break;
-                }
-            }
-        }
-    }
-
-    res
-}
-
 /// Resolve a path filter to the target module(s) it points to.
 /// Returns the modules whose symbol indices should be searched.
 ///
@@ -452,6 +426,33 @@ impl<'db> SymbolIndex<'db> {
 
         module_symbols(db, InternedModuleId::new(db, hir::ModuleId::from(module)))
     }
+
+    /// The symbol index for all extern prelude crates.
+    pub fn extern_prelude_symbols(db: &dyn HirDatabase) -> &SymbolIndex<'_> {
+        #[salsa::tracked(returns(ref))]
+        fn extern_prelude_symbols<'db>(db: &'db dyn HirDatabase) -> SymbolIndex<'db> {
+            let _p = tracing::info_span!("extern_prelude_symbols").entered();
+
+            // We call this without attaching because this runs in parallel, so we need to attach here.
+            hir::attach_db(db, || {
+                let mut collector = SymbolCollector::new(db, false);
+
+                for krate in Crate::all(db) {
+                    if krate
+                        .display_name(db)
+                        .is_none_or(|name| name.canonical_name().as_str() == "build-script-build")
+                    {
+                        continue;
+                    }
+                    collector.push_crate_root(krate);
+                }
+
+                SymbolIndex::new(collector.finish())
+            })
+        }
+
+        extern_prelude_symbols(db)
+    }
 }
 
 impl fmt::Debug for SymbolIndex<'_> {
@@ -555,6 +556,7 @@ impl Query {
     /// Search symbols in the given indices.
     pub(crate) fn search<'db, T>(
         &self,
+        db: &'db RootDatabase,
         indices: &[&'db SymbolIndex<'db>],
         cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow,
     ) -> Option {
@@ -568,7 +570,7 @@ impl Query {
                 for index in indices.iter() {
                     op = op.add(index.map.search(&automaton));
                 }
-                self.search_maps(indices, op.union(), cb)
+                self.search_maps(db, indices, op.union(), cb)
             }
             SearchMode::Fuzzy => {
                 let automaton = fst::automaton::Subsequence::new(&self.lowercased);
@@ -576,7 +578,7 @@ impl Query {
                 for index in indices.iter() {
                     op = op.add(index.map.search(&automaton));
                 }
-                self.search_maps(indices, op.union(), cb)
+                self.search_maps(db, indices, op.union(), cb)
             }
             SearchMode::Prefix => {
                 let automaton = fst::automaton::Str::new(&self.lowercased).starts_with();
@@ -584,13 +586,14 @@ impl Query {
                 for index in indices.iter() {
                     op = op.add(index.map.search(&automaton));
                 }
-                self.search_maps(indices, op.union(), cb)
+                self.search_maps(db, indices, op.union(), cb)
             }
         }
     }
 
     fn search_maps<'db, T>(
         &self,
+        db: &'db RootDatabase,
         indices: &[&'db SymbolIndex<'db>],
         mut stream: fst::map::Union<'_>,
         mut cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow,
@@ -598,18 +601,21 @@ impl Query {
         let ignore_underscore_prefixed = !self.query.starts_with("__");
         while let Some((_, indexed_values)) = stream.next() {
             for &IndexedValue { index, value } in indexed_values {
-                let symbol_index = &indices[index];
+                let symbol_index = indices[index];
                 let (start, end) = SymbolIndex::map_value_to_range(value);
 
                 for symbol in &symbol_index.symbols[start..end] {
                     let non_type_for_type_only_query = self.only_types
-                        && !matches!(
+                        && !(matches!(
                             symbol.def,
                             hir::ModuleDef::Adt(..)
                                 | hir::ModuleDef::TypeAlias(..)
                                 | hir::ModuleDef::BuiltinType(..)
                                 | hir::ModuleDef::Trait(..)
-                        );
+                        ) || matches!(
+                            symbol.def,
+                            hir::ModuleDef::Module(module) if module.is_crate_root(db)
+                        ));
                     if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) {
                         continue;
                     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
index 71680699b739..0c28c312f83b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -154,34 +154,6 @@
                 do_not_complete: Yes,
                 _marker: PhantomData<&()>,
             },
-            FileSymbol {
-                name: "ra_test_fixture",
-                def: Module(
-                    Module {
-                        id: ModuleIdLt {
-                            [salsa id]: Id(3800),
-                        },
-                    },
-                ),
-                loc: DeclarationLocation {
-                    hir_file_id: FileId(
-                        EditionedFileId(
-                            Id(3000),
-                        ),
-                    ),
-                    ptr: SyntaxNodePtr {
-                        kind: SOURCE_FILE,
-                        range: 0..128,
-                    },
-                    name_ptr: None,
-                },
-                container_name: None,
-                is_alias: false,
-                is_assoc: false,
-                is_import: false,
-                do_not_complete: Yes,
-                _marker: PhantomData<&()>,
-            },
             FileSymbol {
                 name: "s1",
                 def: Adt(
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 2d62a56fe22d..4b588572d328 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -919,34 +919,6 @@
                 do_not_complete: Yes,
                 _marker: PhantomData<&()>,
             },
-            FileSymbol {
-                name: "ra_test_fixture",
-                def: Module(
-                    Module {
-                        id: ModuleIdLt {
-                            [salsa id]: Id(3800),
-                        },
-                    },
-                ),
-                loc: DeclarationLocation {
-                    hir_file_id: FileId(
-                        EditionedFileId(
-                            Id(3000),
-                        ),
-                    ),
-                    ptr: SyntaxNodePtr {
-                        kind: SOURCE_FILE,
-                        range: 0..793,
-                    },
-                    name_ptr: None,
-                },
-                container_name: None,
-                is_alias: false,
-                is_assoc: false,
-                is_import: false,
-                do_not_complete: Yes,
-                _marker: PhantomData<&()>,
-            },
             FileSymbol {
                 name: "really_define_struct",
                 def: Macro(
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 3795d3d4146d..dcc9a8c0d5f7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -494,10 +494,9 @@ pub(super) fn highlight_def(
             (Highlight::new(HlTag::Symbol(SymbolKind::Field)), Some(field.attrs(sema.db)))
         }
         Definition::TupleField(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Field)), None),
-        Definition::Crate(krate) => (
-            Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)).into(),
-            Some(krate.attrs(sema.db)),
-        ),
+        Definition::Crate(krate) => {
+            (Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)), Some(krate.attrs(sema.db)))
+        }
         Definition::Module(module) => {
             let h = Highlight::new(HlTag::Symbol(if module.is_crate_root(db) {
                 SymbolKind::CrateRoot
@@ -661,7 +660,7 @@ pub(super) fn highlight_def(
             (h, None)
         }
         Definition::ExternCrateDecl(extern_crate) => {
-            let mut highlight = Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)).into();
+            let mut highlight = Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot));
             if extern_crate.alias(db).is_none() {
                 highlight |= HlMod::Library;
             }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 9f3c6742d651..b4a7b44d165a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -1452,7 +1452,7 @@ foo = { path = "../foo" }
         json!([
         {
           "name": "bar",
-          "kind": 2,
+          "kind": 4,
           "location": {
             "uri": "file:///[..]bar/src/lib.rs",
             "range": {
@@ -1511,7 +1511,7 @@ version = "0.0.0"
         json!([
         {
           "name": "baz",
-          "kind": 2,
+          "kind": 4,
           "location": {
             "uri": "file:///[..]baz/src/lib.rs",
             "range": {

From 55f6901e967dda86ac15cf836777f1b32855feb7 Mon Sep 17 00:00:00 2001
From: Wilfred Hughes 
Date: Mon, 29 Dec 2025 03:23:48 -0800
Subject: [PATCH 090/583] Fix lowering with supertrait predicates

Previously both valid and invalid Rust code could crash r-a due to a
cyclic query during lowering.
---
 .../rust-analyzer/crates/hir-ty/src/lower.rs  | 38 +++++++++++----
 .../crates/hir-ty/src/tests/regression.rs     | 48 +++++++++++++++++++
 2 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 9befca11b3e5..5789bf02a42e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -53,7 +53,7 @@ use tracing::debug;
 use triomphe::{Arc, ThinArc};
 
 use crate::{
-    FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+    FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, all_super_traits,
     consteval::intern_const_ref,
     db::{HirDatabase, InternedOpaqueTyId},
     generics::{Generics, generics, trait_self_param_idx},
@@ -1624,11 +1624,16 @@ pub(crate) fn field_types_with_diagnostics_query<'db>(
     (res, create_diagnostics(ctx.diagnostics))
 }
 
+/// Predicates for `param_id` of the form `P: SomeTrait`. If
+/// `assoc_name` is provided, only return predicates referencing traits
+/// that have an associated type of that name.
+///
 /// This query exists only to be used when resolving short-hand associated types
 /// like `T::Item`.
 ///
 /// See the analogous query in rustc and its comment:
 /// 
+///
 /// This is a query mostly to handle cycles somewhat gracefully; e.g. the
 /// following bounds are disallowed: `T: Foo, U: Foo`, but
 /// these are fine: `T: Foo, U: Foo<()>`.
@@ -1652,7 +1657,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
     );
 
     // we have to filter out all other predicates *first*, before attempting to lower them
-    let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred {
+    let has_relevant_bound = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred {
         WherePredicate::ForLifetime { target, bound, .. }
         | WherePredicate::TypeBound { target, bound, .. } => {
             let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) };
@@ -1700,11 +1705,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
                         return false;
                     };
 
-                    rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| {
-                        tr.0.trait_items(db).items.iter().any(|(name, item)| {
-                            matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name
-                        })
-                    })
+                    trait_or_supertrait_has_assoc_type(db, tr, assoc_name)
                 }
                 TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false,
             }
@@ -1717,7 +1718,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
     {
         ctx.store = maybe_parent_generics.store();
         for pred in maybe_parent_generics.where_predicates() {
-            if predicate(pred, &mut ctx) {
+            if has_relevant_bound(pred, &mut ctx) {
                 predicates.extend(
                     ctx.lower_where_predicate(
                         pred,
@@ -1757,6 +1758,27 @@ pub(crate) fn generic_predicates_for_param_cycle_result(
     StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store())
 }
 
+/// Check if this trait or any of its supertraits define an associated
+/// type with the given name.
+fn trait_or_supertrait_has_assoc_type(
+    db: &dyn HirDatabase,
+    tr: TraitId,
+    assoc_name: &Name,
+) -> bool {
+    for trait_id in all_super_traits(db, tr) {
+        if trait_id
+            .trait_items(db)
+            .items
+            .iter()
+            .any(|(name, item)| matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name)
+        {
+            return true;
+        }
+    }
+
+    false
+}
+
 #[inline]
 pub(crate) fn type_alias_bounds<'db>(
     db: &'db dyn HirDatabase,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index df49d7999fee..a04c46f8eabd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -2598,3 +2598,51 @@ trait ColumnLike {
     "#,
     );
 }
+
+#[test]
+fn issue_21006_generic_predicates_for_param_supertrait_cycle() {
+    check_no_mismatches(
+        r#"
+trait VCipherSuite {}
+
+trait CipherSuite
+where
+    OprfHash: Hash,
+{
+}
+
+type Bar = ::Hash;
+
+type OprfHash = ::Hash;
+
+impl Foo {
+    fn seal() {}
+}
+        "#,
+    );
+}
+
+#[test]
+fn issue_21006_self_assoc_trait() {
+    check_types(
+        r#"
+trait Baz {
+    fn baz(&self);
+}
+
+trait Foo {
+    type Assoc;
+}
+
+trait Bar: Foo
+where
+    Self::Assoc: Baz,
+{
+    fn bar(v: Self::Assoc) {
+        let _ = v.baz();
+        //  ^ ()
+    }
+}
+        "#,
+    );
+}

From 01ebc285e1300de9f43a02b80a5bc408dab7bb3c Mon Sep 17 00:00:00 2001
From: Alex Butler 
Date: Mon, 12 Jan 2026 12:28:17 +0000
Subject: [PATCH 091/583] smol_str: update changelog 0.3.5

---
 src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md b/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md
index b7da6d18a440..4aa25fa13446 100644
--- a/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md
+++ b/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md
@@ -1,6 +1,6 @@
 # Changelog
 
-## Unreleased
+## 0.3.5 - 2026-01-08
 - Optimise `SmolStr::clone` 4-5x speedup inline, 0.5x heap (slow down).
 
 ## 0.3.4 - 2025-10-23

From c20e6a12269678cf4012b5962ccfb73db18c940f Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Mon, 12 Jan 2026 20:26:24 +0800
Subject: [PATCH 092/583] Fix not complete `mut` and `raw` in `&x.foo()`

Example
---
```rust
fn main() {
    let _ = &$0x.foo();
}
```

**Before this PR**

```rust
...
kw loop
kw match
kw return
kw self::
...
```

**After this PR**

```rust
...
kw loop
kw match
kw mut
kw raw
kw return
kw self::
...
```
---
 .../ide-completion/src/context/analysis.rs    | 10 ++++----
 .../ide-completion/src/tests/expression.rs    | 25 ++++++++++++++++++-
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 65bae5b66e17..0db93b0837cd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -1305,14 +1305,14 @@ fn classify_name_ref<'db>(
 
     let make_path_kind_expr = |expr: ast::Expr| {
         let it = expr.syntax();
+        let prev_token = iter::successors(it.first_token(), |it| it.prev_token())
+            .skip(1)
+            .find(|it| !it.kind().is_trivia());
         let in_block_expr = is_in_block(it);
         let (in_loop_body, innermost_breakable) = is_in_breakable(it).unzip();
         let after_if_expr = is_after_if_expr(it.clone());
-        let ref_expr_parent =
-            path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
-        let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev)
-            .map(|it| it.kind() == SyntaxKind::AMP)
-            .unwrap_or(false);
+        let after_amp = prev_token.as_ref().is_some_and(|it| it.kind() == SyntaxKind::AMP);
+        let ref_expr_parent = prev_token.and_then(|it| it.parent()).and_then(ast::RefExpr::cast);
         let (innermost_ret_ty, self_param) = {
             let find_ret_ty = |it: SyntaxNode| {
                 if let Some(item) = ast::Item::cast(it.clone()) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index 78f003dd210b..ff005a29218b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -706,7 +706,30 @@ fn completes_after_ref_expr() {
             kw while
             kw while let
         "#]],
-    )
+    );
+    check(
+        r#"fn main() { let _ = &$0x.foo() }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw const
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw mut
+            kw raw
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    );
 }
 
 #[test]

From 62e777ca76c62d34993dbf576957a6a9e224f034 Mon Sep 17 00:00:00 2001
From: Roberto Aloi 
Date: Mon, 12 Jan 2026 14:11:12 +0100
Subject: [PATCH 093/583] Fix overlapping cfg attributes for
 wasm32-unknown-emscripten target

---
 src/tools/rust-analyzer/crates/stdx/src/process.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/stdx/src/process.rs b/src/tools/rust-analyzer/crates/stdx/src/process.rs
index 2efeed45e44e..7c4ae978b04a 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/process.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/process.rs
@@ -76,7 +76,7 @@ pub fn spawn_with_streaming_output(
     Ok(Output { status, stdout, stderr })
 }
 
-#[cfg(unix)]
+#[cfg(all(unix, not(target_arch = "wasm32")))]
 mod imp {
     use std::{
         io::{self, prelude::*},

From 9a5aa90516602b288de34dd65161d90f2db202a6 Mon Sep 17 00:00:00 2001
From: Eric Huss 
Date: Mon, 12 Jan 2026 09:34:11 -0800
Subject: [PATCH 094/583] Add some clarifications and fixes for fmt syntax

This tries to clarify a few things regarding fmt syntax:

- The comment on `Parser::word` seems to be wrong, as that
  underscore-prefixed words are just fine. This was changed in
  https://github.com/rust-lang/rust/pull/66847.
- I struggled to follow the description of the width argument. It
  referred to a "second argument", but I don't know what second argument
  it is referring to (which is the first?). Either way, I rewrote the
  paragraph to try to be a little more explicit, and to use shorter
  sentences.
- The description of the precision argument wasn't really clear about
  the distinction of an Nth argument and a named argument. I added
  a sentence to try to emphasize the difference.
- `IDENTIFIER_OR_KEYWORD` was changed recently in
  https://github.com/rust-lang/reference/pull/2049 to include bare `_`.
  But fmt named arguments are not allowed to be a bare `_`.
---
 compiler/rustc_parse_format/src/lib.rs |  2 +-
 library/alloc/src/fmt.rs               | 15 ++++++++++-----
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 86326fc6536c..a687a45480dc 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -753,7 +753,7 @@ impl<'input> Parser<'input> {
     }
 
     /// Parses a word starting at the current position. A word is the same as a
-    /// Rust identifier, except that it can't start with `_` character.
+    /// Rust identifier or keyword, except that it can't be a bare `_` character.
     fn word(&mut self) -> &'input str {
         let index = self.input_vec_index;
         match self.peek() {
diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs
index 3d7c580be8c9..e3ff2ba51aba 100644
--- a/library/alloc/src/fmt.rs
+++ b/library/alloc/src/fmt.rs
@@ -136,9 +136,10 @@
 //! padding specified by fill/alignment will be used to take up the required
 //! space (see below).
 //!
-//! The value for the width can also be provided as a [`usize`] in the list of
-//! parameters by adding a postfix `$`, indicating that the second argument is
-//! a [`usize`] specifying the width.
+//! The width can also be provided dynamically by referencing another argument
+//! with a `$` suffix. Use `{:N$}` to reference the Nth positional argument
+//! (where N is an integer), or `{:name$}` to reference a named argument. The
+//! referenced argument must be of type [`usize`].
 //!
 //! Referring to an argument with the dollar syntax does not affect the "next
 //! argument" counter, so it's usually a good idea to refer to arguments by
@@ -236,7 +237,8 @@
 //!
 //! 2. An integer or name followed by dollar sign `.N$`:
 //!
-//!    use format *argument* `N` (which must be a `usize`) as the precision.
+//!    use the value of format *argument* `N` (which must be a `usize`) as the precision.
+//!    An integer refers to a positional argument, and a name refers to a named argument.
 //!
 //! 3. An asterisk `.*`:
 //!
@@ -363,7 +365,10 @@
 //! - `ws` is any character for which [`char::is_whitespace`] returns `true`, has no semantic
 //!   meaning and is completely optional,
 //! - `integer` is a decimal integer that may contain leading zeroes and must fit into an `usize` and
-//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as defined by the [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html).
+//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as
+//!   defined by the [Rust language
+//!   reference](https://doc.rust-lang.org/reference/identifiers.html), except
+//!   for a bare `_`.
 //!
 //! # Formatting traits
 //!

From 3777ebc6ca156092e47318a20c9c2ae40d9813e2 Mon Sep 17 00:00:00 2001
From: Moulins 
Date: Thu, 18 Dec 2025 19:23:37 +0100
Subject: [PATCH 095/583] Don't expose redundant information in
 `rustc_public`'s `LayoutShape`

Enum variant layouts don't need to store a full `LayoutShape`; just storing
the fields offsets is enough and all other information can be inferred from
the parent layout:
- size, align and ABI don't make much sense for individual variants and
  should generally be taken from the parent layout instead;
- variants always have `fields: FieldsShape::Arbitrary { .. }` and
  `variant: VariantShape::Single { .. }`.
---
 compiler/rustc_public/src/abi.rs              | 19 ++++++++++++++++++-
 .../src/unstable/convert/stable/abi.rs        | 12 ++++++++++--
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs
index aced39059f6b..413bc835e50a 100644
--- a/compiler/rustc_public/src/abi.rs
+++ b/compiler/rustc_public/src/abi.rs
@@ -188,10 +188,27 @@ pub enum VariantsShape {
         tag: Scalar,
         tag_encoding: TagEncoding,
         tag_field: usize,
-        variants: Vec,
+        variants: Vec,
     },
 }
 
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
+pub struct VariantFields {
+    /// Offsets for the first byte of each field,
+    /// ordered to match the source definition order.
+    /// I.e.: It follows the same order as [super::ty::VariantDef::fields()].
+    /// This vector does not go in increasing order.
+    pub offsets: Vec,
+}
+
+impl VariantFields {
+    pub fn fields_by_offset_order(&self) -> Vec {
+        let mut indices = (0..self.offsets.len()).collect::>();
+        indices.sort_by_key(|idx| self.offsets[*idx]);
+        indices
+    }
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
 pub enum TagEncoding {
     /// The tag directly stores the discriminant, but possibly with a smaller layout
diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs
index 03328d084ee9..f6b750f75aea 100644
--- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs
+++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs
@@ -11,7 +11,7 @@ use rustc_target::callconv;
 use crate::abi::{
     AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength,
     IntegerType, Layout, LayoutShape, PassMode, Primitive, ReprFlags, ReprOptions, Scalar,
-    TagEncoding, TyAndLayout, ValueAbi, VariantsShape, WrappingRange,
+    TagEncoding, TyAndLayout, ValueAbi, VariantFields, VariantsShape, WrappingRange,
 };
 use crate::compiler_interface::BridgeTys;
 use crate::target::MachineSize as Size;
@@ -212,7 +212,15 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Variants VariantFields {
+                                offsets: offsets.iter().as_slice().stable(tables, cx),
+                            },
+                            _ => panic!("variant layout should be Arbitrary"),
+                        })
+                        .collect(),
                 }
             }
         }

From 2c32c0e8047eb5535f96c3b7e468cfe023568b31 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Tue, 13 Jan 2026 14:56:51 +0200
Subject: [PATCH 096/583] Make `naked_asm!()` always return `!`

As it should.
---
 .../rust-analyzer/crates/hir-ty/src/infer/expr.rs  |  8 ++++++--
 .../crates/hir-ty/src/tests/simple.rs              | 14 ++++++++++++++
 .../crates/test-utils/src/minicore.rs              |  4 ++++
 3 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 62339779a562..c57d41cc5f73 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -8,7 +8,7 @@ use hir_def::{
     expr_store::path::{GenericArgs as HirGenericArgs, Path},
     hir::{
         Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId,
-        LabelId, Literal, Pat, PatId, Statement, UnaryOp,
+        InlineAsmKind, LabelId, Literal, Pat, PatId, Statement, UnaryOp,
     },
     resolver::ValueNs,
 };
@@ -1037,7 +1037,11 @@ impl<'db> InferenceContext<'_, 'db> {
                     // FIXME: `sym` should report for things that are not functions or statics.
                     AsmOperand::Sym(_) => (),
                 });
-                if diverge { self.types.types.never } else { self.types.types.unit }
+                if diverge || asm.kind == InlineAsmKind::NakedAsm {
+                    self.types.types.never
+                } else {
+                    self.types.types.unit
+                }
             }
         };
         // use a new type variable if we got unknown here
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 6367521841ab..d02e455fc3dc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3983,3 +3983,17 @@ fn foo() {
         "#]],
     );
 }
+
+#[test]
+fn naked_asm_returns_never() {
+    check_no_mismatches(
+        r#"
+//- minicore: asm
+
+#[unsafe(naked)]
+extern "C" fn foo() -> ! {
+    core::arch::naked_asm!("");
+}
+    "#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index 01274a9835f4..580a619cf108 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -1880,6 +1880,10 @@ mod arch {
     pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) {
         /* compiler built-in */
     }
+    #[rustc_builtin_macro]
+    pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) {
+        /* compiler built-in */
+    }
 }
 // endregion:asm
 

From a2154802699c6de58702bcf4e3211ccb977cd3be Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Tue, 13 Jan 2026 19:00:03 +0800
Subject: [PATCH 097/583] Migrate `unwrap_block` assist to use SyntaxEditor

- Fix invalid match in let-stmt
- Fix multiple statements loses indent

Example
---
```rust
fn main() {
    let value = match rel_path {
        Ok(rel_path) => {$0
            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
            Some((*id, rel_path))
        }
        Err(_) => None,
    };
}
```

**Before this PR**

```rust
fn main() {
    let value = let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
    let value = Some((*id, rel_path));
}
```

**After this PR**

```rust
fn main() {
    let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
    let value = Some((*id, rel_path));
}
```

---

```rust
fn main() {
    let mut a = {$0
        1;
        2;
        3
    };
}
```

**Before this PR**

```rust
fn main() {
    1;
2;
    let mut a = 3;
}
```

**After this PR**

```rust
fn main() -> i32 {
    1;
    2;
    let mut a = 3;
}
```
---
 .../ide-assists/src/handlers/unwrap_block.rs  | 257 +++++++++---------
 1 file changed, 136 insertions(+), 121 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
index a83f6835ca61..e4f5e3523bd2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
@@ -1,10 +1,12 @@
 use syntax::{
-    AstNode, SyntaxKind, T, TextRange,
+    AstNode, SyntaxElement, SyntaxKind, SyntaxNode, T,
     ast::{
         self,
         edit::{AstNodeEdit, IndentLevel},
         make,
     },
+    match_ast,
+    syntax_editor::{Element, Position, SyntaxEditor},
 };
 
 use crate::{AssistContext, AssistId, Assists};
@@ -27,123 +29,108 @@ use crate::{AssistContext, AssistId, Assists};
 // }
 // ```
 pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let assist_id = AssistId::refactor_rewrite("unwrap_block");
-    let assist_label = "Unwrap block";
     let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
-    let mut block = ast::BlockExpr::cast(l_curly_token.parent_ancestors().nth(1)?)?;
+    let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?;
     let target = block.syntax().text_range();
-    let mut parent = block.syntax().parent()?;
-    if ast::MatchArm::can_cast(parent.kind()) {
-        parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
-    }
+    let mut container = block.syntax().clone();
+    let mut replacement = block.clone();
+    let mut prefer_container = None;
 
-    let kind = parent.kind();
-    if matches!(kind, SyntaxKind::STMT_LIST | SyntaxKind::EXPR_STMT) {
-        acc.add(assist_id, assist_label, target, |builder| {
-            builder.replace(block.syntax().text_range(), update_expr_string(block.to_string()));
-        })
-    } else if matches!(kind, SyntaxKind::LET_STMT) {
-        let parent = ast::LetStmt::cast(parent)?;
-        let pattern = ast::Pat::cast(parent.syntax().first_child()?)?;
-        let ty = parent.ty();
-        let list = block.stmt_list()?;
-        let replaced = match list.syntax().last_child() {
-            Some(last) => {
-                let stmts: Vec = list.statements().collect();
-                let initializer = ast::Expr::cast(last)?;
-                let let_stmt = make::let_stmt(pattern, ty, Some(initializer));
-                if !stmts.is_empty() {
-                    let block = make::block_expr(stmts, None);
-                    format!("{}\n    {}", update_expr_string(block.to_string()), let_stmt)
-                } else {
-                    let_stmt.to_string()
-                }
-            }
-            None => {
-                let empty_tuple = make::ext::expr_unit();
-                make::let_stmt(pattern, ty, Some(empty_tuple)).to_string()
-            }
-        };
-        acc.add(assist_id, assist_label, target, |builder| {
-            builder.replace(parent.syntax().text_range(), replaced);
-        })
-    } else {
-        let parent = ast::Expr::cast(parent)?;
-        match parent.clone() {
-            ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
-            ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
-            ast::Expr::IfExpr(if_expr) => {
-                let then_branch = if_expr.then_branch()?;
-                if then_branch == block {
-                    if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
-                        // For `else if` blocks
-                        let ancestor_then_branch = ancestor.then_branch()?;
-
-                        return acc.add(assist_id, assist_label, target, |edit| {
-                            let range_to_del_else_if = TextRange::new(
-                                ancestor_then_branch.syntax().text_range().end(),
-                                l_curly_token.text_range().start(),
-                            );
-                            let range_to_del_rest = TextRange::new(
-                                then_branch.syntax().text_range().end(),
-                                if_expr.syntax().text_range().end(),
-                            );
-
-                            edit.delete(range_to_del_rest);
-                            edit.delete(range_to_del_else_if);
-                            edit.replace(
-                                target,
-                                update_expr_string_without_newline(then_branch.to_string()),
-                            );
-                        });
-                    }
-                } else {
-                    return acc.add(assist_id, assist_label, target, |edit| {
-                        let range_to_del = TextRange::new(
-                            then_branch.syntax().text_range().end(),
-                            l_curly_token.text_range().start(),
-                        );
-
-                        edit.delete(range_to_del);
-                        edit.replace(target, update_expr_string_without_newline(block.to_string()));
+    let from_indent = block.indent_level();
+    let into_indent = loop {
+        let parent = container.parent()?;
+        container = match_ast! {
+            match parent {
+                ast::ForExpr(it) => it.syntax().clone(),
+                ast::LoopExpr(it) => it.syntax().clone(),
+                ast::WhileExpr(it) => it.syntax().clone(),
+                ast::MatchArm(it) => it.parent_match().syntax().clone(),
+                ast::LetStmt(it) => {
+                    replacement = wrap_let(&it, replacement);
+                    prefer_container = Some(it.syntax().clone());
+                    it.syntax().clone()
+                },
+                ast::IfExpr(it) => {
+                    prefer_container.get_or_insert_with(|| {
+                        if let Some(else_branch) = it.else_branch()
+                            && *else_branch.syntax() == container
+                        {
+                            else_branch.syntax().clone()
+                        } else {
+                            it.syntax().clone()
+                        }
                     });
-                }
+                    it.syntax().clone()
+                },
+                ast::ExprStmt(it) => it.syntax().clone(),
+                ast::StmtList(it) => break it.indent_level(),
+                _ => return None,
             }
-            _ => return None,
         };
+    };
+    let replacement = replacement.stmt_list()?;
 
-        acc.add(assist_id, assist_label, target, |builder| {
-            builder.replace(parent.syntax().text_range(), update_expr_string(block.to_string()));
-        })
-    }
+    acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| {
+        let mut edit = builder.make_editor(block.syntax());
+        let replacement = replacement.dedent(from_indent).indent(into_indent);
+        let container = prefer_container.unwrap_or(container);
+
+        edit.replace_with_many(&container, extract_statements(replacement));
+        delete_else_before(container, &mut edit);
+
+        builder.add_file_edits(ctx.vfs_file_id(), edit);
+    })
 }
 
-fn update_expr_string(expr_string: String) -> String {
-    update_expr_string_with_pat(expr_string, &[' ', '\n'])
+fn delete_else_before(container: SyntaxNode, edit: &mut SyntaxEditor) {
+    let Some(else_token) = container
+        .siblings_with_tokens(syntax::Direction::Prev)
+        .skip(1)
+        .map_while(|it| it.into_token())
+        .find(|it| it.kind() == T![else])
+    else {
+        return;
+    };
+    itertools::chain(else_token.prev_token(), else_token.next_token())
+        .filter(|it| it.kind() == SyntaxKind::WHITESPACE)
+        .for_each(|it| edit.delete(it));
+    let indent = IndentLevel::from_node(&container);
+    let newline = make::tokens::whitespace(&format!("\n{indent}"));
+    edit.replace(else_token, newline);
 }
 
-fn update_expr_string_without_newline(expr_string: String) -> String {
-    update_expr_string_with_pat(expr_string, &[' '])
+fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExpr {
+    let try_wrap_assign = || {
+        let initializer = assign.initializer()?.syntax().syntax_element();
+        let replacement = replacement.clone_subtree();
+        let assign = assign.clone_for_update();
+        let tail_expr = replacement.tail_expr()?;
+        let before =
+            assign.syntax().children_with_tokens().take_while(|it| *it != initializer).collect();
+        let after = assign
+            .syntax()
+            .children_with_tokens()
+            .skip_while(|it| *it != initializer)
+            .skip(1)
+            .collect();
+
+        let mut edit = SyntaxEditor::new(replacement.syntax().clone());
+        edit.insert_all(Position::before(tail_expr.syntax()), before);
+        edit.insert_all(Position::after(tail_expr.syntax()), after);
+        ast::BlockExpr::cast(edit.finish().new_root().clone())
+    };
+    try_wrap_assign().unwrap_or(replacement)
 }
 
-fn update_expr_string_with_pat(expr_str: String, whitespace_pat: &[char]) -> String {
-    // Remove leading whitespace, index to remove the leading '{',
-    // then continue to remove leading whitespace.
-    // We cannot assume the `{` is the first character because there are block modifiers
-    // (`unsafe`, `async` etc.).
-    let after_open_brace_index = expr_str.find('{').map_or(0, |it| it + 1);
-    let expr_str = expr_str[after_open_brace_index..].trim_start_matches(whitespace_pat);
-
-    // Remove trailing whitespace, index [..expr_str.len() - 1] to remove the trailing '}',
-    // then continue to remove trailing whitespace.
-    let expr_str = expr_str.trim_end_matches(whitespace_pat);
-    let expr_str = expr_str[..expr_str.len() - 1].trim_end_matches(whitespace_pat);
-
-    expr_str
-        .lines()
-        .map(|line| line.replacen("    ", "", 1)) // Delete indentation
-        .collect::>()
-        .join("\n")
+fn extract_statements(stmt_list: ast::StmtList) -> Vec {
+    let mut elements = stmt_list
+        .syntax()
+        .children_with_tokens()
+        .filter(|it| !matches!(it.kind(), T!['{'] | T!['}']))
+        .skip_while(|it| it.kind() == SyntaxKind::WHITESPACE)
+        .collect::>();
+    while elements.pop_if(|it| it.kind() == SyntaxKind::WHITESPACE).is_some() {}
+    elements
 }
 
 #[cfg(test)]
@@ -593,6 +580,30 @@ fn main() {
         );
     }
 
+    #[test]
+    fn unwrap_match_arm_in_let() {
+        check_assist(
+            unwrap_block,
+            r#"
+fn main() {
+    let value = match rel_path {
+        Ok(rel_path) => {$0
+            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+            Some((*id, rel_path))
+        }
+        Err(_) => None,
+    };
+}
+"#,
+            r#"
+fn main() {
+    let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+    let value = Some((*id, rel_path));
+}
+"#,
+        );
+    }
+
     #[test]
     fn simple_if_in_while_bad_cursor_position() {
         check_assist_not_applicable(
@@ -750,19 +761,6 @@ fn main() -> i32 {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    let x = {$0};
-}
-"#,
-            r#"
-fn main() {
-    let x = ();
-}
-"#,
-        );
-        check_assist(
-            unwrap_block,
-            r#"
 fn main() {
     let x = {$0
         bar
@@ -784,8 +782,7 @@ fn main() -> i32 {
 "#,
             r#"
 fn main() -> i32 {
-    1;
-    let _ = 2;
+    1; let _ = 2;
 }
 "#,
         );
@@ -795,11 +792,29 @@ fn main() -> i32 {
 fn main() -> i32 {
     let mut a = {$01; 2};
 }
+"#,
+            r#"
+fn main() -> i32 {
+    1; let mut a = 2;
+}
+"#,
+        );
+        check_assist(
+            unwrap_block,
+            r#"
+fn main() -> i32 {
+    let mut a = {$0
+        1;
+        2;
+        3
+    };
+}
 "#,
             r#"
 fn main() -> i32 {
     1;
-    let mut a = 2;
+    2;
+    let mut a = 3;
 }
 "#,
         );

From 8fb704c17510f22700d4a84fc7151f67d9d20a27 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Tue, 13 Jan 2026 19:57:37 +0100
Subject: [PATCH 098/583] fix: Hide renamed imports from macros in symbol index

---
 src/tools/rust-analyzer/crates/hir/src/symbols.rs | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 4461659f5c4e..c088f3aa0cc0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -5,8 +5,8 @@ use std::marker::PhantomData;
 use base_db::FxIndexSet;
 use either::Either;
 use hir_def::{
-    AdtId, AssocItemId, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId,
-    ModuleDefId, ModuleId, TraitId,
+    AdtId, AssocItemId, AstIdLoc, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId,
+    Lookup, MacroId, ModuleDefId, ModuleId, TraitId,
     db::DefDatabase,
     item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob},
     nameres::crate_def_map,
@@ -169,6 +169,7 @@ impl<'a> SymbolCollector<'a> {
 
     fn collect_from_module(&mut self, module_id: ModuleId) {
         let collect_pub_only = self.collect_pub_only;
+        let is_block_module = module_id.is_block_module(self.db);
         let push_decl = |this: &mut Self, def: ModuleDefId, name, vis| {
             if collect_pub_only && vis != Visibility::Public {
                 return;
@@ -240,6 +241,10 @@ impl<'a> SymbolCollector<'a> {
             let source = import_child_source_cache
                 .entry(i.use_)
                 .or_insert_with(|| i.use_.child_source(this.db));
+            if is_block_module && source.file_id.is_macro() {
+                // Macros tend to generate a lot of imports, the user really won't care about them
+                return;
+            }
             let Some(use_tree_src) = source.value.get(i.idx) else { return };
             let rename = use_tree_src.rename().and_then(|rename| rename.name());
             let name_syntax = match rename {
@@ -276,6 +281,12 @@ impl<'a> SymbolCollector<'a> {
                     return;
                 }
                 let loc = i.lookup(this.db);
+                if is_block_module && loc.ast_id().file_id.is_macro() {
+                    // Macros (especially derivves) tend to generate renamed extern crate items,
+                    // the user really won't care about them
+                    return;
+                }
+
                 let source = loc.source(this.db);
                 let rename = source.value.rename().and_then(|rename| rename.name());
 

From 8f953dea3aad273bc5e1450be7a15449ad07d7bb Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Tue, 13 Jan 2026 20:30:38 +0100
Subject: [PATCH 099/583] feat: Implement support for `feature(new_range)`

---
 .../crates/hir-def/src/lang_item.rs           |   5 +
 .../crates/hir-expand/src/mod_path.rs         |   4 +
 .../rust-analyzer/crates/hir-ty/src/infer.rs  |  28 ++++-
 .../crates/hir-ty/src/tests/patterns.rs       | 118 +++++++++---------
 .../crates/hir-ty/src/tests/simple.rs         |  41 ++++--
 .../crates/hir/src/source_analyzer.rs         |  80 ++++++++----
 .../crates/intern/src/symbol/symbols.rs       |   6 +
 .../crates/test-utils/src/minicore.rs         |  92 +++++++++-----
 8 files changed, 246 insertions(+), 128 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index 092ff6e48671..51dd55301f44 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -499,6 +499,11 @@ language_item_table! { LangItems =>
     RangeToInclusive,        sym::RangeToInclusive,    StructId;
     RangeTo,                 sym::RangeTo,             StructId;
 
+    RangeFromCopy,           sym::RangeFromCopy,           StructId;
+    RangeInclusiveCopy,      sym::RangeInclusiveCopy,      StructId;
+    RangeCopy,               sym::RangeCopy,               StructId;
+    RangeToInclusiveCopy,    sym::RangeToInclusiveCopy,    StructId;
+
     String,                  sym::String,              StructId;
     CStr,                    sym::CStr,                StructId;
     Ordering,                sym::Ordering,            EnumId;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
index 1712c28aa8ab..78228cf82e67 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
@@ -423,6 +423,10 @@ macro_rules! __known_path {
     (core::ops::RangeTo) => {};
     (core::ops::RangeToInclusive) => {};
     (core::ops::RangeInclusive) => {};
+    (core::range::Range) => {};
+    (core::range::RangeFrom) => {};
+    (core::range::RangeInclusive) => {};
+    (core::range::RangeToInclusive) => {};
     (core::future::Future) => {};
     (core::future::IntoFuture) => {};
     (core::fmt::Debug) => {};
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index d527a4ae29c2..35d744e7d16b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -1815,18 +1815,34 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
         Some(struct_.into())
     }
 
+    fn has_new_range_feature(&self) -> bool {
+        self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range)
+    }
+
     fn resolve_range(&self) -> Option {
-        let struct_ = self.lang_items.Range?;
+        let struct_ = if self.has_new_range_feature() {
+            self.lang_items.RangeCopy?
+        } else {
+            self.lang_items.Range?
+        };
         Some(struct_.into())
     }
 
     fn resolve_range_inclusive(&self) -> Option {
-        let struct_ = self.lang_items.RangeInclusiveStruct?;
+        let struct_ = if self.has_new_range_feature() {
+            self.lang_items.RangeInclusiveCopy?
+        } else {
+            self.lang_items.RangeInclusiveStruct?
+        };
         Some(struct_.into())
     }
 
     fn resolve_range_from(&self) -> Option {
-        let struct_ = self.lang_items.RangeFrom?;
+        let struct_ = if self.has_new_range_feature() {
+            self.lang_items.RangeFromCopy?
+        } else {
+            self.lang_items.RangeFrom?
+        };
         Some(struct_.into())
     }
 
@@ -1836,7 +1852,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
     }
 
     fn resolve_range_to_inclusive(&self) -> Option {
-        let struct_ = self.lang_items.RangeToInclusive?;
+        let struct_ = if self.has_new_range_feature() {
+            self.lang_items.RangeToInclusiveCopy?
+        } else {
+            self.lang_items.RangeToInclusive?
+        };
         Some(struct_.into())
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 0b776938c5b3..8c7d29f99371 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -13,11 +13,11 @@ fn infer_pattern() {
             let a = z;
             let (c, d) = (1, "hello");
 
-            for (e, f) in some_iter {
+            for (e, f) in [(0, 1)] {
                 let g = e;
             }
 
-            if let [val] = opt {
+            if let [val] = [y] {
                 let h = val;
             }
 
@@ -33,7 +33,7 @@ fn infer_pattern() {
         "#,
         expect![[r#"
             8..9 'x': &'? i32
-            17..400 '{     ...o_x; }': ()
+            17..399 '{     ...o_x; }': ()
             27..28 'y': &'? i32
             31..32 'x': &'? i32
             42..44 '&z': &'? i32
@@ -47,58 +47,62 @@ fn infer_pattern() {
             82..94 '(1, "hello")': (i32, &'? str)
             83..84 '1': i32
             86..93 '"hello"': &'static str
-            101..151 'for (e...     }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
-            101..151 'for (e...     }': <{unknown} as IntoIterator>::IntoIter
-            101..151 'for (e...     }': !
-            101..151 'for (e...     }': {unknown}
-            101..151 'for (e...     }': &'? mut {unknown}
-            101..151 'for (e...     }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            101..151 'for (e...     }': Option<<{unknown} as Iterator>::Item>
-            101..151 'for (e...     }': ()
-            101..151 'for (e...     }': ()
-            101..151 'for (e...     }': ()
-            101..151 'for (e...     }': ()
-            105..111 '(e, f)': ({unknown}, {unknown})
-            106..107 'e': {unknown}
-            109..110 'f': {unknown}
-            115..124 'some_iter': {unknown}
-            125..151 '{     ...     }': ()
-            139..140 'g': {unknown}
-            143..144 'e': {unknown}
-            157..204 'if let...     }': ()
-            160..175 'let [val] = opt': bool
-            164..169 '[val]': [{unknown}]
-            165..168 'val': {unknown}
-            172..175 'opt': [{unknown}]
-            176..204 '{     ...     }': ()
-            190..191 'h': {unknown}
-            194..197 'val': {unknown}
-            210..236 'if let...rue {}': ()
-            213..233 'let x ... &true': bool
-            217..225 'x @ true': &'? bool
-            221..225 'true': bool
-            221..225 'true': bool
-            228..233 '&true': &'? bool
-            229..233 'true': bool
-            234..236 '{}': ()
-            246..252 'lambda': impl Fn(u64, u64, i32) -> i32
-            255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
-            256..257 'a': u64
-            264..265 'b': u64
-            267..268 'c': i32
-            275..287 '{ a + b; c }': i32
-            277..278 'a': u64
-            277..282 'a + b': u64
-            281..282 'b': u64
-            284..285 'c': i32
-            298..310 'ref ref_to_x': &'? &'? i32
-            313..314 'x': &'? i32
-            324..333 'mut mut_x': &'? i32
-            336..337 'x': &'? i32
-            347..367 'ref mu...f_to_x': &'? mut &'? i32
-            370..371 'x': &'? i32
-            381..382 'k': &'? mut &'? i32
-            385..397 'mut_ref_to_x': &'? mut &'? i32
+            101..150 'for (e...     }': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter
+            101..150 'for (e...     }': IntoIter<(i32, i32), 1>
+            101..150 'for (e...     }': !
+            101..150 'for (e...     }': IntoIter<(i32, i32), 1>
+            101..150 'for (e...     }': &'? mut IntoIter<(i32, i32), 1>
+            101..150 'for (e...     }': fn next>(&'? mut IntoIter<(i32, i32), 1>) -> Option< as Iterator>::Item>
+            101..150 'for (e...     }': Option<(i32, i32)>
+            101..150 'for (e...     }': ()
+            101..150 'for (e...     }': ()
+            101..150 'for (e...     }': ()
+            101..150 'for (e...     }': ()
+            105..111 '(e, f)': (i32, i32)
+            106..107 'e': i32
+            109..110 'f': i32
+            115..123 '[(0, 1)]': [(i32, i32); 1]
+            116..122 '(0, 1)': (i32, i32)
+            117..118 '0': i32
+            120..121 '1': i32
+            124..150 '{     ...     }': ()
+            138..139 'g': i32
+            142..143 'e': i32
+            156..203 'if let...     }': ()
+            159..174 'let [val] = [y]': bool
+            163..168 '[val]': [&'? i32; 1]
+            164..167 'val': &'? i32
+            171..174 '[y]': [&'? i32; 1]
+            172..173 'y': &'? i32
+            175..203 '{     ...     }': ()
+            189..190 'h': &'? i32
+            193..196 'val': &'? i32
+            209..235 'if let...rue {}': ()
+            212..232 'let x ... &true': bool
+            216..224 'x @ true': &'? bool
+            220..224 'true': bool
+            220..224 'true': bool
+            227..232 '&true': &'? bool
+            228..232 'true': bool
+            233..235 '{}': ()
+            245..251 'lambda': impl Fn(u64, u64, i32) -> i32
+            254..286 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
+            255..256 'a': u64
+            263..264 'b': u64
+            266..267 'c': i32
+            274..286 '{ a + b; c }': i32
+            276..277 'a': u64
+            276..281 'a + b': u64
+            280..281 'b': u64
+            283..284 'c': i32
+            297..309 'ref ref_to_x': &'? &'? i32
+            312..313 'x': &'? i32
+            323..332 'mut mut_x': &'? i32
+            335..336 'x': &'? i32
+            346..366 'ref mu...f_to_x': &'? mut &'? i32
+            369..370 'x': &'? i32
+            380..381 'k': &'? mut &'? i32
+            384..396 'mut_ref_to_x': &'? mut &'? i32
         "#]],
     );
 }
@@ -380,7 +384,7 @@ fn infer_pattern_match_string_literal() {
 fn infer_pattern_match_byte_string_literal() {
     check_infer_with_mismatches(
         r#"
-        //- minicore: index
+        //- minicore: index, range
         struct S;
         impl core::ops::Index for [T; N] {
             type Output = [u8];
@@ -395,7 +399,7 @@ fn infer_pattern_match_byte_string_literal() {
         "#,
         expect![[r#"
             105..109 'self': &'? [T; N]
-            111..116 'index': {unknown}
+            111..116 'index': RangeFull
             157..180 '{     ...     }': &'? [u8]
             167..174 'loop {}': !
             172..174 '{}': ()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index a9a5e96f75cc..988d1dc7f242 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -64,20 +64,37 @@ fn type_alias_in_struct_lit() {
 
 #[test]
 fn infer_ranges() {
-    check_types(
+    check_no_mismatches(
         r#"
-//- minicore: range
-fn test() {
-    let a = ..;
-    let b = 1..;
-    let c = ..2u32;
-    let d = 1..2usize;
-    let e = ..=10;
-    let f = 'a'..='z';
+//- minicore: range, new_range
 
-    let t = (a, b, c, d, e, f);
-    t;
-} //^ (RangeFull, RangeFrom, RangeTo, Range, RangeToInclusive, RangeInclusive)
+fn test() {
+    let _: core::ops::RangeFull = ..;
+    let _: core::ops::RangeFrom = 1..;
+    let _: core::ops::RangeTo = ..2u32;
+    let _: core::ops::Range = 1..2usize;
+    let _: core::ops::RangeToInclusive = ..=10;
+    let _: core::ops::RangeInclusive = 'a'..='z';
+}
+"#,
+    );
+}
+
+#[test]
+fn infer_ranges_new_range() {
+    check_no_mismatches(
+        r#"
+//- minicore: range, new_range
+#![feature(new_range)]
+
+fn test() {
+    let _: core::ops::RangeFull = ..;
+    let _: core::range::RangeFrom = 1..;
+    let _: core::ops::RangeTo = ..2u32;
+    let _: core::range::Range = 1..2usize;
+    let _: core::range::RangeToInclusive = ..=10;
+    let _: core::range::RangeInclusive = 'a'..='z';
+}
 "#,
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 6ba7a42c1946..38e9c5b3f714 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -531,18 +531,12 @@ impl<'db> SourceAnalyzer<'db> {
         db: &'db dyn HirDatabase,
         range_pat: &ast::RangePat,
     ) -> Option {
-        let path: ModPath = match (range_pat.op_kind()?, range_pat.start(), range_pat.end()) {
-            (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
-            (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
-            (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
-            (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
-            (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
-
-            (RangeOp::Exclusive, None, None) => return None,
-            (RangeOp::Inclusive, None, None) => return None,
-            (RangeOp::Inclusive, Some(_), None) => return None,
-        };
-        self.resolver.resolve_known_struct(db, &path)
+        self.resolve_range_struct(
+            db,
+            range_pat.op_kind()?,
+            range_pat.start().is_some(),
+            range_pat.end().is_some(),
+        )
     }
 
     pub(crate) fn resolve_range_expr(
@@ -550,19 +544,59 @@ impl<'db> SourceAnalyzer<'db> {
         db: &'db dyn HirDatabase,
         range_expr: &ast::RangeExpr,
     ) -> Option {
-        let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) {
-            (RangeOp::Exclusive, None, None) => path![core::ops::RangeFull],
-            (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
-            (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
-            (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
-            (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
-            (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
+        self.resolve_range_struct(
+            db,
+            range_expr.op_kind()?,
+            range_expr.start().is_some(),
+            range_expr.end().is_some(),
+        )
+    }
 
+    fn resolve_range_struct(
+        &self,
+        db: &'db dyn HirDatabase,
+        op_kind: RangeOp,
+        has_start: bool,
+        has_end: bool,
+    ) -> Option {
+        let has_new_range =
+            self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range);
+        let lang_items = self.lang_items(db);
+        match (op_kind, has_start, has_end) {
+            (RangeOp::Exclusive, false, false) => lang_items.RangeFull,
+            (RangeOp::Exclusive, false, true) => lang_items.RangeTo,
+            (RangeOp::Exclusive, true, false) => {
+                if has_new_range {
+                    lang_items.RangeFromCopy
+                } else {
+                    lang_items.RangeFrom
+                }
+            }
+            (RangeOp::Exclusive, true, true) => {
+                if has_new_range {
+                    lang_items.RangeCopy
+                } else {
+                    lang_items.Range
+                }
+            }
+            (RangeOp::Inclusive, false, true) => {
+                if has_new_range {
+                    lang_items.RangeToInclusiveCopy
+                } else {
+                    lang_items.RangeToInclusive
+                }
+            }
+            (RangeOp::Inclusive, true, true) => {
+                if has_new_range {
+                    lang_items.RangeInclusiveCopy
+                } else {
+                    lang_items.RangeInclusiveStruct
+                }
+            }
             // [E0586] inclusive ranges must be bounded at the end
-            (RangeOp::Inclusive, None, None) => return None,
-            (RangeOp::Inclusive, Some(_), None) => return None,
-        };
-        self.resolver.resolve_known_struct(db, &path)
+            (RangeOp::Inclusive, false, false) => None,
+            (RangeOp::Inclusive, true, false) => None,
+        }
     }
 
     pub(crate) fn resolve_await_to_poll(
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index 781e58a1204e..513f51faba7f 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -525,6 +525,12 @@ define_symbols! {
     arbitrary_self_types,
     arbitrary_self_types_pointers,
     supertrait_item_shadowing,
+    new_range,
+    range,
+    RangeCopy,
+    RangeFromCopy,
+    RangeInclusiveCopy,
+    RangeToInclusiveCopy,
     hash,
     partial_cmp,
     cmp,
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index c3429356d9e5..26e125f8f2d6 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -58,6 +58,7 @@
 //!     pin:
 //!     pointee: copy, send, sync, ord, hash, unpin, phantom_data
 //!     range:
+//!     new_range:
 //!     receiver: deref
 //!     result:
 //!     send: sized
@@ -175,7 +176,9 @@ pub mod marker {
 
     // region:clone
     impl Clone for PhantomData {
-        fn clone(&self) -> Self { Self }
+        fn clone(&self) -> Self {
+            Self
+        }
     }
     // endregion:clone
 
@@ -1128,6 +1131,32 @@ pub mod ops {
     // endregion:dispatch_from_dyn
 }
 
+// region:new_range
+pub mod range {
+    #[lang = "RangeCopy"]
+    pub struct Range {
+        pub start: Idx,
+        pub end: Idx,
+    }
+
+    #[lang = "RangeFromCopy"]
+    pub struct RangeFrom {
+        pub start: Idx,
+    }
+
+    #[lang = "RangeInclusiveCopy"]
+    pub struct RangeInclusive {
+        pub start: Idx,
+        pub end: Idx,
+    }
+
+    #[lang = "RangeToInclusiveCopy"]
+    pub struct RangeToInclusive {
+        pub end: Idx,
+    }
+}
+// endregion:new_range
+
 // region:eq
 pub mod cmp {
     use crate::marker::PointeeSized;
@@ -1144,7 +1173,9 @@ pub mod cmp {
 
     // region:builtin_impls
     impl PartialEq for () {
-        fn eq(&self, other: &()) -> bool { true }
+        fn eq(&self, other: &()) -> bool {
+            true
+        }
     }
     // endregion:builtin_impls
 
@@ -1567,10 +1598,7 @@ pub mod pin {
     }
     // endregion:dispatch_from_dyn
     // region:coerce_unsized
-    impl crate::ops::CoerceUnsized> for Pin where
-        Ptr: crate::ops::CoerceUnsized
-    {
-    }
+    impl crate::ops::CoerceUnsized> for Pin where Ptr: crate::ops::CoerceUnsized {}
     // endregion:coerce_unsized
 }
 // endregion:pin
@@ -1792,9 +1820,9 @@ pub mod iter {
                 fn from_iter>(iter: T) -> Self;
             }
         }
-        pub use self::collect::{IntoIterator, FromIterator};
+        pub use self::collect::{FromIterator, IntoIterator};
     }
-    pub use self::traits::{IntoIterator, FromIterator, Iterator};
+    pub use self::traits::{FromIterator, IntoIterator, Iterator};
 }
 // endregion:iterator
 
@@ -2087,30 +2115,30 @@ macro_rules! column {
 pub mod prelude {
     pub mod v1 {
         pub use crate::{
-            clone::Clone,                            // :clone
-            cmp::{Eq, PartialEq},                    // :eq
-            cmp::{Ord, PartialOrd},                  // :ord
-            convert::AsMut,                          // :as_mut
-            convert::AsRef,                          // :as_ref
-            convert::{From, Into, TryFrom, TryInto}, // :from
-            default::Default,                        // :default
-            iter::{IntoIterator, Iterator, FromIterator}, // :iterator
-            macros::builtin::{derive, derive_const}, // :derive
-            marker::Copy,                            // :copy
-            marker::Send,                            // :send
-            marker::Sized,                           // :sized
-            marker::Sync,                            // :sync
-            mem::drop,                               // :drop
-            mem::size_of,                            // :size_of
-            ops::Drop,                               // :drop
-            ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn
-            ops::{Fn, FnMut, FnOnce},                // :fn
-            option::Option::{self, None, Some},      // :option
-            panic,                                   // :panic
-            result::Result::{self, Err, Ok},         // :result
-            str::FromStr,                            // :str
-            fmt::derive::Debug,                      // :fmt, derive
-            hash::derive::Hash,                      // :hash, derive
+            clone::Clone,                                 // :clone
+            cmp::{Eq, PartialEq},                         // :eq
+            cmp::{Ord, PartialOrd},                       // :ord
+            convert::AsMut,                               // :as_mut
+            convert::AsRef,                               // :as_ref
+            convert::{From, Into, TryFrom, TryInto},      // :from
+            default::Default,                             // :default
+            fmt::derive::Debug,                           // :fmt, derive
+            hash::derive::Hash,                           // :hash, derive
+            iter::{FromIterator, IntoIterator, Iterator}, // :iterator
+            macros::builtin::{derive, derive_const},      // :derive
+            marker::Copy,                                 // :copy
+            marker::Send,                                 // :send
+            marker::Sized,                                // :sized
+            marker::Sync,                                 // :sync
+            mem::drop,                                    // :drop
+            mem::size_of,                                 // :size_of
+            ops::Drop,                                    // :drop
+            ops::{AsyncFn, AsyncFnMut, AsyncFnOnce},      // :async_fn
+            ops::{Fn, FnMut, FnOnce},                     // :fn
+            option::Option::{self, None, Some},           // :option
+            panic,                                        // :panic
+            result::Result::{self, Err, Ok},              // :result
+            str::FromStr,                                 // :str
         };
     }
 

From 6948a66ad81e149d8a0af5dcc0888b9a44f3d2b4 Mon Sep 17 00:00:00 2001
From: Shoyu Vanilla 
Date: Wed, 14 Jan 2026 05:40:27 +0900
Subject: [PATCH 100/583] fix: Sync cast checks to rustc again

---
 .../crates/hir-ty/src/infer/cast.rs           | 270 +++++++++++++-----
 .../crates/hir-ty/src/infer/unify.rs          |  10 -
 .../src/handlers/invalid_cast.rs              |  10 +-
 3 files changed, 201 insertions(+), 89 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
index d073b06ccc8a..d69b00adb7f7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
@@ -2,8 +2,10 @@
 
 use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags};
 use rustc_ast_ir::Mutability;
+use rustc_hash::FxHashSet;
 use rustc_type_ir::{
-    Flags, InferTy, TypeFlags, UintTy,
+    InferTy, TypeVisitableExt, UintTy, elaborate,
+    error::TypeError,
     inherent::{AdtDef, BoundExistentialPredicates as _, IntoKind, Ty as _},
 };
 use stdx::never;
@@ -12,7 +14,10 @@ use crate::{
     InferenceDiagnostic,
     db::HirDatabase,
     infer::{AllowTwoPhase, InferenceContext, expr::ExprIsRead},
-    next_solver::{BoundExistentialPredicates, DbInterner, ParamTy, Ty, TyKind},
+    next_solver::{
+        BoundExistentialPredicates, ExistentialPredicate, ParamTy, Region, Ty, TyKind,
+        infer::traits::ObligationCause,
+    },
 };
 
 #[derive(Debug)]
@@ -66,12 +71,13 @@ pub enum CastError {
     DifferingKinds,
     SizedUnsizedCast,
     IllegalCast,
-    IntToFatCast,
+    IntToWideCast,
     NeedDeref,
     NeedViaPtr,
     NeedViaThinPtr,
     NeedViaInt,
     NonScalar,
+    PtrPtrAddingAutoTraits,
     // We don't want to report errors with unknown types currently.
     // UnknownCastPtrKind,
     // UnknownExprPtrKind,
@@ -137,22 +143,13 @@ impl<'db> CastCheck<'db> {
             return Ok(());
         }
 
-        if !self.cast_ty.flags().contains(TypeFlags::HAS_TY_INFER)
-            && !ctx.table.is_sized(self.cast_ty)
-        {
+        if !self.cast_ty.has_infer_types() && !ctx.table.is_sized(self.cast_ty) {
             return Err(InferenceDiagnostic::CastToUnsized {
                 expr: self.expr,
                 cast_ty: self.cast_ty.store(),
             });
         }
 
-        // Chalk doesn't support trait upcasting and fails to solve some obvious goals
-        // when the trait environment contains some recursive traits (See issue #18047)
-        // We skip cast checks for such cases for now, until the next-gen solver.
-        if contains_dyn_trait(self.cast_ty) {
-            return Ok(());
-        }
-
         self.do_check(ctx).map_err(|e| e.into_diagnostic(self.expr, self.expr_ty, self.cast_ty))
     }
 
@@ -162,22 +159,23 @@ impl<'db> CastCheck<'db> {
                 (Some(t_from), Some(t_cast)) => (t_from, t_cast),
                 (None, Some(t_cast)) => match self.expr_ty.kind() {
                     TyKind::FnDef(..) => {
-                        let sig =
-                            self.expr_ty.callable_sig(ctx.interner()).expect("FnDef had no sig");
-                        let sig = ctx.table.normalize_associated_types_in(sig);
+                        // rustc calls `FnCtxt::normalize` on this but it's a no-op in next-solver
+                        let sig = self.expr_ty.fn_sig(ctx.interner());
                         let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig);
-                        if ctx
-                            .coerce(
-                                self.source_expr.into(),
-                                self.expr_ty,
-                                fn_ptr,
-                                AllowTwoPhase::No,
-                                ExprIsRead::Yes,
-                            )
-                            .is_ok()
-                        {
-                        } else {
-                            return Err(CastError::IllegalCast);
+                        match ctx.coerce(
+                            self.source_expr.into(),
+                            self.expr_ty,
+                            fn_ptr,
+                            AllowTwoPhase::No,
+                            ExprIsRead::Yes,
+                        ) {
+                            Ok(_) => {}
+                            Err(TypeError::IntrinsicCast) => {
+                                return Err(CastError::IllegalCast);
+                            }
+                            Err(_) => {
+                                return Err(CastError::NonScalar);
+                            }
                         }
 
                         (CastTy::FnPtr, t_cast)
@@ -213,23 +211,41 @@ impl<'db> CastCheck<'db> {
         // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym
 
         match (t_from, t_cast) {
+            // These types have invariants! can't cast into them.
             (_, CastTy::Int(Int::CEnum) | CastTy::FnPtr) => Err(CastError::NonScalar),
+
+            // * -> Bool
             (_, CastTy::Int(Int::Bool)) => Err(CastError::CastToBool),
-            (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()),
+
+            // * -> Char
+            (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()), // u8-char-cast
             (_, CastTy::Int(Int::Char)) => Err(CastError::CastToChar),
+
+            // prim -> float,ptr
             (CastTy::Int(Int::Bool | Int::CEnum | Int::Char), CastTy::Float) => {
                 Err(CastError::NeedViaInt)
             }
+
             (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..))
             | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast),
-            (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst),
+
+            // ptr -> ptr
+            (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst), // ptr-ptr-cast
+
+            // // ptr-addr-cast
             (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, src),
+            (CastTy::FnPtr, CastTy::Int(_)) => Ok(()),
+
+            // addr-ptr-cast
             (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, dst),
+
+            // fn-ptr-cast
             (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, dst),
+
+            // prim -> prim
             (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()),
             (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()),
             (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()),
-            (CastTy::FnPtr, CastTy::Int(_)) => Ok(()),
         }
     }
 
@@ -241,10 +257,16 @@ impl<'db> CastCheck<'db> {
         t_cast: Ty<'db>,
         m_cast: Mutability,
     ) -> Result<(), CastError> {
-        // Mutability order is opposite to rustc. `Mut < Not`
-        if m_expr <= m_cast
+        let t_expr = ctx.table.try_structurally_resolve_type(t_expr);
+        let t_cast = ctx.table.try_structurally_resolve_type(t_cast);
+
+        if m_expr >= m_cast
             && let TyKind::Array(ety, _) = t_expr.kind()
+            && ctx.infcx().can_eq(ctx.table.param_env, ety, t_cast)
         {
+            // Due to historical reasons we allow directly casting references of
+            // arrays into raw pointers of their element type.
+
             // Coerce to a raw pointer so that we generate RawPtr in MIR.
             let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr);
             if ctx
@@ -265,14 +287,9 @@ impl<'db> CastCheck<'db> {
                 );
             }
 
-            // This is a less strict condition than rustc's `demand_eqtype`,
-            // but false negative is better than false positive
-            if ctx
-                .coerce(self.source_expr.into(), ety, t_cast, AllowTwoPhase::No, ExprIsRead::Yes)
-                .is_ok()
-            {
-                return Ok(());
-            }
+            // this will report a type mismatch if needed
+            let _ = ctx.demand_eqtype(self.expr.into(), ety, t_cast);
+            return Ok(());
         }
 
         Err(CastError::IllegalCast)
@@ -289,30 +306,147 @@ impl<'db> CastCheck<'db> {
 
         match (src_kind, dst_kind) {
             (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()),
+
             // (_, None) => Err(CastError::UnknownCastPtrKind),
             // (None, _) => Err(CastError::UnknownExprPtrKind),
             (_, None) | (None, _) => Ok(()),
+
+            // Cast to thin pointer is OK
             (_, Some(PointerKind::Thin)) => Ok(()),
+
+            // thin -> fat? report invalid cast (don't complain about vtable kinds)
             (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
+
+            // trait object -> trait object? need to do additional checks
             (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
                 match (src_tty.principal_def_id(), dst_tty.principal_def_id()) {
+                    // A + SrcAuto> -> B + DstAuto>. need to make sure
+                    // - `Src` and `Dst` traits are the same
+                    // - traits have the same generic arguments
+                    // - projections are the same
+                    // - `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`
+                    //
+                    // Note that trait upcasting goes through a different mechanism (`coerce_unsized`)
+                    // and is unaffected by this check.
                     (Some(src_principal), Some(dst_principal)) => {
                         if src_principal == dst_principal {
                             return Ok(());
                         }
-                        let src_principal = ctx.db.trait_signature(src_principal.0);
-                        let dst_principal = ctx.db.trait_signature(dst_principal.0);
-                        if src_principal.flags.contains(TraitFlags::AUTO)
-                            && dst_principal.flags.contains(TraitFlags::AUTO)
+
+                        // We need to reconstruct trait object types.
+                        // `m_src` and `m_dst` won't work for us here because they will potentially
+                        // contain wrappers, which we do not care about.
+                        //
+                        // e.g. we want to allow `dyn T -> (dyn T,)`, etc.
+                        //
+                        // We also need to skip auto traits to emit an FCW and not an error.
+                        let src_obj = Ty::new_dynamic(
+                            ctx.interner(),
+                            BoundExistentialPredicates::new_from_iter(
+                                ctx.interner(),
+                                src_tty.iter().filter(|pred| {
+                                    !matches!(
+                                        pred.skip_binder(),
+                                        ExistentialPredicate::AutoTrait(_)
+                                    )
+                                }),
+                            ),
+                            Region::new_erased(ctx.interner()),
+                        );
+                        let dst_obj = Ty::new_dynamic(
+                            ctx.interner(),
+                            BoundExistentialPredicates::new_from_iter(
+                                ctx.interner(),
+                                dst_tty.iter().filter(|pred| {
+                                    !matches!(
+                                        pred.skip_binder(),
+                                        ExistentialPredicate::AutoTrait(_)
+                                    )
+                                }),
+                            ),
+                            Region::new_erased(ctx.interner()),
+                        );
+
+                        // `dyn Src = dyn Dst`, this checks for matching traits/generics/projections
+                        // This is `fcx.demand_eqtype`, but inlined to give a better error.
+                        if ctx
+                            .table
+                            .at(&ObligationCause::dummy())
+                            .eq(src_obj, dst_obj)
+                            .map(|infer_ok| ctx.table.register_infer_ok(infer_ok))
+                            .is_err()
                         {
-                            Ok(())
-                        } else {
-                            Err(CastError::DifferingKinds)
+                            return Err(CastError::DifferingKinds);
                         }
+
+                        // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`.
+                        // Emit an FCW otherwise.
+                        let src_auto: FxHashSet<_> = src_tty
+                            .auto_traits()
+                            .into_iter()
+                            .chain(
+                                elaborate::supertrait_def_ids(ctx.interner(), src_principal)
+                                    .filter(|trait_| {
+                                        ctx.db
+                                            .trait_signature(trait_.0)
+                                            .flags
+                                            .contains(TraitFlags::AUTO)
+                                    }),
+                            )
+                            .collect();
+
+                        let added = dst_tty
+                            .auto_traits()
+                            .into_iter()
+                            .any(|trait_| !src_auto.contains(&trait_));
+
+                        if added {
+                            return Err(CastError::PtrPtrAddingAutoTraits);
+                        }
+
+                        Ok(())
                     }
-                    _ => Err(CastError::Unknown),
+
+                    // dyn Auto -> dyn Auto'? ok.
+                    (None, None) => Ok(()),
+
+                    // dyn Trait -> dyn Auto? not ok (for now).
+                    //
+                    // Although dropping the principal is already allowed for unsizing coercions
+                    // (e.g. `*const (dyn Trait + Auto)` to `*const dyn Auto`), dropping it is
+                    // currently **NOT** allowed for (non-coercion) ptr-to-ptr casts (e.g
+                    // `*const Foo` to `*const Bar` where `Foo` has a `dyn Trait + Auto` tail
+                    // and `Bar` has a `dyn Auto` tail), because the underlying MIR operations
+                    // currently work very differently:
+                    //
+                    // * A MIR unsizing coercion on raw pointers to trait objects (`*const dyn Src`
+                    //   to `*const dyn Dst`) is currently equivalent to downcasting the source to
+                    //   the concrete sized type that it was originally unsized from first (via a
+                    //   ptr-to-ptr cast from `*const Src` to `*const T` with `T: Sized`) and then
+                    //   unsizing this thin pointer to the target type (unsizing `*const T` to
+                    //   `*const Dst`). In particular, this means that the pointer's metadata
+                    //   (vtable) will semantically change, e.g. for const eval and miri, even
+                    //   though the vtables will always be merged for codegen.
+                    //
+                    // * A MIR ptr-to-ptr cast is currently equivalent to a transmute and does not
+                    //   change the pointer metadata (vtable) at all.
+                    //
+                    // In addition to this potentially surprising difference between coercion and
+                    // non-coercion casts, casting away the principal with a MIR ptr-to-ptr cast
+                    // is currently considered undefined behavior:
+                    //
+                    // As a validity invariant of pointers to trait objects, we currently require
+                    // that the principal of the vtable in the pointer metadata exactly matches
+                    // the principal of the pointee type, where "no principal" is also considered
+                    // a kind of principal.
+                    (Some(_), None) => Err(CastError::DifferingKinds),
+
+                    // dyn Auto -> dyn Trait? not ok.
+                    (None, Some(_)) => Err(CastError::DifferingKinds),
                 }
             }
+
+            // fat -> fat? metadata kinds must match
             (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()),
             (_, _) => Err(CastError::DifferingKinds),
         }
@@ -342,9 +476,9 @@ impl<'db> CastCheck<'db> {
             None => Ok(()),
             Some(PointerKind::Error) => Ok(()),
             Some(PointerKind::Thin) => Ok(()),
-            Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast),
-            Some(PointerKind::Length) => Err(CastError::IntToFatCast),
-            Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToFatCast),
+            Some(PointerKind::VTable(_)) => Err(CastError::IntToWideCast),
+            Some(PointerKind::Length) => Err(CastError::IntToWideCast),
+            Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToWideCast),
         }
     }
 
@@ -363,15 +497,20 @@ impl<'db> CastCheck<'db> {
     }
 }
 
+/// The kind of pointer and associated metadata (thin, length or vtable) - we
+/// only allow casts between wide pointers if their metadata have the same
+/// kind.
 #[derive(Debug, PartialEq, Eq)]
 enum PointerKind<'db> {
-    // thin pointer
+    /// No metadata attached, ie pointer to sized type or foreign type
     Thin,
-    // trait object
+    /// A trait object
     VTable(BoundExistentialPredicates<'db>),
-    // slice
+    /// Slice
     Length,
+    /// The unsize info of this projection or opaque type
     OfAlias,
+    /// The unsize info of this parameter
     OfParam(ParamTy),
     Error,
 }
@@ -439,24 +578,3 @@ fn pointer_kind<'db>(
         }
     }
 }
-
-fn contains_dyn_trait<'db>(ty: Ty<'db>) -> bool {
-    use std::ops::ControlFlow;
-
-    use rustc_type_ir::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
-
-    struct DynTraitVisitor;
-
-    impl<'db> TypeVisitor> for DynTraitVisitor {
-        type Result = ControlFlow<()>;
-
-        fn visit_ty(&mut self, ty: Ty<'db>) -> ControlFlow<()> {
-            match ty.kind() {
-                TyKind::Dynamic(..) => ControlFlow::Break(()),
-                _ => ty.super_visit_with(self),
-            }
-        }
-    }
-
-    ty.visit_with(&mut DynTraitVisitor).is_break()
-}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index d55fc0ab0da6..2057159c46d2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -261,16 +261,6 @@ impl<'db> InferenceTable<'db> {
         self.infer_ctxt.canonicalize_response(t)
     }
 
-    // FIXME: We should get rid of this method. We cannot deeply normalize during inference, only when finishing.
-    // Inference should use shallow normalization (`try_structurally_resolve_type()`) only, when needed.
-    pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T
-    where
-        T: TypeFoldable> + Clone,
-    {
-        let ty = self.resolve_vars_with_obligations(ty);
-        self.at(&ObligationCause::new()).deeply_normalize(ty.clone()).unwrap_or(ty)
-    }
-
     pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> {
         self.infer_ctxt
             .at(&ObligationCause::new(), self.param_env)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index a59077b757b1..7479f8147d2e 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -51,7 +51,7 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_
             DiagnosticCode::RustcHardError("E0606"),
             format_ty!(ctx, "casting `{}` as `{}` is invalid", d.expr_ty, d.cast_ty),
         ),
-        CastError::IntToFatCast => (
+        CastError::IntToWideCast => (
             DiagnosticCode::RustcHardError("E0606"),
             format_ty!(ctx, "cannot cast `{}` to a fat pointer `{}`", d.expr_ty, d.cast_ty),
         ),
@@ -95,6 +95,10 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_
             DiagnosticCode::RustcHardError("E0605"),
             format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty),
         ),
+        CastError::PtrPtrAddingAutoTraits => (
+            DiagnosticCode::RustcHardError("E0804"),
+            "cannot add auto trait to dyn bound via pointer cast".to_owned(),
+        ),
         // CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => (
         //     DiagnosticCode::RustcHardError("E0641"),
         //     "cannot cast to a pointer of an unknown kind".to_owned(),
@@ -444,8 +448,8 @@ fn main() {
     q as *const [i32];
   //^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]`
 
-    // FIXME: This should emit diagnostics but disabled to prevent many false positives
     let t: *mut (dyn Trait + 'static) = 0 as *mut _;
+                                      //^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*mut (dyn Trait + 'static)`
 
     let mut fail: *const str = 0 as *const str;
                              //^^^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const str`
@@ -543,7 +547,7 @@ fn main() {
     fn ptr_to_trait_obj_ok() {
         check_diagnostics(
             r#"
-//- minicore: pointee
+//- minicore: pointee, send, sync
 trait Trait<'a> {}
 
 fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> {

From cd6c41283838023ee3b22709106d666869ec9a6f Mon Sep 17 00:00:00 2001
From: Zachary S 
Date: Tue, 13 Jan 2026 22:40:12 -0600
Subject: [PATCH 101/583] Add test for feature-gating `mut ref` patterns in
 struct field shorthand.

---
 tests/ui/feature-gates/feature-gate-mut-ref.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.rs b/tests/ui/feature-gates/feature-gate-mut-ref.rs
index 752ae35d8a9a..eac2a59002e2 100644
--- a/tests/ui/feature-gates/feature-gate-mut-ref.rs
+++ b/tests/ui/feature-gates/feature-gate-mut-ref.rs
@@ -10,4 +10,7 @@ fn main() {
     let mut ref x = 10; //~  ERROR [E0658]
     #[cfg(false)]
     let mut ref mut y = 10; //~  ERROR [E0658]
+
+    struct Foo { x: i32 }
+    let Foo { mut ref x } = Foo { x: 10 };
 }

From f809e332d895bc80d28d8d2cb10297a3fe9ca4f2 Mon Sep 17 00:00:00 2001
From: Zachary S 
Date: Tue, 13 Jan 2026 12:57:04 -0600
Subject: [PATCH 102/583] Feature-gate `mut ref` patterns in struct field
 shorthand.

---
 compiler/rustc_parse/src/parser/pat.rs             |  6 ++++++
 tests/ui/feature-gates/feature-gate-mut-ref.rs     |  2 +-
 tests/ui/feature-gates/feature-gate-mut-ref.stderr | 12 +++++++++++-
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index d7f3a36122e5..f3e12d48a6a5 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1750,6 +1750,12 @@ impl<'a> Parser<'a> {
             hi = self.prev_token.span;
             let ann = BindingMode(by_ref, mutability);
             let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
+            if matches!(
+                fieldpat.kind,
+                PatKind::Ident(BindingMode(ByRef::Yes(..), Mutability::Mut), ..)
+            ) {
+                self.psess.gated_spans.gate(sym::mut_ref, fieldpat.span);
+            }
             let subpat = if is_box {
                 self.mk_pat(lo.to(hi), PatKind::Box(Box::new(fieldpat)))
             } else {
diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.rs b/tests/ui/feature-gates/feature-gate-mut-ref.rs
index eac2a59002e2..74b1fba5dfea 100644
--- a/tests/ui/feature-gates/feature-gate-mut-ref.rs
+++ b/tests/ui/feature-gates/feature-gate-mut-ref.rs
@@ -12,5 +12,5 @@ fn main() {
     let mut ref mut y = 10; //~  ERROR [E0658]
 
     struct Foo { x: i32 }
-    let Foo { mut ref x } = Foo { x: 10 };
+    let Foo { mut ref x } = Foo { x: 10 }; //~  ERROR [E0658]
 }
diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.stderr b/tests/ui/feature-gates/feature-gate-mut-ref.stderr
index d3eb674e92de..921ff878bf4d 100644
--- a/tests/ui/feature-gates/feature-gate-mut-ref.stderr
+++ b/tests/ui/feature-gates/feature-gate-mut-ref.stderr
@@ -38,6 +38,16 @@ LL |     let mut ref mut y = 10;
    = help: add `#![feature(mut_ref)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: aborting due to 4 previous errors
+error[E0658]: mutable by-reference bindings are experimental
+  --> $DIR/feature-gate-mut-ref.rs:15:15
+   |
+LL |     let Foo { mut ref x } = Foo { x: 10 };
+   |               ^^^^^^^^^
+   |
+   = note: see issue #123076  for more information
+   = help: add `#![feature(mut_ref)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0658`.

From 05939a8d38959b1ff418a0f54663eaba0f5e3108 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Wed, 14 Jan 2026 17:54:23 +0800
Subject: [PATCH 103/583] Fix false positive precedence in `(2 as i32) < 3`

Example
---
```rust
fn f() { _ = $0(1 as u32) << 10; }
```

**Before this PR**

This is syntax error

```rust
fn f() { _ = 1 as u32 << 10; }
```

**After this PR**

Assist not applicable
---
 .../src/handlers/remove_parentheses.rs        |  6 +++++
 .../crates/syntax/src/ast/prec.rs             | 24 +++++++++++++++----
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
index aa4d2bcadb01..f07da489e23a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
@@ -321,6 +321,12 @@ mod tests {
         );
     }
 
+    #[test]
+    fn remove_parens_conflict_cast_before_l_angle() {
+        check_assist_not_applicable(remove_parentheses, r#"fn f() { _ = $0(1 as u32) << 10; }"#);
+        check_assist_not_applicable(remove_parentheses, r#"fn f() { _ = $0(1 as u32) < 10; }"#);
+    }
+
     #[test]
     fn remove_parens_double_paren_stmt() {
         check_assist(
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
index 8c88224a761a..d99cf492616e 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
@@ -154,6 +154,11 @@ fn check_ancestry(ancestor: &SyntaxNode, descendent: &SyntaxNode) -> bool {
     bail()
 }
 
+fn next_token_of(node: &SyntaxNode) -> Option {
+    let last = node.last_token()?;
+    skip_trivia_token(last.next_token()?, Direction::Next)
+}
+
 impl Expr {
     pub fn precedence(&self) -> ExprPrecedence {
         precedence(self)
@@ -197,6 +202,8 @@ impl Expr {
         if is_parent_call_expr && is_field_expr {
             return true;
         }
+        let place_of_parent =
+            || place_of.ancestors().find(|it| it.parent().is_none_or(|p| &p == parent.syntax()));
 
         // Special-case block weirdness
         if parent.child_is_followed_by_a_block() {
@@ -226,15 +233,24 @@ impl Expr {
         // For `&&`, we avoid introducing ` && ` into a binary chain.
 
         if self.precedence() == ExprPrecedence::Jump
-            && let Some(node) =
-                place_of.ancestors().find(|it| it.parent().is_none_or(|p| &p == parent.syntax()))
-            && let Some(next) =
-                node.last_token().and_then(|t| skip_trivia_token(t.next_token()?, Direction::Next))
+            && let Some(node) = place_of_parent()
+            && let Some(next) = next_token_of(&node)
             && matches!(next.kind(), T![||] | T![&&])
         {
             return true;
         }
 
+        // Special-case `2 as x < 3`
+        if let ast::Expr::CastExpr(it) = self
+            && let Some(ty) = it.ty()
+            && ty.syntax().last_token().and_then(|it| ast::NameLike::cast(it.parent()?)).is_some()
+            && let Some(node) = place_of_parent()
+            && let Some(next) = next_token_of(&node)
+            && matches!(next.kind(), T![<] | T![<<])
+        {
+            return true;
+        }
+
         if self.is_paren_like()
             || parent.is_paren_like()
             || self.is_prefix()

From 4b5b42c82c1cf2095fcb183c72406eb82c908b21 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Wed, 14 Jan 2026 16:34:31 +0530
Subject: [PATCH 104/583] remove postcard from legacy

---
 .../src/bidirectional_protocol.rs             |  9 +---
 .../proc-macro-api/src/legacy_protocol.rs     |  8 +---
 .../crates/proc-macro-api/src/lib.rs          |  3 --
 .../crates/proc-macro-api/src/process.rs      | 45 ++++++-------------
 .../crates/proc-macro-srv-cli/src/main.rs     |  5 ---
 .../proc-macro-srv-cli/src/main_loop.rs       |  1 -
 6 files changed, 16 insertions(+), 55 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index e44723a6a389..5996f882981c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -212,14 +212,7 @@ fn run_request(
     if let Some(err) = srv.exited() {
         return Err(err.clone());
     }
-
-    match srv.use_postcard() {
-        true => srv.run_bidirectional::(msg, callback),
-        false => Err(ServerError {
-            message: "bidirectional messaging does not support JSON".to_owned(),
-            io: None,
-        }),
-    }
+    srv.run_bidirectional::(msg, callback)
 }
 
 pub fn reject_subrequests(req: SubRequest) -> Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index 4524d1b66bfe..aabe5a011851 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -19,7 +19,7 @@ use crate::{
     },
     process::ProcMacroServerProcess,
     transport::codec::Codec,
-    transport::codec::{json::JsonProtocol, postcard::PostcardProtocol},
+    transport::codec::json::JsonProtocol,
     version,
 };
 
@@ -148,11 +148,7 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result(send_request::, req)
-    } else {
-        srv.send_task::<_, _, JsonProtocol>(send_request::, req)
-    }
+    srv.send_task::<_, _, JsonProtocol>(send_request::, req)
 }
 
 /// Sends a request to the server and reads the response.
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 822809943a36..01195c10feef 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -49,8 +49,6 @@ pub mod version {
 pub enum ProtocolFormat {
     /// JSON-based legacy protocol (newline-delimited JSON).
     JsonLegacy,
-    /// Postcard-based legacy protocol (COBS-encoded postcard).
-    PostcardLegacy,
     /// Bidirectional postcard protocol with sub-request support.
     BidirectionalPostcardPrototype,
 }
@@ -59,7 +57,6 @@ impl fmt::Display for ProtocolFormat {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             ProtocolFormat::JsonLegacy => write!(f, "json-legacy"),
-            ProtocolFormat::PostcardLegacy => write!(f, "postcard-legacy"),
             ProtocolFormat::BidirectionalPostcardPrototype => {
                 write!(f, "bidirectional-postcard-prototype")
             }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index 4f8762158790..cd387dad0d0b 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -43,7 +43,6 @@ impl std::fmt::Debug for ProcMacroServerProcess {
 #[derive(Debug, Clone)]
 pub(crate) enum Protocol {
     LegacyJson { mode: SpanMode },
-    LegacyPostcard { mode: SpanMode },
     BidirectionalPostcardPrototype { mode: SpanMode },
 }
 
@@ -136,7 +135,6 @@ impl ProcMacroServerProcess {
         {
             &[
                 Some(ProtocolFormat::BidirectionalPostcardPrototype),
-                Some(ProtocolFormat::PostcardLegacy),
                 Some(ProtocolFormat::JsonLegacy),
             ]
         } else {
@@ -155,9 +153,6 @@ impl ProcMacroServerProcess {
                         Some(ProtocolFormat::BidirectionalPostcardPrototype) => {
                             Protocol::BidirectionalPostcardPrototype { mode: SpanMode::Id }
                         }
-                        Some(ProtocolFormat::PostcardLegacy) => {
-                            Protocol::LegacyPostcard { mode: SpanMode::Id }
-                        }
                         Some(ProtocolFormat::JsonLegacy) | None => {
                             Protocol::LegacyJson { mode: SpanMode::Id }
                         }
@@ -185,7 +180,6 @@ impl ProcMacroServerProcess {
                     {
                         match &mut srv.protocol {
                             Protocol::LegacyJson { mode }
-                            | Protocol::LegacyPostcard { mode }
                             | Protocol::BidirectionalPostcardPrototype { mode } => *mode = new_mode,
                         }
                     }
@@ -208,10 +202,6 @@ impl ProcMacroServerProcess {
         self.exited.get().map(|it| &it.0)
     }
 
-    pub(crate) fn use_postcard(&self) -> bool {
-        matches!(self.protocol, Protocol::LegacyPostcard { .. })
-    }
-
     /// Retrieves the API version of the proc-macro server.
     pub(crate) fn version(&self) -> u32 {
         self.version
@@ -221,7 +211,6 @@ impl ProcMacroServerProcess {
     pub(crate) fn rust_analyzer_spans(&self) -> bool {
         match self.protocol {
             Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer,
-            Protocol::LegacyPostcard { mode } => mode == SpanMode::RustAnalyzer,
             Protocol::BidirectionalPostcardPrototype { mode } => mode == SpanMode::RustAnalyzer,
         }
     }
@@ -229,9 +218,7 @@ impl ProcMacroServerProcess {
     /// Checks the API version of the running proc-macro server.
     fn version_check(&self, callback: Option>) -> Result {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::version_check(self)
-            }
+            Protocol::LegacyJson { .. } => legacy_protocol::version_check(self),
             Protocol::BidirectionalPostcardPrototype { .. } => {
                 let cb = callback.expect("callback required for bidirectional protocol");
                 bidirectional_protocol::version_check(self, cb)
@@ -245,9 +232,7 @@ impl ProcMacroServerProcess {
         callback: Option>,
     ) -> Result {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::enable_rust_analyzer_spans(self)
-            }
+            Protocol::LegacyJson { .. } => legacy_protocol::enable_rust_analyzer_spans(self),
             Protocol::BidirectionalPostcardPrototype { .. } => {
                 let cb = callback.expect("callback required for bidirectional protocol");
                 bidirectional_protocol::enable_rust_analyzer_spans(self, cb)
@@ -262,9 +247,7 @@ impl ProcMacroServerProcess {
         callback: Option>,
     ) -> Result, String>, ServerError> {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::find_proc_macros(self, dylib_path)
-            }
+            Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path),
             Protocol::BidirectionalPostcardPrototype { .. } => {
                 let cb = callback.expect("callback required for bidirectional protocol");
                 bidirectional_protocol::find_proc_macros(self, dylib_path, cb)
@@ -285,18 +268,16 @@ impl ProcMacroServerProcess {
         callback: Option>,
     ) -> Result, ServerError> {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::expand(
-                    proc_macro,
-                    subtree,
-                    attr,
-                    env,
-                    def_site,
-                    call_site,
-                    mixed_site,
-                    current_dir,
-                )
-            }
+            Protocol::LegacyJson { .. } => legacy_protocol::expand(
+                proc_macro,
+                subtree,
+                attr,
+                env,
+                def_site,
+                call_site,
+                mixed_site,
+                current_dir,
+            ),
             Protocol::BidirectionalPostcardPrototype { .. } => bidirectional_protocol::expand(
                 proc_macro,
                 subtree,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
index a246d4d3f28f..928753659f1c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
@@ -67,7 +67,6 @@ impl ValueEnum for ProtocolFormatArg {
     fn value_variants<'a>() -> &'a [Self] {
         &[
             ProtocolFormatArg(ProtocolFormat::JsonLegacy),
-            ProtocolFormatArg(ProtocolFormat::PostcardLegacy),
             ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype),
         ]
     }
@@ -75,9 +74,6 @@ impl ValueEnum for ProtocolFormatArg {
     fn to_possible_value(&self) -> Option {
         match self.0 {
             ProtocolFormat::JsonLegacy => Some(clap::builder::PossibleValue::new("json-legacy")),
-            ProtocolFormat::PostcardLegacy => {
-                Some(clap::builder::PossibleValue::new("postcard-legacy"))
-            }
             ProtocolFormat::BidirectionalPostcardPrototype => {
                 Some(clap::builder::PossibleValue::new("bidirectional-postcard-prototype"))
             }
@@ -87,7 +83,6 @@ impl ValueEnum for ProtocolFormatArg {
     fn from_str(input: &str, _ignore_case: bool) -> Result {
         match input {
             "json-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::JsonLegacy)),
-            "postcard-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::PostcardLegacy)),
             "bidirectional-postcard-prototype" => {
                 Ok(ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype))
             }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
index 5180ede9fb90..70e1e091c197 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
@@ -41,7 +41,6 @@ pub fn run(
 ) -> io::Result<()> {
     match format {
         ProtocolFormat::JsonLegacy => run_old::(stdin, stdout),
-        ProtocolFormat::PostcardLegacy => run_old::(stdin, stdout),
         ProtocolFormat::BidirectionalPostcardPrototype => {
             run_new::(stdin, stdout)
         }

From 31817f6deaf57c58f713bb61666d32bbd8306a3b Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Wed, 14 Jan 2026 16:34:57 +0530
Subject: [PATCH 105/583] remove flatten from ExpandMacro message in
 bidirectional messages

---
 .../crates/proc-macro-api/src/bidirectional_protocol/msg.rs      | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
index 57e7b1ee8f68..c56ed5191694 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
@@ -70,7 +70,6 @@ pub struct ExpandMacro {
     pub lib: Utf8PathBuf,
     pub env: Vec<(String, String)>,
     pub current_dir: Option,
-    #[serde(flatten)]
     pub data: ExpandMacroData,
 }
 

From 98e1f7103b5d6bfcfcad849fa30a19614303160d Mon Sep 17 00:00:00 2001
From: Roberto Aloi 
Date: Wed, 14 Jan 2026 13:15:09 +0100
Subject: [PATCH 106/583] Bump camino to 1.2.2

---
 src/tools/rust-analyzer/Cargo.lock | 4 ++--
 src/tools/rust-analyzer/Cargo.toml | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index d6c6250e13dc..a2a18cf8eeea 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -178,9 +178,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "camino"
-version = "1.2.0"
+version = "1.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603"
+checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
 dependencies = [
  "serde_core",
 ]
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 8003cb2fba8e..04b513b38b58 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -107,7 +107,7 @@ anyhow = "1.0.98"
 arrayvec = "0.7.6"
 bitflags = "2.9.1"
 cargo_metadata = "0.23.0"
-camino = "1.1.10"
+camino = "1.2.2"
 crossbeam-channel = "0.5.15"
 dissimilar = "1.0.10"
 dot = "0.1.4"

From 0d8aa8991c33f7dec592856760b3a1c32251bfba Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Wed, 14 Jan 2026 09:16:17 +0100
Subject: [PATCH 107/583] fix: Fix path symbol search not respecting re-exports

---
 src/tools/rust-analyzer/crates/hir/src/lib.rs | 17 ++++
 .../crates/ide-db/src/symbol_index.rs         | 97 ++++++++++++++-----
 2 files changed, 91 insertions(+), 23 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 78be5a7e8fa9..252d71fb80a4 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -610,6 +610,23 @@ impl Module {
         res
     }
 
+    pub fn modules_in_scope(&self, db: &dyn HirDatabase, pub_only: bool) -> Vec<(Name, Module)> {
+        let def_map = self.id.def_map(db);
+        let scope = &def_map[self.id].scope;
+
+        let mut res = Vec::new();
+
+        for (name, item) in scope.types() {
+            if let ModuleDefId::ModuleId(m) = item.def
+                && (!pub_only || item.vis == Visibility::Public)
+            {
+                res.push((name.clone(), Module { id: m }));
+            }
+        }
+
+        res
+    }
+
     /// Returns a `ModuleScope`: a set of items, visible in this module.
     pub fn scope(
         self,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index c95b541748ec..d7f4c66f465b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -35,6 +35,7 @@ use hir::{
     import_map::{AssocSearchMode, SearchMode},
     symbols::{FileSymbol, SymbolCollector},
 };
+use itertools::Itertools;
 use rayon::prelude::*;
 use salsa::Update;
 
@@ -224,12 +225,10 @@ pub fn world_symbols(db: &RootDatabase, mut query: Query) -> Vec>
     // Search for crates by name (handles "::" and "::foo" queries)
     let indices: Vec<_> = if query.is_crate_search() {
         query.only_types = false;
-        query.libs = true;
         vec![SymbolIndex::extern_prelude_symbols(db)]
         // If we have a path filter, resolve it to target modules
     } else if !query.path_filter.is_empty() {
         query.only_types = false;
-        query.libs = true;
         let target_modules = resolve_path_to_modules(
             db,
             &query.path_filter,
@@ -313,11 +312,11 @@ fn resolve_path_to_modules(
 
     // If anchor_to_crate is true, first segment MUST be a crate name
     // If anchor_to_crate is false, first segment could be a crate OR a module in local crates
-    let mut candidate_modules: Vec = vec![];
+    let mut candidate_modules: Vec<(Module, bool)> = vec![];
 
     // Add crate root modules for matching crates
     for krate in matching_crates {
-        candidate_modules.push(krate.root_module(db));
+        candidate_modules.push((krate.root_module(db), krate.origin(db).is_local()));
     }
 
     // If not anchored to crate, also search for modules matching first segment in local crates
@@ -329,7 +328,7 @@ fn resolve_path_to_modules(
                     if let Some(name) = child.name(db)
                         && names_match(name.as_str(), first_segment)
                     {
-                        candidate_modules.push(child);
+                        candidate_modules.push((child, true));
                     }
                 }
             }
@@ -340,11 +339,14 @@ fn resolve_path_to_modules(
     for segment in rest_segments {
         candidate_modules = candidate_modules
             .into_iter()
-            .flat_map(|module| {
-                module.children(db).filter(|child| {
-                    child.name(db).is_some_and(|name| names_match(name.as_str(), segment))
-                })
+            .flat_map(|(module, local)| {
+                module
+                    .modules_in_scope(db, !local)
+                    .into_iter()
+                    .filter(|(name, _)| names_match(name.as_str(), segment))
+                    .map(move |(_, module)| (module, local))
             })
+            .unique()
             .collect();
 
         if candidate_modules.is_empty() {
@@ -352,7 +354,7 @@ fn resolve_path_to_modules(
         }
     }
 
-    candidate_modules
+    candidate_modules.into_iter().map(|(module, _)| module).collect()
 }
 
 #[derive(Default)]
@@ -839,7 +841,7 @@ pub struct Foo;
         assert_eq!(item, "foo");
         assert!(anchor);
 
-        // Trailing :: (module browsing)
+        // Trailing ::
         let (path, item, anchor) = Query::parse_path_query("foo::");
         assert_eq!(path, vec!["foo"]);
         assert_eq!(item, "");
@@ -909,7 +911,7 @@ pub mod nested {
     }
 
     #[test]
-    fn test_module_browsing() {
+    fn test_path_search_module() {
         let (mut db, _) = RootDatabase::with_many_files(
             r#"
 //- /lib.rs crate:main
@@ -1066,20 +1068,11 @@ pub fn root_fn() {}
         let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
         assert!(names.contains(&"RootItem"), "Expected RootItem at crate root in {:?}", names);
 
-        // Browse crate root
         let query = Query::new("mylib::".to_owned());
         let symbols = world_symbols(&db, query);
         let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
-        assert!(
-            names.contains(&"RootItem"),
-            "Expected RootItem when browsing crate root in {:?}",
-            names
-        );
-        assert!(
-            names.contains(&"root_fn"),
-            "Expected root_fn when browsing crate root in {:?}",
-            names
-        );
+        assert!(names.contains(&"RootItem"), "Expected RootItem {:?}", names);
+        assert!(names.contains(&"root_fn"), "Expected root_fn {:?}", names);
     }
 
     #[test]
@@ -1163,4 +1156,62 @@ pub struct FooStruct;
         let symbols = world_symbols(&db, query);
         assert!(symbols.is_empty(), "Expected empty results for non-matching crate pattern");
     }
+
+    #[test]
+    fn test_path_search_with_use_reexport() {
+        // Test that module resolution works for `use` items (re-exports), not just `mod` items
+        let (mut db, _) = RootDatabase::with_many_files(
+            r#"
+//- /lib.rs crate:main
+mod inner;
+pub use inner::nested;
+
+//- /inner.rs
+pub mod nested {
+    pub struct NestedStruct;
+    pub fn nested_fn() {}
+}
+"#,
+        );
+
+        let mut local_roots = FxHashSet::default();
+        local_roots.insert(WORKSPACE);
+        LocalRoots::get(&db).set_roots(&mut db).to(local_roots);
+
+        // Search via the re-exported path (main::nested::NestedStruct)
+        // This should work because `nested` is in scope via `pub use inner::nested`
+        let query = Query::new("main::nested::NestedStruct".to_owned());
+        let symbols = world_symbols(&db, query);
+        let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
+        assert!(
+            names.contains(&"NestedStruct"),
+            "Expected NestedStruct via re-exported path in {:?}",
+            names
+        );
+
+        // Also verify the original path still works
+        let query = Query::new("main::inner::nested::NestedStruct".to_owned());
+        let symbols = world_symbols(&db, query);
+        let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
+        assert!(
+            names.contains(&"NestedStruct"),
+            "Expected NestedStruct via original path in {:?}",
+            names
+        );
+
+        // Browse the re-exported module
+        let query = Query::new("main::nested::".to_owned());
+        let symbols = world_symbols(&db, query);
+        let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
+        assert!(
+            names.contains(&"NestedStruct"),
+            "Expected NestedStruct when browsing re-exported module in {:?}",
+            names
+        );
+        assert!(
+            names.contains(&"nested_fn"),
+            "Expected nested_fn when browsing re-exported module in {:?}",
+            names
+        );
+    }
 }

From 6ecee2a415db5f3abf32414372e8325d3ff47f7b Mon Sep 17 00:00:00 2001
From: Wilfred Hughes 
Date: Wed, 14 Jan 2026 18:26:02 +0000
Subject: [PATCH 108/583] internal: Improve docs for discoverConfig

Add concrete examples of CLI invocations and JSONL outputs, use BUCK
for consistency with the first example, and polish the wording.
---
 .../crates/rust-analyzer/src/config.rs        | 116 +++++++++++-------
 .../docs/book/src/configuration_generated.md  | 116 +++++++++++-------
 .../docs/book/src/non_cargo_based_projects.md |   2 +-
 .../rust-analyzer/editors/code/package.json   |   2 +-
 4 files changed, 142 insertions(+), 94 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 28ac94e4deb6..8d6b19a84caa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -478,14 +478,26 @@ config_data! {
         typing_triggerChars: Option = Some("=.".to_owned()),
 
 
-        /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
+        /// Configure a command that rust-analyzer can invoke to
+        /// obtain configuration.
         ///
-        /// [`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.
-        /// `progressLabel` is used for the title in progress indicators, whereas `filesToWatch`
-        /// is used to determine which build system-specific files should be watched in order to
-        /// reload rust-analyzer.
+        /// This is an alternative to manually generating
+        /// `rust-project.json`: it enables rust-analyzer to generate
+        /// rust-project.json on the fly, and regenerate it when
+        /// switching or modifying projects.
+        ///
+        /// This is an object with three fields:
+        ///
+        /// * `command`: the shell command to invoke
+        ///
+        /// * `filesToWatch`: which build system-specific files should
+        /// be watched to trigger regenerating the configuration
+        ///
+        /// * `progressLabel`: the name of the command, used in
+        /// progress indicators in the IDE
+        ///
+        /// Here's an example of a valid configuration:
         ///
-        /// Below is an example of a valid configuration:
         /// ```json
         /// "rust-analyzer.workspace.discoverConfig": {
         ///     "command": [
@@ -500,12 +512,49 @@ config_data! {
         /// }
         /// ```
         ///
-        /// ## Workspace Discovery Protocol
+        /// ## Argument Substitutions
+        ///
+        /// If `command` includes the argument `{arg}`, that argument will be substituted
+        /// with the JSON-serialized form of the following enum:
+        ///
+        /// ```norun
+        /// #[derive(PartialEq, Clone, Debug, Serialize)]
+        /// #[serde(rename_all = "camelCase")]
+        /// pub enum DiscoverArgument {
+        ///    Path(AbsPathBuf),
+        ///    Buildfile(AbsPathBuf),
+        /// }
+        /// ```
+        ///
+        /// rust-analyzer will use the path invocation to find and
+        /// generate a `rust-project.json` and therefore a
+        /// workspace. Example:
+        ///
+        ///
+        /// ```norun
+        /// rust-project develop-json '{ "path": "myproject/src/main.rs" }'
+        /// ```
+        ///
+        /// rust-analyzer will use build file invocations to update an
+        /// existing workspace. Example:
+        ///
+        /// Or with a build file and the configuration above:
+        ///
+        /// ```norun
+        /// rust-project develop-json '{ "buildfile": "myproject/BUCK" }'
+        /// ```
+        ///
+        /// As a reference for implementors, buck2's `rust-project`
+        /// will likely be useful:
+        /// .
+        ///
+        /// ## Discover Command Output
         ///
         /// **Warning**: This format is provisional and subject to change.
         ///
-        /// [`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to
-        /// `DiscoverProjectData::Finished`:
+        /// The discover command should output JSON objects, one per
+        /// line (JSONL format). These objects should correspond to
+        /// this Rust data type:
         ///
         /// ```norun
         /// #[derive(Debug, Clone, Deserialize, Serialize)]
@@ -518,7 +567,14 @@ config_data! {
         /// }
         /// ```
         ///
-        /// As JSON, `DiscoverProjectData::Finished` is:
+        /// For example, a progress event:
+        ///
+        /// ```json
+        /// {"kind":"progress","message":"generating rust-project.json"}
+        /// ```
+        ///
+        /// A finished event can look like this (expanded and
+        /// commented for readability):
         ///
         /// ```json
         /// {
@@ -526,7 +582,7 @@ config_data! {
         ///     "kind": "finished",
         ///     // the file used by a non-Cargo build system to define
         ///     // a package or target.
-        ///     "buildfile": "rust-analyzer/BUILD",
+        ///     "buildfile": "rust-analyzer/BUCK",
         ///     // the contents of a rust-project.json, elided for brevity
         ///     "project": {
         ///         "sysroot": "foo",
@@ -535,41 +591,9 @@ config_data! {
         /// }
         /// ```
         ///
-        /// It is encouraged, but not required, to use the other variants on `DiscoverProjectData`
-        /// to provide a more polished end-user experience.
-        ///
-        /// `DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be
-        /// substituted with the JSON-serialized form of the following enum:
-        ///
-        /// ```norun
-        /// #[derive(PartialEq, Clone, Debug, Serialize)]
-        /// #[serde(rename_all = "camelCase")]
-        /// pub enum DiscoverArgument {
-        ///    Path(AbsPathBuf),
-        ///    Buildfile(AbsPathBuf),
-        /// }
-        /// ```
-        ///
-        /// The JSON representation of `DiscoverArgument::Path` is:
-        ///
-        /// ```json
-        /// {
-        ///     "path": "src/main.rs"
-        /// }
-        /// ```
-        ///
-        /// Similarly, the JSON representation of `DiscoverArgument::Buildfile` is:
-        ///
-        /// ```json
-        /// {
-        ///     "buildfile": "BUILD"
-        /// }
-        /// ```
-        ///
-        /// `DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and
-        /// therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an
-        /// existing workspace. As a reference for implementors, buck2's `rust-project` will likely
-        /// be useful: .
+        /// Only the finished event is required, but the other
+        /// variants are encouraged to give users more feedback about
+        /// progress or errors.
         workspace_discoverConfig: Option = None,
     }
 }
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index c4124aaae075..9bc412631039 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -1619,14 +1619,26 @@ though Cargo might be the eventual consumer.
 
 Default: `null`
 
-Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
+Configure a command that rust-analyzer can invoke to
+obtain configuration.
 
-[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.
-`progressLabel` is used for the title in progress indicators, whereas `filesToWatch`
-is used to determine which build system-specific files should be watched in order to
-reload rust-analyzer.
+This is an alternative to manually generating
+`rust-project.json`: it enables rust-analyzer to generate
+rust-project.json on the fly, and regenerate it when
+switching or modifying projects.
+
+This is an object with three fields:
+
+* `command`: the shell command to invoke
+
+* `filesToWatch`: which build system-specific files should
+be watched to trigger regenerating the configuration
+
+* `progressLabel`: the name of the command, used in
+progress indicators in the IDE
+
+Here's an example of a valid configuration:
 
-Below is an example of a valid configuration:
 ```json
 "rust-analyzer.workspace.discoverConfig": {
     "command": [
@@ -1641,12 +1653,49 @@ Below is an example of a valid configuration:
 }
 ```
 
-## Workspace Discovery Protocol
+## Argument Substitutions
+
+If `command` includes the argument `{arg}`, that argument will be substituted
+with the JSON-serialized form of the following enum:
+
+```norun
+#[derive(PartialEq, Clone, Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum DiscoverArgument {
+   Path(AbsPathBuf),
+   Buildfile(AbsPathBuf),
+}
+```
+
+rust-analyzer will use the path invocation to find and
+generate a `rust-project.json` and therefore a
+workspace. Example:
+
+
+```norun
+rust-project develop-json '{ "path": "myproject/src/main.rs" }'
+```
+
+rust-analyzer will use build file invocations to update an
+existing workspace. Example:
+
+Or with a build file and the configuration above:
+
+```norun
+rust-project develop-json '{ "buildfile": "myproject/BUCK" }'
+```
+
+As a reference for implementors, buck2's `rust-project`
+will likely be useful:
+.
+
+## Discover Command Output
 
 **Warning**: This format is provisional and subject to change.
 
-[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to
-`DiscoverProjectData::Finished`:
+The discover command should output JSON objects, one per
+line (JSONL format). These objects should correspond to
+this Rust data type:
 
 ```norun
 #[derive(Debug, Clone, Deserialize, Serialize)]
@@ -1659,7 +1708,14 @@ enum DiscoverProjectData {
 }
 ```
 
-As JSON, `DiscoverProjectData::Finished` is:
+For example, a progress event:
+
+```json
+{"kind":"progress","message":"generating rust-project.json"}
+```
+
+A finished event can look like this (expanded and
+commented for readability):
 
 ```json
 {
@@ -1667,7 +1723,7 @@ As JSON, `DiscoverProjectData::Finished` is:
     "kind": "finished",
     // the file used by a non-Cargo build system to define
     // a package or target.
-    "buildfile": "rust-analyzer/BUILD",
+    "buildfile": "rust-analyzer/BUCK",
     // the contents of a rust-project.json, elided for brevity
     "project": {
         "sysroot": "foo",
@@ -1676,41 +1732,9 @@ As JSON, `DiscoverProjectData::Finished` is:
 }
 ```
 
-It is encouraged, but not required, to use the other variants on `DiscoverProjectData`
-to provide a more polished end-user experience.
-
-`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be
-substituted with the JSON-serialized form of the following enum:
-
-```norun
-#[derive(PartialEq, Clone, Debug, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub enum DiscoverArgument {
-   Path(AbsPathBuf),
-   Buildfile(AbsPathBuf),
-}
-```
-
-The JSON representation of `DiscoverArgument::Path` is:
-
-```json
-{
-    "path": "src/main.rs"
-}
-```
-
-Similarly, the JSON representation of `DiscoverArgument::Buildfile` is:
-
-```json
-{
-    "buildfile": "BUILD"
-}
-```
-
-`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and
-therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an
-existing workspace. As a reference for implementors, buck2's `rust-project` will likely
-be useful: .
+Only the finished event is required, but the other
+variants are encouraged to give users more feedback about
+progress or errors.
 
 
 ## rust-analyzer.workspace.symbol.search.excludeImports {#workspace.symbol.search.excludeImports}
diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
index a48b025c7b3a..f1f10ae33653 100644
--- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
+++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
@@ -237,7 +237,7 @@ There are four ways to feed `rust-project.json` to rust-analyzer:
     [`"rust-analyzer.workspace.discoverConfig": … }`](./configuration.md#workspace.discoverConfig)
     to specify a workspace discovery command to generate project descriptions
     on-the-fly. Please note that the command output is message-oriented and must
-    follow [the discovery protocol](./configuration.md#workspace-discovery-protocol).
+    output JSONL [as described in the configuration docs](./configuration.md#workspace.discoverConfig).
 
 -   Place `rust-project.json` file at the root of the project, and
     rust-analyzer will discover it.
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 0d91378706a4..a197b7abd84c 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -3135,7 +3135,7 @@
                 "title": "Workspace",
                 "properties": {
                     "rust-analyzer.workspace.discoverConfig": {
-                        "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.\n`progressLabel` is used for the title in progress indicators, whereas `filesToWatch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n        \"command\": [\n                \"rust-project\",\n                \"develop-json\",\n                \"{arg}\"\n        ],\n        \"progressLabel\": \"buck2/rust-project\",\n        \"filesToWatch\": [\n                \"BUCK\"\n        ]\n}\n```\n\n## Workspace Discovery Protocol\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n        Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n        Error { error: String, source: Option },\n        Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n        // the internally-tagged representation of the enum.\n        \"kind\": \"finished\",\n        // the file used by a non-Cargo build system to define\n        // a package or target.\n        \"buildfile\": \"rust-analyzer/BUILD\",\n        // the contents of a rust-project.json, elided for brevity\n        \"project\": {\n                \"sysroot\": \"foo\",\n                \"crates\": []\n        }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n     Path(AbsPathBuf),\n     Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n        \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n        \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: .",
+                        "markdownDescription": "Configure a command that rust-analyzer can invoke to\nobtain configuration.\n\nThis is an alternative to manually generating\n`rust-project.json`: it enables rust-analyzer to generate\nrust-project.json on the fly, and regenerate it when\nswitching or modifying projects.\n\nThis is an object with three fields:\n\n* `command`: the shell command to invoke\n\n* `filesToWatch`: which build system-specific files should\nbe watched to trigger regenerating the configuration\n\n* `progressLabel`: the name of the command, used in\nprogress indicators in the IDE\n\nHere's an example of a valid configuration:\n\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n        \"command\": [\n                \"rust-project\",\n                \"develop-json\",\n                \"{arg}\"\n        ],\n        \"progressLabel\": \"buck2/rust-project\",\n        \"filesToWatch\": [\n                \"BUCK\"\n        ]\n}\n```\n\n## Argument Substitutions\n\nIf `command` includes the argument `{arg}`, that argument will be substituted\nwith the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n     Path(AbsPathBuf),\n     Buildfile(AbsPathBuf),\n}\n```\n\nrust-analyzer will use the path invocation to find and\ngenerate a `rust-project.json` and therefore a\nworkspace. Example:\n\n\n```norun\nrust-project develop-json '{ \"path\": \"myproject/src/main.rs\" }'\n```\n\nrust-analyzer will use build file invocations to update an\nexisting workspace. Example:\n\nOr with a build file and the configuration above:\n\n```norun\nrust-project develop-json '{ \"buildfile\": \"myproject/BUCK\" }'\n```\n\nAs a reference for implementors, buck2's `rust-project`\nwill likely be useful:\n.\n\n## Discover Command Output\n\n**Warning**: This format is provisional and subject to change.\n\nThe discover command should output JSON objects, one per\nline (JSONL format). These objects should correspond to\nthis Rust data type:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n        Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n        Error { error: String, source: Option },\n        Progress { message: String },\n}\n```\n\nFor example, a progress event:\n\n```json\n{\"kind\":\"progress\",\"message\":\"generating rust-project.json\"}\n```\n\nA finished event can look like this (expanded and\ncommented for readability):\n\n```json\n{\n        // the internally-tagged representation of the enum.\n        \"kind\": \"finished\",\n        // the file used by a non-Cargo build system to define\n        // a package or target.\n        \"buildfile\": \"rust-analyzer/BUCK\",\n        // the contents of a rust-project.json, elided for brevity\n        \"project\": {\n                \"sysroot\": \"foo\",\n                \"crates\": []\n        }\n}\n```\n\nOnly the finished event is required, but the other\nvariants are encouraged to give users more feedback about\nprogress or errors.",
                         "default": null,
                         "anyOf": [
                             {

From a1a944880f89b275745d1c5838463433980b3f72 Mon Sep 17 00:00:00 2001
From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Date: Wed, 14 Jan 2026 21:16:54 +0100
Subject: [PATCH 109/583] checksum-freshness: Add binary file inclusion to
 integration test

---
 tests/run-make/checksum-freshness/binary_file | 1 +
 tests/run-make/checksum-freshness/expected.d  | 6 ++++--
 tests/run-make/checksum-freshness/lib.rs      | 3 ++-
 3 files changed, 7 insertions(+), 3 deletions(-)
 create mode 100644 tests/run-make/checksum-freshness/binary_file

diff --git a/tests/run-make/checksum-freshness/binary_file b/tests/run-make/checksum-freshness/binary_file
new file mode 100644
index 000000000000..45f1873fb781
--- /dev/null
+++ b/tests/run-make/checksum-freshness/binary_file
@@ -0,0 +1 @@
+binary
\ No newline at end of file
diff --git a/tests/run-make/checksum-freshness/expected.d b/tests/run-make/checksum-freshness/expected.d
index 51467af53a20..4554c509e36e 100644
--- a/tests/run-make/checksum-freshness/expected.d
+++ b/tests/run-make/checksum-freshness/expected.d
@@ -1,6 +1,8 @@
-lib.d: lib.rs foo.rs
+lib.d: lib.rs foo.rs binary_file
 
 lib.rs:
 foo.rs:
-# checksum:blake3=94af75ee4ed805434484c3de51c9025278e5c3ada2315e2592052e102168a503 file_len:120 lib.rs
+binary_file:
+# checksum:blake3=4ac56f3f877798fb762d714c7bcb72e70133f4cc585f80dbd99c07755ae2c7f6 file_len:222 lib.rs
 # checksum:blake3=2720e17bfda4f3b2a5c96bb61b7e76ed8ebe3359b34128c0e5d8032c090a4f1a file_len:119 foo.rs
+# checksum:blake3=119a5db8711914922c5b1c1908be4958175c5afa95c08888de594725329b5439 file_len:7 binary_file
diff --git a/tests/run-make/checksum-freshness/lib.rs b/tests/run-make/checksum-freshness/lib.rs
index 7bc6757959b1..0cd4243423de 100644
--- a/tests/run-make/checksum-freshness/lib.rs
+++ b/tests/run-make/checksum-freshness/lib.rs
@@ -1,7 +1,8 @@
 // A basic library to be used in tests with no real purpose.
 
 mod foo;
-
+// Binary file with invalid UTF-8 sequence.
+static BINARY_FILE: &[u8] = include_bytes!("binary_file");
 pub fn sum(a: i32, b: i32) -> i32 {
     a + b
 }

From c31add03870e80556703be3c9544109328559379 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Thu, 15 Jan 2026 04:21:53 +0000
Subject: [PATCH 110/583] Prepare for merging from rust-lang/rust

This updates the rust-version file to b6fdaf2a15736cbccf248b532f48e33179614d40.
---
 src/tools/rust-analyzer/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index b53a66c66751..a6ccd9bab393 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-44a5b55557c26353f388400d7da95527256fe260
+b6fdaf2a15736cbccf248b532f48e33179614d40

From d85965634996e60e80440b4fef7415b6379d2487 Mon Sep 17 00:00:00 2001
From: lummax 
Date: Thu, 15 Jan 2026 12:54:01 +0100
Subject: [PATCH 111/583] fix: lookup flycheck by ID instead of vector index

After a recent introduction of per-package flycheck for JSON projects, the code
assumed that `world.flycheck` indices matched `world.workspaces` indices.
However, not all workspaces have flycheck enabled (e.g., JSON projects
without a flycheck template configured), so the flycheck vector can be
shorter than the workspaces vector.

This caused an index-out-of-bounds panic when saving a file in a JSON
project without flycheck configured:

  thread 'Worker' panicked at notification.rs:
  index out of bounds: the len is 0 but the index is 0

Fix by looking up the flycheck handle by its ID (which is the workspace
index set during spawn) rather than using the workspace index directly
as a vector index.
---
 .../src/handlers/notification.rs              | 23 ++++++++++++-------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index d95601043330..6cc40677fb51 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -387,14 +387,21 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                                         } => false,
                                     });
                                 if let Some(idx) = package_workspace_idx {
-                                    let workspace_deps =
-                                        world.all_workspace_dependencies_for_package(&package);
-                                    world.flycheck[idx].restart_for_package(
-                                        package,
-                                        target,
-                                        workspace_deps,
-                                        saved_file.clone(),
-                                    );
+                                    // flycheck handles are indexed by their ID (which is the workspace index),
+                                    // but not all workspaces have flycheck enabled (e.g., JSON projects without
+                                    // a flycheck template). Find the flycheck handle by its ID.
+                                    if let Some(flycheck) =
+                                        world.flycheck.iter().find(|fc| fc.id() == idx)
+                                    {
+                                        let workspace_deps =
+                                            world.all_workspace_dependencies_for_package(&package);
+                                        flycheck.restart_for_package(
+                                            package,
+                                            target,
+                                            workspace_deps,
+                                            saved_file.clone(),
+                                        );
+                                    }
                                 }
                             }
                         }

From 3387a58bc777d31493f37160cdd1f57223f0bc0c Mon Sep 17 00:00:00 2001
From: James Hendry 
Date: Wed, 14 Jan 2026 10:48:24 +0000
Subject: [PATCH 112/583] fN::BITS constants for feature float_bits_const

Also enables the feature for compiler_builtins as otherwise
this causes a warning and conflicts with the Float extension trait

Explicitly use Float trait BITS in miri tests to
prevent warnings against upcoming BITS field for floats
---
 library/compiler-builtins/compiler-builtins/src/lib.rs | 1 +
 library/core/src/num/f128.rs                           | 5 +++++
 library/core/src/num/f16.rs                            | 5 +++++
 library/core/src/num/f32.rs                            | 4 ++++
 library/core/src/num/f64.rs                            | 4 ++++
 src/tools/miri/tests/pass/float.rs                     | 6 +++---
 src/tools/test-float-parse/src/traits.rs               | 2 +-
 7 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs
index c993209699be..342427aca9c7 100644
--- a/library/compiler-builtins/compiler-builtins/src/lib.rs
+++ b/library/compiler-builtins/compiler-builtins/src/lib.rs
@@ -11,6 +11,7 @@
 #![feature(repr_simd)]
 #![feature(macro_metavar_expr_concat)]
 #![feature(rustc_attrs)]
+#![feature(float_bits_const)]
 #![cfg_attr(f16_enabled, feature(f16))]
 #![cfg_attr(f128_enabled, feature(f128))]
 #![no_builtins]
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index bf99fee4fc78..8bb919de729e 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -145,6 +145,11 @@ impl f128 {
     #[unstable(feature = "f128", issue = "116909")]
     pub const RADIX: u32 = 2;
 
+    /// The size of this float type in bits.
+    // #[unstable(feature = "f128", issue = "116909")]
+    #[unstable(feature = "float_bits_const", issue = "151073")]
+    pub const BITS: u32 = 128;
+
     /// Number of significant digits in base 2.
     ///
     /// Note that the size of the mantissa in the bitwise representation is one
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index f39ee22871d5..699b2e3c25c4 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -142,6 +142,11 @@ impl f16 {
     #[unstable(feature = "f16", issue = "116909")]
     pub const RADIX: u32 = 2;
 
+    /// The size of this float type in bits.
+    // #[unstable(feature = "f16", issue = "116909")]
+    #[unstable(feature = "float_bits_const", issue = "151073")]
+    pub const BITS: u32 = 16;
+
     /// Number of significant digits in base 2.
     ///
     /// Note that the size of the mantissa in the bitwise representation is one
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 6fe4285374b2..118ef4ff3d43 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -390,6 +390,10 @@ impl f32 {
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const RADIX: u32 = 2;
 
+    /// The size of this float type in bits.
+    #[unstable(feature = "float_bits_const", issue = "151073")]
+    pub const BITS: u32 = 32;
+
     /// Number of significant digits in base 2.
     ///
     /// Note that the size of the mantissa in the bitwise representation is one
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index d0aca152415e..eacbd3ff20bf 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -390,6 +390,10 @@ impl f64 {
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const RADIX: u32 = 2;
 
+    /// The size of this float type in bits.
+    #[unstable(feature = "float_bits_const", issue = "151073")]
+    pub const BITS: u32 = 64;
+
     /// Number of significant digits in base 2.
     ///
     /// Note that the size of the mantissa in the bitwise representation is one
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index a74a66d5455a..052c83d1aa31 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -353,7 +353,7 @@ macro_rules! test_ftoi_itof {
             assert_itof(i, f, msg);
         }
 
-        let fbits = <$fty>::BITS;
+        let fbits = <$fty as Float>::BITS;
         let fsig_bits = <$fty>::SIGNIFICAND_BITS;
         let ibits = <$ity>::BITS;
         let imax: $ity = <$ity>::MAX;
@@ -528,9 +528,9 @@ macro_rules! test_ftof {
         assert!((<$f1>::NAN as $f2).is_nan(), "{} -> {} nan", stringify!($f1), stringify!($f2));
 
         let min_sub_casted = <$f1>::from_bits(0x1) as $f2;
-        let min_neg_sub_casted = <$f1>::from_bits(0x1 | 1 << (<$f1>::BITS - 1)) as $f2;
+        let min_neg_sub_casted = <$f1>::from_bits(0x1 | 1 << (<$f1 as Float>::BITS - 1)) as $f2;
 
-        if <$f1>::BITS > <$f2>::BITS {
+        if <$f1 as Float>::BITS > <$f2 as Float>::BITS {
             assert_feq(<$f1>::MAX as $f2, <$f2>::INFINITY, "max -> inf");
             assert_feq(<$f1>::MIN as $f2, <$f2>::NEG_INFINITY, "max -> inf");
             assert_biteq(min_sub_casted, f2zero, "min subnormal -> 0.0");
diff --git a/src/tools/test-float-parse/src/traits.rs b/src/tools/test-float-parse/src/traits.rs
index 65a8721bfa5c..0cb44d297bfb 100644
--- a/src/tools/test-float-parse/src/traits.rs
+++ b/src/tools/test-float-parse/src/traits.rs
@@ -155,7 +155,7 @@ macro_rules! impl_float {
                 const BITS: u32 = <$ity>::BITS;
                 const MAN_BITS: u32 = Self::MANTISSA_DIGITS - 1;
                 const MAN_MASK: Self::Int = (Self::Int::ONE << Self::MAN_BITS) - Self::Int::ONE;
-                const SIGN_MASK: Self::Int = Self::Int::ONE << (Self::BITS-1);
+                const SIGN_MASK: Self::Int = Self::Int::ONE << (::BITS-1);
                 fn from_bits(i: Self::Int) -> Self { Self::from_bits(i) }
                 fn to_bits(self) -> Self::Int { self.to_bits() }
                 fn constants() -> &'static Constants {

From b4781c8fe9f97b582b44d615d2edf2bb114f1f2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Thu, 15 Jan 2026 20:39:14 +0000
Subject: [PATCH 113/583] Use default field values in a few more cases

Makes the use expressions significantly shorter.
---
 .../src/diagnostics/conflict_errors.rs        | 48 ++++++-------------
 compiler/rustc_borrowck/src/lib.rs            |  1 +
 .../rustc_hir_analysis/src/hir_wf_check.rs    | 18 ++-----
 compiler/rustc_hir_analysis/src/lib.rs        |  1 +
 4 files changed, 22 insertions(+), 46 deletions(-)

diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 5cfe9db009bf..1b1c72394e75 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -2312,12 +2312,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             tcx: TyCtxt<'hir>,
             issue_span: Span,
             expr_span: Span,
-            body_expr: Option<&'hir hir::Expr<'hir>>,
-            loop_bind: Option<&'hir Ident>,
-            loop_span: Option,
-            head_span: Option,
-            pat_span: Option,
-            head: Option<&'hir hir::Expr<'hir>>,
+            body_expr: Option<&'hir hir::Expr<'hir>> = None,
+            loop_bind: Option<&'hir Ident> = None,
+            loop_span: Option = None,
+            head_span: Option = None,
+            pat_span: Option = None,
+            head: Option<&'hir hir::Expr<'hir>> = None,
         }
         impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
             fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
@@ -2383,17 +2383,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 hir::intravisit::walk_expr(self, ex);
             }
         }
-        let mut finder = ExprFinder {
-            tcx,
-            expr_span: span,
-            issue_span,
-            loop_bind: None,
-            body_expr: None,
-            head_span: None,
-            loop_span: None,
-            pat_span: None,
-            head: None,
-        };
+        let mut finder = ExprFinder { tcx, expr_span: span, issue_span, .. };
         finder.visit_expr(tcx.hir_body(body_id).value);
 
         if let Some(body_expr) = finder.body_expr
@@ -2628,13 +2618,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         struct ExpressionFinder<'tcx> {
             capture_span: Span,
-            closure_change_spans: Vec,
-            closure_arg_span: Option,
-            in_closure: bool,
-            suggest_arg: String,
+            closure_change_spans: Vec = vec![],
+            closure_arg_span: Option = None,
+            in_closure: bool = false,
+            suggest_arg: String = String::new(),
             tcx: TyCtxt<'tcx>,
-            closure_local_id: Option,
-            closure_call_changes: Vec<(Span, String)>,
+            closure_local_id: Option = None,
+            closure_call_changes: Vec<(Span, String)> = vec![],
         }
         impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
             fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
@@ -2715,16 +2705,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }) = self.infcx.tcx.hir_node(self.mir_hir_id())
             && let hir::Node::Expr(expr) = self.infcx.tcx.hir_node(body_id.hir_id)
         {
-            let mut finder = ExpressionFinder {
-                capture_span: *capture_kind_span,
-                closure_change_spans: vec![],
-                closure_arg_span: None,
-                in_closure: false,
-                suggest_arg: String::new(),
-                closure_local_id: None,
-                closure_call_changes: vec![],
-                tcx: self.infcx.tcx,
-            };
+            let mut finder =
+                ExpressionFinder { capture_span: *capture_kind_span, tcx: self.infcx.tcx, .. };
             finder.visit_expr(expr);
 
             if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 91defbad0a0e..f2ea8cef192e 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -4,6 +4,7 @@
 #![allow(internal_features)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
+#![feature(default_field_values)]
 #![feature(file_buffered)]
 #![feature(if_let_guard)]
 #![feature(negative_impls)]
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index f879153c5765..d414f4dbcc24 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -51,12 +51,12 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>(
     struct HirWfCheck<'tcx> {
         tcx: TyCtxt<'tcx>,
         predicate: ty::Predicate<'tcx>,
-        cause: Option>,
-        cause_depth: usize,
+        cause: Option> = None,
+        cause_depth: usize = 0,
         icx: ItemCtxt<'tcx>,
         def_id: LocalDefId,
         param_env: ty::ParamEnv<'tcx>,
-        depth: usize,
+        depth: usize = 0,
     }
 
     impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
@@ -124,16 +124,8 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>(
         }
     }
 
-    let mut visitor = HirWfCheck {
-        tcx,
-        predicate,
-        cause: None,
-        cause_depth: 0,
-        icx,
-        def_id,
-        param_env: tcx.param_env(def_id.to_def_id()),
-        depth: 0,
-    };
+    let param_env = tcx.param_env(def_id.to_def_id());
+    let mut visitor = HirWfCheck { tcx, predicate, icx, def_id, param_env, .. };
 
     // Get the starting `hir::Ty` using our `WellFormedLoc`.
     // We will walk 'into' this type to try to find
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index a51355adf72f..95f0e641c611 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -59,6 +59,7 @@ This API is completely unstable and subject to change.
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 #![feature(assert_matches)]
+#![feature(default_field_values)]
 #![feature(gen_blocks)]
 #![feature(if_let_guard)]
 #![feature(iter_intersperse)]

From 4870a5fb692e0fb796a4db6a8259a22b047e14e9 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Thu, 1 Jan 2026 09:41:09 +0530
Subject: [PATCH 114/583] add worker abstraction

---
 .../src/bidirectional_protocol.rs             | 12 +--
 .../proc-macro-api/src/legacy_protocol.rs     |  4 +-
 .../crates/proc-macro-api/src/lib.rs          | 17 ++--
 .../crates/proc-macro-api/src/process.rs      | 94 +++++++++++++++++--
 4 files changed, 103 insertions(+), 24 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index 5996f882981c..643ba98f5176 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -21,7 +21,7 @@ use crate::{
             serialize_span_data_index_map,
         },
     },
-    process::ProcMacroServerProcess,
+    process::{ProcMacroWorker, SynIO},
     transport::codec::postcard::PostcardProtocol,
     version,
 };
@@ -84,7 +84,7 @@ fn wrap_decode(err: io::Error) -> ServerError {
 }
 
 pub(crate) fn version_check(
-    srv: &ProcMacroServerProcess,
+    srv: &dyn ProcMacroWorker,
     callback: SubCallback<'_>,
 ) -> Result {
     let request = BidirectionalMessage::Request(Request::ApiVersionCheck {});
@@ -101,7 +101,7 @@ pub(crate) fn version_check(
 
 /// Enable support for rust-analyzer span mode if the server supports it.
 pub(crate) fn enable_rust_analyzer_spans(
-    srv: &ProcMacroServerProcess,
+    srv: &dyn ProcMacroWorker,
     callback: SubCallback<'_>,
 ) -> Result {
     let request = BidirectionalMessage::Request(Request::SetConfig(ServerConfig {
@@ -120,7 +120,7 @@ pub(crate) fn enable_rust_analyzer_spans(
 
 /// Finds proc-macros in a given dynamic library.
 pub(crate) fn find_proc_macros(
-    srv: &ProcMacroServerProcess,
+    srv: &dyn ProcMacroWorker,
     dylib_path: &AbsPath,
     callback: SubCallback<'_>,
 ) -> Result, String>, ServerError> {
@@ -175,7 +175,7 @@ pub(crate) fn expand(
         current_dir: Some(current_dir),
     })));
 
-    let response_payload = run_request(&proc_macro.process, task, callback)?;
+    let response_payload = run_request(proc_macro.process.as_ref(), task, callback)?;
 
     match response_payload {
         BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it
@@ -205,7 +205,7 @@ pub(crate) fn expand(
 }
 
 fn run_request(
-    srv: &ProcMacroServerProcess,
+    srv: &dyn ProcMacroWorker,
     msg: BidirectionalMessage,
     callback: SubCallback<'_>,
 ) -> Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index aabe5a011851..56bf863a88e9 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -113,7 +113,7 @@ pub(crate) fn expand(
         current_dir: Some(current_dir),
     };
 
-    let response = send_task(&proc_macro.process, Request::ExpandMacro(Box::new(task)))?;
+    let response = send_task(proc_macro.process.as_ref(), Request::ExpandMacro(Box::new(task)))?;
 
     match response {
         Response::ExpandMacro(it) => Ok(it
@@ -143,7 +143,7 @@ pub(crate) fn expand(
 }
 
 /// Sends a request to the proc-macro server and waits for a response.
-fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result {
+fn send_task(srv: &dyn ProcMacroWorker, req: Request) -> Result {
     if let Some(server_error) = srv.exited() {
         return Err(server_error.clone());
     }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 01195c10feef..dd0c89103a2b 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -27,7 +27,10 @@ use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span};
 use std::{fmt, io, sync::Arc, time::SystemTime};
 
 pub use crate::transport::codec::Codec;
-use crate::{bidirectional_protocol::SubCallback, process::ProcMacroServerProcess};
+use crate::{
+    bidirectional_protocol::SubCallback,
+    process::{ProcMacroServerProcess, ProcMacroWorker},
+};
 
 /// The versions of the server protocol
 pub mod version {
@@ -85,7 +88,7 @@ pub struct ProcMacroClient {
     ///
     /// That means that concurrent salsa requests may block each other when expanding proc macros,
     /// which is unfortunate, but simple and good enough for the time being.
-    process: Arc,
+    worker: Arc,
     path: AbsPathBuf,
 }
 
@@ -107,7 +110,7 @@ impl MacroDylib {
 /// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: Arc,
+    process: Arc,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -171,7 +174,7 @@ impl ProcMacroClient {
         version: Option<&Version>,
     ) -> io::Result {
         let process = ProcMacroServerProcess::run(spawn, version, || "".to_owned())?;
-        Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
+        Ok(ProcMacroClient { worker: Arc::new(process), path: process_path.to_owned() })
     }
 
     /// Returns the absolute path to the proc-macro server.
@@ -186,7 +189,7 @@ impl ProcMacroClient {
         callback: Option>,
     ) -> Result, ServerError> {
         let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
-        let macros = self.process.find_proc_macros(&dylib.path, callback)?;
+        let macros = self.worker.find_proc_macros(&dylib.path, callback)?;
 
         let dylib_path = Arc::new(dylib.path);
         let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
@@ -196,7 +199,7 @@ impl ProcMacroClient {
             Ok(macros) => Ok(macros
                 .into_iter()
                 .map(|(name, kind)| ProcMacro {
-                    process: self.process.clone(),
+                    process: self.worker.clone(),
                     name: name.into(),
                     kind,
                     dylib_path: dylib_path.clone(),
@@ -209,7 +212,7 @@ impl ProcMacroClient {
 
     /// Checks if the proc-macro server has exited.
     pub fn exited(&self) -> Option<&ServerError> {
-        self.process.exited()
+        self.worker.exited()
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index cd387dad0d0b..a206e9fc5d90 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -1,8 +1,9 @@
 //! Handle process life-time and message passing for proc-macro client
 
 use std::{
+    fmt::Debug,
     io::{self, BufRead, BufReader, Read, Write},
-    panic::AssertUnwindSafe,
+    panic::{AssertUnwindSafe, RefUnwindSafe},
     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
     sync::{Arc, Mutex, OnceLock},
 };
@@ -74,12 +75,79 @@ impl ProcessExit for Process {
 }
 
 /// Maintains the state of the proc-macro server process.
-struct ProcessSrvState {
+pub(crate) struct ProcessSrvState {
     process: Box,
     stdin: Box,
     stdout: Box,
 }
 
+impl ProcMacroWorker for ProcMacroServerProcess {
+    fn find_proc_macros(
+        &self,
+        dylib_path: &AbsPath,
+        callback: Option>,
+    ) -> Result, String>, ServerError> {
+        ProcMacroServerProcess::find_proc_macros(self, dylib_path, callback)
+    }
+
+    fn expand(
+        &self,
+        proc_macro: &ProcMacro,
+        subtree: tt::SubtreeView<'_>,
+        attr: Option>,
+        env: Vec<(String, String)>,
+        def_site: Span,
+        call_site: Span,
+        mixed_site: Span,
+        current_dir: String,
+        callback: Option>,
+    ) -> Result, ServerError> {
+        ProcMacroServerProcess::expand(
+            self,
+            proc_macro,
+            subtree,
+            attr,
+            env,
+            def_site,
+            call_site,
+            mixed_site,
+            current_dir,
+            callback,
+        )
+    }
+
+    fn exited(&self) -> Option<&ServerError> {
+        ProcMacroServerProcess::exited(self)
+    }
+
+    fn version(&self) -> u32 {
+        ProcMacroServerProcess::version(self)
+    }
+
+    fn rust_analyzer_spans(&self) -> bool {
+        ProcMacroServerProcess::rust_analyzer_spans(self)
+    }
+
+    fn enable_rust_analyzer_spans(
+        &self,
+        callback: Option>,
+    ) -> Result {
+        ProcMacroServerProcess::enable_rust_analyzer_spans(self, callback)
+    }
+
+    fn use_postcard(&self) -> bool {
+        ProcMacroServerProcess::use_postcard(self)
+    }
+
+    fn state(&self) -> &Mutex {
+        &self.state
+    }
+
+    fn get_exited(&self) -> &OnceLock> {
+        &self.exited
+    }
+}
+
 impl ProcMacroServerProcess {
     /// Starts the proc-macro server and performs a version check
     pub(crate) fn spawn<'a>(
@@ -291,9 +359,13 @@ impl ProcMacroServerProcess {
             ),
         }
     }
+}
 
+pub(crate) struct SynIO;
+
+impl SynIO {
     pub(crate) fn send_task(
-        &self,
+        proc_macro_worker: &dyn ProcMacroWorker,
         send: impl FnOnce(
             &mut dyn Write,
             &mut dyn BufRead,
@@ -302,7 +374,7 @@ impl ProcMacroServerProcess {
         ) -> Result, ServerError>,
         req: Request,
     ) -> Result {
-        self.with_locked_io::(|writer, reader, buf| {
+        SynIO::with_locked_io::(proc_macro_worker, |writer, reader, buf| {
             send(writer, reader, req, buf).and_then(|res| {
                 res.ok_or_else(|| {
                     let message = "proc-macro server did not respond with data".to_owned();
@@ -319,10 +391,10 @@ impl ProcMacroServerProcess {
     }
 
     pub(crate) fn with_locked_io(
-        &self,
+        proc_macro_worker: &dyn ProcMacroWorker,
         f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut C::Buf) -> Result,
     ) -> Result {
-        let state = &mut *self.state.lock().unwrap();
+        let state = &mut *proc_macro_worker.state().lock().unwrap();
         let mut buf = C::Buf::default();
 
         f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| {
@@ -330,7 +402,11 @@ impl ProcMacroServerProcess {
                 match state.process.exit_err() {
                     None => e,
                     Some(server_error) => {
-                        self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone()
+                        proc_macro_worker
+                            .get_exited()
+                            .get_or_init(|| AssertUnwindSafe(server_error))
+                            .0
+                            .clone()
                     }
                 }
             } else {
@@ -340,11 +416,11 @@ impl ProcMacroServerProcess {
     }
 
     pub(crate) fn run_bidirectional(
-        &self,
+        proc_macro_worker: &dyn ProcMacroWorker,
         initial: BidirectionalMessage,
         callback: SubCallback<'_>,
     ) -> Result {
-        self.with_locked_io::(|writer, reader, buf| {
+        SynIO::with_locked_io::(proc_macro_worker, |writer, reader, buf| {
             bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback)
         })
     }

From 98d4496a1e9f03a5193e85304f069757d8032768 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Thu, 1 Jan 2026 13:39:04 +0530
Subject: [PATCH 115/583] add termination flag to procmacroserverprocess

---
 .../crates/proc-macro-api/src/process.rs      | 20 ++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index a206e9fc5d90..efb8e0e84a1f 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -29,6 +29,7 @@ pub(crate) struct ProcMacroServerProcess {
     protocol: Protocol,
     /// Populated when the server exits.
     exited: OnceLock>,
+    single_use: bool,
 }
 
 impl std::fmt::Debug for ProcMacroServerProcess {
@@ -146,6 +147,10 @@ impl ProcMacroWorker for ProcMacroServerProcess {
     fn get_exited(&self) -> &OnceLock> {
         &self.exited
     }
+
+    fn is_reusable(&self) -> bool {
+        !self.single_use
+    }
 }
 
 impl ProcMacroServerProcess {
@@ -226,6 +231,7 @@ impl ProcMacroServerProcess {
                         }
                     },
                     exited: OnceLock::new(),
+                    single_use,
                 })
             };
             let mut srv = create_srv()?;
@@ -335,7 +341,7 @@ impl ProcMacroServerProcess {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        match self.protocol {
+        let result = match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::expand(
                 proc_macro,
                 subtree,
@@ -357,6 +363,18 @@ impl ProcMacroServerProcess {
                 current_dir,
                 callback.expect("callback required for bidirectional protocol"),
             ),
+        };
+
+        if self.is_reusable() {
+            self.terminate();
+        }
+
+        result
+    }
+
+    fn terminate(&self) {
+        if let Ok(mut state) = self.state.lock() {
+            let _ = state.process.child.kill();
         }
     }
 }

From 9fb5d34626d2ce137167e556d53c01f04e410329 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Thu, 1 Jan 2026 16:05:33 +0530
Subject: [PATCH 116/583] add pool of processes

---
 .../src/bidirectional_protocol.rs             |  10 +-
 .../proc-macro-api/src/legacy_protocol.rs     |   2 +-
 .../crates/proc-macro-api/src/lib.rs          |  31 +---
 .../crates/proc-macro-api/src/pool.rs         |  61 ++++++++
 .../crates/proc-macro-api/src/process.rs      | 147 ++++++------------
 5 files changed, 122 insertions(+), 129 deletions(-)
 create mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index 643ba98f5176..137f2dafc0de 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -21,7 +21,7 @@ use crate::{
             serialize_span_data_index_map,
         },
     },
-    process::{ProcMacroWorker, SynIO},
+    process::ProcMacroServerProcess,
     transport::codec::postcard::PostcardProtocol,
     version,
 };
@@ -84,7 +84,7 @@ fn wrap_decode(err: io::Error) -> ServerError {
 }
 
 pub(crate) fn version_check(
-    srv: &dyn ProcMacroWorker,
+    srv: &ProcMacroServerProcess,
     callback: SubCallback<'_>,
 ) -> Result {
     let request = BidirectionalMessage::Request(Request::ApiVersionCheck {});
@@ -101,7 +101,7 @@ pub(crate) fn version_check(
 
 /// Enable support for rust-analyzer span mode if the server supports it.
 pub(crate) fn enable_rust_analyzer_spans(
-    srv: &dyn ProcMacroWorker,
+    srv: &ProcMacroServerProcess,
     callback: SubCallback<'_>,
 ) -> Result {
     let request = BidirectionalMessage::Request(Request::SetConfig(ServerConfig {
@@ -120,7 +120,7 @@ pub(crate) fn enable_rust_analyzer_spans(
 
 /// Finds proc-macros in a given dynamic library.
 pub(crate) fn find_proc_macros(
-    srv: &dyn ProcMacroWorker,
+    srv: &ProcMacroServerProcess,
     dylib_path: &AbsPath,
     callback: SubCallback<'_>,
 ) -> Result, String>, ServerError> {
@@ -205,7 +205,7 @@ pub(crate) fn expand(
 }
 
 fn run_request(
-    srv: &dyn ProcMacroWorker,
+    srv: &ProcMacroServerProcess,
     msg: BidirectionalMessage,
     callback: SubCallback<'_>,
 ) -> Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index 56bf863a88e9..7b546cf7aef6 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -143,7 +143,7 @@ pub(crate) fn expand(
 }
 
 /// Sends a request to the proc-macro server and waits for a response.
-fn send_task(srv: &dyn ProcMacroWorker, req: Request) -> Result {
+fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result {
     if let Some(server_error) = srv.exited() {
         return Err(server_error.clone());
     }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index dd0c89103a2b..ffae28f92c05 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -18,6 +18,7 @@ extern crate rustc_driver as _;
 
 pub mod bidirectional_protocol;
 pub mod legacy_protocol;
+pub mod pool;
 pub mod process;
 pub mod transport;
 
@@ -29,7 +30,8 @@ use std::{fmt, io, sync::Arc, time::SystemTime};
 pub use crate::transport::codec::Codec;
 use crate::{
     bidirectional_protocol::SubCallback,
-    process::{ProcMacroServerProcess, ProcMacroWorker},
+    pool::{ProcMacroServerPool, default_pool_size},
+    process::ProcMacroServerProcess,
 };
 
 /// The versions of the server protocol
@@ -88,7 +90,7 @@ pub struct ProcMacroClient {
     ///
     /// That means that concurrent salsa requests may block each other when expanding proc macros,
     /// which is unfortunate, but simple and good enough for the time being.
-    worker: Arc,
+    pool: Arc,
     path: AbsPathBuf,
 }
 
@@ -110,7 +112,7 @@ impl MacroDylib {
 /// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: Arc,
+    process: Arc,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -188,31 +190,12 @@ impl ProcMacroClient {
         dylib: MacroDylib,
         callback: Option>,
     ) -> Result, ServerError> {
-        let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
-        let macros = self.worker.find_proc_macros(&dylib.path, callback)?;
-
-        let dylib_path = Arc::new(dylib.path);
-        let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
-            .ok()
-            .and_then(|metadata| metadata.modified().ok());
-        match macros {
-            Ok(macros) => Ok(macros
-                .into_iter()
-                .map(|(name, kind)| ProcMacro {
-                    process: self.worker.clone(),
-                    name: name.into(),
-                    kind,
-                    dylib_path: dylib_path.clone(),
-                    dylib_last_modified,
-                })
-                .collect()),
-            Err(message) => Err(ServerError { message, io: None }),
-        }
+        self.pool.load_dylib(&dylib, callback)
     }
 
     /// Checks if the proc-macro server has exited.
     pub fn exited(&self) -> Option<&ServerError> {
-        self.worker.exited()
+        self.pool.exited()
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
new file mode 100644
index 000000000000..685bc05be62a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -0,0 +1,61 @@
+use std::sync::Arc;
+
+use crate::{
+    MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback,
+    process::ProcMacroServerProcess,
+};
+
+#[derive(Debug)]
+pub(crate) struct ProcMacroServerPool {
+    workers: Vec>,
+}
+
+impl ProcMacroServerPool {
+    pub(crate) fn new(workers: Vec>) -> Self {
+        Self { workers }
+    }
+}
+
+impl ProcMacroServerPool {
+    pub(crate) fn exited(&self) -> Option<&ServerError> {
+        for worker in &self.workers {
+            if let Some(e) = worker.exited() {
+                return Some(e);
+            }
+        }
+        None
+    }
+
+    pub(crate) fn load_dylib(
+        &self,
+        dylib: &MacroDylib,
+        _callback: Option>,
+    ) -> Result, ServerError> {
+        let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
+        let mut all_macros = Vec::new();
+
+        for worker in &self.workers {
+            let dylib_path = Arc::new(dylib.path.clone());
+            let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
+                .ok()
+                .and_then(|metadata| metadata.modified().ok());
+            let macros = worker.load_dylib(&dylib.path, None)?;
+
+            for (name, kind) in macros {
+                all_macros.push(ProcMacro {
+                    process: worker.clone(),
+                    name: name.into(),
+                    kind,
+                    dylib_path: Arc::new(dylib.path.clone()),
+                    dylib_last_modified,
+                });
+            }
+        }
+
+        Ok(all_macros)
+    }
+}
+
+pub(crate) fn default_pool_size() -> usize {
+    std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1).min(4)
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index efb8e0e84a1f..30877c5cf491 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -3,7 +3,7 @@
 use std::{
     fmt::Debug,
     io::{self, BufRead, BufReader, Read, Write},
-    panic::{AssertUnwindSafe, RefUnwindSafe},
+    panic::AssertUnwindSafe,
     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
     sync::{Arc, Mutex, OnceLock},
 };
@@ -82,77 +82,6 @@ pub(crate) struct ProcessSrvState {
     stdout: Box,
 }
 
-impl ProcMacroWorker for ProcMacroServerProcess {
-    fn find_proc_macros(
-        &self,
-        dylib_path: &AbsPath,
-        callback: Option>,
-    ) -> Result, String>, ServerError> {
-        ProcMacroServerProcess::find_proc_macros(self, dylib_path, callback)
-    }
-
-    fn expand(
-        &self,
-        proc_macro: &ProcMacro,
-        subtree: tt::SubtreeView<'_>,
-        attr: Option>,
-        env: Vec<(String, String)>,
-        def_site: Span,
-        call_site: Span,
-        mixed_site: Span,
-        current_dir: String,
-        callback: Option>,
-    ) -> Result, ServerError> {
-        ProcMacroServerProcess::expand(
-            self,
-            proc_macro,
-            subtree,
-            attr,
-            env,
-            def_site,
-            call_site,
-            mixed_site,
-            current_dir,
-            callback,
-        )
-    }
-
-    fn exited(&self) -> Option<&ServerError> {
-        ProcMacroServerProcess::exited(self)
-    }
-
-    fn version(&self) -> u32 {
-        ProcMacroServerProcess::version(self)
-    }
-
-    fn rust_analyzer_spans(&self) -> bool {
-        ProcMacroServerProcess::rust_analyzer_spans(self)
-    }
-
-    fn enable_rust_analyzer_spans(
-        &self,
-        callback: Option>,
-    ) -> Result {
-        ProcMacroServerProcess::enable_rust_analyzer_spans(self, callback)
-    }
-
-    fn use_postcard(&self) -> bool {
-        ProcMacroServerProcess::use_postcard(self)
-    }
-
-    fn state(&self) -> &Mutex {
-        &self.state
-    }
-
-    fn get_exited(&self) -> &OnceLock> {
-        &self.exited
-    }
-
-    fn is_reusable(&self) -> bool {
-        !self.single_use
-    }
-}
-
 impl ProcMacroServerProcess {
     /// Starts the proc-macro server and performs a version check
     pub(crate) fn spawn<'a>(
@@ -220,7 +149,11 @@ impl ProcMacroServerProcess {
                 let (process, stdin, stdout) = spawn(format)?;
 
                 io::Result::Ok(ProcMacroServerProcess {
-                    state: Mutex::new(ProcessSrvState { process, stdin, stdout }),
+                    state: Mutex::new(ProcessSrvState {
+                        process,
+                        stdin,
+                        stdout,
+                    }),
                     version: 0,
                     protocol: match format {
                         Some(ProtocolFormat::BidirectionalPostcardPrototype) => {
@@ -271,6 +204,37 @@ impl ProcMacroServerProcess {
         Err(err.unwrap())
     }
 
+    pub(crate) fn load_dylib(
+        &self,
+        dylib_path: &AbsPath,
+        callback: Option>,
+    ) -> Result, ServerError> {
+        let _state = self.state.lock().unwrap();
+
+        // if state.loaded_dylibs.contains(dylib_path) {
+        //     // Already loaded in this worker
+        //     return Ok(Vec::new());
+        // }
+
+        let result = match self.protocol {
+            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
+                legacy_protocol::find_proc_macros(self, dylib_path)?
+            }
+            Protocol::BidirectionalPostcardPrototype { .. } => {
+                let cb = callback.expect("callback required");
+                bidirectional_protocol::find_proc_macros(self, dylib_path, cb)?
+            }
+        };
+
+        match result {
+            Ok(macros) => {
+                // state.loaded_dylibs.insert(dylib_path.to_owned());
+                Ok(macros)
+            }
+            Err(message) => Err(ServerError { message, io: None }),
+        }
+    }
+
     /// Returns the server error if the process has exited.
     pub(crate) fn exited(&self) -> Option<&ServerError> {
         self.exited.get().map(|it| &it.0)
@@ -314,21 +278,6 @@ impl ProcMacroServerProcess {
         }
     }
 
-    /// Finds proc-macros in a given dynamic library.
-    pub(crate) fn find_proc_macros(
-        &self,
-        dylib_path: &AbsPath,
-        callback: Option>,
-    ) -> Result, String>, ServerError> {
-        match self.protocol {
-            Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path),
-            Protocol::BidirectionalPostcardPrototype { .. } => {
-                let cb = callback.expect("callback required for bidirectional protocol");
-                bidirectional_protocol::find_proc_macros(self, dylib_path, cb)
-            }
-        }
-    }
-
     pub(crate) fn expand(
         &self,
         proc_macro: &ProcMacro,
@@ -365,25 +314,25 @@ impl ProcMacroServerProcess {
             ),
         };
 
-        if self.is_reusable() {
+        if !self.is_reusable() {
             self.terminate();
         }
 
         result
     }
 
+    fn is_reusable(&self) -> bool {
+        self.single_use
+    }
+
     fn terminate(&self) {
         if let Ok(mut state) = self.state.lock() {
             let _ = state.process.child.kill();
         }
     }
-}
 
-pub(crate) struct SynIO;
-
-impl SynIO {
     pub(crate) fn send_task(
-        proc_macro_worker: &dyn ProcMacroWorker,
+        &self,
         send: impl FnOnce(
             &mut dyn Write,
             &mut dyn BufRead,
@@ -392,7 +341,7 @@ impl SynIO {
         ) -> Result, ServerError>,
         req: Request,
     ) -> Result {
-        SynIO::with_locked_io::(proc_macro_worker, |writer, reader, buf| {
+        self.with_locked_io::(|writer, reader, buf| {
             send(writer, reader, req, buf).and_then(|res| {
                 res.ok_or_else(|| {
                     let message = "proc-macro server did not respond with data".to_owned();
@@ -409,10 +358,10 @@ impl SynIO {
     }
 
     pub(crate) fn with_locked_io(
-        proc_macro_worker: &dyn ProcMacroWorker,
+        &self,
         f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut C::Buf) -> Result,
     ) -> Result {
-        let state = &mut *proc_macro_worker.state().lock().unwrap();
+        let state = &mut *self.state.lock().unwrap();
         let mut buf = C::Buf::default();
 
         f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| {
@@ -434,11 +383,11 @@ impl SynIO {
     }
 
     pub(crate) fn run_bidirectional(
-        proc_macro_worker: &dyn ProcMacroWorker,
+        &self,
         initial: BidirectionalMessage,
         callback: SubCallback<'_>,
     ) -> Result {
-        SynIO::with_locked_io::(proc_macro_worker, |writer, reader, buf| {
+        self.with_locked_io::(|writer, reader, buf| {
             bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback)
         })
     }

From c685aa912349b96f21485c434d03ee288cfceb9e Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 2 Jan 2026 11:03:20 +0530
Subject: [PATCH 117/583] direct client calls via pool

---
 .../src/bidirectional_protocol.rs             | 11 +--
 .../proc-macro-api/src/legacy_protocol.rs     | 11 +--
 .../crates/proc-macro-api/src/lib.rs          | 21 +-----
 .../crates/proc-macro-api/src/pool.rs         | 72 ++++++++++++++++---
 .../crates/proc-macro-api/src/process.rs      | 65 ++++++-----------
 5 files changed, 98 insertions(+), 82 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index 137f2dafc0de..cd1f6f6f1f33 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -138,6 +138,7 @@ pub(crate) fn find_proc_macros(
 
 pub(crate) fn expand(
     proc_macro: &ProcMacro,
+    process: &ProcMacroServerProcess,
     subtree: tt::SubtreeView<'_>,
     attr: Option>,
     env: Vec<(String, String)>,
@@ -147,7 +148,7 @@ pub(crate) fn expand(
     current_dir: String,
     callback: SubCallback<'_>,
 ) -> Result, crate::ServerError> {
-    let version = proc_macro.process.version();
+    let version = process.version();
     let mut span_data_table = SpanDataIndexMap::default();
     let def_site = span_data_table.insert_full(def_site).0;
     let call_site = span_data_table.insert_full(call_site).0;
@@ -164,7 +165,7 @@ pub(crate) fn expand(
                 call_site,
                 mixed_site,
             },
-            span_data_table: if proc_macro.process.rust_analyzer_spans() {
+            span_data_table: if process.rust_analyzer_spans() {
                 serialize_span_data_index_map(&span_data_table)
             } else {
                 Vec::new()
@@ -175,13 +176,13 @@ pub(crate) fn expand(
         current_dir: Some(current_dir),
     })));
 
-    let response_payload = run_request(proc_macro.process.as_ref(), task, callback)?;
+    let response_payload = run_request(process, task, callback)?;
 
     match response_payload {
         BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it
             .map(|tree| {
                 let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
-                if proc_macro.needs_fixup_change() {
+                if proc_macro.needs_fixup_change(process) {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
@@ -194,7 +195,7 @@ pub(crate) fn expand(
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
                 );
-                if proc_macro.needs_fixup_change() {
+                if proc_macro.needs_fixup_change(process) {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index 7b546cf7aef6..412d20730324 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -77,6 +77,7 @@ pub(crate) fn find_proc_macros(
 
 pub(crate) fn expand(
     proc_macro: &ProcMacro,
+    process: &ProcMacroServerProcess,
     subtree: tt::SubtreeView<'_>,
     attr: Option>,
     env: Vec<(String, String)>,
@@ -85,7 +86,7 @@ pub(crate) fn expand(
     mixed_site: Span,
     current_dir: String,
 ) -> Result, crate::ServerError> {
-    let version = proc_macro.process.version();
+    let version = process.version();
     let mut span_data_table = SpanDataIndexMap::default();
     let def_site = span_data_table.insert_full(def_site).0;
     let call_site = span_data_table.insert_full(call_site).0;
@@ -102,7 +103,7 @@ pub(crate) fn expand(
                 call_site,
                 mixed_site,
             },
-            span_data_table: if proc_macro.process.rust_analyzer_spans() {
+            span_data_table: if process.rust_analyzer_spans() {
                 serialize_span_data_index_map(&span_data_table)
             } else {
                 Vec::new()
@@ -113,13 +114,13 @@ pub(crate) fn expand(
         current_dir: Some(current_dir),
     };
 
-    let response = send_task(proc_macro.process.as_ref(), Request::ExpandMacro(Box::new(task)))?;
+    let response = send_task(process, Request::ExpandMacro(Box::new(task)))?;
 
     match response {
         Response::ExpandMacro(it) => Ok(it
             .map(|tree| {
                 let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
-                if proc_macro.needs_fixup_change() {
+                if proc_macro.needs_fixup_change(process) {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
@@ -132,7 +133,7 @@ pub(crate) fn expand(
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
                 );
-                if proc_macro.needs_fixup_change() {
+                if proc_macro.needs_fixup_change(process) {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index ffae28f92c05..fe17e14024cc 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -112,7 +112,7 @@ impl MacroDylib {
 /// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: Arc,
+    process: ProcMacroServerPool,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -126,7 +126,6 @@ impl PartialEq for ProcMacro {
             && self.kind == other.kind
             && self.dylib_path == other.dylib_path
             && self.dylib_last_modified == other.dylib_last_modified
-            && Arc::ptr_eq(&self.process, &other.process)
     }
 }
 
@@ -210,8 +209,8 @@ impl ProcMacro {
         self.kind
     }
 
-    fn needs_fixup_change(&self) -> bool {
-        let version = self.process.version();
+    fn needs_fixup_change(&self, process: &ProcMacroServerProcess) -> bool {
+        let version = process.version();
         (version::RUST_ANALYZER_SPAN_SUPPORT..version::HASHED_AST_ID).contains(&version)
     }
 
@@ -241,20 +240,6 @@ impl ProcMacro {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        let (mut subtree, mut attr) = (subtree, attr);
-        let (mut subtree_changed, mut attr_changed);
-        if self.needs_fixup_change() {
-            subtree_changed = tt::TopSubtree::from_subtree(subtree);
-            self.change_fixup_to_match_old_server(&mut subtree_changed);
-            subtree = subtree_changed.view();
-
-            if let Some(attr) = &mut attr {
-                attr_changed = tt::TopSubtree::from_subtree(*attr);
-                self.change_fixup_to_match_old_server(&mut attr_changed);
-                *attr = attr_changed.view();
-            }
-        }
-
         self.process.expand(
             self,
             subtree,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index 685bc05be62a..4639374f3e24 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -1,29 +1,38 @@
 use std::sync::Arc;
 
+use tt::Span;
+
 use crate::{
     MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback,
     process::ProcMacroServerProcess,
 };
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub(crate) struct ProcMacroServerPool {
-    workers: Vec>,
+    workers: Arc<[ProcMacroServerProcess]>,
 }
 
 impl ProcMacroServerPool {
-    pub(crate) fn new(workers: Vec>) -> Self {
-        Self { workers }
+    pub(crate) fn new(workers: Vec) -> Self {
+        Self { workers: workers.into() }
     }
 }
 
 impl ProcMacroServerPool {
     pub(crate) fn exited(&self) -> Option<&ServerError> {
-        for worker in &self.workers {
-            if let Some(e) = worker.exited() {
-                return Some(e);
+        for worker in &*self.workers {
+            worker.exited()?;
+        }
+        self.workers[0].exited()
+    }
+
+    fn pick_process(&self) -> &ProcMacroServerProcess {
+        for workers in &*self.workers {
+            if workers.can_use() {
+                return workers;
             }
         }
-        None
+        &self.workers[0]
     }
 
     pub(crate) fn load_dylib(
@@ -34,16 +43,16 @@ impl ProcMacroServerPool {
         let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
         let mut all_macros = Vec::new();
 
-        for worker in &self.workers {
+        for worker in &*self.workers {
             let dylib_path = Arc::new(dylib.path.clone());
             let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
                 .ok()
                 .and_then(|metadata| metadata.modified().ok());
-            let macros = worker.load_dylib(&dylib.path, None)?;
+            let macros = worker.find_proc_macros(&dylib.path, None)?.unwrap();
 
             for (name, kind) in macros {
                 all_macros.push(ProcMacro {
-                    process: worker.clone(),
+                    process: self.clone(),
                     name: name.into(),
                     kind,
                     dylib_path: Arc::new(dylib.path.clone()),
@@ -54,6 +63,47 @@ impl ProcMacroServerPool {
 
         Ok(all_macros)
     }
+
+    pub(crate) fn expand(
+        &self,
+        proc_macro: &ProcMacro,
+        subtree: tt::SubtreeView<'_>,
+        attr: Option>,
+        env: Vec<(String, String)>,
+        def_site: Span,
+        call_site: Span,
+        mixed_site: Span,
+        current_dir: String,
+        callback: Option>,
+    ) -> Result, ServerError> {
+        let process = self.pick_process();
+
+        let (mut subtree, mut attr) = (subtree, attr);
+        let (mut subtree_changed, mut attr_changed);
+        if proc_macro.needs_fixup_change(process) {
+            subtree_changed = tt::TopSubtree::from_subtree(subtree);
+            proc_macro.change_fixup_to_match_old_server(&mut subtree_changed);
+            subtree = subtree_changed.view();
+
+            if let Some(attr) = &mut attr {
+                attr_changed = tt::TopSubtree::from_subtree(*attr);
+                proc_macro.change_fixup_to_match_old_server(&mut attr_changed);
+                *attr = attr_changed.view();
+            }
+        }
+
+        process.expand(
+            proc_macro,
+            subtree,
+            attr,
+            env,
+            def_site,
+            call_site,
+            mixed_site,
+            current_dir,
+            callback,
+        )
+    }
 }
 
 pub(crate) fn default_pool_size() -> usize {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index 30877c5cf491..a41bb58e74be 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -5,7 +5,10 @@ use std::{
     io::{self, BufRead, BufReader, Read, Write},
     panic::AssertUnwindSafe,
     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
-    sync::{Arc, Mutex, OnceLock},
+    sync::{
+        Arc, Mutex, OnceLock,
+        atomic::{AtomicBool, Ordering},
+    },
 };
 
 use paths::AbsPath;
@@ -29,7 +32,7 @@ pub(crate) struct ProcMacroServerProcess {
     protocol: Protocol,
     /// Populated when the server exits.
     exited: OnceLock>,
-    single_use: bool,
+    can_use: AtomicBool,
 }
 
 impl std::fmt::Debug for ProcMacroServerProcess {
@@ -149,11 +152,7 @@ impl ProcMacroServerProcess {
                 let (process, stdin, stdout) = spawn(format)?;
 
                 io::Result::Ok(ProcMacroServerProcess {
-                    state: Mutex::new(ProcessSrvState {
-                        process,
-                        stdin,
-                        stdout,
-                    }),
+                    state: Mutex::new(ProcessSrvState { process, stdin, stdout }),
                     version: 0,
                     protocol: match format {
                         Some(ProtocolFormat::BidirectionalPostcardPrototype) => {
@@ -164,7 +163,7 @@ impl ProcMacroServerProcess {
                         }
                     },
                     exited: OnceLock::new(),
-                    single_use,
+                    can_use: AtomicBool::new(true),
                 })
             };
             let mut srv = create_srv()?;
@@ -204,34 +203,20 @@ impl ProcMacroServerProcess {
         Err(err.unwrap())
     }
 
-    pub(crate) fn load_dylib(
+    /// Finds proc-macros in a given dynamic library.
+    pub(crate) fn find_proc_macros(
         &self,
         dylib_path: &AbsPath,
         callback: Option>,
-    ) -> Result, ServerError> {
-        let _state = self.state.lock().unwrap();
-
-        // if state.loaded_dylibs.contains(dylib_path) {
-        //     // Already loaded in this worker
-        //     return Ok(Vec::new());
-        // }
-
-        let result = match self.protocol {
+    ) -> Result, String>, ServerError> {
+        match self.protocol {
             Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::find_proc_macros(self, dylib_path)?
+                legacy_protocol::find_proc_macros(self, dylib_path)
             }
             Protocol::BidirectionalPostcardPrototype { .. } => {
-                let cb = callback.expect("callback required");
-                bidirectional_protocol::find_proc_macros(self, dylib_path, cb)?
+                let cb = callback.expect("callback required for bidirectional protocol");
+                bidirectional_protocol::find_proc_macros(self, dylib_path, cb)
             }
-        };
-
-        match result {
-            Ok(macros) => {
-                // state.loaded_dylibs.insert(dylib_path.to_owned());
-                Ok(macros)
-            }
-            Err(message) => Err(ServerError { message, io: None }),
         }
     }
 
@@ -290,9 +275,11 @@ impl ProcMacroServerProcess {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
+        self.can_use.store(false, Ordering::Release);
         let result = match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::expand(
                 proc_macro,
+                    self,
                 subtree,
                 attr,
                 env,
@@ -303,6 +290,7 @@ impl ProcMacroServerProcess {
             ),
             Protocol::BidirectionalPostcardPrototype { .. } => bidirectional_protocol::expand(
                 proc_macro,
+                self,
                 subtree,
                 attr,
                 env,
@@ -314,23 +302,10 @@ impl ProcMacroServerProcess {
             ),
         };
 
-        if !self.is_reusable() {
-            self.terminate();
-        }
-
+        self.can_use.store(true, Ordering::Release);
         result
     }
 
-    fn is_reusable(&self) -> bool {
-        self.single_use
-    }
-
-    fn terminate(&self) {
-        if let Ok(mut state) = self.state.lock() {
-            let _ = state.process.child.kill();
-        }
-    }
-
     pub(crate) fn send_task(
         &self,
         send: impl FnOnce(
@@ -391,6 +366,10 @@ impl ProcMacroServerProcess {
             bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback)
         })
     }
+
+    pub(crate) fn can_use(&self) -> bool {
+        self.can_use.load(Ordering::Acquire)
+    }
 }
 
 /// Manages the execution of the proc-macro server process.

From 82e758acc1a76b2a8d8083121b031fc2b3e3a653 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 2 Jan 2026 11:46:00 +0530
Subject: [PATCH 118/583] add better process picker and improve loading dylib

---
 .../crates/proc-macro-api/src/pool.rs         | 48 ++++++++++---------
 .../crates/proc-macro-api/src/process.rs      | 14 +++---
 2 files changed, 32 insertions(+), 30 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index 4639374f3e24..4215b0f2c04f 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -27,12 +27,10 @@ impl ProcMacroServerPool {
     }
 
     fn pick_process(&self) -> &ProcMacroServerProcess {
-        for workers in &*self.workers {
-            if workers.can_use() {
-                return workers;
-            }
-        }
-        &self.workers[0]
+        self.workers
+            .iter()
+            .min_by_key(|w| w.number_of_active_req())
+            .expect("worker pool must not be empty")
     }
 
     pub(crate) fn load_dylib(
@@ -41,27 +39,31 @@ impl ProcMacroServerPool {
         _callback: Option>,
     ) -> Result, ServerError> {
         let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
-        let mut all_macros = Vec::new();
 
-        for worker in &*self.workers {
-            let dylib_path = Arc::new(dylib.path.clone());
-            let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
-                .ok()
-                .and_then(|metadata| metadata.modified().ok());
-            let macros = worker.find_proc_macros(&dylib.path, None)?.unwrap();
+        let dylib_path = Arc::new(dylib.path.clone());
+        let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
+            .ok()
+            .and_then(|metadata| metadata.modified().ok());
 
-            for (name, kind) in macros {
-                all_macros.push(ProcMacro {
-                    process: self.clone(),
-                    name: name.into(),
-                    kind,
-                    dylib_path: Arc::new(dylib.path.clone()),
-                    dylib_last_modified,
-                });
-            }
+        let first = &self.workers[0];
+        let macros = first.find_proc_macros(&dylib.path, None)?.unwrap();
+
+        for worker in &self.workers[1..] {
+            let _ = worker.find_proc_macros(&dylib.path, None)?;
         }
 
-        Ok(all_macros)
+        let result = macros
+            .into_iter()
+            .map(|(name, kind)| ProcMacro {
+                process: self.clone(),
+                name: name.into(),
+                kind,
+                dylib_path: dylib_path.clone(),
+                dylib_last_modified,
+            })
+            .collect();
+
+        Ok(result)
     }
 
     pub(crate) fn expand(
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index a41bb58e74be..775d59174f1c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -7,7 +7,7 @@ use std::{
     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
     sync::{
         Arc, Mutex, OnceLock,
-        atomic::{AtomicBool, Ordering},
+        atomic::{AtomicU32, Ordering},
     },
 };
 
@@ -32,7 +32,7 @@ pub(crate) struct ProcMacroServerProcess {
     protocol: Protocol,
     /// Populated when the server exits.
     exited: OnceLock>,
-    can_use: AtomicBool,
+    active: AtomicU32,
 }
 
 impl std::fmt::Debug for ProcMacroServerProcess {
@@ -163,7 +163,7 @@ impl ProcMacroServerProcess {
                         }
                     },
                     exited: OnceLock::new(),
-                    can_use: AtomicBool::new(true),
+                    active: AtomicU32::new(0),
                 })
             };
             let mut srv = create_srv()?;
@@ -275,7 +275,7 @@ impl ProcMacroServerProcess {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        self.can_use.store(false, Ordering::Release);
+        self.active.fetch_add(1, Ordering::AcqRel);
         let result = match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::expand(
                 proc_macro,
@@ -302,7 +302,7 @@ impl ProcMacroServerProcess {
             ),
         };
 
-        self.can_use.store(true, Ordering::Release);
+        self.active.fetch_sub(1, Ordering::AcqRel);
         result
     }
 
@@ -367,8 +367,8 @@ impl ProcMacroServerProcess {
         })
     }
 
-    pub(crate) fn can_use(&self) -> bool {
-        self.can_use.load(Ordering::Acquire)
+    pub(crate) fn number_of_active_req(&self) -> u32 {
+        self.active.load(Ordering::Acquire)
     }
 }
 

From 922bc7e4d49da5656c901e0a2d4a9805f413dcf8 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 2 Jan 2026 11:56:19 +0530
Subject: [PATCH 119/583] rename process to pool in ProcMacro struct

---
 src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs  | 4 ++--
 src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index fe17e14024cc..09999ea5081a 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -112,7 +112,7 @@ impl MacroDylib {
 /// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: ProcMacroServerPool,
+    pool: ProcMacroServerPool,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -240,7 +240,7 @@ impl ProcMacro {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        self.process.expand(
+        self.pool.expand(
             self,
             subtree,
             attr,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index 4215b0f2c04f..eef8d0194dc5 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -55,7 +55,7 @@ impl ProcMacroServerPool {
         let result = macros
             .into_iter()
             .map(|(name, kind)| ProcMacro {
-                process: self.clone(),
+                pool: self.clone(),
                 name: name.into(),
                 kind,
                 dylib_path: dylib_path.clone(),

From c4c336ad7c291686542826bfd5b0bc8f2e298777 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 2 Jan 2026 12:11:11 +0530
Subject: [PATCH 120/583] keep it clean and tidy

---
 src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index eef8d0194dc5..fe4649441b89 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -1,3 +1,4 @@
+//! This module represents Process Pool
 use std::sync::Arc;
 
 use tt::Span;

From c8a3551bd162c6374d4c49bfc06070488edb44ed Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sat, 3 Jan 2026 11:32:08 +0530
Subject: [PATCH 121/583] change callback from FnMut to Fn as we only transform
 messages and not really change change state

---
 src/tools/rust-analyzer/crates/load-cargo/src/lib.rs        | 6 +++---
 .../crates/proc-macro-api/src/bidirectional_protocol.rs     | 2 +-
 .../rust-analyzer/crates/proc-macro-api/src/process.rs      | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index 8342492a33a4..ccc9aa4291ec 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -435,7 +435,7 @@ pub fn load_proc_macro(
 ) -> ProcMacroLoadResult {
     let res: Result, _> = (|| {
         let dylib = MacroDylib::new(path.to_path_buf());
-        let vec = server.load_dylib(dylib, Some(&mut reject_subrequests)).map_err(|e| {
+        let vec = server.load_dylib(dylib, Some(&reject_subrequests)).map_err(|e| {
             ProcMacroLoadingError::ProcMacroSrvError(format!("{e}").into_boxed_str())
         })?;
         if vec.is_empty() {
@@ -541,7 +541,7 @@ impl ProcMacroExpander for Expander {
         mixed_site: Span,
         current_dir: String,
     ) -> Result {
-        let mut cb = |req| match req {
+        let cb = |req| match req {
             SubRequest::LocalFilePath { file_id } => {
                 let file_id = FileId::from_raw(file_id);
                 let source_root_id = db.file_source_root(file_id).source_root_id(db);
@@ -613,7 +613,7 @@ impl ProcMacroExpander for Expander {
             call_site,
             mixed_site,
             current_dir,
-            Some(&mut cb),
+            Some(&cb),
         ) {
             Ok(Ok(subtree)) => Ok(subtree),
             Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err)),
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index cd1f6f6f1f33..25266c46fe89 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -28,7 +28,7 @@ use crate::{
 
 pub mod msg;
 
-pub type SubCallback<'a> = &'a mut dyn FnMut(SubRequest) -> Result;
+pub type SubCallback<'a> = &'a dyn Fn(SubRequest) -> Result;
 
 pub fn run_conversation(
     writer: &mut dyn Write,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index 775d59174f1c..c1b95fa7f10e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -168,7 +168,7 @@ impl ProcMacroServerProcess {
             };
             let mut srv = create_srv()?;
             tracing::info!("sending proc-macro server version check");
-            match srv.version_check(Some(&mut reject_subrequests)) {
+            match srv.version_check(Some(&reject_subrequests)) {
                 Ok(v) if v > version::CURRENT_API_VERSION => {
                     let process_version = binary_server_version();
                     err = Some(io::Error::other(format!(
@@ -182,7 +182,7 @@ impl ProcMacroServerProcess {
                     srv.version = v;
                     if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT
                         && let Ok(new_mode) =
-                            srv.enable_rust_analyzer_spans(Some(&mut reject_subrequests))
+                            srv.enable_rust_analyzer_spans(Some(&reject_subrequests))
                     {
                         match &mut srv.protocol {
                             Protocol::LegacyJson { mode }

From 66bca6a25214332ff559acb7678d3ce423279e77 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sat, 3 Jan 2026 11:36:09 +0530
Subject: [PATCH 122/583] propagate error from load dylibs

---
 .../crates/proc-macro-api/src/pool.rs         | 28 ++++++++++---------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index fe4649441b89..20389f666883 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -37,23 +37,27 @@ impl ProcMacroServerPool {
     pub(crate) fn load_dylib(
         &self,
         dylib: &MacroDylib,
-        _callback: Option>,
+        callback: Option>,
     ) -> Result, ServerError> {
-        let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
+        let _span = tracing::info_span!("ProcMacroServer::load_dylib").entered();
 
         let dylib_path = Arc::new(dylib.path.clone());
-        let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
-            .ok()
-            .and_then(|metadata| metadata.modified().ok());
+        let dylib_last_modified =
+            std::fs::metadata(dylib_path.as_path()).ok().and_then(|m| m.modified().ok());
 
-        let first = &self.workers[0];
-        let macros = first.find_proc_macros(&dylib.path, None)?.unwrap();
+        let (first, rest) = self.workers.split_first().expect("worker pool must not be empty");
 
-        for worker in &self.workers[1..] {
-            let _ = worker.find_proc_macros(&dylib.path, None)?;
+        let macros = first
+            .find_proc_macros(&dylib.path, callback)?
+            .map_err(|e| ServerError { message: e, io: None })?;
+
+        for worker in rest {
+            worker
+                .find_proc_macros(&dylib.path, callback)?
+                .map_err(|e| ServerError { message: e, io: None })?;
         }
 
-        let result = macros
+        Ok(macros
             .into_iter()
             .map(|(name, kind)| ProcMacro {
                 pool: self.clone(),
@@ -62,9 +66,7 @@ impl ProcMacroServerPool {
                 dylib_path: dylib_path.clone(),
                 dylib_last_modified,
             })
-            .collect();
-
-        Ok(result)
+            .collect())
     }
 
     pub(crate) fn expand(

From 09c91b79a84faebb2e06094f8c8d1592a371a3e4 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sat, 3 Jan 2026 13:43:23 +0530
Subject: [PATCH 123/583] pick workers which have not exited

---
 .../rust-analyzer/crates/proc-macro-api/src/pool.rs    | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index 20389f666883..c75e9742a5d1 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -27,11 +27,15 @@ impl ProcMacroServerPool {
         self.workers[0].exited()
     }
 
-    fn pick_process(&self) -> &ProcMacroServerProcess {
+    fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> {
         self.workers
             .iter()
+            .filter(|w| w.exited().is_none())
             .min_by_key(|w| w.number_of_active_req())
-            .expect("worker pool must not be empty")
+            .ok_or_else(|| ServerError {
+                message: "all proc-macro server workers have exited".into(),
+                io: None,
+            })
     }
 
     pub(crate) fn load_dylib(
@@ -81,7 +85,7 @@ impl ProcMacroServerPool {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        let process = self.pick_process();
+        let process = self.pick_process()?;
 
         let (mut subtree, mut attr) = (subtree, attr);
         let (mut subtree_changed, mut attr_changed);

From 0936597b3ea2944869d1a0b2058746e295eeb16d Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 4 Jan 2026 05:02:15 +0530
Subject: [PATCH 124/583] add version to pool

---
 .../src/bidirectional_protocol.rs             |  4 ++--
 .../proc-macro-api/src/legacy_protocol.rs     |  4 ++--
 .../crates/proc-macro-api/src/lib.rs          | 18 ++++++++++++--
 .../crates/proc-macro-api/src/pool.rs         | 24 +++++++------------
 4 files changed, 28 insertions(+), 22 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index 25266c46fe89..b5f43e1d3726 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -182,7 +182,7 @@ pub(crate) fn expand(
         BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it
             .map(|tree| {
                 let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
-                if proc_macro.needs_fixup_change(process) {
+                if proc_macro.needs_fixup_change() {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
@@ -195,7 +195,7 @@ pub(crate) fn expand(
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
                 );
-                if proc_macro.needs_fixup_change(process) {
+                if proc_macro.needs_fixup_change() {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index 412d20730324..eedf66d46086 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -120,7 +120,7 @@ pub(crate) fn expand(
         Response::ExpandMacro(it) => Ok(it
             .map(|tree| {
                 let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
-                if proc_macro.needs_fixup_change(process) {
+                if proc_macro.needs_fixup_change() {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
@@ -133,7 +133,7 @@ pub(crate) fn expand(
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
                 );
-                if proc_macro.needs_fixup_change(process) {
+                if proc_macro.needs_fixup_change() {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 09999ea5081a..4874e63244c4 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -209,8 +209,8 @@ impl ProcMacro {
         self.kind
     }
 
-    fn needs_fixup_change(&self, process: &ProcMacroServerProcess) -> bool {
-        let version = process.version();
+    fn needs_fixup_change(&self) -> bool {
+        let version = self.pool.version();
         (version::RUST_ANALYZER_SPAN_SUPPORT..version::HASHED_AST_ID).contains(&version)
     }
 
@@ -240,6 +240,20 @@ impl ProcMacro {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
+        let (mut subtree, mut attr) = (subtree, attr);
+        let (mut subtree_changed, mut attr_changed);
+        if self.needs_fixup_change() {
+            subtree_changed = tt::TopSubtree::from_subtree(subtree);
+            self.change_fixup_to_match_old_server(&mut subtree_changed);
+            subtree = subtree_changed.view();
+
+            if let Some(attr) = &mut attr {
+                attr_changed = tt::TopSubtree::from_subtree(*attr);
+                self.change_fixup_to_match_old_server(&mut attr_changed);
+                *attr = attr_changed.view();
+            }
+        }
+
         self.pool.expand(
             self,
             subtree,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index c75e9742a5d1..fd8b726f820e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -1,4 +1,4 @@
-//! This module represents Process Pool
+//! A pool of proc-macro server processes
 use std::sync::Arc;
 
 use tt::Span;
@@ -11,11 +11,13 @@ use crate::{
 #[derive(Debug, Clone)]
 pub(crate) struct ProcMacroServerPool {
     workers: Arc<[ProcMacroServerProcess]>,
+    version: u32,
 }
 
 impl ProcMacroServerPool {
     pub(crate) fn new(workers: Vec) -> Self {
-        Self { workers: workers.into() }
+        let version = workers[0].version();
+        Self { workers: workers.into(), version }
     }
 }
 
@@ -87,20 +89,6 @@ impl ProcMacroServerPool {
     ) -> Result, ServerError> {
         let process = self.pick_process()?;
 
-        let (mut subtree, mut attr) = (subtree, attr);
-        let (mut subtree_changed, mut attr_changed);
-        if proc_macro.needs_fixup_change(process) {
-            subtree_changed = tt::TopSubtree::from_subtree(subtree);
-            proc_macro.change_fixup_to_match_old_server(&mut subtree_changed);
-            subtree = subtree_changed.view();
-
-            if let Some(attr) = &mut attr {
-                attr_changed = tt::TopSubtree::from_subtree(*attr);
-                proc_macro.change_fixup_to_match_old_server(&mut attr_changed);
-                *attr = attr_changed.view();
-            }
-        }
-
         process.expand(
             proc_macro,
             subtree,
@@ -113,6 +101,10 @@ impl ProcMacroServerPool {
             callback,
         )
     }
+
+    pub(crate) fn version(&self) -> u32 {
+        self.version
+    }
 }
 
 pub(crate) fn default_pool_size() -> usize {

From 263015a4a4d4cbbbc1e7cd0e118c10e0a8e740df Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 4 Jan 2026 05:50:16 +0530
Subject: [PATCH 125/583] remove expand from pool

---
 .../crates/proc-macro-api/src/lib.rs          |  2 +-
 .../crates/proc-macro-api/src/pool.rs         | 31 +------------------
 2 files changed, 2 insertions(+), 31 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 4874e63244c4..ad462ff31a1b 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -254,7 +254,7 @@ impl ProcMacro {
             }
         }
 
-        self.pool.expand(
+        self.pool.pick_process()?.expand(
             self,
             subtree,
             attr,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index fd8b726f820e..13a4b5ee8f9e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -1,8 +1,6 @@
 //! A pool of proc-macro server processes
 use std::sync::Arc;
 
-use tt::Span;
-
 use crate::{
     MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback,
     process::ProcMacroServerProcess,
@@ -29,7 +27,7 @@ impl ProcMacroServerPool {
         self.workers[0].exited()
     }
 
-    fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> {
+    pub(crate) fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> {
         self.workers
             .iter()
             .filter(|w| w.exited().is_none())
@@ -75,33 +73,6 @@ impl ProcMacroServerPool {
             .collect())
     }
 
-    pub(crate) fn expand(
-        &self,
-        proc_macro: &ProcMacro,
-        subtree: tt::SubtreeView<'_>,
-        attr: Option>,
-        env: Vec<(String, String)>,
-        def_site: Span,
-        call_site: Span,
-        mixed_site: Span,
-        current_dir: String,
-        callback: Option>,
-    ) -> Result, ServerError> {
-        let process = self.pick_process()?;
-
-        process.expand(
-            proc_macro,
-            subtree,
-            attr,
-            env,
-            def_site,
-            call_site,
-            mixed_site,
-            current_dir,
-            callback,
-        )
-    }
-
     pub(crate) fn version(&self) -> u32 {
         self.version
     }

From e3e7c2905442499a6c3eb778be14730dc16d82af Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:41:39 +0530
Subject: [PATCH 126/583] remove default pool size from pool

---
 src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index 13a4b5ee8f9e..0cb505aa40a9 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -77,7 +77,3 @@ impl ProcMacroServerPool {
         self.version
     }
 }
-
-pub(crate) fn default_pool_size() -> usize {
-    std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1).min(4)
-}

From 96ecd1773c56e11def32667a3d70f4b29563f137 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:44:15 +0530
Subject: [PATCH 127/583] add num process in config

---
 .../crates/rust-analyzer/src/config.rs          | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 28ac94e4deb6..cb6552c32ffb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -380,6 +380,8 @@ config_data! {
         /// The number of worker threads in the main loop. The default `null` means to pick
         /// automatically.
         numThreads: Option = None,
+        /// The number of proc-macro-srv processes 
+        proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
 
         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
         procMacro_attributes_enable: bool = true,
@@ -2641,6 +2643,13 @@ impl Config {
         }
     }
 
+    pub fn proc_macro_num_processes(&self) -> usize {
+        match self.proc_macro_processes() {
+            NumProcesses::Concrete(0) | NumProcesses::Physical => num_cpus::get_physical(),
+            &NumProcesses::Concrete(n) => n,
+        }
+    }
+
     pub fn main_loop_num_threads(&self) -> usize {
         match self.numThreads() {
             Some(NumThreads::Concrete(0)) | None | Some(NumThreads::Physical) => {
@@ -3077,6 +3086,14 @@ pub enum NumThreads {
     Concrete(usize),
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
+#[serde(rename_all = "snake_case")]
+pub enum NumProcesses {
+    Physical,
+    #[serde(untagged)]
+    Concrete(usize),
+}
+
 macro_rules! _default_val {
     ($default:expr, $ty:ty) => {{
         let default_: $ty = $default;

From 9d5e60005addf5eb8635884f051771a8119f0bb2 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:44:44 +0530
Subject: [PATCH 128/583] add proc_macro_processes in load config

---
 .../crates/load-cargo/src/lib.rs              | 24 ++++++++++++++-----
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index ccc9aa4291ec..c2935d94a8a7 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -45,6 +45,7 @@ pub struct LoadCargoConfig {
     pub load_out_dirs_from_check: bool,
     pub with_proc_macro_server: ProcMacroServerChoice,
     pub prefill_caches: bool,
+    pub proc_macro_processes: usize,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -113,15 +114,25 @@ pub fn load_workspace_into_db(
     let proc_macro_server = match &load_config.with_proc_macro_server {
         ProcMacroServerChoice::Sysroot => ws.find_sysroot_proc_macro_srv().map(|it| {
             it.and_then(|it| {
-                ProcMacroClient::spawn(&it, extra_env, ws.toolchain.as_ref()).map_err(Into::into)
+                ProcMacroClient::spawn(
+                    &it,
+                    extra_env,
+                    ws.toolchain.as_ref(),
+                    load_config.proc_macro_processes,
+                )
+                .map_err(Into::into)
             })
             .map_err(|e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str()))
         }),
-        ProcMacroServerChoice::Explicit(path) => {
-            Some(ProcMacroClient::spawn(path, extra_env, ws.toolchain.as_ref()).map_err(|e| {
-                ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())
-            }))
-        }
+        ProcMacroServerChoice::Explicit(path) => Some(
+            ProcMacroClient::spawn(
+                path,
+                extra_env,
+                ws.toolchain.as_ref(),
+                load_config.proc_macro_processes,
+            )
+            .map_err(|e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())),
+        ),
         ProcMacroServerChoice::None => Some(Err(ProcMacroLoadingError::Disabled)),
     };
     match &proc_macro_server {
@@ -657,6 +668,7 @@ mod tests {
             load_out_dirs_from_check: false,
             with_proc_macro_server: ProcMacroServerChoice::None,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (db, _vfs, _proc_macro) =
             load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {}).unwrap();

From 721361f2898b4e3c299e3687b8b4581b2e6c2b48 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:45:06 +0530
Subject: [PATCH 129/583] update all cli workflows

---
 .../crates/proc-macro-api/src/lib.rs          |  5 ++---
 .../rust-analyzer/src/cli/analysis_stats.rs   |  1 +
 .../rust-analyzer/src/cli/diagnostics.rs      |  1 +
 .../crates/rust-analyzer/src/cli/lsif.rs      |  1 +
 .../rust-analyzer/src/cli/prime_caches.rs     |  1 +
 .../crates/rust-analyzer/src/cli/run_tests.rs |  1 +
 .../rust-analyzer/src/cli/rustc_tests.rs      |  1 +
 .../crates/rust-analyzer/src/cli/scip.rs      |  1 +
 .../crates/rust-analyzer/src/cli/ssr.rs       |  2 ++
 .../src/cli/unresolved_references.rs          |  1 +
 .../src/integrated_benchmarks.rs              |  3 +++
 .../crates/rust-analyzer/src/reload.rs        | 20 +++++++++++--------
 12 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index ad462ff31a1b..2c0008ae1d82 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -29,9 +29,7 @@ use std::{fmt, io, sync::Arc, time::SystemTime};
 
 pub use crate::transport::codec::Codec;
 use crate::{
-    bidirectional_protocol::SubCallback,
-    pool::{ProcMacroServerPool, default_pool_size},
-    process::ProcMacroServerProcess,
+    bidirectional_protocol::SubCallback, pool::ProcMacroServerPool, process::ProcMacroServerProcess,
 };
 
 /// The versions of the server protocol
@@ -155,6 +153,7 @@ impl ProcMacroClient {
             Item = (impl AsRef, &'a Option>),
         > + Clone,
         version: Option<&Version>,
+        num_process: usize,
     ) -> io::Result {
         let process = ProcMacroServerProcess::spawn(process_path, env, version)?;
         Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index a02d1a78564f..1995d3889891 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -91,6 +91,7 @@ impl flags::AnalysisStats {
                 }
             },
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
 
         let build_scripts_time = if self.disable_build_scripts {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index 776069f155f0..575c77f8428c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -41,6 +41,7 @@ impl flags::Diagnostics {
             load_out_dirs_from_check: !self.disable_build_scripts,
             with_proc_macro_server,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (db, _vfs, _proc_macro) =
             load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index f3b0699d5515..e5e238db6361 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -293,6 +293,7 @@ impl flags::Lsif {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path));
         let root = ProjectManifest::discover_single(&path)?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs
index 467d8a53884a..d5da6791797b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs
@@ -38,6 +38,7 @@ impl flags::PrimeCaches {
             // we want to ensure that this command, not `load_workspace_at`,
             // is responsible for that work.
             prefill_caches: false,
+            proc_macro_processes: config.proc_macro_num_processes(),
         };
 
         let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
index 82ace8c8b315..d4a56d773e7d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
@@ -23,6 +23,7 @@ impl flags::RunTests {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (ref db, _vfs, _proc_macro) =
             load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 249566d2ac16..e8c6c5f4d4f7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -103,6 +103,7 @@ impl Tester {
             load_out_dirs_from_check: false,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (db, _vfs, _proc_macro) =
             load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 271d2507bcfe..ed0476697c9c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -52,6 +52,7 @@ impl flags::Scip {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: true,
+            proc_macro_processes: config.proc_macro_num_processes(),
         };
         let cargo_config = config.cargo(None);
         let (db, vfs, _) = load_workspace_at(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
index 39186831459c..5c69bda723fb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
@@ -20,6 +20,7 @@ impl flags::Ssr {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (ref db, vfs, _proc_macro) = load_workspace_at(
             &std::env::current_dir()?,
@@ -56,6 +57,7 @@ impl flags::Search {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (ref db, _vfs, _proc_macro) = load_workspace_at(
             &std::env::current_dir()?,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
index 294add682d01..49c6fcb91ebf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -44,6 +44,7 @@ impl flags::UnresolvedReferences {
             load_out_dirs_from_check: !self.disable_build_scripts,
             with_proc_macro_server,
             prefill_caches: false,
+            proc_macro_processes: config.proc_macro_num_processes(),
         };
         let (db, vfs, _proc_macro) =
             load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index c61825b99fec..d16ca2fb48ac 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -53,6 +53,7 @@ fn integrated_highlighting_benchmark() {
         load_out_dirs_from_check: true,
         with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: false,
+        proc_macro_processes: 1,
     };
 
     let (db, vfs, _proc_macro) = {
@@ -121,6 +122,7 @@ fn integrated_completion_benchmark() {
         load_out_dirs_from_check: true,
         with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: true,
+        proc_macro_processes: 1,
     };
 
     let (db, vfs, _proc_macro) = {
@@ -322,6 +324,7 @@ fn integrated_diagnostics_benchmark() {
         load_out_dirs_from_check: true,
         with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: true,
+        proc_macro_processes: 1,
     };
 
     let (db, vfs, _proc_macro) = {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index ccafbd7b30b9..83f4a19b39fa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -701,15 +701,19 @@ impl GlobalState {
                     _ => Default::default(),
                 };
                 info!("Using proc-macro server at {path}");
+                let num_process = self.config.proc_macro_num_processes();
 
-                Some(ProcMacroClient::spawn(&path, &env, ws.toolchain.as_ref()).map_err(|err| {
-                    tracing::error!(
-                        "Failed to run proc-macro server from path {path}, error: {err:?}",
-                    );
-                    anyhow::format_err!(
-                        "Failed to run proc-macro server from path {path}, error: {err:?}",
-                    )
-                }))
+                Some(
+                    ProcMacroClient::spawn(&path, &env, ws.toolchain.as_ref(), num_process)
+                        .map_err(|err| {
+                            tracing::error!(
+                                "Failed to run proc-macro server from path {path}, error: {err:?}",
+                            );
+                            anyhow::format_err!(
+                                "Failed to run proc-macro server from path {path}, error: {err:?}",
+                            )
+                        }),
+                )
             }))
         }
 

From 0587cbdd6fc2321564394828d6172fd268fc6617 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:56:02 +0530
Subject: [PATCH 130/583] optimize pick_process to short circuit and return as
 early as possible if a valid process is found

---
 .../crates/proc-macro-api/src/pool.rs         | 28 +++++++++++++------
 .../crates/rust-analyzer/src/config.rs        |  2 +-
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
index 0cb505aa40a9..a637bc0e480a 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -28,14 +28,26 @@ impl ProcMacroServerPool {
     }
 
     pub(crate) fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> {
-        self.workers
-            .iter()
-            .filter(|w| w.exited().is_none())
-            .min_by_key(|w| w.number_of_active_req())
-            .ok_or_else(|| ServerError {
-                message: "all proc-macro server workers have exited".into(),
-                io: None,
-            })
+        let mut best: Option<&ProcMacroServerProcess> = None;
+        let mut best_load = u32::MAX;
+
+        for w in self.workers.iter().filter(|w| w.exited().is_none()) {
+            let load = w.number_of_active_req();
+
+            if load == 0 {
+                return Ok(w);
+            }
+
+            if load < best_load {
+                best = Some(w);
+                best_load = load;
+            }
+        }
+
+        best.ok_or_else(|| ServerError {
+            message: "all proc-macro server workers have exited".into(),
+            io: None,
+        })
     }
 
     pub(crate) fn load_dylib(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index cb6552c32ffb..409f2468a7b1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -380,7 +380,7 @@ config_data! {
         /// The number of worker threads in the main loop. The default `null` means to pick
         /// automatically.
         numThreads: Option = None,
-        /// The number of proc-macro-srv processes 
+        /// The number of proc-macro-srv processes
         proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
 
         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.

From b49417eea9fcb252ffe62d35068375534d553af0 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 10:22:49 +0530
Subject: [PATCH 131/583] fix test and update some autogen files

---
 .../crates/rust-analyzer/src/config.rs        | 24 ++++++++++++++++--
 .../docs/book/src/configuration_generated.md  | 10 ++++++++
 .../rust-analyzer/editors/code/package.json   | 25 +++++++++++++++++++
 3 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 409f2468a7b1..015e6df96f2f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -380,8 +380,6 @@ config_data! {
         /// The number of worker threads in the main loop. The default `null` means to pick
         /// automatically.
         numThreads: Option = None,
-        /// The number of proc-macro-srv processes
-        proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
 
         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
         procMacro_attributes_enable: bool = true,
@@ -392,6 +390,12 @@ config_data! {
         /// Internal config, path to proc-macro server executable.
         procMacro_server: Option = None,
 
+        /// Number of proc-macro server processes to spawn.
+        ///
+        /// Controls how many independent `proc-macro-srv` processes rust-analyzer
+        /// runs in parallel to handle macro expansion.
+        proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
+
         /// The path where to save memory profiling output.
         ///
         /// **Note:** Memory profiling is not enabled by default in rust-analyzer builds, you need to build
@@ -3920,6 +3924,22 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 },
             ],
         },
+        "NumProcesses" => set! {
+            "anyOf": [
+                {
+                    "type": "number",
+                    "minimum": 0,
+                    "maximum": 255
+                },
+                {
+                    "type": "string",
+                    "enum": ["physical"],
+                    "enumDescriptions": [
+                        "Use the number of physical cores",
+                    ],
+                },
+            ],
+        },
         "Option" => set! {
             "anyOf": [
                 {
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index c4124aaae075..d3f41fb152e1 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -1325,6 +1325,16 @@ Default: `null`
 Internal config, path to proc-macro server executable.
 
 
+## rust-analyzer.proc.macro.processes {#proc.macro.processes}
+
+Default: `1`
+
+Number of proc-macro server processes to spawn.
+
+Controls how many independent `proc-macro-srv` processes rust-analyzer
+runs in parallel to handle macro expansion.
+
+
 ## rust-analyzer.profiling.memoryProfile {#profiling.memoryProfile}
 
 Default: `null`
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 0d91378706a4..a1266c4a67ed 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -2783,6 +2783,31 @@
                     }
                 }
             },
+            {
+                "title": "Proc",
+                "properties": {
+                    "rust-analyzer.proc.macro.processes": {
+                        "markdownDescription": "Number of proc-macro server processes to spawn.\n\nControls how many independent `proc-macro-srv` processes rust-analyzer\nruns in parallel to handle macro expansion.",
+                        "default": 1,
+                        "anyOf": [
+                            {
+                                "type": "number",
+                                "minimum": 0,
+                                "maximum": 255
+                            },
+                            {
+                                "type": "string",
+                                "enum": [
+                                    "physical"
+                                ],
+                                "enumDescriptions": [
+                                    "Use the number of physical cores"
+                                ]
+                            }
+                        ]
+                    }
+                }
+            },
             {
                 "title": "Profiling",
                 "properties": {

From a81da31f4096bf754eed0c40b384ef2f5b4d854c Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 10:32:00 +0530
Subject: [PATCH 132/583] rename from proc_macro_processes to
 procMacro_processes

---
 .../crates/rust-analyzer/src/config.rs        | 10 +++----
 .../docs/book/src/configuration_generated.md  | 16 +++++------
 .../rust-analyzer/editors/code/package.json   | 28 +++++++++----------
 3 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 015e6df96f2f..98495f6150da 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -387,14 +387,14 @@ config_data! {
         /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
         procMacro_enable: bool = true,
 
-        /// Internal config, path to proc-macro server executable.
-        procMacro_server: Option = None,
-
         /// Number of proc-macro server processes to spawn.
         ///
         /// Controls how many independent `proc-macro-srv` processes rust-analyzer
         /// runs in parallel to handle macro expansion.
-        proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
+        procMacro_processes: NumProcesses = NumProcesses::Concrete(1),
+
+        /// Internal config, path to proc-macro server executable.
+        procMacro_server: Option = None,
 
         /// The path where to save memory profiling output.
         ///
@@ -2648,7 +2648,7 @@ impl Config {
     }
 
     pub fn proc_macro_num_processes(&self) -> usize {
-        match self.proc_macro_processes() {
+        match self.procMacro_processes() {
             NumProcesses::Concrete(0) | NumProcesses::Physical => num_cpus::get_physical(),
             &NumProcesses::Concrete(n) => n,
         }
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index d3f41fb152e1..5b1a2e111196 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -1318,14 +1318,7 @@ These proc-macros will be ignored when trying to expand them.
 This config takes a map of crate names with the exported proc-macro names to ignore as values.
 
 
-## rust-analyzer.procMacro.server {#procMacro.server}
-
-Default: `null`
-
-Internal config, path to proc-macro server executable.
-
-
-## rust-analyzer.proc.macro.processes {#proc.macro.processes}
+## rust-analyzer.procMacro.processes {#procMacro.processes}
 
 Default: `1`
 
@@ -1335,6 +1328,13 @@ Controls how many independent `proc-macro-srv` processes rust-analyzer
 runs in parallel to handle macro expansion.
 
 
+## rust-analyzer.procMacro.server {#procMacro.server}
+
+Default: `null`
+
+Internal config, path to proc-macro server executable.
+
+
 ## rust-analyzer.profiling.memoryProfile {#profiling.memoryProfile}
 
 Default: `null`
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index a1266c4a67ed..406e41767f6d 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -2773,20 +2773,7 @@
             {
                 "title": "Proc Macro",
                 "properties": {
-                    "rust-analyzer.procMacro.server": {
-                        "markdownDescription": "Internal config, path to proc-macro server executable.",
-                        "default": null,
-                        "type": [
-                            "null",
-                            "string"
-                        ]
-                    }
-                }
-            },
-            {
-                "title": "Proc",
-                "properties": {
-                    "rust-analyzer.proc.macro.processes": {
+                    "rust-analyzer.procMacro.processes": {
                         "markdownDescription": "Number of proc-macro server processes to spawn.\n\nControls how many independent `proc-macro-srv` processes rust-analyzer\nruns in parallel to handle macro expansion.",
                         "default": 1,
                         "anyOf": [
@@ -2808,6 +2795,19 @@
                     }
                 }
             },
+            {
+                "title": "Proc Macro",
+                "properties": {
+                    "rust-analyzer.procMacro.server": {
+                        "markdownDescription": "Internal config, path to proc-macro server executable.",
+                        "default": null,
+                        "type": [
+                            "null",
+                            "string"
+                        ]
+                    }
+                }
+            },
             {
                 "title": "Profiling",
                 "properties": {

From 8da5de0ca02c316fee3fb97c11052e3d70a32bef Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 11 Jan 2026 15:46:03 +0530
Subject: [PATCH 133/583] rebased changes

---
 .../crates/proc-macro-api/src/lib.rs          | 26 +++++++++++++++----
 .../crates/proc-macro-api/src/process.rs      | 13 +++-------
 2 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 2c0008ae1d82..3acd0b292a31 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -155,8 +155,15 @@ impl ProcMacroClient {
         version: Option<&Version>,
         num_process: usize,
     ) -> io::Result {
-        let process = ProcMacroServerProcess::spawn(process_path, env, version)?;
-        Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
+        let pool_size = num_process;
+        let mut workers = Vec::with_capacity(pool_size);
+        for _ in 0..pool_size {
+            let worker = ProcMacroServerProcess::spawn(process_path, env.clone(), version)?;
+            workers.push(worker);
+        }
+
+        let pool = ProcMacroServerPool::new(workers);
+        Ok(ProcMacroClient { pool: Arc::new(pool), path: process_path.to_owned() })
     }
 
     /// Invokes `spawn` and returns a client connected to the resulting read and write handles.
@@ -170,11 +177,20 @@ impl ProcMacroClient {
             Box,
             Box,
             Box,
-        )>,
+        )> + Clone,
         version: Option<&Version>,
+        num_process: usize,
     ) -> io::Result {
-        let process = ProcMacroServerProcess::run(spawn, version, || "".to_owned())?;
-        Ok(ProcMacroClient { worker: Arc::new(process), path: process_path.to_owned() })
+        let pool_size = num_process;
+        let mut workers = Vec::with_capacity(pool_size);
+        for _ in 0..pool_size {
+            let worker =
+                ProcMacroServerProcess::run(spawn.clone(), version, || "".to_owned())?;
+            workers.push(worker);
+        }
+
+        let pool = ProcMacroServerPool::new(workers);
+        Ok(ProcMacroClient { pool: Arc::new(pool), path: process_path.to_owned() })
     }
 
     /// Returns the absolute path to the proc-macro server.
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index c1b95fa7f10e..2f5bef69abd5 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -210,9 +210,8 @@ impl ProcMacroServerProcess {
         callback: Option>,
     ) -> Result, String>, ServerError> {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::find_proc_macros(self, dylib_path)
-            }
+            Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path),
+
             Protocol::BidirectionalPostcardPrototype { .. } => {
                 let cb = callback.expect("callback required for bidirectional protocol");
                 bidirectional_protocol::find_proc_macros(self, dylib_path, cb)
@@ -279,7 +278,7 @@ impl ProcMacroServerProcess {
         let result = match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::expand(
                 proc_macro,
-                    self,
+                self,
                 subtree,
                 attr,
                 env,
@@ -344,11 +343,7 @@ impl ProcMacroServerProcess {
                 match state.process.exit_err() {
                     None => e,
                     Some(server_error) => {
-                        proc_macro_worker
-                            .get_exited()
-                            .get_or_init(|| AssertUnwindSafe(server_error))
-                            .0
-                            .clone()
+                        self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone()
                     }
                 }
             } else {

From c31698b6958b5c818e4f4c86d3e4d12e128152f9 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Sat, 17 Jan 2026 19:24:19 +0800
Subject: [PATCH 134/583] Improve move_guard redundanted block

Example
---
```rust
fn main() {
    match 92 {
        x $0if x > 10 => {
            let _ = true;
            false
        },
        _ => true
    }
}
```

**Before this PR**

```rust
fn main() {
    match 92 {
        x => if x > 10 {
            {
                let _ = true;
                false
            }
        },
        _ => true
    }
}
```

**After this PR**

```rust
fn main() {
    match 92 {
        x => if x > 10 {
            let _ = true;
            false
        },
        _ => true
    }
}
```
---
 .../ide-assists/src/handlers/move_guard.rs    | 35 ++++++++++++++++---
 .../crates/ide-assists/src/utils.rs           | 11 ++++++
 2 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
index 31baa63372ff..84f02bdfdba6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
@@ -49,7 +49,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
 
     let guard_condition = guard.condition()?.reset_indent();
     let arm_expr = match_arm.expr()?;
-    let then_branch = make::block_expr(None, Some(arm_expr.reset_indent().indent(1.into())));
+    let then_branch = crate::utils::wrap_block(&arm_expr);
     let if_expr = make::expr_if(guard_condition, then_branch, None).indent(arm_expr.indent_level());
 
     let target = guard.syntax().text_range();
@@ -344,6 +344,35 @@ fn main() {
         );
     }
 
+    #[test]
+    fn move_guard_to_block_arm_body_works() {
+        check_assist(
+            move_guard_to_arm_body,
+            r#"
+fn main() {
+    match 92 {
+        x $0if x > 10 => {
+            let _ = true;
+            false
+        },
+        _ => true
+    }
+}
+"#,
+            r#"
+fn main() {
+    match 92 {
+        x => if x > 10 {
+            let _ = true;
+            false
+        },
+        _ => true
+    }
+}
+"#,
+        );
+    }
+
     #[test]
     fn move_let_guard_to_arm_body_works() {
         check_assist(
@@ -395,9 +424,7 @@ fn main() {
             && true
             && true {
             {
-                {
-                    false
-                }
+                false
             }
         },
         _ => true
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 9a96374c00af..4b8c19305793 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -86,6 +86,17 @@ pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option ast::BlockExpr {
+    if let ast::Expr::BlockExpr(block) = expr
+        && let Some(first) = block.syntax().first_token()
+        && first.kind() == T!['{']
+    {
+        block.reset_indent()
+    } else {
+        make::block_expr(None, Some(expr.reset_indent().indent(1.into())))
+    }
+}
+
 /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
 /// `#[test_case(...)]`, `#[tokio::test]` and similar.
 /// Also a regular `#[test]` annotation is supported.

From cbad6dd11772b3d02a4eaca4e049f66907bb72a1 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 16 Jan 2026 16:23:56 +0100
Subject: [PATCH 135/583] fix: Do not show sysroot dependencies in symbol
 search

---
 src/tools/rust-analyzer/crates/base-db/src/input.rs       | 3 ++-
 src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs | 8 +++++++-
 .../rust-analyzer/crates/project-model/src/workspace.rs   | 6 +++++-
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index 240f1264917a..94793a3618e1 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -221,6 +221,7 @@ pub enum LangCrateOrigin {
     ProcMacro,
     Std,
     Test,
+    Dependency,
     Other,
 }
 
@@ -245,7 +246,7 @@ impl fmt::Display for LangCrateOrigin {
             LangCrateOrigin::ProcMacro => "proc_macro",
             LangCrateOrigin::Std => "std",
             LangCrateOrigin::Test => "test",
-            LangCrateOrigin::Other => "other",
+            LangCrateOrigin::Other | LangCrateOrigin::Dependency => "other",
         };
         f.write_str(text)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index d7f4c66f465b..183f6b649537 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -27,7 +27,7 @@ use std::{
     ops::ControlFlow,
 };
 
-use base_db::{LibraryRoots, LocalRoots, RootQueryDb, SourceRootId};
+use base_db::{CrateOrigin, LangCrateOrigin, LibraryRoots, LocalRoots, RootQueryDb, SourceRootId};
 use fst::{Automaton, Streamer, raw::IndexedValue};
 use hir::{
     Crate, Module,
@@ -446,6 +446,12 @@ impl<'db> SymbolIndex<'db> {
                     {
                         continue;
                     }
+                    if let CrateOrigin::Lang(LangCrateOrigin::Dependency | LangCrateOrigin::Other) =
+                        krate.origin(db)
+                    {
+                        // don't show dependencies of the sysroot
+                        continue;
+                    }
                     collector.push_crate_root(krate);
                 }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index fa3a79e041e0..8f15f7e1507c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -1161,6 +1161,8 @@ fn project_json_to_crate_graph(
                                 name: Some(name.canonical_name().to_owned()),
                             }
                         }
+                    } else if is_sysroot {
+                        CrateOrigin::Lang(LangCrateOrigin::Dependency)
                     } else {
                         CrateOrigin::Local { repo: None, name: None }
                     },
@@ -1294,6 +1296,8 @@ fn cargo_to_crate_graph(
                             name: Some(Symbol::intern(&pkg_data.name)),
                         }
                     }
+                } else if cargo.is_sysroot() {
+                    CrateOrigin::Lang(LangCrateOrigin::Dependency)
                 } else {
                     CrateOrigin::Library {
                         repo: pkg_data.repository.clone(),
@@ -1717,7 +1721,7 @@ fn extend_crate_graph_with_sysroot(
                     !matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc),
                 )),
                 LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
-                LangCrateOrigin::Other => (),
+                LangCrateOrigin::Other | LangCrateOrigin::Dependency => (),
             }
         }
     }

From bfbee86a2de8e281db70b08367ce8245082431a2 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 16 Jan 2026 13:35:31 +0100
Subject: [PATCH 136/583] feat: Trigger flycheck if non-workspace files get
 modified

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: dino 
---
 .../crates/rust-analyzer/src/config.rs              |  6 ++++++
 .../rust-analyzer/src/handlers/notification.rs      | 13 +++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 28ac94e4deb6..7382edfa96f7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -1043,6 +1043,7 @@ pub struct Config {
     /// The workspace roots as registered by the LSP client
     workspace_roots: Vec,
     caps: ClientCapabilities,
+    /// The LSP root path, deprecated in favor of `workspace_roots`
     root_path: AbsPathBuf,
     snippets: Vec,
     client_info: Option,
@@ -1366,6 +1367,10 @@ impl Config {
 
         self.discovered_projects_from_command.push(ProjectJsonFromCommand { data, buildfile });
     }
+
+    pub fn workspace_roots(&self) -> &[AbsPathBuf] {
+        &self.workspace_roots
+    }
 }
 
 #[derive(Default, Debug)]
@@ -1742,6 +1747,7 @@ impl Config {
     }
 
     pub fn root_path(&self) -> &AbsPathBuf {
+        // We should probably use `workspace_roots` here if set
         &self.root_path
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index 6cc40677fb51..138310b78f62 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -289,11 +289,24 @@ pub(crate) fn handle_did_change_watched_files(
     state: &mut GlobalState,
     params: DidChangeWatchedFilesParams,
 ) -> anyhow::Result<()> {
+    // we want to trigger flycheck if a file outside of our workspaces has changed,
+    // as to reduce stale diagnostics when outside changes happen
+    let mut trigger_flycheck = false;
     for change in params.changes.iter().unique_by(|&it| &it.uri) {
         if let Ok(path) = from_proto::abs_path(&change.uri) {
+            if !trigger_flycheck {
+                trigger_flycheck =
+                    state.config.workspace_roots().iter().any(|root| !path.starts_with(root));
+            }
             state.loader.handle.invalidate(path);
         }
     }
+
+    if trigger_flycheck && state.config.check_on_save(None) {
+        for flycheck in state.flycheck.iter() {
+            flycheck.restart_workspace(None);
+        }
+    }
     Ok(())
 }
 

From ebcbff2a2e79d0c36442bb19a9eb154dc202f2d5 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 19 Jan 2026 04:50:23 +0200
Subject: [PATCH 137/583] Do not mix the order of builtin/regular derives in
 "Expand macro recursively"

---
 .../rust-analyzer/crates/hir/src/semantics.rs   |  7 +++++--
 .../crates/ide/src/expand_macro.rs              | 17 ++++++++++++++++-
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index e55b693ef018..98f5739600f3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -641,11 +641,14 @@ impl<'db> SemanticsImpl<'db> {
         })
     }
 
-    pub fn expand_derive_macro(&self, attr: &ast::Attr) -> Option>> {
+    pub fn expand_derive_macro(
+        &self,
+        attr: &ast::Attr,
+    ) -> Option>>> {
         let res: Vec<_> = self
             .derive_macro_calls(attr)?
             .into_iter()
-            .flat_map(|call| {
+            .map(|call| {
                 let file_id = call?.left()?;
                 let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id);
                 let root_node = value.0.syntax_node();
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index ba8b3aa9cafe..44285d9315af 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -63,7 +63,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
             .take_while(|it| it != &token)
             .filter(|it| it.kind() == T![,])
             .count();
-        let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone();
+        let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone()?;
         let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?;
         let expansion_span_map = db.expansion_span_map(expansion_file_id);
         let mut expansion = format(
@@ -848,4 +848,19 @@ struct S {
                 u32"#]],
         );
     }
+
+    #[test]
+    fn regression_21489() {
+        check(
+            r#"
+//- proc_macros: derive_identity
+//- minicore: derive, fmt
+#[derive(Debug, proc_macros::DeriveIdentity$0)]
+struct Foo;
+        "#,
+            expect![[r#"
+                proc_macros::DeriveIdentity
+                struct Foo;"#]],
+        );
+    }
 }

From 4dad9b90819bac5aeb50182d77ee0a90c5a196d5 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 19 Jan 2026 05:21:54 +0200
Subject: [PATCH 138/583] Insert type vars and normalize for the type of a used
 `static`

They have their own special path, so they slipped through.
---
 .../crates/hir-ty/src/infer/path.rs           |  1 +
 .../crates/hir-ty/src/tests/simple.rs         | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index b11650bbcd9a..ef1a610a323d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -93,6 +93,7 @@ impl<'db> InferenceContext<'_, 'db> {
         if let GenericDefId::StaticId(_) = generic_def {
             // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type.
             let ty = self.db.value_ty(value_def)?.skip_binder();
+            let ty = self.process_remote_user_written_ty(ty);
             return Some(ValuePathResolution::NonGeneric(ty));
         };
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 28759bcbae61..d2a4149bc630 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3997,3 +3997,46 @@ extern "C" fn foo() -> ! {
     "#,
     );
 }
+
+#[test]
+fn regression_21478() {
+    check_infer(
+        r#"
+//- minicore: unsize, coerce_unsized
+struct LazyLock(T);
+
+impl LazyLock {
+    const fn new() -> Self {
+        loop {}
+    }
+
+    fn force(this: &Self) -> &T {
+        loop {}
+    }
+}
+
+static VALUES_LAZY_LOCK: LazyLock<[u32; { 0 }]> = LazyLock::new();
+
+fn foo() {
+    let _ = LazyLock::force(&VALUES_LAZY_LOCK);
+}
+    "#,
+        expect![[r#"
+            73..96 '{     ...     }': LazyLock
+            83..90 'loop {}': !
+            88..90 '{}': ()
+            111..115 'this': &'? LazyLock
+            130..153 '{     ...     }': &'? T
+            140..147 'loop {}': !
+            145..147 '{}': ()
+            207..220 'LazyLock::new': fn new<[u32; _]>() -> LazyLock<[u32; _]>
+            207..222 'LazyLock::new()': LazyLock<[u32; _]>
+            234..285 '{     ...CK); }': ()
+            244..245 '_': &'? [u32; _]
+            248..263 'LazyLock::force': fn force<[u32; _]>(&'? LazyLock<[u32; _]>) -> &'? [u32; _]
+            248..282 'LazyLo..._LOCK)': &'? [u32; _]
+            264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; _]>
+            265..281 'VALUES...Y_LOCK': LazyLock<[u32; _]>
+        "#]],
+    );
+}

From 1285b1b13c28e5e11637884e741b6b7e8f36efc8 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 19 Jan 2026 07:14:48 +0200
Subject: [PATCH 139/583] Ensure correct capturing of async fn params even when
 they use weird patterns

rustc does the same.
---
 .../crates/hir-def/src/expr_store/lower.rs    | 66 ++++++++++++++-----
 .../hir-def/src/expr_store/tests/body.rs      | 18 +++++
 .../crates/hir-ty/src/tests/regression.rs     |  1 -
 .../crates/hir-ty/src/tests/simple.rs         |  1 -
 .../crates/hir-ty/src/tests/traits.rs         |  3 -
 5 files changed, 68 insertions(+), 21 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index 4ae4271b92f5..79222615929f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -150,6 +150,7 @@ pub(super) fn lower_body(
     };
 
     let body_expr = collector.collect(
+        &mut params,
         body,
         if is_async_fn {
             Awaitable::Yes
@@ -903,24 +904,57 @@ impl<'db> ExprCollector<'db> {
         })
     }
 
-    fn collect(&mut self, expr: Option, awaitable: Awaitable) -> ExprId {
+    /// An `async fn` needs to capture all parameters in the generated `async` block, even if they have
+    /// non-captured patterns such as wildcards (to ensure consistent drop order).
+    fn lower_async_fn(&mut self, params: &mut Vec, body: ExprId) -> ExprId {
+        let mut statements = Vec::new();
+        for param in params {
+            let name = match self.store.pats[*param] {
+                Pat::Bind { id, .. }
+                    if matches!(
+                        self.store.bindings[id].mode,
+                        BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+                    ) =>
+                {
+                    // If this is a direct binding, we can leave it as-is, as it'll always be captured anyway.
+                    continue;
+                }
+                Pat::Bind { id, .. } => {
+                    // If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display.
+                    self.store.bindings[id].name.clone()
+                }
+                _ => self.generate_new_name(),
+            };
+            let binding_id =
+                self.alloc_binding(name.clone(), BindingAnnotation::Mutable, HygieneId::ROOT);
+            let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None });
+            let expr = self.alloc_expr_desugared(Expr::Path(name.into()));
+            statements.push(Statement::Let {
+                pat: *param,
+                type_ref: None,
+                initializer: Some(expr),
+                else_branch: None,
+            });
+            *param = pat_id;
+        }
+
+        self.alloc_expr_desugared(Expr::Async {
+            id: None,
+            statements: statements.into_boxed_slice(),
+            tail: Some(body),
+        })
+    }
+
+    fn collect(
+        &mut self,
+        params: &mut Vec,
+        expr: Option,
+        awaitable: Awaitable,
+    ) -> ExprId {
         self.awaitable_context.replace(awaitable);
         self.with_label_rib(RibKind::Closure, |this| {
-            if awaitable == Awaitable::Yes {
-                match expr {
-                    Some(e) => {
-                        let syntax_ptr = AstPtr::new(&e);
-                        let expr = this.collect_expr(e);
-                        this.alloc_expr_desugared_with_ptr(
-                            Expr::Async { id: None, statements: Box::new([]), tail: Some(expr) },
-                            syntax_ptr,
-                        )
-                    }
-                    None => this.missing_expr(),
-                }
-            } else {
-                this.collect_expr_opt(expr)
-            }
+            let body = this.collect_expr_opt(expr);
+            if awaitable == Awaitable::Yes { this.lower_async_fn(params, body) } else { body }
         })
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
index 504c310684d6..8f857aeeff95 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
@@ -659,3 +659,21 @@ fn main() {
         }"#]]
     .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
 }
+
+#[test]
+fn async_fn_weird_param_patterns() {
+    let (db, body, def) = lower(
+        r#"
+async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {}
+"#,
+    );
+
+    expect![[r#"
+        fn main(self, param1, mut param2, mut 0, param4 @ _, mut 1) async {
+            let ref mut param2 = param2;
+            let _ = 0;
+            let 123 = 1;
+            {}
+        }"#]]
+    .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index a04c46f8eabd..4f1480c39366 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -2235,7 +2235,6 @@ async fn f() -> Bar {}
 "#,
         expect![[r#"
             64..66 '{}': ()
-            64..66 '{}': impl Future
         "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 28759bcbae61..80e21450c7a9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -2139,7 +2139,6 @@ async fn main() {
         "#,
         expect![[r#"
             16..193 '{     ...2 }; }': ()
-            16..193 '{     ...2 }; }': impl Future
             26..27 'x': i32
             30..43 'unsafe { 92 }': i32
             39..41 '92': i32
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index b825a0a8f0e5..390553c0d7a9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -4869,7 +4869,6 @@ async fn baz i32>(c: T) {
         expect![[r#"
             37..38 'a': T
             43..83 '{     ...ait; }': ()
-            43..83 '{     ...ait; }': impl Future
             53..57 'fut1': >::CallRefFuture<'?>
             60..61 'a': T
             60..64 'a(0)': >::CallRefFuture<'?>
@@ -4878,7 +4877,6 @@ async fn baz i32>(c: T) {
             70..80 'fut1.await': i32
             124..129 'mut b': T
             134..174 '{     ...ait; }': ()
-            134..174 '{     ...ait; }': impl Future
             144..148 'fut2': >::CallRefFuture<'?>
             151..152 'b': T
             151..155 'b(0)': >::CallRefFuture<'?>
@@ -4887,7 +4885,6 @@ async fn baz i32>(c: T) {
             161..171 'fut2.await': i32
             216..217 'c': T
             222..262 '{     ...ait; }': ()
-            222..262 '{     ...ait; }': impl Future
             232..236 'fut3': >::CallOnceFuture
             239..240 'c': T
             239..243 'c(0)': >::CallOnceFuture

From 740eb6b59fa663e9f1b721d7f6995dcec4af4bc9 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 16 Jan 2026 01:16:57 +0530
Subject: [PATCH 140/583] remove non-describing field annotation from
 bidirectional message definition

---
 .../proc-macro-api/src/bidirectional_protocol/msg.rs   | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
index c56ed5191694..2644cd406b63 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
@@ -84,18 +84,14 @@ pub struct ExpandMacroData {
     pub macro_body: FlatTree,
     pub macro_name: String,
     pub attributes: Option,
-    #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")]
     #[serde(default)]
     pub has_global_spans: ExpnGlobals,
-
-    #[serde(skip_serializing_if = "Vec::is_empty")]
     #[serde(default)]
     pub span_data_table: Vec,
 }
 
 #[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)]
 pub struct ExpnGlobals {
-    #[serde(skip_serializing)]
     #[serde(default)]
     pub serialize: bool,
     pub def_site: usize,
@@ -103,10 +99,4 @@ pub struct ExpnGlobals {
     pub mixed_site: usize,
 }
 
-impl ExpnGlobals {
-    fn skip_serializing_if(&self) -> bool {
-        !self.serialize
-    }
-}
-
 impl Message for BidirectionalMessage {}

From ffa2dadf0eeab0a484688373cffe9f1939173084 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 19:34:03 +0530
Subject: [PATCH 141/583] remove serialize from Expn Globals

---
 .../crates/proc-macro-api/src/bidirectional_protocol.rs   | 8 +-------
 .../proc-macro-api/src/bidirectional_protocol/msg.rs      | 2 --
 2 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index b5f43e1d3726..a13bff7d7d02 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -23,7 +23,6 @@ use crate::{
     },
     process::ProcMacroServerProcess,
     transport::codec::postcard::PostcardProtocol,
-    version,
 };
 
 pub mod msg;
@@ -159,12 +158,7 @@ pub(crate) fn expand(
             macro_name: proc_macro.name.to_string(),
             attributes: attr
                 .map(|subtree| FlatTree::from_subtree(subtree, version, &mut span_data_table)),
-            has_global_spans: ExpnGlobals {
-                serialize: version >= version::HAS_GLOBAL_SPANS,
-                def_site,
-                call_site,
-                mixed_site,
-            },
+            has_global_spans: ExpnGlobals { def_site, call_site, mixed_site },
             span_data_table: if process.rust_analyzer_spans() {
                 serialize_span_data_index_map(&span_data_table)
             } else {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
index 2644cd406b63..d030498e59c4 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
@@ -92,8 +92,6 @@ pub struct ExpandMacroData {
 
 #[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)]
 pub struct ExpnGlobals {
-    #[serde(default)]
-    pub serialize: bool,
     pub def_site: usize,
     pub call_site: usize,
     pub mixed_site: usize,

From 8fd55694389332c648a76c0ad533ba9e01ecc1fe Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 20:23:30 +0530
Subject: [PATCH 142/583] add bidirectional flow

---
 .../proc-macro-srv-cli/tests/common/utils.rs  | 570 +++++++++++-------
 1 file changed, 357 insertions(+), 213 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
index 722e92eec7e5..63b3a74aa4e8 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
@@ -1,213 +1,357 @@
-use std::{
-    collections::VecDeque,
-    io::{self, BufRead, Read, Write},
-    sync::{Arc, Condvar, Mutex},
-    thread,
-};
-
-use paths::Utf8PathBuf;
-use proc_macro_api::{
-    legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap},
-    transport::codec::json::JsonProtocol,
-};
-use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange};
-use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder};
-
-/// Shared state for an in-memory byte channel.
-#[derive(Default)]
-struct ChannelState {
-    buffer: VecDeque,
-    closed: bool,
-}
-
-type InMemoryChannel = Arc<(Mutex, Condvar)>;
-
-/// Writer end of an in-memory channel.
-pub(crate) struct ChannelWriter {
-    state: InMemoryChannel,
-}
-
-impl Write for ChannelWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result {
-        let (lock, cvar) = &*self.state;
-        let mut state = lock.lock().unwrap();
-        if state.closed {
-            return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed"));
-        }
-        state.buffer.extend(buf);
-        cvar.notify_all();
-        Ok(buf.len())
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        Ok(())
-    }
-}
-
-impl Drop for ChannelWriter {
-    fn drop(&mut self) {
-        let (lock, cvar) = &*self.state;
-        let mut state = lock.lock().unwrap();
-        state.closed = true;
-        cvar.notify_all();
-    }
-}
-
-/// Reader end of an in-memory channel.
-pub(crate) struct ChannelReader {
-    state: InMemoryChannel,
-    internal_buf: Vec,
-}
-
-impl Read for ChannelReader {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result {
-        let (lock, cvar) = &*self.state;
-        let mut state = lock.lock().unwrap();
-
-        while state.buffer.is_empty() && !state.closed {
-            state = cvar.wait(state).unwrap();
-        }
-
-        if state.buffer.is_empty() && state.closed {
-            return Ok(0);
-        }
-
-        let to_read = buf.len().min(state.buffer.len());
-        for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) {
-            *dst = src;
-        }
-        Ok(to_read)
-    }
-}
-
-impl BufRead for ChannelReader {
-    fn fill_buf(&mut self) -> io::Result<&[u8]> {
-        let (lock, cvar) = &*self.state;
-        let mut state = lock.lock().unwrap();
-
-        while state.buffer.is_empty() && !state.closed {
-            state = cvar.wait(state).unwrap();
-        }
-
-        self.internal_buf.clear();
-        self.internal_buf.extend(&state.buffer);
-        Ok(&self.internal_buf)
-    }
-
-    fn consume(&mut self, amt: usize) {
-        let (lock, _) = &*self.state;
-        let mut state = lock.lock().unwrap();
-        let to_drain = amt.min(state.buffer.len());
-        drop(state.buffer.drain(..to_drain));
-    }
-}
-
-/// Creates a connected pair of channels for bidirectional communication.
-fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) {
-    // Channel for client -> server communication
-    let client_to_server = Arc::new((
-        Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }),
-        Condvar::new(),
-    ));
-    let client_writer = ChannelWriter { state: client_to_server.clone() };
-    let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() };
-
-    // Channel for server -> client communication
-    let server_to_client = Arc::new((
-        Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }),
-        Condvar::new(),
-    ));
-
-    let server_writer = ChannelWriter { state: server_to_client.clone() };
-    let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() };
-
-    (client_writer, client_reader, server_writer, server_reader)
-}
-
-pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf {
-    let path = proc_macro_test::PROC_MACRO_TEST_LOCATION;
-    if path.is_empty() {
-        panic!("proc-macro-test dylib not available (requires nightly toolchain)");
-    }
-    path.into()
-}
-
-/// Runs a test with the server in a background thread.
-pub(crate) fn with_server(test_fn: F) -> R
-where
-    F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R,
-{
-    let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) =
-        create_channel_pair();
-
-    let server_handle = thread::spawn(move || {
-        proc_macro_srv_cli::main_loop::run(
-            &mut server_reader,
-            &mut server_writer,
-            proc_macro_api::ProtocolFormat::JsonLegacy,
-        )
-    });
-
-    let result = test_fn(&mut client_writer, &mut client_reader);
-
-    // Close the client writer to signal the server to stop
-    drop(client_writer);
-
-    // Wait for server to finish
-    match server_handle.join() {
-        Ok(Ok(())) => {}
-        Ok(Err(e)) => {
-            // IO error from server is expected when client disconnects
-            if matches!(
-                e.kind(),
-                io::ErrorKind::BrokenPipe
-                    | io::ErrorKind::UnexpectedEof
-                    | io::ErrorKind::InvalidData
-            ) {
-                panic!("Server error: {e}");
-            }
-        }
-        Err(e) => std::panic::resume_unwind(e),
-    }
-
-    result
-}
-
-/// Sends a request and reads the response using JSON protocol.
-pub(crate) fn request(
-    writer: &mut dyn Write,
-    reader: &mut dyn BufRead,
-    request: Request,
-) -> Response {
-    request.write::(writer).expect("failed to write request");
-
-    let mut buf = String::new();
-    Response::read::(reader, &mut buf)
-        .expect("failed to read response")
-        .expect("no response received")
-}
-
-/// Creates a simple empty token tree suitable for testing.
-pub(crate) fn create_empty_token_tree(
-    version: u32,
-    span_data_table: &mut SpanDataIndexMap,
-) -> FlatTree {
-    let anchor = SpanAnchor {
-        file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT),
-        ast_id: span::ROOT_ERASED_FILE_AST_ID,
-    };
-    let span = Span {
-        range: TextRange::empty(0.into()),
-        anchor,
-        ctx: SyntaxContext::root(Edition::CURRENT),
-    };
-
-    let builder = TopSubtreeBuilder::new(Delimiter {
-        open: span,
-        close: span,
-        kind: DelimiterKind::Invisible,
-    });
-    let tt = builder.build();
-
-    FlatTree::from_subtree(tt.view(), version, span_data_table)
-}
+use std::{
+    collections::VecDeque,
+    io::{self, BufRead, Read, Write},
+    sync::{Arc, Condvar, Mutex},
+    thread,
+};
+
+use paths::Utf8PathBuf;
+use proc_macro_api::{
+    ServerError,
+    bidirectional_protocol::msg::{
+        BidirectionalMessage, Request as BiRequest, Response as BiResponse, SubRequest, SubResponse,
+    },
+    legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap},
+    transport::codec::{json::JsonProtocol, postcard::PostcardProtocol},
+};
+use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange};
+use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder};
+
+/// Shared state for an in-memory byte channel.
+#[derive(Default)]
+struct ChannelState {
+    buffer: VecDeque,
+    closed: bool,
+}
+
+type InMemoryChannel = Arc<(Mutex, Condvar)>;
+
+/// Writer end of an in-memory channel.
+pub(crate) struct ChannelWriter {
+    state: InMemoryChannel,
+}
+
+impl Write for ChannelWriter {
+    fn write(&mut self, buf: &[u8]) -> io::Result {
+        let (lock, cvar) = &*self.state;
+        let mut state = lock.lock().unwrap();
+        if state.closed {
+            return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed"));
+        }
+        state.buffer.extend(buf);
+        cvar.notify_all();
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl Drop for ChannelWriter {
+    fn drop(&mut self) {
+        let (lock, cvar) = &*self.state;
+        let mut state = lock.lock().unwrap();
+        state.closed = true;
+        cvar.notify_all();
+    }
+}
+
+/// Reader end of an in-memory channel.
+pub(crate) struct ChannelReader {
+    state: InMemoryChannel,
+    internal_buf: Vec,
+}
+
+impl Read for ChannelReader {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result {
+        let (lock, cvar) = &*self.state;
+        let mut state = lock.lock().unwrap();
+
+        while state.buffer.is_empty() && !state.closed {
+            state = cvar.wait(state).unwrap();
+        }
+
+        if state.buffer.is_empty() && state.closed {
+            return Ok(0);
+        }
+
+        let to_read = buf.len().min(state.buffer.len());
+        for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) {
+            *dst = src;
+        }
+        Ok(to_read)
+    }
+}
+
+impl BufRead for ChannelReader {
+    fn fill_buf(&mut self) -> io::Result<&[u8]> {
+        let (lock, cvar) = &*self.state;
+        let mut state = lock.lock().unwrap();
+
+        while state.buffer.is_empty() && !state.closed {
+            state = cvar.wait(state).unwrap();
+        }
+
+        self.internal_buf.clear();
+        self.internal_buf.extend(&state.buffer);
+        Ok(&self.internal_buf)
+    }
+
+    fn consume(&mut self, amt: usize) {
+        let (lock, _) = &*self.state;
+        let mut state = lock.lock().unwrap();
+        let to_drain = amt.min(state.buffer.len());
+        drop(state.buffer.drain(..to_drain));
+    }
+}
+
+/// Creates a connected pair of channels for bidirectional communication.
+fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) {
+    // Channel for client -> server communication
+    let client_to_server = Arc::new((
+        Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }),
+        Condvar::new(),
+    ));
+    let client_writer = ChannelWriter { state: client_to_server.clone() };
+    let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() };
+
+    // Channel for server -> client communication
+    let server_to_client = Arc::new((
+        Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }),
+        Condvar::new(),
+    ));
+
+    let server_writer = ChannelWriter { state: server_to_client.clone() };
+    let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() };
+
+    (client_writer, client_reader, server_writer, server_reader)
+}
+
+pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf {
+    let path = proc_macro_test::PROC_MACRO_TEST_LOCATION;
+    if path.is_empty() {
+        panic!("proc-macro-test dylib not available (requires nightly toolchain)");
+    }
+    path.into()
+}
+
+/// Creates a simple empty token tree suitable for testing.
+pub(crate) fn create_empty_token_tree(
+    version: u32,
+    span_data_table: &mut SpanDataIndexMap,
+) -> FlatTree {
+    let anchor = SpanAnchor {
+        file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT),
+        ast_id: span::ROOT_ERASED_FILE_AST_ID,
+    };
+    let span = Span {
+        range: TextRange::empty(0.into()),
+        anchor,
+        ctx: SyntaxContext::root(Edition::CURRENT),
+    };
+
+    let builder = TopSubtreeBuilder::new(Delimiter {
+        open: span,
+        close: span,
+        kind: DelimiterKind::Invisible,
+    });
+    let tt = builder.build();
+
+    FlatTree::from_subtree(tt.view(), version, span_data_table)
+}
+
+pub(crate) fn with_server(format: proc_macro_api::ProtocolFormat, test_fn: F) -> R
+where
+    F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R,
+{
+    let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) =
+        create_channel_pair();
+
+    let server_handle = thread::spawn(move || {
+        proc_macro_srv_cli::main_loop::run(&mut server_reader, &mut server_writer, format)
+    });
+
+    let result = test_fn(&mut client_writer, &mut client_reader);
+
+    drop(client_writer);
+
+    match server_handle.join() {
+        Ok(Ok(())) => {}
+        Ok(Err(e)) => {
+            if !matches!(
+                e.kind(),
+                io::ErrorKind::BrokenPipe
+                    | io::ErrorKind::UnexpectedEof
+                    | io::ErrorKind::InvalidData
+            ) {
+                panic!("Server error: {e}");
+            }
+        }
+        Err(e) => std::panic::resume_unwind(e),
+    }
+
+    result
+}
+
+trait TestProtocol {
+    type Request;
+    type Response;
+
+    fn send(&self, writer: &mut dyn Write, req: Self::Request);
+    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response;
+}
+
+struct JsonLegacy;
+
+impl TestProtocol for JsonLegacy {
+    type Request = Request;
+    type Response = Response;
+
+    fn send(&self, writer: &mut dyn Write, req: Request) {
+        req.write::(writer).expect("failed to write request");
+    }
+
+    fn drive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response {
+        let mut buf = String::new();
+        Response::read::(reader, &mut buf)
+            .expect("failed to read response")
+            .expect("no response received")
+    }
+}
+
+struct PostcardBidirectional
+where
+    F: Fn(SubRequest) -> Result,
+{
+    callback: F,
+}
+
+impl TestProtocol for PostcardBidirectional
+where
+    F: Fn(SubRequest) -> Result,
+{
+    type Request = BiRequest;
+    type Response = BiResponse;
+
+    fn send(&self, writer: &mut dyn Write, req: BiRequest) {
+        let msg = BidirectionalMessage::Request(req);
+        msg.write::(writer).expect("failed to write request");
+    }
+
+    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse {
+        let mut buf = Vec::new();
+
+        loop {
+            let msg = BidirectionalMessage::read::(reader, &mut buf)
+                .expect("failed to read message")
+                .expect("no message received");
+
+            match msg {
+                BidirectionalMessage::Response(resp) => return resp,
+                BidirectionalMessage::SubRequest(sr) => {
+                    let reply = (self.callback)(sr).expect("subrequest callback failed");
+                    let msg = BidirectionalMessage::SubResponse(reply);
+                    msg.write::(writer).expect("failed to write subresponse");
+                }
+                other => panic!("unexpected message: {other:?}"),
+            }
+        }
+    }
+}
+
+pub(crate) fn request(
+    writer: &mut dyn Write,
+    reader: &mut dyn BufRead,
+    request: impl Into,
+    callback: Option<&dyn Fn(SubRequest) -> Result>,
+) -> AutoResponse {
+    let protocol = match callback {
+        None => AutoProtocol::Legacy(JsonLegacy),
+        Some(cb) => AutoProtocol::Bidirectional(PostcardBidirectional { callback: cb }),
+    };
+
+    protocol.send(writer, request.into());
+    protocol.drive(reader, writer)
+}
+
+enum AutoProtocol
+where
+    F: Fn(SubRequest) -> Result,
+{
+    Legacy(JsonLegacy),
+    Bidirectional(PostcardBidirectional),
+}
+
+impl TestProtocol for AutoProtocol
+where
+    F: Fn(SubRequest) -> Result,
+{
+    type Request = AutoRequest;
+    type Response = AutoResponse;
+
+    fn send(&self, writer: &mut dyn Write, req: AutoRequest) {
+        match (self, req) {
+            (AutoProtocol::Legacy(p), AutoRequest::Legacy(r)) => {
+                p.send(writer, r);
+            }
+            (AutoProtocol::Bidirectional(p), AutoRequest::Bidirectional(r)) => {
+                p.send(writer, r);
+            }
+            (AutoProtocol::Legacy(_), AutoRequest::Bidirectional(_)) => {
+                panic!("bidirectional request used with legacy protocol");
+            }
+            (AutoProtocol::Bidirectional(_), AutoRequest::Legacy(_)) => {
+                panic!("legacy request used with bidirectional protocol");
+            }
+        }
+    }
+
+    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> AutoResponse {
+        match self {
+            AutoProtocol::Legacy(p) => AutoResponse::Legacy(p.drive(reader, writer)),
+            AutoProtocol::Bidirectional(p) => AutoResponse::Bidirectional(p.drive(reader, writer)),
+        }
+    }
+}
+
+pub(crate) enum AutoRequest {
+    Legacy(Request),
+    Bidirectional(BiRequest),
+}
+
+#[derive(Debug)]
+pub(crate) enum AutoResponse {
+    Legacy(Response),
+    Bidirectional(BiResponse),
+}
+
+impl From for AutoRequest {
+    fn from(req: Request) -> AutoRequest {
+        AutoRequest::Legacy(req)
+    }
+}
+
+impl From for AutoRequest {
+    fn from(req: BiRequest) -> AutoRequest {
+        AutoRequest::Bidirectional(req)
+    }
+}
+
+impl From for Response {
+    fn from(res: AutoResponse) -> Response {
+        match res {
+            AutoResponse::Legacy(res) => res,
+            _ => panic!("Should be legacy response"),
+        }
+    }
+}
+
+impl From for BiResponse {
+    fn from(res: AutoResponse) -> BiResponse {
+        match res {
+            AutoResponse::Bidirectional(res) => res,
+            _ => panic!("Should be bidirectional response"),
+        }
+    }
+}

From ee35fd6cb206e00b0e43b7e676a1aed8eb013684 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 20:23:40 +0530
Subject: [PATCH 143/583] add bidirectional test

---
 .../tests/bidirectional_postcard.rs           | 223 ++++++++++++++++++
 1 file changed, 223 insertions(+)
 create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
new file mode 100644
index 000000000000..08e44bad3723
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
@@ -0,0 +1,223 @@
+#![cfg(feature = "sysroot-abi")]
+
+mod common {
+    pub(crate) mod utils;
+}
+
+use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
+use expect_test::expect;
+use proc_macro_api::{
+    ProtocolFormat::BidirectionalPostcardPrototype,
+    bidirectional_protocol::{
+        msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response},
+        reject_subrequests,
+    },
+    legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode},
+    version::CURRENT_API_VERSION,
+};
+
+#[test]
+fn test_bidi_version_check_bidirectional() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let response =
+            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+
+        match response {
+            Response::ApiVersionCheck(version) => {
+                assert_eq!(version, CURRENT_API_VERSION);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_list_macros() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+        let response =
+            request(writer, reader, Request::ListMacros { dylib_path }, Some(&reject_subrequests))
+                .into();
+
+        let Response::ListMacros(Ok(macros)) = response else {
+            panic!("expected successful ListMacros response");
+        };
+
+        let mut macro_list: Vec<_> =
+            macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect();
+        macro_list.sort();
+        let macro_list_str = macro_list.join("\n");
+
+        expect![[r#"
+            DeriveEmpty [CustomDerive]
+            DeriveError [CustomDerive]
+            DerivePanic [CustomDerive]
+            DeriveReemit [CustomDerive]
+            attr_error [Attr]
+            attr_noop [Attr]
+            attr_panic [Attr]
+            fn_like_clone_tokens [Bang]
+            fn_like_error [Bang]
+            fn_like_mk_idents [Bang]
+            fn_like_mk_literals [Bang]
+            fn_like_noop [Bang]
+            fn_like_panic [Bang]
+            fn_like_span_join [Bang]
+            fn_like_span_line_column [Bang]
+            fn_like_span_ops [Bang]"#]]
+        .assert_eq(¯o_list_str);
+    });
+}
+
+#[test]
+fn test_bidi_list_macros_invalid_path() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let response = request(
+            writer,
+            reader,
+            Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
+            Some(&reject_subrequests),
+        )
+        .into();
+
+        match response {
+            Response::ListMacros(Err(e)) => assert!(
+                e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"),
+                "{e}"
+            ),
+            other => panic!("expected error response, got: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_set_config() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let config = ServerConfig { span_mode: SpanMode::Id };
+        let response =
+            request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into();
+
+        match response {
+            Response::SetConfig(returned_config) => {
+                assert_eq!(returned_config.span_mode, SpanMode::Id);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_set_config_rust_analyzer_mode() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
+        let response =
+            request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into();
+
+        match response {
+            Response::SetConfig(returned_config) => {
+                assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_expand_macro_panic() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let mut span_data_table = SpanDataIndexMap::default();
+        let macro_body =
+            common::utils::create_empty_token_tree(CURRENT_API_VERSION, &mut span_data_table);
+
+        let request1 = Request::ExpandMacro(Box::new(ExpandMacro {
+            lib: dylib_path,
+            env: vec![],
+            current_dir: None,
+            data: ExpandMacroData {
+                macro_body,
+                macro_name: "fn_like_panic".to_owned(),
+                attributes: None,
+                has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 },
+                span_data_table: vec![],
+            },
+        }));
+
+        let response = request(writer, reader, request1, Some(&reject_subrequests)).into();
+
+        match response {
+            Response::ExpandMacro(Err(PanicMessage(msg))) => {
+                assert!(msg.contains("fn_like_panic"), "panic message should mention macro name");
+            }
+            other => panic!("expected panic response, got: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_basic_call_flow() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let response1 =
+            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+        assert!(matches!(response1, Response::ApiVersionCheck(_)));
+
+        let response2 = request(
+            writer,
+            reader,
+            Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }),
+            Some(&reject_subrequests),
+        )
+        .into();
+        assert!(matches!(response2, Response::SetConfig(_)));
+
+        let response3 = request(
+            writer,
+            reader,
+            Request::ListMacros { dylib_path: dylib_path.clone() },
+            Some(&reject_subrequests),
+        )
+        .into();
+        assert!(matches!(response3, Response::ListMacros(Ok(_))));
+    });
+}
+
+#[test]
+fn test_bidi_expand_nonexistent_macro() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let version_response =
+            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+        let Response::ApiVersionCheck(version) = version_response else {
+            panic!("expected version check response");
+        };
+
+        let mut span_data_table = SpanDataIndexMap::default();
+        let macro_body = create_empty_token_tree(version, &mut span_data_table);
+
+        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
+            lib: dylib_path,
+            env: vec![],
+            current_dir: None,
+            data: ExpandMacroData {
+                macro_body,
+                macro_name: "NonexistentMacro".to_owned(),
+                attributes: None,
+                has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 },
+                span_data_table: vec![],
+            },
+        }));
+
+        let response = request(writer, reader, expand_request, Some(&reject_subrequests)).into();
+
+        match response {
+            Response::ExpandMacro(Err(PanicMessage(msg))) => {
+                expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg)
+            }
+            other => panic!("expected error for nonexistent macro, got: {other:?}"),
+        }
+    });
+}

From a151d7dc5617ecd8c0cf8fbb688654c66e9776b6 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 20:23:49 +0530
Subject: [PATCH 144/583] adapt json test

---
 .../proc-macro-srv-cli/tests/legacy_json.rs   | 457 +++++++++---------
 1 file changed, 233 insertions(+), 224 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
index 1fa886219a8a..8daee7b2bceb 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
@@ -1,224 +1,233 @@
-//! Integration tests for the proc-macro-srv-cli main loop.
-//!
-//! These tests exercise the full client-server RPC procedure using in-memory
-//! channels without needing to spawn the actual server and client processes.
-
-#![cfg(feature = "sysroot-abi")]
-
-mod common {
-    pub(crate) mod utils;
-}
-
-use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
-use expect_test::expect;
-use proc_macro_api::{
-    legacy_protocol::msg::{
-        ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig,
-        SpanDataIndexMap, SpanMode,
-    },
-    version::CURRENT_API_VERSION,
-};
-
-#[test]
-fn test_version_check() {
-    with_server(|writer, reader| {
-        let response = request(writer, reader, Request::ApiVersionCheck {});
-
-        match response {
-            Response::ApiVersionCheck(version) => {
-                assert_eq!(version, CURRENT_API_VERSION);
-            }
-            other => panic!("unexpected response: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_list_macros() {
-    with_server(|writer, reader| {
-        let dylib_path = proc_macro_test_dylib_path();
-        let response = request(writer, reader, Request::ListMacros { dylib_path });
-
-        let Response::ListMacros(Ok(macros)) = response else {
-            panic!("expected successful ListMacros response");
-        };
-
-        let mut macro_list: Vec<_> =
-            macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect();
-        macro_list.sort();
-        let macro_list_str = macro_list.join("\n");
-
-        expect![[r#"
-            DeriveEmpty [CustomDerive]
-            DeriveError [CustomDerive]
-            DerivePanic [CustomDerive]
-            DeriveReemit [CustomDerive]
-            attr_error [Attr]
-            attr_noop [Attr]
-            attr_panic [Attr]
-            fn_like_clone_tokens [Bang]
-            fn_like_error [Bang]
-            fn_like_mk_idents [Bang]
-            fn_like_mk_literals [Bang]
-            fn_like_noop [Bang]
-            fn_like_panic [Bang]
-            fn_like_span_join [Bang]
-            fn_like_span_line_column [Bang]
-            fn_like_span_ops [Bang]"#]]
-        .assert_eq(¯o_list_str);
-    });
-}
-
-#[test]
-fn test_list_macros_invalid_path() {
-    with_server(|writer, reader| {
-        let response = request(
-            writer,
-            reader,
-            Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
-        );
-
-        match response {
-            Response::ListMacros(Err(e)) => assert!(
-                e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"),
-                "{e}"
-            ),
-            other => panic!("expected error response, got: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_set_config() {
-    with_server(|writer, reader| {
-        let config = ServerConfig { span_mode: SpanMode::Id };
-        let response = request(writer, reader, Request::SetConfig(config));
-
-        match response {
-            Response::SetConfig(returned_config) => {
-                assert_eq!(returned_config.span_mode, SpanMode::Id);
-            }
-            other => panic!("unexpected response: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_set_config_rust_analyzer_mode() {
-    with_server(|writer, reader| {
-        let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
-        let response = request(writer, reader, Request::SetConfig(config));
-
-        match response {
-            Response::SetConfig(returned_config) => {
-                assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer);
-            }
-            other => panic!("unexpected response: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_expand_macro_panic() {
-    with_server(|writer, reader| {
-        let dylib_path = proc_macro_test_dylib_path();
-
-        let version_response = request(writer, reader, Request::ApiVersionCheck {});
-        let Response::ApiVersionCheck(version) = version_response else {
-            panic!("expected version check response");
-        };
-
-        let mut span_data_table = SpanDataIndexMap::default();
-        let macro_body = create_empty_token_tree(version, &mut span_data_table);
-
-        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
-            lib: dylib_path,
-            env: vec![],
-            current_dir: None,
-            data: ExpandMacroData {
-                macro_body,
-                macro_name: "fn_like_panic".to_owned(),
-                attributes: None,
-                has_global_spans: ExpnGlobals {
-                    serialize: version >= 3,
-                    def_site: 0,
-                    call_site: 0,
-                    mixed_site: 0,
-                },
-                span_data_table: vec![],
-            },
-        }));
-
-        let response = request(writer, reader, expand_request);
-
-        match response {
-            Response::ExpandMacro(Err(PanicMessage(msg))) => {
-                assert!(msg.contains("fn_like_panic"), "panic message should mention the macro");
-            }
-            Response::ExpandMacro(Ok(_)) => {
-                panic!("expected panic, but macro succeeded");
-            }
-            other => panic!("unexpected response: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_basic_call_flow() {
-    with_server(|writer, reader| {
-        let dylib_path = proc_macro_test_dylib_path();
-
-        let response1 = request(writer, reader, Request::ApiVersionCheck {});
-        assert!(matches!(response1, Response::ApiVersionCheck(_)));
-
-        let response2 =
-            request(writer, reader, Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }));
-        assert!(matches!(response2, Response::SetConfig(_)));
-
-        let response3 =
-            request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() });
-        assert!(matches!(response3, Response::ListMacros(Ok(_))));
-    });
-}
-
-#[test]
-fn test_expand_nonexistent_macro() {
-    with_server(|writer, reader| {
-        let dylib_path = proc_macro_test_dylib_path();
-
-        let version_response = request(writer, reader, Request::ApiVersionCheck {});
-        let Response::ApiVersionCheck(version) = version_response else {
-            panic!("expected version check response");
-        };
-
-        let mut span_data_table = SpanDataIndexMap::default();
-        let macro_body = create_empty_token_tree(version, &mut span_data_table);
-
-        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
-            lib: dylib_path,
-            env: vec![],
-            current_dir: None,
-            data: ExpandMacroData {
-                macro_body,
-                macro_name: "NonexistentMacro".to_owned(),
-                attributes: None,
-                has_global_spans: ExpnGlobals {
-                    serialize: version >= 3,
-                    def_site: 0,
-                    call_site: 0,
-                    mixed_site: 0,
-                },
-                span_data_table: vec![],
-            },
-        }));
-
-        let response = request(writer, reader, expand_request);
-
-        match response {
-            Response::ExpandMacro(Err(PanicMessage(msg))) => {
-                expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg)
-            }
-            other => panic!("expected error for nonexistent macro, got: {other:?}"),
-        }
-    });
-}
+//! Integration tests for the proc-macro-srv-cli main loop.
+//!
+//! These tests exercise the full client-server RPC procedure using in-memory
+//! channels without needing to spawn the actual server and client processes.
+
+#![cfg(feature = "sysroot-abi")]
+
+mod common {
+    pub(crate) mod utils;
+}
+
+use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
+use expect_test::expect;
+use proc_macro_api::{
+    ProtocolFormat::JsonLegacy,
+    legacy_protocol::msg::{
+        ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig,
+        SpanDataIndexMap, SpanMode,
+    },
+    version::CURRENT_API_VERSION,
+};
+
+#[test]
+fn test_version_check() {
+    with_server(JsonLegacy, |writer, reader| {
+        let response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+
+        match response {
+            Response::ApiVersionCheck(version) => {
+                assert_eq!(version, CURRENT_API_VERSION);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_list_macros() {
+    with_server(JsonLegacy, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+        let response = request(writer, reader, Request::ListMacros { dylib_path }, None).into();
+
+        let Response::ListMacros(Ok(macros)) = response else {
+            panic!("expected successful ListMacros response");
+        };
+
+        let mut macro_list: Vec<_> =
+            macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect();
+        macro_list.sort();
+        let macro_list_str = macro_list.join("\n");
+
+        expect![[r#"
+            DeriveEmpty [CustomDerive]
+            DeriveError [CustomDerive]
+            DerivePanic [CustomDerive]
+            DeriveReemit [CustomDerive]
+            attr_error [Attr]
+            attr_noop [Attr]
+            attr_panic [Attr]
+            fn_like_clone_tokens [Bang]
+            fn_like_error [Bang]
+            fn_like_mk_idents [Bang]
+            fn_like_mk_literals [Bang]
+            fn_like_noop [Bang]
+            fn_like_panic [Bang]
+            fn_like_span_join [Bang]
+            fn_like_span_line_column [Bang]
+            fn_like_span_ops [Bang]"#]]
+        .assert_eq(¯o_list_str);
+    });
+}
+
+#[test]
+fn test_list_macros_invalid_path() {
+    with_server(JsonLegacy, |writer, reader| {
+        let response = request(
+            writer,
+            reader,
+            Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
+            None,
+        )
+        .into();
+
+        match response {
+            Response::ListMacros(Err(e)) => assert!(
+                e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"),
+                "{e}"
+            ),
+            other => panic!("expected error response, got: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_set_config() {
+    with_server(JsonLegacy, |writer, reader| {
+        let config = ServerConfig { span_mode: SpanMode::Id };
+        let response = request(writer, reader, Request::SetConfig(config), None).into();
+
+        match response {
+            Response::SetConfig(returned_config) => {
+                assert_eq!(returned_config.span_mode, SpanMode::Id);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_set_config_rust_analyzer_mode() {
+    with_server(JsonLegacy, |writer, reader| {
+        let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
+        let response = request(writer, reader, Request::SetConfig(config), None).into();
+
+        match response {
+            Response::SetConfig(returned_config) => {
+                assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_expand_macro_panic() {
+    with_server(JsonLegacy, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let Response::ApiVersionCheck(version) = version_response else {
+            panic!("expected version check response");
+        };
+
+        let mut span_data_table = SpanDataIndexMap::default();
+        let macro_body = create_empty_token_tree(version, &mut span_data_table);
+
+        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
+            lib: dylib_path,
+            env: vec![],
+            current_dir: None,
+            data: ExpandMacroData {
+                macro_body,
+                macro_name: "fn_like_panic".to_owned(),
+                attributes: None,
+                has_global_spans: ExpnGlobals {
+                    serialize: version >= 3,
+                    def_site: 0,
+                    call_site: 0,
+                    mixed_site: 0,
+                },
+                span_data_table: vec![],
+            },
+        }));
+
+        let response = request(writer, reader, expand_request, None).into();
+
+        match response {
+            Response::ExpandMacro(Err(PanicMessage(msg))) => {
+                assert!(msg.contains("fn_like_panic"), "panic message should mention the macro");
+            }
+            Response::ExpandMacro(Ok(_)) => {
+                panic!("expected panic, but macro succeeded");
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_basic_call_flow() {
+    with_server(JsonLegacy, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let response1 = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        assert!(matches!(response1, Response::ApiVersionCheck(_)));
+
+        let response2 = request(
+            writer,
+            reader,
+            Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }),
+            None,
+        )
+        .into();
+        assert!(matches!(response2, Response::SetConfig(_)));
+
+        let response3 =
+            request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }, None)
+                .into();
+        assert!(matches!(response3, Response::ListMacros(Ok(_))));
+    });
+}
+
+#[test]
+fn test_expand_nonexistent_macro() {
+    with_server(JsonLegacy, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let Response::ApiVersionCheck(version) = version_response else {
+            panic!("expected version check response");
+        };
+
+        let mut span_data_table = SpanDataIndexMap::default();
+        let macro_body = create_empty_token_tree(version, &mut span_data_table);
+
+        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
+            lib: dylib_path,
+            env: vec![],
+            current_dir: None,
+            data: ExpandMacroData {
+                macro_body,
+                macro_name: "NonexistentMacro".to_owned(),
+                attributes: None,
+                has_global_spans: ExpnGlobals {
+                    serialize: version >= 3,
+                    def_site: 0,
+                    call_site: 0,
+                    mixed_site: 0,
+                },
+                span_data_table: vec![],
+            },
+        }));
+
+        let response = request(writer, reader, expand_request, None).into();
+
+        match response {
+            Response::ExpandMacro(Err(PanicMessage(msg))) => {
+                expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg)
+            }
+            other => panic!("expected error for nonexistent macro, got: {other:?}"),
+        }
+    });
+}

From 095b0138028d51e45ca987e0b0b1dcf4e16a876d Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 20:38:58 +0530
Subject: [PATCH 145/583] rename send and drive to request and receive and
 remove auto*

---
 .../tests/bidirectional_postcard.rs           |  48 +++----
 .../proc-macro-srv-cli/tests/common/utils.rs  | 118 ++++--------------
 .../proc-macro-srv-cli/tests/legacy_json.rs   |  37 +++---
 3 files changed, 67 insertions(+), 136 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
index 08e44bad3723..33ca1d791de7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
@@ -4,7 +4,9 @@ mod common {
     pub(crate) mod utils;
 }
 
-use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
+use common::utils::{
+    create_empty_token_tree, proc_macro_test_dylib_path, request_bidirectional, with_server,
+};
 use expect_test::expect;
 use proc_macro_api::{
     ProtocolFormat::BidirectionalPostcardPrototype,
@@ -20,7 +22,7 @@ use proc_macro_api::{
 fn test_bidi_version_check_bidirectional() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
         let response =
-            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests);
 
         match response {
             Response::ApiVersionCheck(version) => {
@@ -35,9 +37,12 @@ fn test_bidi_version_check_bidirectional() {
 fn test_bidi_list_macros() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
-        let response =
-            request(writer, reader, Request::ListMacros { dylib_path }, Some(&reject_subrequests))
-                .into();
+        let response = request_bidirectional(
+            writer,
+            reader,
+            Request::ListMacros { dylib_path },
+            &reject_subrequests,
+        );
 
         let Response::ListMacros(Ok(macros)) = response else {
             panic!("expected successful ListMacros response");
@@ -72,13 +77,12 @@ fn test_bidi_list_macros() {
 #[test]
 fn test_bidi_list_macros_invalid_path() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
-        let response = request(
+        let response = request_bidirectional(
             writer,
             reader,
             Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
-            Some(&reject_subrequests),
-        )
-        .into();
+            reject_subrequests,
+        );
 
         match response {
             Response::ListMacros(Err(e)) => assert!(
@@ -95,7 +99,7 @@ fn test_bidi_set_config() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
         let config = ServerConfig { span_mode: SpanMode::Id };
         let response =
-            request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests);
 
         match response {
             Response::SetConfig(returned_config) => {
@@ -111,7 +115,7 @@ fn test_bidi_set_config_rust_analyzer_mode() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
         let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
         let response =
-            request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests);
 
         match response {
             Response::SetConfig(returned_config) => {
@@ -144,7 +148,7 @@ fn test_bidi_expand_macro_panic() {
             },
         }));
 
-        let response = request(writer, reader, request1, Some(&reject_subrequests)).into();
+        let response = request_bidirectional(writer, reader, request1, reject_subrequests);
 
         match response {
             Response::ExpandMacro(Err(PanicMessage(msg))) => {
@@ -161,25 +165,23 @@ fn test_bidi_basic_call_flow() {
         let dylib_path = proc_macro_test_dylib_path();
 
         let response1 =
-            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests);
         assert!(matches!(response1, Response::ApiVersionCheck(_)));
 
-        let response2 = request(
+        let response2 = request_bidirectional(
             writer,
             reader,
             Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }),
-            Some(&reject_subrequests),
-        )
-        .into();
+            reject_subrequests,
+        );
         assert!(matches!(response2, Response::SetConfig(_)));
 
-        let response3 = request(
+        let response3 = request_bidirectional(
             writer,
             reader,
             Request::ListMacros { dylib_path: dylib_path.clone() },
-            Some(&reject_subrequests),
-        )
-        .into();
+            reject_subrequests,
+        );
         assert!(matches!(response3, Response::ListMacros(Ok(_))));
     });
 }
@@ -190,7 +192,7 @@ fn test_bidi_expand_nonexistent_macro() {
         let dylib_path = proc_macro_test_dylib_path();
 
         let version_response =
-            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests);
         let Response::ApiVersionCheck(version) = version_response else {
             panic!("expected version check response");
         };
@@ -211,7 +213,7 @@ fn test_bidi_expand_nonexistent_macro() {
             },
         }));
 
-        let response = request(writer, reader, expand_request, Some(&reject_subrequests)).into();
+        let response = request_bidirectional(writer, reader, expand_request, reject_subrequests);
 
         match response {
             Response::ExpandMacro(Err(PanicMessage(msg))) => {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
index 63b3a74aa4e8..85c394734b33 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
@@ -198,21 +198,22 @@ trait TestProtocol {
     type Request;
     type Response;
 
-    fn send(&self, writer: &mut dyn Write, req: Self::Request);
-    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response;
+    fn request(&self, writer: &mut dyn Write, req: Self::Request);
+    fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response;
 }
 
+#[allow(dead_code)]
 struct JsonLegacy;
 
 impl TestProtocol for JsonLegacy {
     type Request = Request;
     type Response = Response;
 
-    fn send(&self, writer: &mut dyn Write, req: Request) {
+    fn request(&self, writer: &mut dyn Write, req: Request) {
         req.write::(writer).expect("failed to write request");
     }
 
-    fn drive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response {
+    fn receive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response {
         let mut buf = String::new();
         Response::read::(reader, &mut buf)
             .expect("failed to read response")
@@ -220,6 +221,7 @@ impl TestProtocol for JsonLegacy {
     }
 }
 
+#[allow(dead_code)]
 struct PostcardBidirectional
 where
     F: Fn(SubRequest) -> Result,
@@ -234,12 +236,12 @@ where
     type Request = BiRequest;
     type Response = BiResponse;
 
-    fn send(&self, writer: &mut dyn Write, req: BiRequest) {
+    fn request(&self, writer: &mut dyn Write, req: BiRequest) {
         let msg = BidirectionalMessage::Request(req);
         msg.write::(writer).expect("failed to write request");
     }
 
-    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse {
+    fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse {
         let mut buf = Vec::new();
 
         loop {
@@ -260,98 +262,28 @@ where
     }
 }
 
-pub(crate) fn request(
+#[allow(dead_code)]
+pub(crate) fn request_legacy(
     writer: &mut dyn Write,
     reader: &mut dyn BufRead,
-    request: impl Into,
-    callback: Option<&dyn Fn(SubRequest) -> Result>,
-) -> AutoResponse {
-    let protocol = match callback {
-        None => AutoProtocol::Legacy(JsonLegacy),
-        Some(cb) => AutoProtocol::Bidirectional(PostcardBidirectional { callback: cb }),
-    };
-
-    protocol.send(writer, request.into());
-    protocol.drive(reader, writer)
+    request: Request,
+) -> Response {
+    let protocol = JsonLegacy;
+    protocol.request(writer, request);
+    protocol.receive(reader, writer)
 }
 
-enum AutoProtocol
+#[allow(dead_code)]
+pub(crate) fn request_bidirectional(
+    writer: &mut dyn Write,
+    reader: &mut dyn BufRead,
+    request: BiRequest,
+    callback: F,
+) -> BiResponse
 where
     F: Fn(SubRequest) -> Result,
 {
-    Legacy(JsonLegacy),
-    Bidirectional(PostcardBidirectional),
-}
-
-impl TestProtocol for AutoProtocol
-where
-    F: Fn(SubRequest) -> Result,
-{
-    type Request = AutoRequest;
-    type Response = AutoResponse;
-
-    fn send(&self, writer: &mut dyn Write, req: AutoRequest) {
-        match (self, req) {
-            (AutoProtocol::Legacy(p), AutoRequest::Legacy(r)) => {
-                p.send(writer, r);
-            }
-            (AutoProtocol::Bidirectional(p), AutoRequest::Bidirectional(r)) => {
-                p.send(writer, r);
-            }
-            (AutoProtocol::Legacy(_), AutoRequest::Bidirectional(_)) => {
-                panic!("bidirectional request used with legacy protocol");
-            }
-            (AutoProtocol::Bidirectional(_), AutoRequest::Legacy(_)) => {
-                panic!("legacy request used with bidirectional protocol");
-            }
-        }
-    }
-
-    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> AutoResponse {
-        match self {
-            AutoProtocol::Legacy(p) => AutoResponse::Legacy(p.drive(reader, writer)),
-            AutoProtocol::Bidirectional(p) => AutoResponse::Bidirectional(p.drive(reader, writer)),
-        }
-    }
-}
-
-pub(crate) enum AutoRequest {
-    Legacy(Request),
-    Bidirectional(BiRequest),
-}
-
-#[derive(Debug)]
-pub(crate) enum AutoResponse {
-    Legacy(Response),
-    Bidirectional(BiResponse),
-}
-
-impl From for AutoRequest {
-    fn from(req: Request) -> AutoRequest {
-        AutoRequest::Legacy(req)
-    }
-}
-
-impl From for AutoRequest {
-    fn from(req: BiRequest) -> AutoRequest {
-        AutoRequest::Bidirectional(req)
-    }
-}
-
-impl From for Response {
-    fn from(res: AutoResponse) -> Response {
-        match res {
-            AutoResponse::Legacy(res) => res,
-            _ => panic!("Should be legacy response"),
-        }
-    }
-}
-
-impl From for BiResponse {
-    fn from(res: AutoResponse) -> BiResponse {
-        match res {
-            AutoResponse::Bidirectional(res) => res,
-            _ => panic!("Should be bidirectional response"),
-        }
-    }
+    let protocol = PostcardBidirectional { callback };
+    protocol.request(writer, request);
+    protocol.receive(reader, writer)
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
index 8daee7b2bceb..c0dbfd1679f7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
@@ -9,7 +9,9 @@ mod common {
     pub(crate) mod utils;
 }
 
-use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
+use common::utils::{
+    create_empty_token_tree, proc_macro_test_dylib_path, request_legacy, with_server,
+};
 use expect_test::expect;
 use proc_macro_api::{
     ProtocolFormat::JsonLegacy,
@@ -23,7 +25,7 @@ use proc_macro_api::{
 #[test]
 fn test_version_check() {
     with_server(JsonLegacy, |writer, reader| {
-        let response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let response = request_legacy(writer, reader, Request::ApiVersionCheck {});
 
         match response {
             Response::ApiVersionCheck(version) => {
@@ -38,7 +40,7 @@ fn test_version_check() {
 fn test_list_macros() {
     with_server(JsonLegacy, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
-        let response = request(writer, reader, Request::ListMacros { dylib_path }, None).into();
+        let response = request_legacy(writer, reader, Request::ListMacros { dylib_path });
 
         let Response::ListMacros(Ok(macros)) = response else {
             panic!("expected successful ListMacros response");
@@ -73,13 +75,11 @@ fn test_list_macros() {
 #[test]
 fn test_list_macros_invalid_path() {
     with_server(JsonLegacy, |writer, reader| {
-        let response = request(
+        let response = request_legacy(
             writer,
             reader,
             Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
-            None,
-        )
-        .into();
+        );
 
         match response {
             Response::ListMacros(Err(e)) => assert!(
@@ -95,7 +95,7 @@ fn test_list_macros_invalid_path() {
 fn test_set_config() {
     with_server(JsonLegacy, |writer, reader| {
         let config = ServerConfig { span_mode: SpanMode::Id };
-        let response = request(writer, reader, Request::SetConfig(config), None).into();
+        let response = request_legacy(writer, reader, Request::SetConfig(config));
 
         match response {
             Response::SetConfig(returned_config) => {
@@ -110,7 +110,7 @@ fn test_set_config() {
 fn test_set_config_rust_analyzer_mode() {
     with_server(JsonLegacy, |writer, reader| {
         let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
-        let response = request(writer, reader, Request::SetConfig(config), None).into();
+        let response = request_legacy(writer, reader, Request::SetConfig(config));
 
         match response {
             Response::SetConfig(returned_config) => {
@@ -126,7 +126,7 @@ fn test_expand_macro_panic() {
     with_server(JsonLegacy, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
 
-        let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let version_response = request_legacy(writer, reader, Request::ApiVersionCheck {});
         let Response::ApiVersionCheck(version) = version_response else {
             panic!("expected version check response");
         };
@@ -152,7 +152,7 @@ fn test_expand_macro_panic() {
             },
         }));
 
-        let response = request(writer, reader, expand_request, None).into();
+        let response = request_legacy(writer, reader, expand_request);
 
         match response {
             Response::ExpandMacro(Err(PanicMessage(msg))) => {
@@ -171,21 +171,18 @@ fn test_basic_call_flow() {
     with_server(JsonLegacy, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
 
-        let response1 = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let response1 = request_legacy(writer, reader, Request::ApiVersionCheck {});
         assert!(matches!(response1, Response::ApiVersionCheck(_)));
 
-        let response2 = request(
+        let response2 = request_legacy(
             writer,
             reader,
             Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }),
-            None,
-        )
-        .into();
+        );
         assert!(matches!(response2, Response::SetConfig(_)));
 
         let response3 =
-            request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }, None)
-                .into();
+            request_legacy(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() });
         assert!(matches!(response3, Response::ListMacros(Ok(_))));
     });
 }
@@ -195,7 +192,7 @@ fn test_expand_nonexistent_macro() {
     with_server(JsonLegacy, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
 
-        let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let version_response = request_legacy(writer, reader, Request::ApiVersionCheck {});
         let Response::ApiVersionCheck(version) = version_response else {
             panic!("expected version check response");
         };
@@ -221,7 +218,7 @@ fn test_expand_nonexistent_macro() {
             },
         }));
 
-        let response = request(writer, reader, expand_request, None).into();
+        let response = request_legacy(writer, reader, expand_request);
 
         match response {
             Response::ExpandMacro(Err(PanicMessage(msg))) => {

From 29aad0493b082c5d36a006fa39656d61b2c3ddc3 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 19 Jan 2026 09:58:15 +0200
Subject: [PATCH 146/583] Cache `Clauses::empty()`

---
 .../rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs
index 5758e2dc7e93..6f4fae707317 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs
@@ -273,9 +273,8 @@ impl<'db> std::fmt::Debug for Clauses<'db> {
 
 impl<'db> Clauses<'db> {
     #[inline]
-    pub fn empty(_interner: DbInterner<'db>) -> Self {
-        // FIXME: Get from a static.
-        Self::new_from_slice(&[])
+    pub fn empty(interner: DbInterner<'db>) -> Self {
+        interner.default_types().empty.clauses
     }
 
     #[inline]

From 635bacf46627409cb4569b93d38bdf23282966f3 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 15 Nov 2025 10:01:56 +0100
Subject: [PATCH 147/583] const-eval: always do mem-to-mem copies if there
 might be padding involved

---
 .../rustc_const_eval/src/interpret/place.rs   | 25 +++++++++++--
 .../src/interpret/projection.rs               |  2 +-
 tests/ui/consts/const-eval/ptr_fragments.rs   |  1 +
 .../const-eval/ptr_fragments_in_final.rs      | 36 +++++++++++++++++++
 .../const-eval/ptr_fragments_in_final.stderr  | 10 +++++-
 5 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index d472c14253b5..70d10751dbb8 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -884,10 +884,29 @@ where
                 dest.layout().ty,
             );
         }
+        // If the source has padding, we want to always do the mem-to-mem copy to ensure consistent
+        // padding in the target independent of layout choices.
+        let src_has_padding = match src.layout().backend_repr {
+            BackendRepr::Scalar(_) => false,
+            BackendRepr::ScalarPair(left, right) => {
+                let left_size = left.size(self);
+                let right_size = right.size(self);
+                // We have padding if the sizes don't add up to the total.
+                left_size + right_size != src.layout().size
+            }
+            // Everything else can only exist in memory anyway.
+            _ => true,
+        };
 
-        // Let us see if the layout is simple so we take a shortcut,
-        // avoid force_allocation.
-        let src = match self.read_immediate_raw(src)? {
+        let src_val = if src_has_padding {
+            // Do our best to get an mplace. If there's no mplace, then this is stored as an
+            // "optimized" local, so its padding is definitely uninitialized and we are fine.
+            src.to_op(self)?.as_mplace_or_imm()
+        } else {
+            // Do our best to get an immediate, to avoid having to force_allocate the destination.
+            self.read_immediate_raw(src)?
+        };
+        let src = match src_val {
             Right(src_val) => {
                 assert!(!src.layout().is_unsized());
                 assert!(!dest.layout().is_unsized());
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 027e634ef7f7..db72c02e308c 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -97,7 +97,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
     }
 
     /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
-    /// reading from this thing.
+    /// reading from this thing. This will never actually do a read from memory!
     fn to_op>(
         &self,
         ecx: &InterpCx<'tcx, M>,
diff --git a/tests/ui/consts/const-eval/ptr_fragments.rs b/tests/ui/consts/const-eval/ptr_fragments.rs
index d27804084d73..f4d92127e189 100644
--- a/tests/ui/consts/const-eval/ptr_fragments.rs
+++ b/tests/ui/consts/const-eval/ptr_fragments.rs
@@ -70,6 +70,7 @@ const _PARTIAL_OVERWRITE: () = {
 #[allow(dead_code)]
 #[cfg(not(target_arch = "s390x"))] // u128 is less aligned on s390x, removing the padding
 fn fragment_in_dst_padding_gets_overwritten() {
+    // We can't use `repr(align)` here as that would make this not a `ScalarPair` any more.
     #[repr(C)]
     struct Pair {
         x: u128,
diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs
index 4037a2d23722..76f292c3f8b5 100644
--- a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs
+++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs
@@ -37,4 +37,40 @@ const MIXED_PTR: MaybeUninit<*const u8> = { //~ERROR: partial pointer in final v
     }
 };
 
+/// This has pointer bytes in the padding of the memory that the final value is read from.
+/// To ensure consistent behavior, we want to *always* copy that padding, even if the value
+/// could be represented as a more efficient ScalarPair. Hence this must fail to compile.
+fn fragment_in_padding() -> impl Copy {
+    // We can't use `repr(align)` here as that would make this not a `ScalarPair` any more.
+    #[repr(C)]
+    #[derive(Clone, Copy)]
+    struct Thing {
+        x: u128,
+        y: usize,
+        // at least one pointer worth of padding
+    }
+    // Ensure there is indeed padding.
+    const _: () = assert!(mem::size_of::() > 16 + mem::size_of::());
+
+    #[derive(Clone, Copy)]
+    union PreservePad {
+        thing: Thing,
+        bytes: [u8; mem::size_of::()],
+    }
+
+    const A: Thing = unsafe { //~ERROR: partial pointer in final value
+        let mut buffer = [PreservePad { bytes: [0u8; mem::size_of::()] }; 2];
+        // The offset half a pointer from the end, so that copying a `Thing` copies exactly
+        // half the pointer.
+        let offset = mem::size_of::() - mem::size_of::()/2;
+        // Ensure this is inside the padding.
+        assert!(offset >= std::mem::offset_of!(Thing, y) + mem::size_of::());
+
+        (&raw mut buffer).cast::<&i32>().byte_add(offset).write_unaligned(&1);
+        buffer[0].thing
+    };
+
+    A
+}
+
 fn main() {}
diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr
index 41a822416581..de0cd4db7e15 100644
--- a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr
+++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr
@@ -14,5 +14,13 @@ LL | const MIXED_PTR: MaybeUninit<*const u8> = {
    |
    = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
 
-error: aborting due to 2 previous errors
+error: encountered partial pointer in final value of constant
+  --> $DIR/ptr_fragments_in_final.rs:61:5
+   |
+LL |     const A: Thing = unsafe {
+   |     ^^^^^^^^^^^^^^
+   |
+   = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
+
+error: aborting due to 3 previous errors
 

From 5a76a60d016d7ed5cd5fcecc131010dbce8148e0 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Mon, 17 Nov 2025 09:10:45 +0100
Subject: [PATCH 148/583] add fast-path for wide pointers

---
 .../rustc_const_eval/src/interpret/place.rs     | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 70d10751dbb8..fb07d5f0d0d6 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -5,8 +5,8 @@
 use either::{Either, Left, Right};
 use rustc_abi::{BackendRepr, HasDataLayout, Size};
 use rustc_data_structures::assert_matches;
-use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::TyAndLayout;
+use rustc_middle::ty::{self, Ty};
 use rustc_middle::{bug, mir, span_bug};
 use tracing::field::Empty;
 use tracing::{instrument, trace};
@@ -884,18 +884,27 @@ where
                 dest.layout().ty,
             );
         }
-        // If the source has padding, we want to always do the mem-to-mem copy to ensure consistent
+        // If the source has padding, we want to always do a mem-to-mem copy to ensure consistent
         // padding in the target independent of layout choices.
         let src_has_padding = match src.layout().backend_repr {
             BackendRepr::Scalar(_) => false,
+            BackendRepr::ScalarPair(left, right)
+                if matches!(src.layout().ty.kind(), ty::Ref(..) | ty::RawPtr(..)) =>
+            {
+                // Wide pointers never have padding, so we can avoid calling `size()`.
+                debug_assert_eq!(left.size(self) + right.size(self), src.layout().size);
+                false
+            }
             BackendRepr::ScalarPair(left, right) => {
                 let left_size = left.size(self);
                 let right_size = right.size(self);
                 // We have padding if the sizes don't add up to the total.
                 left_size + right_size != src.layout().size
             }
-            // Everything else can only exist in memory anyway.
-            _ => true,
+            // Everything else can only exist in memory anyway, so it doesn't matter.
+            BackendRepr::SimdVector { .. }
+            | BackendRepr::ScalableVector { .. }
+            | BackendRepr::Memory { .. } => true,
         };
 
         let src_val = if src_has_padding {

From f548a19d49576cc6bd33dbf8ef6cce4c9a21c472 Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 19 Jan 2026 21:22:14 +0000
Subject: [PATCH 149/583] remove `reason = "newly added"` from
 `#[unstable(...)]`

---
 library/core/src/result.rs    | 4 ++--
 library/core/src/slice/mod.rs | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/library/core/src/result.rs b/library/core/src/result.rs
index 9afa71ec0f11..52f3d43dfd6d 100644
--- a/library/core/src/result.rs
+++ b/library/core/src/result.rs
@@ -1354,7 +1354,7 @@ impl Result {
     /// let s: String = only_good_news().into_ok();
     /// println!("{s}");
     /// ```
-    #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")]
+    #[unstable(feature = "unwrap_infallible", issue = "61695")]
     #[inline]
     #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
     #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
@@ -1391,7 +1391,7 @@ impl Result {
     /// let error: String = only_bad_news().into_err();
     /// println!("{error}");
     /// ```
-    #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")]
+    #[unstable(feature = "unwrap_infallible", issue = "61695")]
     #[inline]
     #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
     #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index a2d3fa401b62..bbf8b0595206 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2520,7 +2520,7 @@ impl [T] {
     /// )));
     /// assert_eq!(s.split_once(|&x| x == 0), None);
     /// ```
-    #[unstable(feature = "slice_split_once", reason = "newly added", issue = "112811")]
+    #[unstable(feature = "slice_split_once", issue = "112811")]
     #[inline]
     pub fn split_once(&self, pred: F) -> Option<(&[T], &[T])>
     where
@@ -2548,7 +2548,7 @@ impl [T] {
     /// )));
     /// assert_eq!(s.rsplit_once(|&x| x == 0), None);
     /// ```
-    #[unstable(feature = "slice_split_once", reason = "newly added", issue = "112811")]
+    #[unstable(feature = "slice_split_once", issue = "112811")]
     #[inline]
     pub fn rsplit_once(&self, pred: F) -> Option<(&[T], &[T])>
     where

From 0adf24ac20c978c53ba67ed91963090e5e9ee17b Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 19 Jan 2026 21:24:36 +0000
Subject: [PATCH 150/583] remove `#[deprecated]` from unstable & internal
 `SipHasher13&24`

---
 library/alloctests/tests/c_str2.rs |  6 +-----
 library/core/src/hash/mod.rs       |  1 -
 library/core/src/hash/sip.rs       | 13 ++++++-------
 library/std/src/hash/random.rs     |  4 ----
 4 files changed, 7 insertions(+), 17 deletions(-)

diff --git a/library/alloctests/tests/c_str2.rs b/library/alloctests/tests/c_str2.rs
index fe7686bd1c59..87c303de2ed4 100644
--- a/library/alloctests/tests/c_str2.rs
+++ b/library/alloctests/tests/c_str2.rs
@@ -3,9 +3,7 @@ use alloc::rc::Rc;
 use alloc::sync::Arc;
 use core::assert_matches::assert_matches;
 use core::ffi::{CStr, FromBytesUntilNulError, c_char};
-#[allow(deprecated)]
-use core::hash::SipHasher13 as DefaultHasher;
-use core::hash::{Hash, Hasher};
+use core::hash::{Hash, Hasher, SipHasher13 as DefaultHasher};
 
 #[test]
 fn c_to_rust() {
@@ -57,11 +55,9 @@ fn equal_hash() {
     let ptr = data.as_ptr() as *const c_char;
     let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) };
 
-    #[allow(deprecated)]
     let mut s = DefaultHasher::new();
     cstr.hash(&mut s);
     let cstr_hash = s.finish();
-    #[allow(deprecated)]
     let mut s = DefaultHasher::new();
     CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s);
     let cstring_hash = s.finish();
diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs
index a800e1b41fbe..eea611857120 100644
--- a/library/core/src/hash/mod.rs
+++ b/library/core/src/hash/mod.rs
@@ -87,7 +87,6 @@
 #[allow(deprecated)]
 pub use self::sip::SipHasher;
 #[unstable(feature = "hashmap_internals", issue = "none")]
-#[allow(deprecated)]
 #[doc(hidden)]
 pub use self::sip::SipHasher13;
 use crate::{fmt, marker};
diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs
index 4569c7da035d..eed1044bd2a9 100644
--- a/library/core/src/hash/sip.rs
+++ b/library/core/src/hash/sip.rs
@@ -11,8 +11,11 @@ use crate::{cmp, ptr};
 /// (e.g., `collections::HashMap` uses it by default).
 ///
 /// See: 
-#[unstable(feature = "hashmap_internals", issue = "none")]
-#[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")]
+#[unstable(
+    feature = "hashmap_internals",
+    issue = "none",
+    reason = "use `std::hash::DefaultHasher` instead"
+)]
 #[derive(Debug, Clone, Default)]
 #[doc(hidden)]
 pub struct SipHasher13 {
@@ -23,7 +26,6 @@ pub struct SipHasher13 {
 ///
 /// See: 
 #[unstable(feature = "hashmap_internals", issue = "none")]
-#[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")]
 #[derive(Debug, Clone, Default)]
 struct SipHasher24 {
     hasher: Hasher,
@@ -137,8 +139,7 @@ unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {
         out |= (unsafe { *buf.get_unchecked(start + i) } as u64) << (i * 8);
         i += 1;
     }
-    //FIXME(fee1-dead): use debug_assert_eq
-    debug_assert!(i == len);
+    debug_assert_eq!(i, len);
     out
 }
 
@@ -167,7 +168,6 @@ impl SipHasher13 {
     #[inline]
     #[unstable(feature = "hashmap_internals", issue = "none")]
     #[rustc_const_unstable(feature = "const_default", issue = "143894")]
-    #[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")]
     pub const fn new() -> SipHasher13 {
         SipHasher13::new_with_keys(0, 0)
     }
@@ -176,7 +176,6 @@ impl SipHasher13 {
     #[inline]
     #[unstable(feature = "hashmap_internals", issue = "none")]
     #[rustc_const_unstable(feature = "const_default", issue = "143894")]
-    #[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")]
     pub const fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 {
         SipHasher13 { hasher: Hasher::new_with_keys(key0, key1) }
     }
diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs
index fab090e31f03..3c1b21eec975 100644
--- a/library/std/src/hash/random.rs
+++ b/library/std/src/hash/random.rs
@@ -7,7 +7,6 @@
 //!
 //! [`collections`]: crate::collections
 
-#[allow(deprecated)]
 use super::{BuildHasher, Hasher, SipHasher13};
 use crate::cell::Cell;
 use crate::fmt;
@@ -81,7 +80,6 @@ impl RandomState {
 impl BuildHasher for RandomState {
     type Hasher = DefaultHasher;
     #[inline]
-    #[allow(deprecated)]
     fn build_hasher(&self) -> DefaultHasher {
         DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1))
     }
@@ -91,7 +89,6 @@ impl BuildHasher for RandomState {
 ///
 /// The internal algorithm is not specified, and so it and its hashes should
 /// not be relied upon over releases.
-#[allow(deprecated)]
 #[derive(Clone, Debug)]
 #[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
 pub struct DefaultHasher(SipHasher13);
@@ -104,7 +101,6 @@ impl DefaultHasher {
     /// instances created through `new` or `default`.
     #[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
     #[inline]
-    #[allow(deprecated)]
     #[rustc_const_unstable(feature = "const_default", issue = "143894")]
     #[must_use]
     pub const fn new() -> DefaultHasher {

From a175d05e8503458ad57849e1a9ea0607c67f28b9 Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 19 Jan 2026 21:25:32 +0000
Subject: [PATCH 151/583] remove old `#[allow(deprecated)]` for `env::home_dir`
 calls

---
 library/std/tests/env_modify.rs            | 1 -
 library/test/src/term/terminfo/searcher.rs | 1 -
 2 files changed, 2 deletions(-)

diff --git a/library/std/tests/env_modify.rs b/library/std/tests/env_modify.rs
index fe0ae68806e9..4cd87bf7a002 100644
--- a/library/std/tests/env_modify.rs
+++ b/library/std/tests/env_modify.rs
@@ -99,7 +99,6 @@ fn test_env_set_var() {
 
 #[test]
 #[cfg_attr(not(any(unix, windows)), ignore, allow(unused))]
-#[allow(deprecated)]
 fn env_home_dir() {
     use std::path::PathBuf;
 
diff --git a/library/test/src/term/terminfo/searcher.rs b/library/test/src/term/terminfo/searcher.rs
index 1b29598cf804..1f9d0bb345b7 100644
--- a/library/test/src/term/terminfo/searcher.rs
+++ b/library/test/src/term/terminfo/searcher.rs
@@ -9,7 +9,6 @@ use std::{env, fs};
 mod tests;
 
 /// Returns path to database entry for `term`
-#[allow(deprecated)]
 pub(crate) fn get_dbpath_for_term(term: &str) -> Option {
     let mut dirs_to_search = Vec::new();
     let first_char = term.chars().next()?;

From 7f71511d58084e6eee2d904af265adad66dba4d0 Mon Sep 17 00:00:00 2001
From: Tshepang Mbambo 
Date: Tue, 20 Jan 2026 00:25:02 +0200
Subject: [PATCH 152/583] inline outlier links, for consistency

---
 src/doc/rustc-dev-guide/src/SUMMARY.md | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index daaaef42d909..a9ae1f707e53 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -197,7 +197,7 @@
 - [Opaque types](./opaque-types-type-alias-impl-trait.md)
     - [Inference details](./opaque-types-impl-trait-inference.md)
     - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md)
-    - [Region inference restrictions][opaque-infer]
+    - [Region inference restrictions](./borrow_check/opaque-types-region-inference-restrictions.md)
 - [Const condition checking](./effects.md)
 - [Pattern and exhaustiveness checking](./pat-exhaustive-checking.md)
 - [Unsafety checking](./unsafety-checking.md)
@@ -212,7 +212,7 @@
         - [Constraint propagation](./borrow_check/region_inference/constraint_propagation.md)
         - [Lifetime parameters](./borrow_check/region_inference/lifetime_parameters.md)
         - [Member constraints](./borrow_check/region_inference/member_constraints.md)
-        - [Placeholders and universes][pau]
+        - [Placeholders and universes](./borrow_check/region_inference/placeholders_and_universes.md)
         - [Closure constraints](./borrow_check/region_inference/closure_constraints.md)
         - [Error reporting](./borrow_check/region_inference/error_reporting.md)
     - [Two-phase-borrows](./borrow_check/two_phase_borrows.md)
@@ -263,8 +263,3 @@
 [Appendix E: Bibliography](./appendix/bibliography.md)
 
 [Appendix Z: HumorRust](./appendix/humorust.md)
-
----
-
-[pau]: ./borrow_check/region_inference/placeholders_and_universes.md
-[opaque-infer]: ./borrow_check/opaque-types-region-inference-restrictions.md

From 9fad6f6d6088f7b0bd7002a98069452183843f63 Mon Sep 17 00:00:00 2001
From: Tshepang Mbambo 
Date: Tue, 20 Jan 2026 03:44:26 +0200
Subject: [PATCH 153/583] replace _ with - in path names (for consistency)

---
 src/doc/rustc-dev-guide/book.toml             | 30 +++++++++-
 src/doc/rustc-dev-guide/src/SUMMARY.md        | 56 +++++++++----------
 .../src/appendix/background.md                |  3 +-
 .../src/appendix/code-index.md                |  2 +-
 .../rustc-dev-guide/src/appendix/glossary.md  |  4 +-
 .../src/{borrow_check.md => borrow-check.md}  |  4 +-
 .../drop-check.md}                            |  0
 .../moves-and-initialization.md}              | 12 ++--
 .../moves-and-initialization/move-paths.md}   |  0
 ...que-types-region-inference-restrictions.md |  6 +-
 .../region-inference.md}                      |  6 +-
 .../region-inference/closure-constraints.md}  |  0
 .../constraint-propagation.md}                |  6 +-
 .../region-inference/error-reporting.md}      |  0
 .../region-inference/lifetime-parameters.md}  |  2 +-
 .../region-inference/member-constraints.md}   |  0
 .../placeholders-and-universes.md}            |  0
 .../two-phase-borrows.md}                     |  0
 .../type-check.md}                            |  0
 src/doc/rustc-dev-guide/src/const-eval.md     |  2 +-
 src/doc/rustc-dev-guide/src/contributing.md   |  2 +-
 ...parameters.md => early-late-parameters.md} |  0
 src/doc/rustc-dev-guide/src/feature-gates.md  |  6 +-
 ...mmary.md => generic-parameters-summary.md} |  4 +-
 ...atures.md => implementing-new-features.md} |  4 +-
 src/doc/rustc-dev-guide/src/memory.md         |  6 +-
 src/doc/rustc-dev-guide/src/normalization.md  | 20 +++----
 .../src/opaque-types-impl-trait-inference.md  |  2 +-
 src/doc/rustc-dev-guide/src/overview.md       |  6 +-
 src/doc/rustc-dev-guide/src/profiling.md      |  4 +-
 .../profiling/{with_perf.md => with-perf.md}  |  2 +-
 ...{with_rustc_perf.md => with-rustc-perf.md} |  0
 .../{wpa_profiling.md => wpa-profiling.md}    |  0
 .../rustc-dev-guide/src/solve/opaque-types.md |  4 +-
 .../src/stability-guarantees.md               |  2 +-
 src/doc/rustc-dev-guide/src/stability.md      |  2 +-
 ...zation_guide.md => stabilization-guide.md} |  2 +-
 ...te.md => stabilization-report-template.md} |  2 +-
 src/doc/rustc-dev-guide/src/tests/perf.md     |  2 +-
 src/doc/rustc-dev-guide/src/traits/caching.md |  2 +-
 .../rustc-dev-guide/src/traits/resolution.md  | 12 ++--
 src/doc/rustc-dev-guide/src/ty-fold.md        |  2 +-
 .../src/{ty_module => ty-module}/binders.md   |  0
 .../early-binder.md}                          |  0
 .../generic-arguments.md}                     |  0
 .../instantiating-binders.md}                 | 28 +++++-----
 .../param-ty-const-regions.md}                | 10 ++--
 ...meter_envs.md => typing-parameter-envs.md} |  0
 src/doc/rustc-dev-guide/src/walkthrough.md    |  2 +-
 49 files changed, 143 insertions(+), 116 deletions(-)
 rename src/doc/rustc-dev-guide/src/{borrow_check.md => borrow-check.md} (95%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/drop_check.md => borrow-check/drop-check.md} (100%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/moves_and_initialization.md => borrow-check/moves-and-initialization.md} (94%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/moves_and_initialization/move_paths.md => borrow-check/moves-and-initialization/move-paths.md} (100%)
 rename src/doc/rustc-dev-guide/src/{borrow_check => borrow-check}/opaque-types-region-inference-restrictions.md (99%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/region_inference.md => borrow-check/region-inference.md} (98%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/region_inference/closure_constraints.md => borrow-check/region-inference/closure-constraints.md} (100%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/region_inference/constraint_propagation.md => borrow-check/region-inference/constraint-propagation.md} (98%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/region_inference/error_reporting.md => borrow-check/region-inference/error-reporting.md} (100%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/region_inference/lifetime_parameters.md => borrow-check/region-inference/lifetime-parameters.md} (98%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/region_inference/member_constraints.md => borrow-check/region-inference/member-constraints.md} (100%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/region_inference/placeholders_and_universes.md => borrow-check/region-inference/placeholders-and-universes.md} (100%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/two_phase_borrows.md => borrow-check/two-phase-borrows.md} (100%)
 rename src/doc/rustc-dev-guide/src/{borrow_check/type_check.md => borrow-check/type-check.md} (100%)
 rename src/doc/rustc-dev-guide/src/{early_late_parameters.md => early-late-parameters.md} (100%)
 rename src/doc/rustc-dev-guide/src/{generic_parameters_summary.md => generic-parameters-summary.md} (96%)
 rename src/doc/rustc-dev-guide/src/{implementing_new_features.md => implementing-new-features.md} (99%)
 rename src/doc/rustc-dev-guide/src/profiling/{with_perf.md => with-perf.md} (99%)
 rename src/doc/rustc-dev-guide/src/profiling/{with_rustc_perf.md => with-rustc-perf.md} (100%)
 rename src/doc/rustc-dev-guide/src/profiling/{wpa_profiling.md => wpa-profiling.md} (100%)
 rename src/doc/rustc-dev-guide/src/{stabilization_guide.md => stabilization-guide.md} (99%)
 rename src/doc/rustc-dev-guide/src/{stabilization_report_template.md => stabilization-report-template.md} (99%)
 rename src/doc/rustc-dev-guide/src/{ty_module => ty-module}/binders.md (100%)
 rename src/doc/rustc-dev-guide/src/{ty_module/early_binder.md => ty-module/early-binder.md} (100%)
 rename src/doc/rustc-dev-guide/src/{ty_module/generic_arguments.md => ty-module/generic-arguments.md} (100%)
 rename src/doc/rustc-dev-guide/src/{ty_module/instantiating_binders.md => ty-module/instantiating-binders.md} (95%)
 rename src/doc/rustc-dev-guide/src/{ty_module/param_ty_const_regions.md => ty-module/param-ty-const-regions.md} (97%)
 rename src/doc/rustc-dev-guide/src/{typing_parameter_envs.md => typing-parameter-envs.md} (100%)

diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml
index 15a597e5addb..18ca2c85a617 100644
--- a/src/doc/rustc-dev-guide/book.toml
+++ b/src/doc/rustc-dev-guide/book.toml
@@ -57,11 +57,39 @@ cache-timeout = 90000
 warning-policy = "error"
 
 [output.html.redirect]
+"/borrow_check.html" = "borrow-check.html"
+"/borrow_check/drop_check.html" = "borrow-check/drop-check.html"
+"/borrow_check/moves_and_initialization.html" = "borrow-check/moves-and-initialization.html"
+"/borrow_check/moves_and_initialization/move_paths.html" = "borrow-check/moves-and-initialization/move-paths.html"
+"/borrow_check/opaque-types-region-inference-restrictions.html" = "borrow-check/opaque-types-region-inference-restrictions.html"
+"/borrow_check/region_inference.html" = "borrow-check/region-inference.html"
+"/borrow_check/region_inference/closure_constraints.html" = "borrow-check/region-inference/closure-constraints.html"
+"/borrow_check/region_inference/constraint_propagation.html" = "borrow-check/region-inference/constraint-propagation.html"
+"/borrow_check/region_inference/error_reporting.html" = "borrow-check/region-inference/error-reporting.html"
+"/borrow_check/region_inference/lifetime_parameters.html" = "borrow-check/region-inference/lifetime-parameters.html"
+"/borrow_check/region_inference/member_constraints.html" = "borrow-check/region-inference/member-constraints.html"
+"/borrow_check/region_inference/placeholders_and_universes.html" = "borrow-check/region-inference/placeholders-and-universes.html"
+"/borrow_check/two_phase_borrows.html" = "borrow-check/two-phase-borrows.html"
+"/borrow_check/type_check.html" = "borrow-check/type-check.html"
 "/compiletest.html" = "tests/compiletest.html"
-"/diagnostics/sessiondiagnostic.html" = "diagnostic-structs.html"
 "/diagnostics/diagnostic-codes.html" = "error-codes.html"
+"/diagnostics/sessiondiagnostic.html" = "diagnostic-structs.html"
+"/early_late_parameters.html" = "early-late-parameters.html"
+"/generic_parameters_summary.html" = "generic-parameters-summary.html"
+"/implementing_new_features.html" = "implementing-new-features.html"
 "/miri.html" = "const-eval/interpret.html"
+"/profiling/with_perf.html" = "profiling/with-perf.html"
+"/profiling/with_rustc_perf.html" = "profiling/with-rustc-perf.html"
+"/profiling/wpa_profiling.html" = "profiling/wpa-profiling.html"
+"/stabilization_guide.html" = "stabilization-guide.html"
+"/stabilization_report_template.html" = "stabilization-report-template.html"
 "/tests/fuchsia.html" = "ecosystem-test-jobs/fuchsia.html"
 "/tests/headers.html" = "directives.html"
 "/tests/integration.html" = "ecosystem.html"
 "/tests/rust-for-linux.html" = "ecosystem-test-jobs/rust-for-linux.html"
+"/ty_module/binders.html" = "ty-module/binders.html"
+"/ty_module/early_binder.html" = "ty-module/early-binder.html"
+"/ty_module/generic_arguments.html" = "ty-module/generic-arguments.html"
+"/ty_module/instantiating_binders.html" = "ty-module/instantiating-binders.html"
+"/ty_module/param_ty_const_regions.html" = "ty-module/param-ty-const-regions.html"
+"/typing_parameter_envs.html" = "typing-parameter-envs.html"
diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index a9ae1f707e53..71c25003b193 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -39,9 +39,9 @@
 - [Debugging the compiler](./compiler-debugging.md)
     - [Using the tracing/logging instrumentation](./tracing.md)
 - [Profiling the compiler](./profiling.md)
-    - [with the linux perf tool](./profiling/with_perf.md)
-    - [with Windows Performance Analyzer](./profiling/wpa_profiling.md)
-    - [with the Rust benchmark suite](./profiling/with_rustc_perf.md)
+    - [with the linux perf tool](./profiling/with-perf.md)
+    - [with Windows Performance Analyzer](./profiling/wpa-profiling.md)
+    - [with the Rust benchmark suite](./profiling/with-rustc-perf.md)
 - [crates.io dependencies](./crates-io.md)
 
 # Contributing to Rust
@@ -51,11 +51,11 @@
 - [Using Git](./git.md)
 - [Mastering @rustbot](./rustbot.md)
 - [Walkthrough: a typical contribution](./walkthrough.md)
-- [Implementing new language features](./implementing_new_features.md)
+- [Implementing new language features](./implementing-new-features.md)
 - [Stability guarantees](./stability-guarantees.md)
 - [Stability attributes](./stability.md)
-- [Stabilizing language features](./stabilization_guide.md)
-    - [Stabilization report template](./stabilization_report_template.md)
+- [Stabilizing language features](./stabilization-guide.md)
+    - [Stabilization report template](./stabilization-report-template.md)
 - [Feature Gates](./feature-gates.md)
 - [Coding conventions](./conventions.md)
 - [Procedures for breaking changes](./bug-fix-procedure.md)
@@ -154,17 +154,17 @@
 # Analysis
 
 - [Prologue](./part-4-intro.md)
-- [Generic parameter definitions](./generic_parameters_summary.md)
-    - [`EarlyBinder` and instantiating parameters](./ty_module/early_binder.md)
-- [Binders and Higher ranked regions](./ty_module/binders.md)
-    - [Instantiating binders](./ty_module/instantiating_binders.md)
-- [Early vs Late bound parameters](./early_late_parameters.md)
+- [Generic parameter definitions](./generic-parameters-summary.md)
+    - [`EarlyBinder` and instantiating parameters](./ty-module/early-binder.md)
+- [Binders and Higher ranked regions](./ty-module/binders.md)
+    - [Instantiating binders](./ty-module/instantiating-binders.md)
+- [Early vs Late bound parameters](./early-late-parameters.md)
 - [The `ty` module: representing types](./ty.md)
-    - [ADTs and Generic Arguments](./ty_module/generic_arguments.md)
-    - [Parameter types/consts/regions](./ty_module/param_ty_const_regions.md)
+    - [ADTs and Generic Arguments](./ty-module/generic-arguments.md)
+    - [Parameter types/consts/regions](./ty-module/param-ty-const-regions.md)
 - [`TypeFolder` and `TypeFoldable`](./ty-fold.md)
 - [Aliases and Normalization](./normalization.md)
-- [Typing/Param Envs](./typing_parameter_envs.md)
+- [Typing/Param Envs](./typing-parameter-envs.md)
 - [Type inference](./type-inference.md)
 - [Trait solving](./traits/resolution.md)
     - [Higher-ranked trait bounds](./traits/hrtb.md)
@@ -197,25 +197,25 @@
 - [Opaque types](./opaque-types-type-alias-impl-trait.md)
     - [Inference details](./opaque-types-impl-trait-inference.md)
     - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md)
-    - [Region inference restrictions](./borrow_check/opaque-types-region-inference-restrictions.md)
+    - [Region inference restrictions](./borrow-check/opaque-types-region-inference-restrictions.md)
 - [Const condition checking](./effects.md)
 - [Pattern and exhaustiveness checking](./pat-exhaustive-checking.md)
 - [Unsafety checking](./unsafety-checking.md)
 - [MIR dataflow](./mir/dataflow.md)
 - [Drop elaboration](./mir/drop-elaboration.md)
-- [The borrow checker](./borrow_check.md)
-    - [Tracking moves and initialization](./borrow_check/moves_and_initialization.md)
-        - [Move paths](./borrow_check/moves_and_initialization/move_paths.md)
-    - [MIR type checker](./borrow_check/type_check.md)
-    - [Drop check](./borrow_check/drop_check.md)
-    - [Region inference](./borrow_check/region_inference.md)
-        - [Constraint propagation](./borrow_check/region_inference/constraint_propagation.md)
-        - [Lifetime parameters](./borrow_check/region_inference/lifetime_parameters.md)
-        - [Member constraints](./borrow_check/region_inference/member_constraints.md)
-        - [Placeholders and universes](./borrow_check/region_inference/placeholders_and_universes.md)
-        - [Closure constraints](./borrow_check/region_inference/closure_constraints.md)
-        - [Error reporting](./borrow_check/region_inference/error_reporting.md)
-    - [Two-phase-borrows](./borrow_check/two_phase_borrows.md)
+- [The borrow checker](./borrow-check.md)
+    - [Tracking moves and initialization](./borrow-check/moves-and-initialization.md)
+        - [Move paths](./borrow-check/moves-and-initialization/move-paths.md)
+    - [MIR type checker](./borrow-check/type-check.md)
+    - [Drop check](./borrow-check/drop-check.md)
+    - [Region inference](./borrow-check/region-inference.md)
+        - [Constraint propagation](./borrow-check/region-inference/constraint-propagation.md)
+        - [Lifetime parameters](./borrow-check/region-inference/lifetime-parameters.md)
+        - [Member constraints](./borrow-check/region-inference/member-constraints.md)
+        - [Placeholders and universes](./borrow-check/region-inference/placeholders-and-universes.md)
+        - [Closure constraints](./borrow-check/region-inference/closure-constraints.md)
+        - [Error reporting](./borrow-check/region-inference/error-reporting.md)
+    - [Two-phase-borrows](./borrow-check/two-phase-borrows.md)
 - [Closure capture inference](./closure.md)
 - [Async closures/"coroutine-closures"](coroutine-closures.md)
 
diff --git a/src/doc/rustc-dev-guide/src/appendix/background.md b/src/doc/rustc-dev-guide/src/appendix/background.md
index d36927e82f74..e76285080394 100644
--- a/src/doc/rustc-dev-guide/src/appendix/background.md
+++ b/src/doc/rustc-dev-guide/src/appendix/background.md
@@ -243,8 +243,7 @@ use in lambda calculus evaluation (see [this Wikipedia article][wikideb] for
 more). In `rustc`, we use de Bruijn indices to [represent generic types][sub].
 
 [wikideb]: https://en.wikipedia.org/wiki/De_Bruijn_index
-[sub]: ../ty_module/generic_arguments.md
-
+[sub]: ../ty-module/generic-arguments.md
 
 Here is a basic example of how de Bruijn indices might be used for closures (we
 don't actually do this in `rustc` though!):
diff --git a/src/doc/rustc-dev-guide/src/appendix/code-index.md b/src/doc/rustc-dev-guide/src/appendix/code-index.md
index bf9d3bd46564..3e1eed17eba6 100644
--- a/src/doc/rustc-dev-guide/src/appendix/code-index.md
+++ b/src/doc/rustc-dev-guide/src/appendix/code-index.md
@@ -39,5 +39,5 @@ Item            |  Kind    | Short description           | Chapter            |
 [Emitting Diagnostics]: ../diagnostics.html
 [Macro expansion]: ../macro-expansion.html
 [Name resolution]: ../name-resolution.html
-[Parameter Environment]: ../typing_parameter_envs.html
+[Parameter Environment]: ../typing-parameter-envs.html
 [Trait Solving: Goals and Clauses]: ../traits/goals-and-clauses.html#domain-goals
diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md
index 901fb68c0513..43935b12a238 100644
--- a/src/doc/rustc-dev-guide/src/appendix/glossary.md
+++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md
@@ -53,10 +53,10 @@ Term                                                  | Meaning
 normalize          |  A general term for converting to a more canonical form, but in the case of rustc typically refers to [associated type normalization](../traits/goals-and-clauses.md#normalizeprojection---type).
 newtype              |  A wrapper around some other type (e.g., `struct Foo(T)` is a "newtype" for `T`). This is commonly used in Rust to give a stronger type for indices.
 niche                  |  Invalid bit patterns for a type _that can be used_ for layout optimizations. Some types cannot have certain bit patterns. For example, the `NonZero*` integers or the reference `&T` cannot be represented by a 0 bitstring. This means the compiler can perform layout optimizations by taking advantage of the invalid "niche value". An example application for this is the [*Discriminant elision on `Option`-like enums*](https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#discriminant-elision-on-option-like-enums), which allows using a type's niche as the ["tag"](#tag) for an `enum` without requiring a separate field.
-NLL                      |  Short for [non-lexical lifetimes](../borrow_check/region_inference.md), this is an extension to Rust's borrowing system to make it be based on the control-flow graph.
+NLL                      |  Short for [non-lexical lifetimes](../borrow-check/region-inference.md), this is an extension to Rust's borrowing system to make it be based on the control-flow graph.
 node-id or `NodeId`  |  An index identifying a particular node in the AST or HIR; gradually being phased out and replaced with `HirId`. See [the HIR chapter for more](../hir.md#identifiers-in-the-hir).
 obligation        |  Something that must be proven by the trait system. ([see more](../traits/resolution.md))
-placeholder      |  **NOTE: skolemization is deprecated by placeholder** a way of handling subtyping around "for-all" types (e.g., `for<'a> fn(&'a u32)`) as well as solving higher-ranked trait bounds (e.g., `for<'a> T: Trait<'a>`). See [the chapter on placeholder and universes](../borrow_check/region_inference/placeholders_and_universes.md) for more details.
+placeholder      |  **NOTE: skolemization is deprecated by placeholder** a way of handling subtyping around "for-all" types (e.g., `for<'a> fn(&'a u32)`) as well as solving higher-ranked trait bounds (e.g., `for<'a> T: Trait<'a>`). See [the chapter on placeholder and universes](../borrow-check/region-inference/placeholders-and-universes.md) for more details.
 point                  |  Used in the NLL analysis to refer to some particular location in the MIR; typically used to refer to a node in the control-flow graph.
 projection        |  A general term for a "relative path", e.g. `x.f` is a "field projection", and `T::Item` is an ["associated type projection"](../traits/goals-and-clauses.md#trait-ref).
 promoted constants        |  Constants extracted from a function and lifted to static scope; see [this section](../mir/index.md#promoted) for more details.
diff --git a/src/doc/rustc-dev-guide/src/borrow_check.md b/src/doc/rustc-dev-guide/src/borrow-check.md
similarity index 95%
rename from src/doc/rustc-dev-guide/src/borrow_check.md
rename to src/doc/rustc-dev-guide/src/borrow-check.md
index f206da42a82c..826bcf8582ca 100644
--- a/src/doc/rustc-dev-guide/src/borrow_check.md
+++ b/src/doc/rustc-dev-guide/src/borrow-check.md
@@ -42,10 +42,10 @@ the [`mir_borrowck`] query.
 - Next, we perform a number of
   [dataflow analyses](./appendix/background.md#dataflow) that
   compute what data is moved and when.
-- We then do a [second type check](borrow_check/type_check.md) across the MIR:
+- We then do a [second type check](borrow-check/type-check.md) across the MIR:
   the purpose of this type check is to determine all of the constraints between
   different regions.
-- Next, we do [region inference](borrow_check/region_inference.md), which computes
+- Next, we do [region inference](borrow-check/region-inference.md), which computes
   the values of each region — basically, the points in the control-flow graph where
   each lifetime must be valid according to the constraints we collected.
 - At this point, we can compute the "borrows in scope" at each point.
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/drop_check.md b/src/doc/rustc-dev-guide/src/borrow-check/drop-check.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/borrow_check/drop_check.md
rename to src/doc/rustc-dev-guide/src/borrow-check/drop-check.md
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/moves_and_initialization.md b/src/doc/rustc-dev-guide/src/borrow-check/moves-and-initialization.md
similarity index 94%
rename from src/doc/rustc-dev-guide/src/borrow_check/moves_and_initialization.md
rename to src/doc/rustc-dev-guide/src/borrow-check/moves-and-initialization.md
index 043db2f5354e..f9eaef8e2b46 100644
--- a/src/doc/rustc-dev-guide/src/borrow_check/moves_and_initialization.md
+++ b/src/doc/rustc-dev-guide/src/borrow-check/moves-and-initialization.md
@@ -20,15 +20,15 @@ Consider this example:
 ```rust,ignore
 fn foo() {
     let a: Vec;
-    
+
     // a is not initialized yet
-    
+
     a = vec![22];
-    
+
     // a is initialized here
-    
+
     std::mem::drop(a); // a is moved here
-    
+
     // a is no longer initialized here
 
     let l = a.len(); //~ ERROR
@@ -44,7 +44,7 @@ moves `a` into the call, and hence it becomes uninitialized again.
 To make it easier to peruse, this section is broken into a number of
 subsections:
 
-- [Move paths](./moves_and_initialization/move_paths.html) the
+- [Move paths](./moves-and-initialization/move-paths.md) the
   *move path* concept that we use to track which local variables (or parts of
   local variables, in some cases) are initialized.
 - TODO *Rest not yet written* =)
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/moves_and_initialization/move_paths.md b/src/doc/rustc-dev-guide/src/borrow-check/moves-and-initialization/move-paths.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/borrow_check/moves_and_initialization/move_paths.md
rename to src/doc/rustc-dev-guide/src/borrow-check/moves-and-initialization/move-paths.md
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/opaque-types-region-inference-restrictions.md b/src/doc/rustc-dev-guide/src/borrow-check/opaque-types-region-inference-restrictions.md
similarity index 99%
rename from src/doc/rustc-dev-guide/src/borrow_check/opaque-types-region-inference-restrictions.md
rename to src/doc/rustc-dev-guide/src/borrow-check/opaque-types-region-inference-restrictions.md
index 9877dfc61e9c..3a060ccfa282 100644
--- a/src/doc/rustc-dev-guide/src/borrow_check/opaque-types-region-inference-restrictions.md
+++ b/src/doc/rustc-dev-guide/src/borrow-check/opaque-types-region-inference-restrictions.md
@@ -158,7 +158,7 @@ See [#113971] for how we used to conflate the difference.
 
 [#113971]: https://github.com/rust-lang/rust/issues/113971
 [SCC]: https://en.wikipedia.org/wiki/Strongly_connected_component
-[member constraints]: ./region_inference/member_constraints.md
+[member constraints]: region-inference/member-constraints.md
 
 **interaction with "once modulo regions" restriction**
 In the example above, note the opaque type in the signature is `Opaque<'a>` and the one in the
@@ -195,7 +195,7 @@ fn test<'a>() -> Opaque<'a> {
 }
 ```
 
-**Motivation:** 
+**Motivation:**
 In closure bodies, external lifetimes, although being categorized as "universal" lifetimes,
 behave more like existential lifetimes in that the relations between them are not known ahead of
 time, instead their values are inferred just like existential lifetimes and the requirements are
@@ -208,7 +208,7 @@ Here is an example that details how :
 ```rust
 type Opaque<'x, 'y> = impl Sized;
 
-// 
+//
 fn test<'a, 'b>(s: &'a str) -> impl FnOnce() -> Opaque<'a, 'b> {
     move || { s }
     //~^ ERROR hidden type for `Opaque<'_, '_>` captures lifetime that does not appear in bounds
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference.md b/src/doc/rustc-dev-guide/src/borrow-check/region-inference.md
similarity index 98%
rename from src/doc/rustc-dev-guide/src/borrow_check/region_inference.md
rename to src/doc/rustc-dev-guide/src/borrow-check/region-inference.md
index ba67bec45b65..2a1c34df1a77 100644
--- a/src/doc/rustc-dev-guide/src/borrow_check/region_inference.md
+++ b/src/doc/rustc-dev-guide/src/borrow-check/region-inference.md
@@ -34,14 +34,14 @@ The MIR-based region analysis consists of two major functions:
   - The [NLL RFC] also includes fairly thorough (and hopefully readable)
     coverage.
 
-[cp]: ./region_inference/constraint_propagation.md
+[cp]: ./region-inference/constraint-propagation.md
 [fvb]: ../appendix/background.md#free-vs-bound
 [`replace_regions_in_mir`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/nll/fn.replace_regions_in_mir.html
 [`compute_regions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/nll/fn.compute_regions.html
 [`RegionInferenceContext`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html
 [`solve`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#method.solve
 [NLL RFC]: https://rust-lang.github.io/rfcs/2094-nll.html
-[MIR type checker]: ./type_check.md
+[MIR type checker]: ./type-check.md
 
 ## Universal regions
 
@@ -97,7 +97,7 @@ The kinds of region elements are as follows:
 - There is an element `!1` for each placeholder region `!1`. This
   corresponds (intuitively) to some unknown set of other elements –
   for details on placeholders, see the section
-  [placeholders and universes](region_inference/placeholders_and_universes.md).
+  [placeholders and universes](region-inference/placeholders-and-universes.md).
 
 ## Constraints
 
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md b/src/doc/rustc-dev-guide/src/borrow-check/region-inference/closure-constraints.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md
rename to src/doc/rustc-dev-guide/src/borrow-check/region-inference/closure-constraints.md
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/constraint_propagation.md b/src/doc/rustc-dev-guide/src/borrow-check/region-inference/constraint-propagation.md
similarity index 98%
rename from src/doc/rustc-dev-guide/src/borrow_check/region_inference/constraint_propagation.md
rename to src/doc/rustc-dev-guide/src/borrow-check/region-inference/constraint-propagation.md
index c3f8c03cb29f..580a0aebe9a4 100644
--- a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/constraint_propagation.md
+++ b/src/doc/rustc-dev-guide/src/borrow-check/region-inference/constraint-propagation.md
@@ -11,7 +11,7 @@ on one at a time (each of them is fairly independent from the others):
 - [member constraints][m_c] (`member R_m of [R_c...]`), which arise from impl Trait.
 
 [`propagate_constraints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#method.propagate_constraints
-[m_c]: ./member_constraints.md
+[m_c]: ./member-constraints.md
 
 In this chapter, we'll explain the "heart" of constraint propagation,
 covering both liveness and outlives constraints.
@@ -29,7 +29,7 @@ given some set of constraints `{C}` and it computes a set of values
   - For each constraint C:
     - Update `Values` as needed to satisfy the constraint
 
-[riv]: ../region_inference.md#region-variables
+[riv]: ../region-inference.md#region-variables
 
 As a simple example, if we have a liveness constraint `R live at E`,
 then we can apply `Values(R) = Values(R) union {E}` to make the
@@ -211,7 +211,7 @@ have already processed a given SCC or not. For each successor `S2`, once
 we have computed `S2`'s value, we can union those elements into the
 value for `S1`. (Although we have to be careful in this process to
 properly handle [higher-ranked
-placeholders](./placeholders_and_universes.html). Note that the value
+placeholders](./placeholders-and-universes.md). Note that the value
 for `S1` already contains the liveness constraints, since they were
 added in [`RegionInferenceContext::new`].
 
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/error_reporting.md b/src/doc/rustc-dev-guide/src/borrow-check/region-inference/error-reporting.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/borrow_check/region_inference/error_reporting.md
rename to src/doc/rustc-dev-guide/src/borrow-check/region-inference/error-reporting.md
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/lifetime_parameters.md b/src/doc/rustc-dev-guide/src/borrow-check/region-inference/lifetime-parameters.md
similarity index 98%
rename from src/doc/rustc-dev-guide/src/borrow_check/region_inference/lifetime_parameters.md
rename to src/doc/rustc-dev-guide/src/borrow-check/region-inference/lifetime-parameters.md
index 2d337dbc020f..3795cea3c6bc 100644
--- a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/lifetime_parameters.md
+++ b/src/doc/rustc-dev-guide/src/borrow-check/region-inference/lifetime-parameters.md
@@ -54,7 +54,7 @@ In fact, the universal regions can be further subdivided based on
 where they were brought into scope (see the [`RegionClassification`]
 type). These subdivisions are not important for the topics discussed
 here, but become important when we consider [closure constraint
-propagation](./closure_constraints.html), so we discuss them there.
+propagation](./closure-constraints.md), so we discuss them there.
 
 [`RegionClassification`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/universal_regions/enum.RegionClassification.html#variant.Local
 
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/member_constraints.md b/src/doc/rustc-dev-guide/src/borrow-check/region-inference/member-constraints.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/borrow_check/region_inference/member_constraints.md
rename to src/doc/rustc-dev-guide/src/borrow-check/region-inference/member-constraints.md
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/placeholders_and_universes.md b/src/doc/rustc-dev-guide/src/borrow-check/region-inference/placeholders-and-universes.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/borrow_check/region_inference/placeholders_and_universes.md
rename to src/doc/rustc-dev-guide/src/borrow-check/region-inference/placeholders-and-universes.md
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/two_phase_borrows.md b/src/doc/rustc-dev-guide/src/borrow-check/two-phase-borrows.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/borrow_check/two_phase_borrows.md
rename to src/doc/rustc-dev-guide/src/borrow-check/two-phase-borrows.md
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/type_check.md b/src/doc/rustc-dev-guide/src/borrow-check/type-check.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/borrow_check/type_check.md
rename to src/doc/rustc-dev-guide/src/borrow-check/type-check.md
diff --git a/src/doc/rustc-dev-guide/src/const-eval.md b/src/doc/rustc-dev-guide/src/const-eval.md
index ca6a35a5e97e..a3fee034ec6e 100644
--- a/src/doc/rustc-dev-guide/src/const-eval.md
+++ b/src/doc/rustc-dev-guide/src/const-eval.md
@@ -35,7 +35,7 @@ They're the wrappers of the `const_eval` query.
   Statics are special; all other functions do not represent statics correctly
   and have thus assertions preventing their use on statics.
 
-The `const_eval_*` functions use a [`ParamEnv`](./typing_parameter_envs.html) of environment
+The `const_eval_*` functions use a [`ParamEnv`](./typing-parameter-envs.md) of environment
 in which the constant is evaluated (e.g. the function within which the constant is used)
 and a [`GlobalId`]. The `GlobalId` is made up of an `Instance` referring to a constant
 or static or of an `Instance` of a function and an index into the function's `Promoted` table.
diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md
index 46d0dc23394a..83f4253a6a10 100644
--- a/src/doc/rustc-dev-guide/src/contributing.md
+++ b/src/doc/rustc-dev-guide/src/contributing.md
@@ -60,7 +60,7 @@ See [The Rust Book] for more details on Rust’s train release model.
   This is the only channel where unstable features are intended to be used,
   which happens via opt-in feature gates.
 
-See [this chapter on implementing new features](./implementing_new_features.md) for more
+See [this chapter on implementing new features](./implementing-new-features.md) for more
 information.
 
 [The Rust Book]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
diff --git a/src/doc/rustc-dev-guide/src/early_late_parameters.md b/src/doc/rustc-dev-guide/src/early-late-parameters.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/early_late_parameters.md
rename to src/doc/rustc-dev-guide/src/early-late-parameters.md
diff --git a/src/doc/rustc-dev-guide/src/feature-gates.md b/src/doc/rustc-dev-guide/src/feature-gates.md
index 9806f73c483c..d2a517360714 100644
--- a/src/doc/rustc-dev-guide/src/feature-gates.md
+++ b/src/doc/rustc-dev-guide/src/feature-gates.md
@@ -12,7 +12,7 @@ mechanism][libs-gate].
 
 See ["Stability in code"][adding] in the "Implementing new features" section for instructions.
 
-[adding]: ./implementing_new_features.md#stability-in-code
+[adding]: ./implementing-new-features.md#stability-in-code
 
 ## Removing a feature gate
 
@@ -80,5 +80,5 @@ for instructions. There are additional steps you will need to take beyond just
 updating the declaration!
 
 
-["Stability in code"]: ./implementing_new_features.md#stability-in-code
-["Updating the feature-gate listing"]: ./stabilization_guide.md#updating-the-feature-gate-listing
+["Stability in code"]: ./implementing-new-features.md#stability-in-code
+["Updating the feature-gate listing"]: ./stabilization-guide.md#updating-the-feature-gate-listing
diff --git a/src/doc/rustc-dev-guide/src/generic_parameters_summary.md b/src/doc/rustc-dev-guide/src/generic-parameters-summary.md
similarity index 96%
rename from src/doc/rustc-dev-guide/src/generic_parameters_summary.md
rename to src/doc/rustc-dev-guide/src/generic-parameters-summary.md
index da38ba0455c2..29a07e297e5e 100644
--- a/src/doc/rustc-dev-guide/src/generic_parameters_summary.md
+++ b/src/doc/rustc-dev-guide/src/generic-parameters-summary.md
@@ -16,7 +16,7 @@ trait Trait {
 
 The `ty::Generics` used for `foo` would contain `[U]` and a parent of `Some(Trait)`. `Trait` would have a `ty::Generics` containing `[Self, T]` with a parent of `None`.
 
-The [`GenericParamDef`] struct is used to represent each individual generic parameter in a `ty::Generics` listing. The `GenericParamDef` struct contains information about the generic parameter, for example its name, defid, what kind of parameter it is (i.e. type, const, lifetime). 
+The [`GenericParamDef`] struct is used to represent each individual generic parameter in a `ty::Generics` listing. The `GenericParamDef` struct contains information about the generic parameter, for example its name, defid, what kind of parameter it is (i.e. type, const, lifetime).
 
 `GenericParamDef` also contains a `u32` index representing what position the parameter is (starting from the outermost parent), this is the value used to represent usages of generic parameters (more on this in the [chapter on representing types][ch_representing_types]).
 
@@ -25,4 +25,4 @@ Interestingly, `ty::Generics` does not currently contain _every_ generic paramet
 [ch_representing_types]: ./ty.md
 [`ty::Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html
 [`GenericParamDef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/generics/struct.GenericParamDef.html
-[ch_binders]: ./ty_module/binders.md
+[ch_binders]: ./ty-module/binders.md
diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing-new-features.md
similarity index 99%
rename from src/doc/rustc-dev-guide/src/implementing_new_features.md
rename to src/doc/rustc-dev-guide/src/implementing-new-features.md
index 4526a7af0f46..bb5c0bbba6e3 100644
--- a/src/doc/rustc-dev-guide/src/implementing_new_features.md
+++ b/src/doc/rustc-dev-guide/src/implementing-new-features.md
@@ -225,7 +225,7 @@ The below steps needs to be followed in order to implement a new unstable featur
 [`rustc_ast_passes::feature_gate::check_crate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_passes/feature_gate/fn.check_crate.html
 [value the stability of Rust]: https://github.com/rust-lang/rfcs/blob/master/text/1122-language-semver.md
 [stability in code]: #stability-in-code
-[here]: ./stabilization_guide.md
+[here]: ./stabilization-guide.md
 [tracking issue]: #tracking-issues
 [add-feature-gate]: ./feature-gates.md#adding-a-feature-gate
 
@@ -278,7 +278,7 @@ backward incompatible changes are generally no longer permitted
 To learn more about stabilization, see the [stabilization guide][stab].
 
 
-[stab]: ./stabilization_guide.md
+[stab]: ./stabilization-guide.md
 [rust-blog]: https://github.com/rust-lang/blog.rust-lang.org/
 [twir]: https://github.com/rust-lang/this-week-in-rust
 [twir-cft]: https://this-week-in-rust.org/blog/2025/01/22/this-week-in-rust-583/#calls-for-testing
diff --git a/src/doc/rustc-dev-guide/src/memory.md b/src/doc/rustc-dev-guide/src/memory.md
index f766a51898e4..24e7205a3565 100644
--- a/src/doc/rustc-dev-guide/src/memory.md
+++ b/src/doc/rustc-dev-guide/src/memory.md
@@ -54,14 +54,14 @@ represented as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`).
   defined and discussed in depth in the [`AdtDef and DefId`][adtdefid] section.
 - [`Predicate`] defines something the trait system has to prove (see [traits] module).
 
-[`GenericArgs`]: ./ty_module/generic_arguments.md#the-genericargs-type
-[adtdefid]: ./ty_module/generic_arguments.md#adtdef-and-defid
+[`GenericArgs`]: ./ty-module/generic-arguments.md#the-genericargs-type
+[adtdefid]: ./ty-module/generic-arguments.md#adtdef-and-defid
 [`TraitRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TraitRef.html
 [`AdtDef` and `DefId`]: ./ty.md#adts-representation
 [`def-id`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html
 [`GenericArgs`]: ./generic_arguments.html#GenericArgs
 [`mk_args`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.mk_args
-[adtdefid]: ./ty_module/generic_arguments.md#adtdef-and-defid
+[adtdefid]: ./ty-module/generic-arguments.md#adtdef-and-defid
 [`Predicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Predicate.html
 [`TraitRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TraitRef.html
 [`ty::TyKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/type.TyKind.html
diff --git a/src/doc/rustc-dev-guide/src/normalization.md b/src/doc/rustc-dev-guide/src/normalization.md
index 53e20f1c0db7..b458b04c1719 100644
--- a/src/doc/rustc-dev-guide/src/normalization.md
+++ b/src/doc/rustc-dev-guide/src/normalization.md
@@ -30,7 +30,7 @@ fn bar>() {
 }
 ```
 
-When an alias can't yet be normalized but may wind up normalizable in the [current environment](./typing_parameter_envs.md), we consider it to be an "ambiguous" alias. This can occur when an alias contains inference variables which prevent being able to determine how the trait is implemented:
+When an alias can't yet be normalized but may wind up normalizable in the [current environment](./typing-parameter-envs.md), we consider it to be an "ambiguous" alias. This can occur when an alias contains inference variables which prevent being able to determine how the trait is implemented:
 ```rust
 fn foo() {
     // This alias is considered to be "ambiguous"
@@ -113,7 +113,7 @@ fn bar() {
     foo::<{ FREE_CONST }>();
     // The const arg is represented with some anonymous constant:
     // ```pseudo-rust
-    // const ANON: usize = FREE_CONST; 
+    // const ANON: usize = FREE_CONST;
     // foo::();
     // ```
 }
@@ -127,7 +127,7 @@ This is likely to change as const generics functionality is improved, for exampl
 
 There are two forms of normalization, structural (sometimes called *shallow*) and deep. Structural normalization should be thought of as only normalizing the "outermost" part of a type. On the other hand deep normalization will normalize *all* aliases in a type.
 
-In practice structural normalization can result in more than just the outer layer of the type being normalized, but this behaviour should not be relied upon. Unnormalizable non-rigid aliases making use of bound variables (`for<'a>`) cannot be normalized by either kind of normalization. 
+In practice structural normalization can result in more than just the outer layer of the type being normalized, but this behaviour should not be relied upon. Unnormalizable non-rigid aliases making use of bound variables (`for<'a>`) cannot be normalized by either kind of normalization.
 
 As an example: conceptually, structurally normalizing the type `Vec<::Assoc>` would be a no-op, whereas deeply normalizing would give `Vec`. In practice even structural normalization would give `Vec`, though, again, this should not be relied upon.
 
@@ -137,9 +137,9 @@ Changing the alias to use bound variables will result in different behaviour; `V
 
 Structurally normalizing aliases is a little bit more nuanced than replacing the alias with whatever it is defined as being equal to in its definition; the result of normalizing an alias should either be a rigid type or an inference variable (which will later be inferred to a rigid type). To accomplish this we do two things:
 
-First, when normalizing an ambiguous alias it is normalized to an inference variable instead of leaving it as-is, this has two main effects: 
+First, when normalizing an ambiguous alias it is normalized to an inference variable instead of leaving it as-is, this has two main effects:
 - Even though an inference variable is not a rigid type, it will always wind up inferred *to* a rigid type so we ensure that the result of normalization will not need to be normalized again
-- Inference variables are used in all cases where a type is non-rigid, allowing the rest of the compiler to not have to deal with *both* ambiguous aliases *and* inference variables 
+- Inference variables are used in all cases where a type is non-rigid, allowing the rest of the compiler to not have to deal with *both* ambiguous aliases *and* inference variables
 
 Secondly, instead of having normalization directly return the type specified in the definition of the alias, we normalize the type first before returning it[^1]. We do this so that normalization is idempotent/callers do not need to run it in a loop.
 
@@ -207,7 +207,7 @@ In practice `query_normalize` is used for normalization in the borrow checker, a
 
 ##### `tcx.normalize_erasing_regions`
 
-[`normalize_erasing_regions`][norm_erasing_regions] is generally used by parts of the compiler that are not doing type system analysis. This normalization entry point does not handle inference variables, lifetimes, or any diagnostics. Lints and codegen make heavy use of this entry point as they typically are working with fully inferred aliases that can be assumed to be well formed (or at least, are not responsible for erroring on). 
+[`normalize_erasing_regions`][norm_erasing_regions] is generally used by parts of the compiler that are not doing type system analysis. This normalization entry point does not handle inference variables, lifetimes, or any diagnostics. Lints and codegen make heavy use of this entry point as they typically are working with fully inferred aliases that can be assumed to be well formed (or at least, are not responsible for erroring on).
 
 [query_norm]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.query_normalize
 [norm_erasing_regions]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.normalize_erasing_regions
@@ -232,7 +232,7 @@ One of the big changes between the old and new solver is our approach to when we
 
 ### Old solver
 
-All types are expected to be normalized as soon as possible, so that all types encountered in the type system are either rigid or an inference variable (which will later be inferred to a rigid term). 
+All types are expected to be normalized as soon as possible, so that all types encountered in the type system are either rigid or an inference variable (which will later be inferred to a rigid term).
 
 As a concrete example: equality of aliases is implemented by assuming they're rigid and recursively equating the generic arguments of the alias.
 
@@ -242,7 +242,7 @@ It's expected that all types potentially contain ambiguous or unnormalized alias
 
 As a concrete example: equality of aliases is implemented by a custom goal kind ([`PredicateKind::AliasRelate`][aliasrelate]) so that it can handle normalization of the aliases itself instead of assuming all alias types being equated are rigid.
 
-Despite this approach we still deeply normalize during [writeback][writeback] for performance/simplicity, so that types in the MIR can still be assumed to have been deeply normalized. 
+Despite this approach we still deeply normalize during [writeback][writeback] for performance/simplicity, so that types in the MIR can still be assumed to have been deeply normalized.
 
 [aliasrelate]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.PredicateKind.html#variant.AliasRelate
 [writeback]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/writeback/index.html
@@ -257,7 +257,7 @@ It was a frequent occurrence that normalization calls would be missing, resultin
 
 ### Normalizing parameter environments
 
-Another problem was that it was not possible to normalize `ParamEnv`s correctly in the old solver as normalization itself would expect a normalized `ParamEnv` in order to give correct results. See the chapter on `ParamEnv`s for more information: [`Typing/ParamEnv`s: Normalizing all bounds](./typing_parameter_envs.md#normalizing-all-bounds)
+Another problem was that it was not possible to normalize `ParamEnv`s correctly in the old solver as normalization itself would expect a normalized `ParamEnv` in order to give correct results. See the chapter on `ParamEnv`s for more information: [`Typing/ParamEnv`s: Normalizing all bounds](./typing-parameter-envs.md#normalizing-all-bounds)
 
 ### Unnormalizable non-rigid aliases in higher ranked types
 
@@ -269,7 +269,7 @@ Leaving the alias unnormalized would also be wrong as the old solver expects all
 
 Ultimately this means that it is not always possible to ensure all aliases inside of a value are rigid.
 
-[universe]: borrow_check/region_inference/placeholders_and_universes.md#what-is-a-universe
+[universe]: borrow-check/region-inference/placeholders-and-universes.md#what-is-a-universe
 [deeply_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/trait.NormalizeExt.html#tymethod.deeply_normalize
 
 ## Handling uses of diverging aliases
diff --git a/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md b/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md
index 42600ad87f8c..ac908493ee56 100644
--- a/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md
+++ b/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md
@@ -5,7 +5,7 @@ This kind of type inference is particularly complex because,
 unlike other kinds of type inference,
 it can work across functions and function bodies.
 
-[hidden type]: ./borrow_check/region_inference/member_constraints.html?highlight=%22hidden%20type%22#member-constraints
+[hidden type]: ./borrow-check/region-inference/member-constraints.html?highlight=%22hidden%20type%22#member-constraints
 [opaque type]: ./opaque-types-type-alias-impl-trait.md
 
 ## Running example
diff --git a/src/doc/rustc-dev-guide/src/overview.md b/src/doc/rustc-dev-guide/src/overview.md
index 1200a854f8ed..7858c09fe724 100644
--- a/src/doc/rustc-dev-guide/src/overview.md
+++ b/src/doc/rustc-dev-guide/src/overview.md
@@ -153,7 +153,7 @@ the final binary.
 [`simplify_try`]: https://github.com/rust-lang/rust/pull/66282
 [`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html
 [`Ty<'tcx>`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
-[borrow checking]: borrow_check.md
+[borrow checking]: borrow-check.md
 [codegen]: backend/codegen.md
 [hir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/index.html
 [lex]: the-parser.md
@@ -344,7 +344,7 @@ Compiler performance is a problem that we would like to improve on
 (and are always working on). One aspect of that is parallelizing
 `rustc` itself.
 
-Currently, there is only one part of rustc that is parallel by default: 
+Currently, there is only one part of rustc that is parallel by default:
 [code generation](./parallel-rustc.md#Codegen).
 
 However, the rest of the compiler is still not yet parallel. There have been
@@ -428,7 +428,7 @@ For more details on bootstrapping, see
   - Definition: [`rustc_middle/src/mir`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/index.html)
   - Definition of sources that manipulates the MIR: [`rustc_mir_build`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/index.html), [`rustc_mir_dataflow`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_dataflow/index.html), [`rustc_mir_transform`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/index.html)
 - The Borrow Checker
-  - Guide: [MIR Borrow Check](borrow_check.md)
+  - Guide: [MIR Borrow Check](borrow-check.md)
   - Definition: [`rustc_borrowck`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/index.html)
   - Main entry point: [`mir_borrowck` query](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/fn.mir_borrowck.html)
 - `MIR` Optimizations
diff --git a/src/doc/rustc-dev-guide/src/profiling.md b/src/doc/rustc-dev-guide/src/profiling.md
index de06bd7cda7b..519d9b5488cb 100644
--- a/src/doc/rustc-dev-guide/src/profiling.md
+++ b/src/doc/rustc-dev-guide/src/profiling.md
@@ -12,7 +12,7 @@ Depending on what you're trying to measure, there are several different approach
     See [their docs](https://github.com/rust-lang/measureme/blob/master/summarize/README.md) for more information.
 
 - If you want function level performance data or even just more details than the above approaches:
-  - Consider using a native code profiler such as [perf](profiling/with_perf.md)
+  - Consider using a native code profiler such as [perf](profiling/with-perf.md)
   - or [tracy](https://github.com/nagisa/rust_tracy_client) for a nanosecond-precision,
     full-featured graphical interface.
 
@@ -23,7 +23,7 @@ Depending on what you're trying to measure, there are several different approach
 
 - If you want to profile memory usage, you can use various tools depending on what operating system
   you are using.
-  - For Windows, read our [WPA guide](profiling/wpa_profiling.md).
+  - For Windows, read our [WPA guide](profiling/wpa-profiling.md).
 
 ## Optimizing rustc's bootstrap times with `cargo-llvm-lines`
 
diff --git a/src/doc/rustc-dev-guide/src/profiling/with_perf.md b/src/doc/rustc-dev-guide/src/profiling/with-perf.md
similarity index 99%
rename from src/doc/rustc-dev-guide/src/profiling/with_perf.md
rename to src/doc/rustc-dev-guide/src/profiling/with-perf.md
index e452dde5226d..50281b6b6008 100644
--- a/src/doc/rustc-dev-guide/src/profiling/with_perf.md
+++ b/src/doc/rustc-dev-guide/src/profiling/with-perf.md
@@ -62,7 +62,7 @@ cargo install addr2line --features="bin"
 
 Often we want to analyze a specific test from `perf.rust-lang.org`.
 The easiest way to do that is to use the [rustc-perf][rustc-perf]
-benchmarking suite, this approach is described [here](with_rustc_perf.md).
+benchmarking suite, this approach is described [here](with-rustc-perf.md).
 
 Instead of using the benchmark suite CLI, you can also profile the benchmarks manually. First,
 you need to clone the [rustc-perf][rustc-perf] repository:
diff --git a/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md b/src/doc/rustc-dev-guide/src/profiling/with-rustc-perf.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md
rename to src/doc/rustc-dev-guide/src/profiling/with-rustc-perf.md
diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa_profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/profiling/wpa_profiling.md
rename to src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md
diff --git a/src/doc/rustc-dev-guide/src/solve/opaque-types.md b/src/doc/rustc-dev-guide/src/solve/opaque-types.md
index ac038e354f53..6bb4534608db 100644
--- a/src/doc/rustc-dev-guide/src/solve/opaque-types.md
+++ b/src/doc/rustc-dev-guide/src/solve/opaque-types.md
@@ -51,7 +51,7 @@ We then check whether we're able to *semantically* unify the generic arguments o
 with the arguments of any opaque type already in the opaque types storage. If so, we unify the
 previously stored type with the expected type of this `normalizes-to` call: [source][eq-prev][^1].
 
-If not, we insert the expected type in the opaque types storage: [source][insert-storage][^2]. 
+If not, we insert the expected type in the opaque types storage: [source][insert-storage][^2].
 Finally, we check whether the item bounds of the opaque hold for the expected type:
 [source][item-bounds-ck].
 
@@ -98,7 +98,7 @@ end up leaking placeholders.
 The handling of member constraints does not change in the new solver. See the
 [relevant existing chapter][member-constraints] for that.
 
-[member-constraints]: ../borrow_check/region_inference/member_constraints.md
+[member-constraints]: ../borrow-check/region-inference/member-constraints.md
 
 ## calling methods on opaque types
 
diff --git a/src/doc/rustc-dev-guide/src/stability-guarantees.md b/src/doc/rustc-dev-guide/src/stability-guarantees.md
index 21c4f3594d84..ed548c250c84 100644
--- a/src/doc/rustc-dev-guide/src/stability-guarantees.md
+++ b/src/doc/rustc-dev-guide/src/stability-guarantees.md
@@ -14,7 +14,7 @@ This page gives an overview of our stability guarantees.
 ## rustc-dev-guide links
 
 * [Stabilizing library features](./stability.md)
-* [Stabilizing language features](./stabilization_guide.md)
+* [Stabilizing language features](./stabilization-guide.md)
 * [What qualifies as a bug fix?](./bug-fix-procedure.md#what-qualifies-as-a-bug-fix)
 
 ## Exemptions
diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md
index 3c4c65fdd5a8..f2f2dd909fae 100644
--- a/src/doc/rustc-dev-guide/src/stability.md
+++ b/src/doc/rustc-dev-guide/src/stability.md
@@ -4,7 +4,7 @@ This section is about the stability attributes and schemes that allow stable
 APIs to use unstable APIs internally in the rustc standard library.
 
 **NOTE**: this section is for *library* features, not *language* features. For instructions on
-stabilizing a language feature see [Stabilizing Features](./stabilization_guide.md).
+stabilizing a language feature see [Stabilizing Features](./stabilization-guide.md).
 
 ## unstable
 
diff --git a/src/doc/rustc-dev-guide/src/stabilization_guide.md b/src/doc/rustc-dev-guide/src/stabilization-guide.md
similarity index 99%
rename from src/doc/rustc-dev-guide/src/stabilization_guide.md
rename to src/doc/rustc-dev-guide/src/stabilization-guide.md
index 6167546a2bc8..f159fad89596 100644
--- a/src/doc/rustc-dev-guide/src/stabilization_guide.md
+++ b/src/doc/rustc-dev-guide/src/stabilization-guide.md
@@ -49,7 +49,7 @@ The stabilization reports summarizes:
 
 The [*Stabilization Template*][srt] includes a series of questions that aim to surface connections between this feature and lang's subteams (e.g. types, opsem, lang-docs, etc.) and to identify items that are commonly overlooked.
 
-[srt]: ./stabilization_report_template.md
+[srt]: ./stabilization-report-template.md
 
 The stabilization report is typically posted as the main comment on the stabilization PR (see the next section).
 
diff --git a/src/doc/rustc-dev-guide/src/stabilization_report_template.md b/src/doc/rustc-dev-guide/src/stabilization-report-template.md
similarity index 99%
rename from src/doc/rustc-dev-guide/src/stabilization_report_template.md
rename to src/doc/rustc-dev-guide/src/stabilization-report-template.md
index 793f7d7e45cf..328907904a6f 100644
--- a/src/doc/rustc-dev-guide/src/stabilization_report_template.md
+++ b/src/doc/rustc-dev-guide/src/stabilization-report-template.md
@@ -2,7 +2,7 @@
 
 ## What is this?
 
-This is a template for [stabilization reports](./stabilization_guide.md) of **language features**. The questions aim to solicit the details most often needed. These details help reviewers to identify potential problems upfront. Not all parts of the template will apply to every stabilization. If a question doesn't apply, explain briefly why.
+This is a template for [stabilization reports](./stabilization-guide.md) of **language features**. The questions aim to solicit the details most often needed. These details help reviewers to identify potential problems upfront. Not all parts of the template will apply to every stabilization. If a question doesn't apply, explain briefly why.
 
 Copy everything after the separator and edit it as Markdown. Replace each *TODO* with your answer.
 
diff --git a/src/doc/rustc-dev-guide/src/tests/perf.md b/src/doc/rustc-dev-guide/src/tests/perf.md
index a0aa3c033174..567b2d7a97e4 100644
--- a/src/doc/rustc-dev-guide/src/tests/perf.md
+++ b/src/doc/rustc-dev-guide/src/tests/perf.md
@@ -19,7 +19,7 @@ The result of a perf run is a comparison between two versions of the compiler
 (by their commit hashes).
 
 You can also use `rustc-perf` to manually benchmark and profile the compiler
-[locally](../profiling/with_rustc_perf.md).
+[locally](../profiling/with-rustc-perf.md).
 
 ### Automatic perf runs
 
diff --git a/src/doc/rustc-dev-guide/src/traits/caching.md b/src/doc/rustc-dev-guide/src/traits/caching.md
index c44722a1d9a3..be72f6e89f9a 100644
--- a/src/doc/rustc-dev-guide/src/traits/caching.md
+++ b/src/doc/rustc-dev-guide/src/traits/caching.md
@@ -61,7 +61,7 @@ to be pretty clearly safe and also still retains a very high hit rate
 **TODO**: it looks like `pick_candidate_cache` no longer exists. In
 general, is this section still accurate at all?
 
-[`ParamEnv`]: ../typing_parameter_envs.html
+[`ParamEnv`]: ../typing-parameter-envs.html
 [`tcx`]: ../ty.html
 [#18290]: https://github.com/rust-lang/rust/issues/18290
 [#22019]: https://github.com/rust-lang/rust/issues/22019
diff --git a/src/doc/rustc-dev-guide/src/traits/resolution.md b/src/doc/rustc-dev-guide/src/traits/resolution.md
index ccb2b04268e8..f668d6ccf619 100644
--- a/src/doc/rustc-dev-guide/src/traits/resolution.md
+++ b/src/doc/rustc-dev-guide/src/traits/resolution.md
@@ -130,9 +130,9 @@ Once this first pass is done, we can examine the set of candidates. If
 it is a singleton set, then we are done: this is the only impl in
 scope that could possibly apply. Otherwise, we can **winnow** down the set
 of candidates by using where clauses and other conditions. Winnowing uses
-`evaluate_candidate` to check whether the nested obligations may apply. 
-If this still leaves more than 1 candidate, we use ` fn candidate_should_be_dropped_in_favor_of` 
-to prefer some candidates over others. 
+`evaluate_candidate` to check whether the nested obligations may apply.
+If this still leaves more than 1 candidate, we use ` fn candidate_should_be_dropped_in_favor_of`
+to prefer some candidates over others.
 
 
 If this reduced set yields a single, unambiguous entry, we're good to go,
@@ -181,7 +181,7 @@ in that list. If so, it is considered satisfied. More precisely, we
 want to check whether there is a where-clause obligation that is for
 the same trait (or some subtrait) and which can match against the obligation.
 
-[parameter environment]: ../typing_parameter_envs.html
+[parameter environment]: ../typing-parameter-envs.html
 
 Consider this simple example:
 
@@ -240,8 +240,8 @@ confirmation is done based on (in this case) the `Target` type parameter.
 
 As mentioned above, during type checking, we do not store the results of trait
 selection. At codegen time, we repeat the trait selection to choose a particular
-impl for each method call. This is done using `fn codegen_select_candidate`. 
-In this second selection, we do not consider any where-clauses to be in scope 
+impl for each method call. This is done using `fn codegen_select_candidate`.
+In this second selection, we do not consider any where-clauses to be in scope
 because we know that each resolution will resolve to a particular impl.
 
 One interesting twist has to do with nested obligations. In general, in codegen,
diff --git a/src/doc/rustc-dev-guide/src/ty-fold.md b/src/doc/rustc-dev-guide/src/ty-fold.md
index 120a266e3536..bf0a51e6b7cf 100644
--- a/src/doc/rustc-dev-guide/src/ty-fold.md
+++ b/src/doc/rustc-dev-guide/src/ty-fold.md
@@ -99,7 +99,7 @@ it replaces it for something from the list of substitutions, otherwise recursive
 To replace it, calls [ty_for_param]
 and all that does is index into the list of substitutions with the index of the `Param`.
 
-[a previous chapter]: ty_module/instantiating_binders.md
+[a previous chapter]: ty-module/instantiating-binders.md
 [`TypeFoldable`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait.TypeFoldable.html
 [`TypeFolder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait.TypeFolder.html
 [`fold_ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait.TypeFolder.html#method.fold_ty
diff --git a/src/doc/rustc-dev-guide/src/ty_module/binders.md b/src/doc/rustc-dev-guide/src/ty-module/binders.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/ty_module/binders.md
rename to src/doc/rustc-dev-guide/src/ty-module/binders.md
diff --git a/src/doc/rustc-dev-guide/src/ty_module/early_binder.md b/src/doc/rustc-dev-guide/src/ty-module/early-binder.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/ty_module/early_binder.md
rename to src/doc/rustc-dev-guide/src/ty-module/early-binder.md
diff --git a/src/doc/rustc-dev-guide/src/ty_module/generic_arguments.md b/src/doc/rustc-dev-guide/src/ty-module/generic-arguments.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/ty_module/generic_arguments.md
rename to src/doc/rustc-dev-guide/src/ty-module/generic-arguments.md
diff --git a/src/doc/rustc-dev-guide/src/ty_module/instantiating_binders.md b/src/doc/rustc-dev-guide/src/ty-module/instantiating-binders.md
similarity index 95%
rename from src/doc/rustc-dev-guide/src/ty_module/instantiating_binders.md
rename to src/doc/rustc-dev-guide/src/ty-module/instantiating-binders.md
index 7e29b9543714..3c7408465c44 100644
--- a/src/doc/rustc-dev-guide/src/ty_module/instantiating_binders.md
+++ b/src/doc/rustc-dev-guide/src/ty-module/instantiating-binders.md
@@ -21,7 +21,7 @@ Unlike `EarlyBinder` we typically do not instantiate `Binder` with some concrete
 
 ## Instantiating with inference variables
 
-We instantiate binders with inference variables when we are trying to infer a possible instantiation of the binder, e.g. calling higher ranked function pointers or attempting to use a higher ranked where-clause to prove some bound. For example, given the `higher_ranked_fn_ptr` from the example above, if we were to call it with `&10_u32` we would: 
+We instantiate binders with inference variables when we are trying to infer a possible instantiation of the binder, e.g. calling higher ranked function pointers or attempting to use a higher ranked where-clause to prove some bound. For example, given the `higher_ranked_fn_ptr` from the example above, if we were to call it with `&10_u32` we would:
 - Instantiate the binder with infer vars yielding a signature of `fn(&'?0 u32) -> &'?0 u32)`
 - Equate the type of the provided argument `&10_u32` (&'static u32) with the type in the signature, `&'?0 u32`, inferring `'?0 = 'static`
 - The provided arguments were correct as we were successfully able to unify the types of the provided arguments with the types of the arguments in fn ptr signature
@@ -35,7 +35,7 @@ Instantiating binders with inference variables can be accomplished by using the
 
 ## Instantiating with placeholders
 
-Placeholders are very similar to `Ty/ConstKind::Param`/`ReEarlyParam`, they represent some unknown type that is only equal to itself. `Ty`/`Const` and `Region` all have a [`Placeholder`] variant that is comprised of a [`Universe`] and a [`BoundVar`]. 
+Placeholders are very similar to `Ty/ConstKind::Param`/`ReEarlyParam`, they represent some unknown type that is only equal to itself. `Ty`/`Const` and `Region` all have a [`Placeholder`] variant that is comprised of a [`Universe`] and a [`BoundVar`].
 
 The `Universe` tracks which binder the placeholder originated from, and the `BoundVar` tracks which parameter on said binder that this placeholder corresponds to. Equality of placeholders is determined solely by whether the universes are equal and the `BoundVar`s are equal. See the [chapter on Placeholders and Universes][ch_placeholders_universes] for more information.
 
@@ -49,7 +49,7 @@ Note: in the original example of this chapter it was mentioned that we should no
 
 ### Why have both `RePlaceholder` and `ReBound`?
 
-You may be wondering why we have both of these variants, afterall the data stored in `Placeholder` is effectively equivalent to that of `ReBound`: something to track which binder, and an index to track which parameter the `Binder` introduced. 
+You may be wondering why we have both of these variants, afterall the data stored in `Placeholder` is effectively equivalent to that of `ReBound`: something to track which binder, and an index to track which parameter the `Binder` introduced.
 
 The main reason for this is that `Bound` is a more syntactic representation of bound variables whereas `Placeholder` is a more semantic representation. As a concrete example:
 ```rust
@@ -77,7 +77,7 @@ Given these trait implementations `u32: Bar` should _not_ hold. `&'a u32` only i
 This end result is incorrect as we had two separate binders introducing their own generic parameters, the trait bound should have ended up as something like `for<'a1, 'a2> &'^1 u32: Other<'^0>` which is _not_ satisfied by the `impl<'a> Other<'a> for &'a u32`.
 
 While in theory we could make this work it would be quite involved and more complex than the current setup, we would have to:
-- "rewrite" bound variables to have a higher `DebruijnIndex` whenever instantiating a `Binder`/`EarlyBinder` with a `Bound` ty/const/region 
+- "rewrite" bound variables to have a higher `DebruijnIndex` whenever instantiating a `Binder`/`EarlyBinder` with a `Bound` ty/const/region
 - When inferring an inference variable to a bound var, if that bound var is from a binder entered after creating the infer var, we would have to lower the `DebruijnIndex` of the var.
 - Separately track what binder an inference variable was created inside of, also what the innermost binder it can name parameters from (currently we only have to track the latter)
 - When resolving inference variables rewrite any bound variables according to the current binder depth of the infcx
@@ -90,18 +90,18 @@ where
     for<'a> T: Trait<'a, for<'b> fn(&'b T, &'a u32)>
 { ... }
 ```
-That where clause would be written as:  
-`for<'a> T: Trait<'^0, for<'b> fn(&'^0 T, &'^1_0 u32)>`  
+That where clause would be written as:
+`for<'a> T: Trait<'^0, for<'b> fn(&'^0 T, &'^1_0 u32)>`
 Despite there being two references to the `'a` parameter they are both represented differently: `^0` and `^1_0`, due to the fact that the latter usage is nested under a second `Binder` for the inner function pointer type.
 
 This is in contrast to `Placeholder` ty/const/regions which do not have this limitation due to the fact that `Universe`s are specific to the current `InferCtxt` not the usage site of the parameter.
 
-It is trivially possible to instantiate `EarlyBinder`s and unify inference variables with existing `Placeholder`s as no matter what context the `Placeholder` is in, it will have the same representation. As an example if we were to instantiate the binder on the higher ranked where clause from above, it would be represented like so:  
-`T: Trait<'!1_0, for<'b> fn(&'^0 T, &'!1_0 u32)>`  
+It is trivially possible to instantiate `EarlyBinder`s and unify inference variables with existing `Placeholder`s as no matter what context the `Placeholder` is in, it will have the same representation. As an example if we were to instantiate the binder on the higher ranked where clause from above, it would be represented like so:
+`T: Trait<'!1_0, for<'b> fn(&'^0 T, &'!1_0 u32)>`
 the `RePlaceholder` representation for both usages of `'a` are the same despite one being underneath another `Binder`.
 
-If we were to then instantiate the binder on the function pointer we would get a type such as:  
-`fn(&'!2_0 T, ^'!1_0 u32)`  
+If we were to then instantiate the binder on the function pointer we would get a type such as:
+`fn(&'!2_0 T, ^'!1_0 u32)`
 the `RePlaceholder` for the `'b` parameter is in a higher universe to track the fact that its binder was instantiated after the binder for `'a`.
 
 ## Instantiating with `ReLateParam`
@@ -119,8 +119,8 @@ impl Trait for Whatever {
         b
     }
 }
-``` 
-the lifetime `'a` in the type `&'a u32` in the function body would be represented as: 
+```
+the lifetime `'a` in the type `&'a u32` in the function body would be represented as:
 ```
 ReLateParam(
     {impl#0}::foo,
@@ -135,10 +135,10 @@ Generally whenever we have a `Binder` for late bound parameters on a function/cl
 As a concrete example, accessing the signature of a function we are type checking will be represented as `EarlyBinder>`. As we are already "inside" of these binders, we would call `instantiate_identity` followed by `liberate_late_bound_regions`.
 
 [`liberate_late_bound_regions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.liberate_late_bound_regions
-[representing-types]: param_ty_const_regions.md
+[representing-types]: param-ty-const-regions.md
 [`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html
 [`enter_forall`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/struct.InferCtxt.html#method.enter_forall
-[ch_placeholders_universes]: ../borrow_check/region_inference/placeholders_and_universes.md
+[ch_placeholders_universes]: ../borrow-check/region-inference/placeholders-and-universes.md
 [`instantiate_binder_with_fresh_vars`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/struct.InferCtxt.html#method.instantiate_binder_with_fresh_vars
 [`InferCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/struct.InferCtxt.html
 [`EarlyBinder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.EarlyBinder.html
diff --git a/src/doc/rustc-dev-guide/src/ty_module/param_ty_const_regions.md b/src/doc/rustc-dev-guide/src/ty-module/param-ty-const-regions.md
similarity index 97%
rename from src/doc/rustc-dev-guide/src/ty_module/param_ty_const_regions.md
rename to src/doc/rustc-dev-guide/src/ty-module/param-ty-const-regions.md
index 493693c9a440..ce4e887862c7 100644
--- a/src/doc/rustc-dev-guide/src/ty_module/param_ty_const_regions.md
+++ b/src/doc/rustc-dev-guide/src/ty-module/param-ty-const-regions.md
@@ -49,9 +49,9 @@ impl Foo {
 }
 ```
 
-Concretely given the `ty::Generics` for the item the parameter is defined on, if the index is `2` then starting from the root `parent`, it will be the third parameter to be introduced. For example in the above example, `Z` has index `2` and is the third generic parameter to be introduced, starting from the `impl` block. 
+Concretely given the `ty::Generics` for the item the parameter is defined on, if the index is `2` then starting from the root `parent`, it will be the third parameter to be introduced. For example in the above example, `Z` has index `2` and is the third generic parameter to be introduced, starting from the `impl` block.
 
-The index fully defines the `Ty` and is the only part of `TyKind::Param` that matters for reasoning about the code we are compiling. 
+The index fully defines the `Ty` and is the only part of `TyKind::Param` that matters for reasoning about the code we are compiling.
 
 Generally we do not care what the name is and only use the index. The name is included for diagnostics and debug logs as otherwise it would be
 incredibly difficult to understand the output, i.e. `Vec: Sized` vs `Vec: Sized`. In debug output, parameter types are
@@ -59,7 +59,7 @@ often printed out as `{name}/#{index}`, for example in the function `foo` if we
 
 An alternative representation would be to only have the name, however using an index is more efficient as it means we can index into `GenericArgs` when instantiating generic parameters with some arguments. We would otherwise have to store `GenericArgs` as a `HashMap` and do a hashmap lookup everytime we used a generic item.
 
-In theory an index would also allow for having multiple distinct parameters that use the same name, e.g. 
+In theory an index would also allow for having multiple distinct parameters that use the same name, e.g.
 `impl Foo { fn bar() { .. } }`.
 The rules against shadowing make this difficult but those language rules could change in the future.
 
@@ -85,9 +85,9 @@ fn foo<'a, 'b, T: 'a>(one: T, two: &'a &'b u32) -> &'b u32 {
 
 `RegionKind::LateParam` is discussed more in the chapter on [instantiating binders][ch_instantiating_binders].
 
-[ch_early_late_bound]: ../early_late_parameters.md
+[ch_early_late_bound]: ../early-late-parameters.md
 [ch_binders]: ./binders.md
-[ch_instantiating_binders]: ./instantiating_binders.md
+[ch_instantiating_binders]: ./instantiating-binders.md
 [`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html
 [`RegionKind::EarlyParam`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.RegionKind.html#variant.ReEarlyParam
 [`RegionKind::LateParam`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.RegionKind.html#variant.ReLateParam
diff --git a/src/doc/rustc-dev-guide/src/typing_parameter_envs.md b/src/doc/rustc-dev-guide/src/typing-parameter-envs.md
similarity index 100%
rename from src/doc/rustc-dev-guide/src/typing_parameter_envs.md
rename to src/doc/rustc-dev-guide/src/typing-parameter-envs.md
diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md
index 5ba89f984a70..94d7ec7376db 100644
--- a/src/doc/rustc-dev-guide/src/walkthrough.md
+++ b/src/doc/rustc-dev-guide/src/walkthrough.md
@@ -284,6 +284,6 @@ A note is added to the [Release notes][relnotes] about the feature.
 
 [stab]: https://github.com/rust-lang/rust/pull/56245
 
-Steps to stabilize the feature can be found at [Stabilizing Features](./stabilization_guide.md).
+Steps to stabilize the feature can be found at [Stabilizing Features](./stabilization-guide.md).
 
 [relnotes]: https://github.com/rust-lang/rust/blob/HEAD/RELEASES.md

From db142559a2a13bcaa0f987ce0d0978369b60cad8 Mon Sep 17 00:00:00 2001
From: Redddy 
Date: Tue, 20 Jan 2026 18:48:45 +0900
Subject: [PATCH 154/583] Add link to internals.rust-lang.org in walkthrough

---
 src/doc/rustc-dev-guide/src/walkthrough.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md
index 5ba89f984a70..7a36b3207633 100644
--- a/src/doc/rustc-dev-guide/src/walkthrough.md
+++ b/src/doc/rustc-dev-guide/src/walkthrough.md
@@ -97,7 +97,7 @@ If that sounds like a lot of work, it's because it is.
 But no fear!
 Even if you're not a compiler hacker, you can get great feedback by doing a _pre-RFC_.
 This is an _informal_ discussion of the idea.
-The best place to do this is internals.rust-lang.org.
+The best place to do this is [internals.rust-lang.org](https://internals.rust-lang.org).
 Your post doesn't have to follow any particular structure.
 It doesn't even need to be a cohesive idea.
 Generally, you will get tons of feedback that you can integrate back to produce a good RFC.

From e07da1fb86ce0ccbe6c5b542f545fd304efb2e3f Mon Sep 17 00:00:00 2001
From: Youseok Yang 
Date: Tue, 20 Jan 2026 07:16:59 +0000
Subject: [PATCH 155/583] feat(hir-ty): add method references_only_ty_error to
 detect type errors

Add a new method `references_only_ty_error` to the `Ty` implementation
to determine if a type contains only type errors, ignoring const and
lifetime errors. Enhance test suite for const generic method resolution.
---
 .../hir-ty/src/method_resolution/probe.rs     |  6 +-
 .../crates/hir-ty/src/next_solver/ty.rs       | 19 +++++
 .../hir-ty/src/tests/regression/new_solver.rs | 71 ++++++++++++++++++-
 3 files changed, 92 insertions(+), 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
index 42a590e8b4cb..fdd501723fb5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
@@ -1246,9 +1246,9 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
             .filter(|step| step.reachable_via_deref)
             .filter(|step| {
                 debug!("pick_all_method: step={:?}", step);
-                // skip types that are from a type error or that would require dereferencing
-                // a raw pointer
-                !step.self_ty.value.value.references_non_lt_error() && !step.from_unsafe_deref
+                // Skip types with type errors (but not const/lifetime errors, which are
+                // often spurious due to incomplete const evaluation) and raw pointer derefs.
+                !step.self_ty.value.value.references_only_ty_error() && !step.from_unsafe_deref
             })
             .try_for_each(|step| {
                 let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
index 66a24d394990..1173028a1092 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
@@ -508,6 +508,11 @@ impl<'db> Ty<'db> {
         references_non_lt_error(&self)
     }
 
+    /// Whether the type contains a type error (ignoring const and lifetime errors).
+    pub fn references_only_ty_error(self) -> bool {
+        references_only_ty_error(&self)
+    }
+
     pub fn callable_sig(self, interner: DbInterner<'db>) -> Option>> {
         match self.kind() {
             TyKind::FnDef(callable, args) => {
@@ -777,6 +782,20 @@ impl<'db> TypeVisitor> for ReferencesNonLifetimeError {
     }
 }
 
+pub fn references_only_ty_error<'db, T: TypeVisitableExt>>(t: &T) -> bool {
+    t.references_error() && t.visit_with(&mut ReferencesOnlyTyError).is_break()
+}
+
+struct ReferencesOnlyTyError;
+
+impl<'db> TypeVisitor> for ReferencesOnlyTyError {
+    type Result = ControlFlow<()>;
+
+    fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
+        if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) }
+    }
+}
+
 impl<'db> std::fmt::Debug for Ty<'db> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         self.inner().internee.fmt(f)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
index be6ab23ad761..f47a26d429fd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -471,7 +471,76 @@ fn foo() {
             244..246 '_x': {unknown}
             249..257 'to_bytes': fn to_bytes() -> [u8; _]
             249..259 'to_bytes()': [u8; _]
-            249..268 'to_byt..._vec()': {unknown}
+            249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item>
+        "#]],
+    );
+}
+
+#[test]
+fn regression_21315() {
+    check_infer(
+        r#"
+struct Consts;
+impl Consts { const MAX: usize = 0; }
+
+struct Between(T);
+
+impl Between {
+    fn sep_once(self, _sep: &str, _other: Self) -> Self {
+        self
+    }
+}
+
+trait Parser: Sized {
+    fn at_least(self) -> Between {
+        Between(self)
+    }
+    fn at_most(self) -> Between<0, N, Self> {
+        Between(self)
+    }
+}
+
+impl Parser for char {}
+
+fn test_at_least() {
+    let num = '9'.at_least::<1>();
+    let _ver = num.sep_once(".", num);
+}
+
+fn test_at_most() {
+    let num = '9'.at_most::<1>();
+}
+    "#,
+        expect![[r#"
+            48..49 '0': usize
+            182..186 'self': Between
+            188..192 '_sep': &'? str
+            200..206 '_other': Between
+            222..242 '{     ...     }': Between
+            232..236 'self': Between
+            300..304 'self': Self
+            343..372 '{     ...     }': Between
+            353..360 'Between': fn Between(Self) -> Between
+            353..366 'Between(self)': Between
+            361..365 'self': Self
+            404..408 'self': Self
+            433..462 '{     ...     }': Between<0, N, Self>
+            443..450 'Between': fn Between<0, N, Self>(Self) -> Between<0, N, Self>
+            443..456 'Between(self)': Between<0, N, Self>
+            451..455 'self': Self
+            510..587 '{     ...um); }': ()
+            520..523 'num': Between<1, _, char>
+            526..529 ''9'': char
+            526..545 ''9'.at...:<1>()': Between<1, _, char>
+            555..559 '_ver': Between<1, _, char>
+            562..565 'num': Between<1, _, char>
+            562..584 'num.se..., num)': Between<1, _, char>
+            575..578 '"."': &'static str
+            580..583 'num': Between<1, _, char>
+            607..644 '{     ...>(); }': ()
+            617..620 'num': Between<0, 1, char>
+            623..626 ''9'': char
+            623..641 ''9'.at...:<1>()': Between<0, 1, char>
         "#]],
     );
 }

From f28c32abc6209c342c8af8a36b6f518b0144e9c3 Mon Sep 17 00:00:00 2001
From: Redddy 
Date: Tue, 20 Jan 2026 21:43:50 +0900
Subject: [PATCH 156/583] Document experimental RFC process in walkthrough

---
 src/doc/rustc-dev-guide/src/walkthrough.md | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md
index 7a36b3207633..24a1e16a979c 100644
--- a/src/doc/rustc-dev-guide/src/walkthrough.md
+++ b/src/doc/rustc-dev-guide/src/walkthrough.md
@@ -159,6 +159,17 @@ Here is the tracking issue on for our [`?` macro feature][tracking].
 
 [tracking]: https://github.com/rust-lang/rust/issues/48075
 
+## Experimental RFC (eRFC)
+
+An eRFC is a variant of the RFC process used for complex features wherethe high-level need
+is clear, but the design space is too large to settle on a detailed specification upfront.
+Instead of providing a final design, an eRFC outlines a high-level strategy to authorize
+a period of active experimentation. This allows the team to implement the feature behind
+a feature gate and gather practical data, which then informs a subsequent formal RFC for stabilization.
+While this process was used for major features like coroutines, the explicit "eRFC" label
+is rarely used today. The project now generally prefers approving a standard RFC for an initial
+version and iterating on it through the nightly channel before final stabilization.
+
 
 
 ## Implementation

From 156cb44ab008da476a3a5dbe81d3517ebb5cb60d Mon Sep 17 00:00:00 2001
From: Redddy 
Date: Tue, 20 Jan 2026 21:53:25 +0900
Subject: [PATCH 157/583] Add link to RFC2033 in walkthrough

---
 src/doc/rustc-dev-guide/src/walkthrough.md | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md
index 24a1e16a979c..0522e9d444a9 100644
--- a/src/doc/rustc-dev-guide/src/walkthrough.md
+++ b/src/doc/rustc-dev-guide/src/walkthrough.md
@@ -166,9 +166,11 @@ is clear, but the design space is too large to settle on a detailed specificatio
 Instead of providing a final design, an eRFC outlines a high-level strategy to authorize
 a period of active experimentation. This allows the team to implement the feature behind
 a feature gate and gather practical data, which then informs a subsequent formal RFC for stabilization.
-While this process was used for major features like coroutines, the explicit "eRFC" label
-is rarely used today. The project now generally prefers approving a standard RFC for an initial
-version and iterating on it through the nightly channel before final stabilization.
+While this process was used for major features like coroutines ([see RFC2033][rfc2033]),
+the explicit "eRFC" label is rarely used today. The project now generally prefers approving a standard
+RFC for an initial version and iterating on it through the nightly channel before final stabilization.
+
+[rfc2033]: https://github.com/rust-lang/rfcs/pull/2033#issuecomment-309057591
 
 
 

From 61b9b33c1cd8857c2e79caba782ed13edb654100 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Tue, 20 Jan 2026 23:02:34 +0800
Subject: [PATCH 158/583] Fix demorgan applicable on pattern conditional

Example
---
```rust
fn f() {
    if let 1 = 1 &&$0 true { }
}
```

**Before this PR**

```rust
fn f() {
    if !(!let 1 = 1 || false) { }
}
```

**After this PR**

Assist not applicable
---
 .../ide-assists/src/handlers/apply_demorgan.rs   | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
index d193e8a9d8dc..80d0a6da1243 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -3,7 +3,7 @@ use std::collections::VecDeque;
 use ide_db::{
     assists::GroupLabel,
     famous_defs::FamousDefs,
-    syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
+    syntax_helpers::node_ext::{for_each_tail_expr, is_pattern_cond, walk_expr},
 };
 use syntax::{
     NodeOrToken, SyntaxKind, T,
@@ -69,6 +69,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
         }
     }
 
+    if is_pattern_cond(bin_expr.clone().into()) {
+        return None;
+    }
+
     let op = bin_expr.op_kind()?;
     let (inv_token, prec) = match op {
         ast::BinaryOp::LogicOp(ast::LogicOp::And) => (SyntaxKind::PIPE2, ExprPrecedence::LOr),
@@ -375,6 +379,16 @@ fn f() { !(S <= S || S < S) }
         )
     }
 
+    #[test]
+    fn demorgan_doesnt_handles_pattern() {
+        check_assist_not_applicable(
+            apply_demorgan,
+            r#"
+fn f() { if let 1 = 1 &&$0 true { } }
+"#,
+        );
+    }
+
     #[test]
     fn demorgan_on_not() {
         check_assist(

From 8fe3738c611c2f1d04b1a422e43c12d6741294f2 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Tue, 20 Jan 2026 18:39:02 +0000
Subject: [PATCH 159/583] Prepare for merging from rust-lang/rust

This updates the rust-version file to 5c49c4f7c8393c861b849441d27f5d40e0f1e33b.
---
 src/doc/rustc-dev-guide/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index b53a66c66751..d6e02b0dd2b2 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-44a5b55557c26353f388400d7da95527256fe260
+5c49c4f7c8393c861b849441d27f5d40e0f1e33b

From 307b9d974a873e33fd3e67573881160007830b87 Mon Sep 17 00:00:00 2001
From: Tshepang Mbambo 
Date: Tue, 20 Jan 2026 20:50:10 +0200
Subject: [PATCH 160/583] fix dangling reference

---
 src/doc/rustc-dev-guide/src/tests/compiletest.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md
index 7f22bc27600c..82708be16f03 100644
--- a/src/doc/rustc-dev-guide/src/tests/compiletest.md
+++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md
@@ -439,6 +439,8 @@ The tests in [`tests/build-std`] check that `-Zbuild-std` works. This is current
 just a run-make test suite with a single recipe. The recipe generates test cases
 and runs them in parallel.
 
+[`tests/build-std`]: https://github.com/rust-lang/rust/tree/HEAD/tests/build-std
+
 #### Using Rust recipes
 
 Each test should be in a separate directory with a `rmake.rs` Rust program,

From b75e040c76cedba7dcc8b6a0565c9a36ce32a1c6 Mon Sep 17 00:00:00 2001
From: Tshepang Mbambo 
Date: Tue, 20 Jan 2026 20:51:45 +0200
Subject: [PATCH 161/583] fix grammar

---
 src/doc/rustc-dev-guide/src/tests/compiletest.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md
index 82708be16f03..3d482ddc5423 100644
--- a/src/doc/rustc-dev-guide/src/tests/compiletest.md
+++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md
@@ -80,7 +80,7 @@ The following test suites are available, with links for more information:
 
 ### The build-std test suite
 
-[`build-std`](#build-std-tests) test that -Zbuild-std works.
+[`build-std`](#build-std-tests) tests that -Zbuild-std works.
 
 ### Rustdoc test suites
 

From 5c8ba2643a9b8c2734b998dbc888202f26ee521b Mon Sep 17 00:00:00 2001
From: Tshepang Mbambo 
Date: Tue, 20 Jan 2026 20:53:14 +0200
Subject: [PATCH 162/583] missing pause

---
 src/doc/rustc-dev-guide/src/tests/ui.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md
index e13419d1e01c..8e3c4c133a3c 100644
--- a/src/doc/rustc-dev-guide/src/tests/ui.md
+++ b/src/doc/rustc-dev-guide/src/tests/ui.md
@@ -139,7 +139,7 @@ prefixing each source line are replaced with `LL`). In extremely rare
 situations, this mode can be disabled with the directive `//@
 compile-flags: -Z ui-testing=no`.
 
-When using `-Z ui-testing=no` the `--diagnostic-width` argument should also
+When using `-Z ui-testing=no`, the `--diagnostic-width` argument should also
 be set to avoid tests failing or passing depending on the width of the terminal
 from which the UI test suite is being run.
 

From 43111396e36bf29344535f2394b906d689ed9b40 Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Fri, 9 Jan 2026 14:09:17 -0800
Subject: [PATCH 163/583] move initialization of omp/ol runtimes into
 global_ctor/dtor

---
 compiler/rustc_codegen_llvm/src/builder.rs    | 13 ---
 .../src/builder/gpu_offload.rs                | 91 ++++++++++++-------
 compiler/rustc_codegen_llvm/src/common.rs     |  4 +
 compiler/rustc_codegen_llvm/src/intrinsic.rs  |  5 +-
 4 files changed, 68 insertions(+), 45 deletions(-)

diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 9379faf1156f..35bf629ae81a 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -188,19 +188,6 @@ impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> {
             load
         }
     }
-
-    fn memset(&mut self, ptr: &'ll Value, fill_byte: &'ll Value, size: &'ll Value, align: Align) {
-        unsafe {
-            llvm::LLVMRustBuildMemSet(
-                self.llbuilder,
-                ptr,
-                align.bytes() as c_uint,
-                fill_byte,
-                size,
-                false,
-            );
-        }
-    }
 }
 
 /// Empty string, to be used where LLVM expects an instruction name, indicating
diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs
index f1735b9a0f58..0cf4c1d4f8c7 100644
--- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs
+++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs
@@ -19,8 +19,6 @@ pub(crate) struct OffloadGlobals<'ll> {
     pub launcher_fn: &'ll llvm::Value,
     pub launcher_ty: &'ll llvm::Type,
 
-    pub bin_desc: &'ll llvm::Type,
-
     pub kernel_args_ty: &'ll llvm::Type,
 
     pub offload_entry_ty: &'ll llvm::Type,
@@ -31,8 +29,6 @@ pub(crate) struct OffloadGlobals<'ll> {
 
     pub ident_t_global: &'ll llvm::Value,
 
-    pub register_lib: &'ll llvm::Value,
-    pub unregister_lib: &'ll llvm::Value,
     pub init_rtls: &'ll llvm::Value,
 }
 
@@ -44,15 +40,6 @@ impl<'ll> OffloadGlobals<'ll> {
         let (begin_mapper, _, end_mapper, mapper_fn_ty) = gen_tgt_data_mappers(cx);
         let ident_t_global = generate_at_one(cx);
 
-        let tptr = cx.type_ptr();
-        let ti32 = cx.type_i32();
-        let tgt_bin_desc_ty = vec![ti32, tptr, tptr, tptr];
-        let bin_desc = cx.type_named_struct("struct.__tgt_bin_desc");
-        cx.set_struct_body(bin_desc, &tgt_bin_desc_ty, false);
-
-        let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void());
-        let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", reg_lib_decl);
-        let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl);
         let init_ty = cx.type_func(&[], cx.type_void());
         let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty);
 
@@ -63,20 +50,77 @@ impl<'ll> OffloadGlobals<'ll> {
         OffloadGlobals {
             launcher_fn,
             launcher_ty,
-            bin_desc,
             kernel_args_ty,
             offload_entry_ty,
             begin_mapper,
             end_mapper,
             mapper_fn_ty,
             ident_t_global,
-            register_lib,
-            unregister_lib,
             init_rtls,
         }
     }
 }
 
+// We need to register offload before using it. We also should unregister it once we are done, for
+// good measures. Previously we have done so before and after each individual offload intrinsic
+// call, but that comes at a performance cost. The repeated (un)register calls might also confuse
+// the LLVM ompOpt pass, which tries to move operations to a better location. The easiest solution,
+// which we copy from clang, is to just have those two calls once, in the global ctor/dtor section
+// of the final binary.
+pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) {
+    let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void());
+    let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", reg_lib_decl);
+    let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl);
+
+    let ptr_null = cx.const_null(cx.type_ptr());
+    let const_struct = cx.const_struct(&[cx.get_const_i32(0), ptr_null, ptr_null, ptr_null], false);
+    let omp_descriptor =
+        add_global(cx, ".omp_offloading.descriptor", const_struct, InternalLinkage);
+    // @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 1, ptr @.omp_offloading.device_images, ptr @__start_llvm_offload_entries, ptr @__stop_llvm_offload_entries }
+    // @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 0, ptr null, ptr null, ptr null }
+
+    let atexit = cx.type_func(&[cx.type_ptr()], cx.type_i32());
+    let atexit_fn = declare_offload_fn(cx, "atexit", atexit);
+
+    let desc_ty = cx.type_func(&[], cx.type_void());
+    let reg_name = ".omp_offloading.descriptor_reg";
+    let unreg_name = ".omp_offloading.descriptor_unreg";
+    let desc_reg_fn = declare_offload_fn(cx, reg_name, desc_ty);
+    let desc_unreg_fn = declare_offload_fn(cx, unreg_name, desc_ty);
+    llvm::set_linkage(desc_reg_fn, InternalLinkage);
+    llvm::set_linkage(desc_unreg_fn, InternalLinkage);
+    llvm::set_section(desc_reg_fn, c".text.startup");
+    llvm::set_section(desc_unreg_fn, c".text.startup");
+
+    // define internal void @.omp_offloading.descriptor_reg() section ".text.startup" {
+    // entry:
+    //   call void @__tgt_register_lib(ptr @.omp_offloading.descriptor)
+    //   %0 = call i32 @atexit(ptr @.omp_offloading.descriptor_unreg)
+    //   ret void
+    // }
+    let bb = Builder::append_block(cx, desc_reg_fn, "entry");
+    let mut a = Builder::build(cx, bb);
+    a.call(reg_lib_decl, None, None, register_lib, &[omp_descriptor], None, None);
+    a.call(atexit, None, None, atexit_fn, &[desc_unreg_fn], None, None);
+    a.ret_void();
+
+    // define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" {
+    // entry:
+    //   call void @__tgt_unregister_lib(ptr @.omp_offloading.descriptor)
+    //   ret void
+    // }
+    let bb = Builder::append_block(cx, desc_unreg_fn, "entry");
+    let mut a = Builder::build(cx, bb);
+    a.call(reg_lib_decl, None, None, unregister_lib, &[omp_descriptor], None, None);
+    a.ret_void();
+
+    // @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @.omp_offloading.descriptor_reg, ptr null }]
+    let args = vec![cx.get_const_i32(101), desc_reg_fn, ptr_null];
+    let const_struct = cx.const_struct(&args, false);
+    let arr = cx.const_array(cx.val_ty(const_struct), &[const_struct]);
+    add_global(cx, "llvm.global_ctors", arr, AppendingLinkage);
+}
+
 pub(crate) struct OffloadKernelDims<'ll> {
     num_workgroups: &'ll Value,
     threads_per_block: &'ll Value,
@@ -487,9 +531,6 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
     let tgt_decl = offload_globals.launcher_fn;
     let tgt_target_kernel_ty = offload_globals.launcher_ty;
 
-    // %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr }
-    let tgt_bin_desc = offload_globals.bin_desc;
-
     let tgt_kernel_decl = offload_globals.kernel_args_ty;
     let begin_mapper_decl = offload_globals.begin_mapper;
     let end_mapper_decl = offload_globals.end_mapper;
@@ -513,12 +554,9 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
     }
 
     // Step 0)
-    // %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr }
-    // %6 = alloca %struct.__tgt_bin_desc, align 8
     unsafe {
         llvm::LLVMRustPositionBuilderPastAllocas(&builder.llbuilder, builder.llfn());
     }
-    let tgt_bin_desc_alloca = builder.direct_alloca(tgt_bin_desc, Align::EIGHT, "EmptyDesc");
 
     let ty = cx.type_array(cx.type_ptr(), num_args);
     // Baseptr are just the input pointer to the kernel, stored in a local alloca
@@ -536,7 +574,6 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
     unsafe {
         llvm::LLVMPositionBuilderAtEnd(&builder.llbuilder, bb);
     }
-    builder.memset(tgt_bin_desc_alloca, cx.get_const_i8(0), cx.get_const_i64(32), Align::EIGHT);
 
     // Now we allocate once per function param, a copy to be passed to one of our maps.
     let mut vals = vec![];
@@ -574,15 +611,9 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
         geps.push(gep);
     }
 
-    let mapper_fn_ty = cx.type_func(&[cx.type_ptr()], cx.type_void());
-    let register_lib_decl = offload_globals.register_lib;
-    let unregister_lib_decl = offload_globals.unregister_lib;
     let init_ty = cx.type_func(&[], cx.type_void());
     let init_rtls_decl = offload_globals.init_rtls;
 
-    // FIXME(offload): Later we want to add them to the wrapper code, rather than our main function.
-    // call void @__tgt_register_lib(ptr noundef %6)
-    builder.call(mapper_fn_ty, None, None, register_lib_decl, &[tgt_bin_desc_alloca], None, None);
     // call void @__tgt_init_all_rtls()
     builder.call(init_ty, None, None, init_rtls_decl, &[], None, None);
 
@@ -679,6 +710,4 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
         num_args,
         s_ident_t,
     );
-
-    builder.call(mapper_fn_ty, None, None, unregister_lib_decl, &[tgt_bin_desc_alloca], None, None);
 }
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index b0cf9925019d..f2261ab79340 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -124,6 +124,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> {
     pub(crate) fn const_null(&self, t: &'ll Type) -> &'ll Value {
         unsafe { llvm::LLVMConstNull(t) }
     }
+
+    pub(crate) fn const_struct(&self, elts: &[&'ll Value], packed: bool) -> &'ll Value {
+        struct_in_context(self.llcx(), elts, packed)
+    }
 }
 
 impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 20eac4cf92c2..97bc929dff32 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -30,7 +30,9 @@ use tracing::debug;
 use crate::abi::FnAbiLlvmExt;
 use crate::builder::Builder;
 use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call};
-use crate::builder::gpu_offload::{OffloadKernelDims, gen_call_handling, gen_define_handling};
+use crate::builder::gpu_offload::{
+    OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload,
+};
 use crate::context::CodegenCx;
 use crate::declare::declare_raw_fn;
 use crate::errors::{
@@ -1410,6 +1412,7 @@ fn codegen_offload<'ll, 'tcx>(
             return;
         }
     };
+    register_offload(cx);
     let offload_data = gen_define_handling(&cx, &metadata, target_symbol, offload_globals);
     gen_call_handling(bx, &offload_data, &args, &types, &metadata, offload_globals, &offload_dims);
 }

From 8e937d4f4a105d01603e0e2d9b8a94ebce9f1079 Mon Sep 17 00:00:00 2001
From: Roberto Aloi 
Date: Wed, 21 Jan 2026 10:52:07 +0100
Subject: [PATCH 164/583] Bump notify from 8.0.0. to 8.2.0

---
 src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index bd6c8331e66c..ce7ea53b5373 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
 tracing.workspace = true
 walkdir = "2.5.0"
 crossbeam-channel.workspace = true
-notify = "8.0.0"
+notify = "8.2.0"
 rayon = "1.10.0"
 
 stdx.workspace = true

From 6753155bd67536b5469113e957adb93eff75dd27 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Thu, 22 Jan 2026 04:25:38 +0000
Subject: [PATCH 165/583] Prepare for merging from rust-lang/rust

This updates the rust-version file to 004d710faff53f8764a1cf69d87a5a5963850b60.
---
 src/tools/rust-analyzer/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index a6ccd9bab393..1fe86330b4a8 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-b6fdaf2a15736cbccf248b532f48e33179614d40
+004d710faff53f8764a1cf69d87a5a5963850b60

From 672828e07750eaf6cd55dde953272413cc64391e Mon Sep 17 00:00:00 2001
From: justanotheranonymoususer
 
Date: Mon, 5 Jan 2026 17:55:54 +0200
Subject: [PATCH 166/583] Add missing mut to pin.rs docs

Per my understanding, needed for mut access next line.
---
 library/core/src/pin.rs | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs
index 74ecb5ee4946..e49faf9eddbd 100644
--- a/library/core/src/pin.rs
+++ b/library/core/src/pin.rs
@@ -831,15 +831,13 @@
 //!     fn get_pin_mut(self: [Pin]<[`&mut Self`]>) -> [Pin]<[`&mut T`]>.
 //!     Then we could do the following:
 //!     ```compile_fail
-//!     # use std::cell::RefCell;
-//!     # use std::pin::Pin;
-//!     fn exploit_ref_cell(rc: Pin<&mut RefCell>) {
+//!     fn exploit_ref_cell(mut rc: Pin<&mut RefCell>) {
 //!         // Here we get pinned access to the `T`.
 //!         let _: Pin<&mut T> = rc.as_mut().get_pin_mut();
 //!
 //!         // And here we have `&mut T` to the same data.
 //!         let shared: &RefCell = rc.into_ref().get_ref();
-//!         let borrow = shared.borrow_mut();
+//!         let mut borrow = shared.borrow_mut();
 //!         let content = &mut *borrow;
 //!     }
 //!     ```

From ee6dd56425125fde399c962f6a9f7c9a527616d0 Mon Sep 17 00:00:00 2001
From: Urgau 
Date: Thu, 22 Jan 2026 22:43:56 +0100
Subject: [PATCH 167/583] Add test for invalid `--remap-path-scope` arguments

---
 .../ui/compile-flags/invalid/remap-path-scope.foo.stderr | 2 ++
 tests/ui/compile-flags/invalid/remap-path-scope.rs       | 9 +++++++++
 .../invalid/remap-path-scope.underscore.stderr           | 2 ++
 3 files changed, 13 insertions(+)
 create mode 100644 tests/ui/compile-flags/invalid/remap-path-scope.foo.stderr
 create mode 100644 tests/ui/compile-flags/invalid/remap-path-scope.rs
 create mode 100644 tests/ui/compile-flags/invalid/remap-path-scope.underscore.stderr

diff --git a/tests/ui/compile-flags/invalid/remap-path-scope.foo.stderr b/tests/ui/compile-flags/invalid/remap-path-scope.foo.stderr
new file mode 100644
index 000000000000..dc72089e0d57
--- /dev/null
+++ b/tests/ui/compile-flags/invalid/remap-path-scope.foo.stderr
@@ -0,0 +1,2 @@
+error: argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `documentation`, `debuginfo`, `coverage`, `object`, `all`
+
diff --git a/tests/ui/compile-flags/invalid/remap-path-scope.rs b/tests/ui/compile-flags/invalid/remap-path-scope.rs
new file mode 100644
index 000000000000..545c8c86cec7
--- /dev/null
+++ b/tests/ui/compile-flags/invalid/remap-path-scope.rs
@@ -0,0 +1,9 @@
+// Error on invalid --remap-path-scope arguments
+
+//@ revisions: foo underscore
+//@ [foo]compile-flags: --remap-path-scope=foo
+//@ [underscore]compile-flags: --remap-path-scope=macro_object
+
+//~? ERROR argument for `--remap-path-scope
+
+fn main() {}
diff --git a/tests/ui/compile-flags/invalid/remap-path-scope.underscore.stderr b/tests/ui/compile-flags/invalid/remap-path-scope.underscore.stderr
new file mode 100644
index 000000000000..dc72089e0d57
--- /dev/null
+++ b/tests/ui/compile-flags/invalid/remap-path-scope.underscore.stderr
@@ -0,0 +1,2 @@
+error: argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `documentation`, `debuginfo`, `coverage`, `object`, `all`
+

From e86cfa0577af318b408fc786fa2fcac0a9961371 Mon Sep 17 00:00:00 2001
From: KaiTomotake 
Date: Thu, 22 Jan 2026 11:57:45 +0900
Subject: [PATCH 168/583] 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 000000000000..6504fd64f64d
--- /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 6710b7f1dfce4911e6dcfacf1ff621d8c0a72498 Mon Sep 17 00:00:00 2001
From: Tshepang Mbambo 
Date: Fri, 23 Jan 2026 11:24:01 +0200
Subject: [PATCH 169/583] coercions.md: add date-check marker

r? @BoxyUwU
---
 src/doc/rustc-dev-guide/src/hir-typeck/coercions.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md b/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md
index 158ac0885d32..c63c64f5f945 100644
--- a/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md
+++ b/src/doc/rustc-dev-guide/src/hir-typeck/coercions.md
@@ -1,4 +1,5 @@
 # Coercions
+
  
 Coercions are implicit operations which transform a value into a different type. A coercion *site* is a position where a coercion is able to be implicitly performed. There are two kinds of coercion sites: 
 - one-to-one

From 4e9e37ee5889dbcfd8af30f6693adfe1c61c6f95 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 23 Jan 2026 11:57:14 +0100
Subject: [PATCH 170/583] internal: Add tests for rust-lang/rust#146972

---
 .../crates/hir-def/src/nameres/tests.rs       |  2 +-
 .../hir-def/src/nameres/tests/imports.rs      | 63 +++++++++++++++++++
 .../hir-def/src/nameres/tests/primitives.rs   | 23 -------
 3 files changed, 64 insertions(+), 24 deletions(-)
 create mode 100644 src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs
 delete mode 100644 src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 23d60d58f085..fe55252e2540 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -1,8 +1,8 @@
 mod globs;
+mod imports;
 mod incremental;
 mod macros;
 mod mod_resolution;
-mod primitives;
 
 use base_db::RootQueryDb;
 use expect_test::{Expect, expect};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs
new file mode 100644
index 000000000000..b1960b785a83
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs
@@ -0,0 +1,63 @@
+use super::*;
+
+#[test]
+fn kw_path_renames() {
+    check(
+        r#"
+macro_rules! m {
+    () => {
+        pub use $crate as dollar_crate;
+        pub use $crate::{self as self_dollar_crate};
+    };
+}
+
+pub use self as this;
+pub use crate as krate;
+
+pub use crate::{self as self_krate};
+m!();
+
+mod foo {
+    pub use super as zuper;
+    pub use super::{self as self_zuper};
+}
+"#,
+        expect![[r#"
+            crate
+            - dollar_crate : type (import)
+            - foo : type
+            - krate : type (import)
+            - self_dollar_crate : type (import)
+            - self_krate : type (import)
+            - this : type (import)
+            - (legacy) m : macro!
+
+            crate::foo
+            - self_zuper : type (import)
+            - zuper : type (import)
+            - (legacy) m : macro!
+        "#]],
+    );
+}
+
+#[test]
+fn primitive_reexport() {
+    check(
+        r#"
+//- /lib.rs
+mod foo;
+use foo::int;
+
+//- /foo.rs
+pub use i32 as int;
+"#,
+        expect![[r#"
+            crate
+            - foo : type
+            - int : type (import)
+
+            crate::foo
+            - int : type (import)
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs
deleted file mode 100644
index 861690238d47..000000000000
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use super::*;
-
-#[test]
-fn primitive_reexport() {
-    check(
-        r#"
-//- /lib.rs
-mod foo;
-use foo::int;
-
-//- /foo.rs
-pub use i32 as int;
-"#,
-        expect![[r#"
-            crate
-            - foo : type
-            - int : type (import)
-
-            crate::foo
-            - int : type (import)
-        "#]],
-    );
-}

From c6441d8f007ab550d4500f71902bcf24b6033204 Mon Sep 17 00:00:00 2001
From: Jakob Koschel 
Date: Fri, 23 Jan 2026 11:43:02 +0000
Subject: [PATCH 171/583] Fix sanitizer target builds on CI

---
 src/ci/docker/host-x86_64/dist-various-2/Dockerfile | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
index 7c165e38f8f9..323cd409a978 100644
--- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
@@ -113,7 +113,6 @@ ENV TARGETS=$TARGETS,wasm32-wasip1
 ENV TARGETS=$TARGETS,wasm32-wasip1-threads
 ENV TARGETS=$TARGETS,wasm32-wasip2
 ENV TARGETS=$TARGETS,wasm32v1-none
-ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnuasan
 ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnux32
 ENV TARGETS=$TARGETS,x86_64-fortanix-unknown-sgx
 ENV TARGETS=$TARGETS,nvptx64-nvidia-cuda
@@ -126,6 +125,8 @@ ENV TARGETS=$TARGETS,i686-unknown-uefi
 ENV TARGETS=$TARGETS,x86_64-unknown-uefi
 ENV TARGETS=$TARGETS,riscv64gc-unknown-linux-musl
 
+ENV TARGETS_SANITIZERS=x86_64-unknown-linux-gnuasan
+
 # As per https://bugs.launchpad.net/ubuntu/+source/gcc-defaults/+bug/1300211
 # we need asm in the search path for gcc-9 (for gnux32) but not in the search path of the
 # cross compilers.
@@ -139,4 +140,4 @@ ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-llvm-bitcode-lin
   --musl-root-armv7=/musl-armv7 \
   --musl-root-riscv64gc=/musl-riscv64gc
 
-ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS
+ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS && python3 ../x.py dist --host='' --set build.sanitizers=true --target $TARGETS_SANITIZERS

From 7ecd7cd582d9cc7a8ab23b36301c6cd5ddcf54c7 Mon Sep 17 00:00:00 2001
From: Redddy 
Date: Fri, 23 Jan 2026 22:20:50 +0900
Subject: [PATCH 172/583] Fix typo

Co-authored-by: Yuki Okushi 
---
 src/doc/rustc-dev-guide/src/walkthrough.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md
index 0522e9d444a9..4bfff17b5fdd 100644
--- a/src/doc/rustc-dev-guide/src/walkthrough.md
+++ b/src/doc/rustc-dev-guide/src/walkthrough.md
@@ -161,12 +161,12 @@ Here is the tracking issue on for our [`?` macro feature][tracking].
 
 ## Experimental RFC (eRFC)
 
-An eRFC is a variant of the RFC process used for complex features wherethe high-level need
+An eRFC is a variant of the RFC process used for complex features where the high-level need
 is clear, but the design space is too large to settle on a detailed specification upfront.
 Instead of providing a final design, an eRFC outlines a high-level strategy to authorize
 a period of active experimentation. This allows the team to implement the feature behind
 a feature gate and gather practical data, which then informs a subsequent formal RFC for stabilization.
-While this process was used for major features like coroutines ([see RFC2033][rfc2033]),
+While this process was used for major features like coroutines ([see RFC 2033][rfc2033]),
 the explicit "eRFC" label is rarely used today. The project now generally prefers approving a standard
 RFC for an initial version and iterating on it through the nightly channel before final stabilization.
 

From 36c0fb5a2dbb8b33407d814585e610d05d555030 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Fri, 23 Jan 2026 15:57:18 +0100
Subject: [PATCH 173/583] Add new `byte_value` and `char_value` methods to
 `proc_macro::Literal`

---
 library/proc_macro/src/lib.rs | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index a005f743ddfa..2a75c4489095 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -54,7 +54,9 @@ use std::{error, fmt};
 pub use diagnostic::{Diagnostic, Level, MultiSpan};
 #[unstable(feature = "proc_macro_value", issue = "136652")]
 pub use rustc_literal_escaper::EscapeError;
-use rustc_literal_escaper::{MixedUnit, unescape_byte_str, unescape_c_str, unescape_str};
+use rustc_literal_escaper::{
+    MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str,
+};
 #[unstable(feature = "proc_macro_totokens", issue = "130977")]
 pub use to_tokens::ToTokens;
 
@@ -1443,6 +1445,28 @@ impl Literal {
         })
     }
 
+    /// Returns the unescaped char value if the current literal is a char.
+    #[unstable(feature = "proc_macro_value", issue = "136652")]
+    pub fn byte_value(&self) -> Result {
+        self.0.symbol.with(|symbol| match self.0.kind {
+            bridge::LitKind::Char => {
+                unescape_byte(symbol).map_err(ConversionErrorKind::FailedToUnescape)
+            }
+            _ => Err(ConversionErrorKind::InvalidLiteralKind),
+        })
+    }
+
+    /// Returns the unescaped char value if the current literal is a char.
+    #[unstable(feature = "proc_macro_value", issue = "136652")]
+    pub fn char_value(&self) -> Result {
+        self.0.symbol.with(|symbol| match self.0.kind {
+            bridge::LitKind::Char => {
+                unescape_char(symbol).map_err(ConversionErrorKind::FailedToUnescape)
+            }
+            _ => Err(ConversionErrorKind::InvalidLiteralKind),
+        })
+    }
+
     /// Returns the unescaped string value if the current literal is a string or a string literal.
     #[unstable(feature = "proc_macro_value", issue = "136652")]
     pub fn str_value(&self) -> Result {

From 33b77c8886ebc14cea9965eabd724265c2955266 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= 
Date: Fri, 23 Jan 2026 17:53:48 +0100
Subject: [PATCH 174/583] Pass on the `feedable` query modifier to macros

---
 compiler/rustc_macros/src/query.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs
index 1ad9bbc3b4b3..b13361b3e6a5 100644
--- a/compiler/rustc_macros/src/query.rs
+++ b/compiler/rustc_macros/src/query.rs
@@ -373,6 +373,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
             no_hash,
             anon,
             eval_always,
+            feedable,
             depth_limit,
             separate_provide_extern,
             return_result_from_ensure_ok,

From 8c697128ebc92af06c69a66a5c5293ba774b38eb Mon Sep 17 00:00:00 2001
From: Andre Bogus 
Date: Wed, 24 Dec 2025 17:37:16 +0100
Subject: [PATCH 175/583] refactor rustc-hash integration

---
 compiler/rustc_data_structures/src/fx.rs          | 6 ++----
 compiler/rustc_data_structures/src/unord.rs       | 2 +-
 compiler/rustc_type_ir/src/data_structures/mod.rs | 8 +++-----
 3 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs
index 026ec5c230ec..cad775cc9864 100644
--- a/compiler/rustc_data_structures/src/fx.rs
+++ b/compiler/rustc_data_structures/src/fx.rs
@@ -1,11 +1,9 @@
-use std::hash::BuildHasherDefault;
-
 pub use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet, FxHasher};
 
 pub type StdEntry<'a, K, V> = std::collections::hash_map::Entry<'a, K, V>;
 
-pub type FxIndexMap = indexmap::IndexMap>;
-pub type FxIndexSet = indexmap::IndexSet>;
+pub type FxIndexMap = indexmap::IndexMap;
+pub type FxIndexSet = indexmap::IndexSet;
 pub type IndexEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>;
 pub type IndexOccupiedEntry<'a, K, V> = indexmap::map::OccupiedEntry<'a, K, V>;
 
diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs
index 0a9a86d7a43b..eb29ef3b4d0a 100644
--- a/compiler/rustc_data_structures/src/unord.rs
+++ b/compiler/rustc_data_structures/src/unord.rs
@@ -8,10 +8,10 @@ use std::hash::Hash;
 use std::iter::{Product, Sum};
 use std::ops::Index;
 
-use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
 use rustc_macros::{Decodable_NoContext, Encodable_NoContext};
 
 use crate::fingerprint::Fingerprint;
+use crate::fx::{FxBuildHasher, FxHashMap, FxHashSet};
 use crate::stable_hasher::{HashStable, StableCompare, StableHasher, ToStableHashKey};
 
 /// `UnordItems` is the order-less version of `Iterator`. It only contains methods
diff --git a/compiler/rustc_type_ir/src/data_structures/mod.rs b/compiler/rustc_type_ir/src/data_structures/mod.rs
index a72669cbd189..c2b629f1d11c 100644
--- a/compiler/rustc_type_ir/src/data_structures/mod.rs
+++ b/compiler/rustc_type_ir/src/data_structures/mod.rs
@@ -1,11 +1,9 @@
-use std::hash::BuildHasherDefault;
-
 pub use ena::unify::{NoError, UnifyKey, UnifyValue};
-use rustc_hash::FxHasher;
+use rustc_hash::FxBuildHasher;
 pub use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
 
-pub type IndexMap = indexmap::IndexMap>;
-pub type IndexSet = indexmap::IndexSet>;
+pub type IndexMap = indexmap::IndexMap;
+pub type IndexSet = indexmap::IndexSet;
 
 mod delayed_map;
 

From bd91c887b5873e5fbc94f3b492c1af3355d10fe9 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Sat, 24 Jan 2026 11:39:14 +0800
Subject: [PATCH 176/583] Improve extract_function name

If the name contains `_`, it is likely to be descriptive

Example
---
```rust
fn foo(kind: i32) {
    let is_complex = $0kind != 0$0;
}
```

**Before this PR**

```rust
fn foo(kind: i32) {
    let is_complex = fun_name(kind);
}

fn fun_name(kind: i32) -> bool {
    kind != 0
}
```

**After this PR**

```rust
fn foo(kind: i32) {
    let is_complex = is_complex(kind);
}

fn is_complex(kind: i32) -> bool {
    kind != 0
}
```
---
 .../src/handlers/extract_function.rs          | 26 +++++++++++++++----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 294e5f7da8b3..2230c391cbad 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -120,7 +120,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
 
             let params = body.extracted_function_params(ctx, &container_info, locals_used);
 
-            let name = make_function_name(&semantics_scope);
+            let name = make_function_name(&semantics_scope, &body);
 
             let fun = Function {
                 name,
@@ -241,7 +241,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     )
 }
 
-fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef {
+fn make_function_name(
+    semantics_scope: &hir::SemanticsScope<'_>,
+    body: &FunctionBody,
+) -> ast::NameRef {
     let mut names_in_scope = vec![];
     semantics_scope.process_all_names(&mut |name, _| {
         names_in_scope.push(
@@ -252,7 +255,10 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef
 
     let default_name = "fun_name";
 
-    let mut name = default_name.to_owned();
+    let mut name = body
+        .suggest_name()
+        .filter(|name| name.contains('_'))
+        .unwrap_or_else(|| default_name.to_owned());
     let mut counter = 0;
     while names_in_scope.contains(&name) {
         counter += 1;
@@ -779,6 +785,16 @@ impl FunctionBody {
     fn contains_node(&self, node: &SyntaxNode) -> bool {
         self.contains_range(node.text_range())
     }
+
+    fn suggest_name(&self) -> Option {
+        if let Some(ast::Pat::IdentPat(pat)) = self.parent().and_then(ast::LetStmt::cast)?.pat()
+            && let Some(name) = pat.name().and_then(|it| it.ident_token())
+        {
+            Some(name.text().to_owned())
+        } else {
+            None
+        }
+    }
 }
 
 impl FunctionBody {
@@ -5430,12 +5446,12 @@ impl Struct {
 
 impl Trait for Struct {
     fn bar(&self) -> i32 {
-        let three_squared = fun_name();
+        let three_squared = three_squared();
         self.0 + three_squared
     }
 }
 
-fn $0fun_name() -> i32 {
+fn $0three_squared() -> i32 {
     3 * 3
 }
 "#,

From c0c6e2166d586d8b4b99afd3005725d9aa15f9ea Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Thu, 22 Jan 2026 20:22:06 -0800
Subject: [PATCH 177/583] make generic test invariant of function order

---
 tests/codegen-llvm/autodiff/generic.rs | 43 +++++++++++++++++---------
 1 file changed, 28 insertions(+), 15 deletions(-)

diff --git a/tests/codegen-llvm/autodiff/generic.rs b/tests/codegen-llvm/autodiff/generic.rs
index 6f56460a2b6d..b31468c91c9c 100644
--- a/tests/codegen-llvm/autodiff/generic.rs
+++ b/tests/codegen-llvm/autodiff/generic.rs
@@ -1,6 +1,14 @@
 //@ compile-flags: -Zautodiff=Enable -Zautodiff=NoPostopt -C opt-level=3 -Clto=fat
 //@ no-prefer-dynamic
 //@ needs-enzyme
+//@ revisions: F32 F64 Main
+
+// Here we verify that the function `square` can be differentiated over f64.
+// This is interesting to test, since the user never calls `square` with f64, so on it's own rustc
+// would have no reason to monomorphize it that way. However, Enzyme needs the f64 version of
+// `square` in order to be able to differentiate it, so we have logic to enforce the
+// monomorphization. Here, we test this logic.
+
 #![feature(autodiff)]
 
 use std::autodiff::autodiff_reverse;
@@ -12,32 +20,37 @@ fn square + Copy>(x: &T) -> T {
 }
 
 // Ensure that `d_square::` code is generated
-//
-// CHECK: ; generic::square
-// CHECK-NEXT: ; Function Attrs: {{.*}}
-// CHECK-NEXT: define internal {{.*}} float
-// CHECK-NEXT: start:
-// CHECK-NOT: ret
-// CHECK: fmul float
+
+// F32-LABEL: ; generic::square::
+// F32-NEXT: ; Function Attrs: {{.*}}
+// F32-NEXT: define internal {{.*}} float
+// F32-NEXT: start:
+// F32-NOT: ret
+// F32: fmul float
 
 // Ensure that `d_square::` code is generated even if `square::` was never called
-//
-// CHECK: ; generic::square
-// CHECK-NEXT: ; Function Attrs:
-// CHECK-NEXT: define internal {{.*}} double
-// CHECK-NEXT: start:
-// CHECK-NOT: ret
-// CHECK: fmul double
+
+// F64-LABEL: ; generic::d_square::
+// F64-NEXT: ; Function Attrs: {{.*}}
+// F64-NEXT: define internal {{.*}} void
+// F64-NEXT: start:
+// F64-NEXT:   {{(tail )?}}call {{(fastcc )?}}void @diffe_{{.*}}(double {{.*}}, ptr {{.*}})
+// F64-NEXT: ret void
+
+// Main-LABEL: ; generic::main
+// Main: ; call generic::square::
+// Main: ; call generic::d_square::
 
 fn main() {
     let xf32: f32 = std::hint::black_box(3.0);
     let xf64: f64 = std::hint::black_box(3.0);
+    let seed: f64 = std::hint::black_box(1.0);
 
     let outputf32 = square::(&xf32);
     assert_eq!(9.0, outputf32);
 
     let mut df_dxf64: f64 = std::hint::black_box(0.0);
 
-    let output_f64 = d_square::(&xf64, &mut df_dxf64, 1.0);
+    let output_f64 = d_square::(&xf64, &mut df_dxf64, seed);
     assert_eq!(6.0, df_dxf64);
 }

From d7877615b41be6bccfad2e6858b9abc7e1834f6e Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Fri, 23 Jan 2026 20:37:58 -0800
Subject: [PATCH 178/583] Update test after new mangling scheme, make test more
 robust

---
 tests/codegen-llvm/autodiff/identical_fnc.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/codegen-llvm/autodiff/identical_fnc.rs b/tests/codegen-llvm/autodiff/identical_fnc.rs
index 1c18e7acc4b6..a8b186c302ea 100644
--- a/tests/codegen-llvm/autodiff/identical_fnc.rs
+++ b/tests/codegen-llvm/autodiff/identical_fnc.rs
@@ -8,7 +8,7 @@
 // merged placeholder function anymore, and compilation would fail. We prevent this by disabling
 // LLVM's merge_function pass before AD. Here we implicetely test that our solution keeps working.
 // We also explicetly test that we keep running merge_function after AD, by checking for two
-// identical function calls in the LLVM-IR, while having two different calls in the Rust code.
+// identical function calls in the LLVM-IR, despite having two different calls in the Rust code.
 #![feature(autodiff)]
 
 use std::autodiff::autodiff_reverse;
@@ -27,14 +27,14 @@ fn square2(x: &f64) -> f64 {
 
 // CHECK:; identical_fnc::main
 // CHECK-NEXT:; Function Attrs:
-// CHECK-NEXT:define internal void @_ZN13identical_fnc4main17h6009e4f751bf9407E()
+// CHECK-NEXT:define internal void
 // CHECK-NEXT:start:
 // CHECK-NOT:br
 // CHECK-NOT:ret
 // CHECK:; call identical_fnc::d_square
-// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1)
+// CHECK-NEXT:call fastcc void @[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1)
 // CHECK:; call identical_fnc::d_square
-// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2)
+// CHECK-NEXT:call fastcc void @[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2)
 
 fn main() {
     let x = std::hint::black_box(3.0);

From f5cdd5d86e1269cba321033e51e6dcd8c513b240 Mon Sep 17 00:00:00 2001
From: Brian Cain 
Date: Fri, 23 Jan 2026 15:00:11 -0600
Subject: [PATCH 179/583] Update hexagon target linker configurations

* hexagon-unknown-qurt: Use hexagon-clang from Hexagon SDK instead of
rust-lld
* hexagon-unknown-linux-musl: Use hexagon-unknown-linux-musl-clang from
the open source toolchain instead of rust-lld.
* hexagon-unknown-none-elf: Keep rust-lld but fix the linker flavor.

rust-lld is appropriate for a baremetal target but for traditional
programs that depend on libc, using clang's driver makes the most
sense.
---
 .../targets/hexagon_unknown_linux_musl.rs     |  6 ++---
 .../spec/targets/hexagon_unknown_none_elf.rs  |  5 +++-
 .../src/spec/targets/hexagon_unknown_qurt.rs  |  4 ++--
 .../hexagon-unknown-linux-musl.md             | 23 +++++++++++--------
 .../platform-support/hexagon-unknown-qurt.md  | 13 +++++++----
 5 files changed, 32 insertions(+), 19 deletions(-)

diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs
index 82811cda00ce..92c477f53f8f 100644
--- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs
@@ -1,4 +1,4 @@
-use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, base};
+use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, base};
 
 pub(crate) fn target() -> Target {
     let mut base = base::linux_musl::opts();
@@ -8,8 +8,8 @@ pub(crate) fn target() -> Target {
     base.features = "-small-data,+hvx-length128b".into();
 
     base.has_rpath = true;
-    base.linker = Some("rust-lld".into());
-    base.linker_flavor = LinkerFlavor::Unix(Cc::Yes);
+    base.linker = Some("hexagon-unknown-linux-musl-clang".into());
+    base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::No);
 
     base.c_enum_min_bits = Some(8);
 
diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs
index 55ec3697a15e..3809057e255c 100644
--- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs
@@ -1,4 +1,6 @@
-use crate::spec::{Arch, PanicStrategy, Target, TargetMetadata, TargetOptions};
+use crate::spec::{
+    Arch, Cc, LinkerFlavor, Lld, PanicStrategy, Target, TargetMetadata, TargetOptions,
+};
 
 pub(crate) fn target() -> Target {
     Target {
@@ -28,6 +30,7 @@ pub(crate) fn target() -> Target {
             emit_debug_gdb_scripts: false,
             c_enum_min_bits: Some(8),
             linker: Some("rust-lld".into()),
+            linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
             ..Default::default()
         },
     }
diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs
index 746e0cb11dcb..dcc92b4bdcc4 100644
--- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs
+++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs
@@ -24,8 +24,8 @@ pub(crate) fn target() -> Target {
             os: Os::Qurt,
             vendor: "unknown".into(),
             cpu: "hexagonv69".into(),
-            linker: Some("rust-lld".into()),
-            linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+            linker: Some("hexagon-clang".into()),
+            linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No),
             exe_suffix: ".elf".into(),
             dynamic_linking: true,
             executables: true,
diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md
index d74dd843eb25..eefe82133906 100644
--- a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md
+++ b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md
@@ -36,14 +36,19 @@ Also included in that toolchain is the C library that can be used when creating
 dynamically linked executables.
 
 ```text
-# /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello
+# /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello
 ```
 
 ## Linking
 
-This target selects `rust-lld` by default.  Another option to use is
-[eld](https://github.com/qualcomm/eld), which is also provided with
-the opensource hexagon toolchain and the Hexagon SDK.
+This target uses `hexagon-unknown-linux-musl-clang` as the default linker.
+The linker is available from [the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon/releases)
+at paths like `/opt/clang+llvm-21.1.8-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/hexagon-unknown-linux-musl-clang`.
+
+Alternative linkers include:
+- [eld](https://github.com/qualcomm/eld), which is provided with
+  the opensource hexagon toolchain and the Hexagon SDK
+- `rust-lld` can be used by specifying `-C linker=rust-lld`
 
 ## Building the target
 Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this
@@ -63,9 +68,9 @@ cxx = "hexagon-unknown-linux-musl-clang++"
 linker = "hexagon-unknown-linux-musl-clang"
 ar = "hexagon-unknown-linux-musl-ar"
 ranlib = "hexagon-unknown-linux-musl-ranlib"
-musl-root = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
+musl-root = "/opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
 llvm-libunwind = 'in-tree'
-qemu-rootfs = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
+qemu-rootfs = "/opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
 ```
 
 
@@ -88,7 +93,7 @@ target = "hexagon-unknown-linux-musl"
 [target.hexagon-unknown-linux-musl]
 linker = "hexagon-unknown-linux-musl-clang"
 ar = "hexagon-unknown-linux-musl-ar"
-runner = "qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
+runner = "qemu-hexagon -L /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
 ```
 
 Edit the "runner" in `.cargo/config` to point to the path to your toolchain's
@@ -96,13 +101,13 @@ C library.
 
 ```text
 ...
-runner = "qemu-hexagon -L /path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
+runner = "qemu-hexagon -L /path/to/my/inst/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
 ...
 ```
 
 Build/run your rust program with `qemu-hexagon` in your `PATH`:
 
 ```text
-export PATH=/path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH
+export PATH=/path/to/my/inst/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH
 cargo run -Zbuild-std -Zbuild-std-features=llvm-libunwind
 ```
diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md
index 7928804d0954..d33a90bf188c 100644
--- a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md
+++ b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md
@@ -33,10 +33,15 @@ required for building programs for this target.
 
 ## Linking
 
-This target selects `rust-lld` by default.  Another option to use is
-[eld](https://github.com/qualcomm/eld), which is also provided with
-[the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon)
-and the Hexagon SDK.
+This target uses `hexagon-clang` from the Hexagon SDK as the default linker.
+The linker is available at paths like
+`/opt/Hexagon_SDK/6.4.0.2/tools/HEXAGON_Tools/19.0.04/Tools/bin/hexagon-clang`.
+
+Alternative linkers include:
+- [eld](https://github.com/qualcomm/eld), which is provided with both
+  [the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon)
+  and the Hexagon SDK
+- `rust-lld` can be used by specifying `-C linker=rust-lld`
 
 ## Building the target
 

From 7bcc8a705383710fe8ae1105799d4e1cb852f0e0 Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Fri, 23 Jan 2026 21:54:04 -0800
Subject: [PATCH 180/583] update abi handling test

---
 tests/codegen-llvm/autodiff/abi_handling.rs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tests/codegen-llvm/autodiff/abi_handling.rs b/tests/codegen-llvm/autodiff/abi_handling.rs
index 5c8126898a8d..a8bc482fc293 100644
--- a/tests/codegen-llvm/autodiff/abi_handling.rs
+++ b/tests/codegen-llvm/autodiff/abi_handling.rs
@@ -38,14 +38,14 @@ fn square(x: f32) -> f32 {
 // CHECK-LABEL: ; abi_handling::df1
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal { float, float }
-// debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0)
+// debug-SAME: (ptr {{.*}}%x, ptr {{.*}}%bx_0)
 // release-NEXT: define internal fastcc float
 // release-SAME: (float %x.0.val, float %x.4.val)
 
 // CHECK-LABEL: ; abi_handling::f1
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal float
-// debug-SAME: (ptr align 4 %x)
+// debug-SAME: (ptr {{.*}}%x)
 // release-NEXT: define internal fastcc noundef float
 // release-SAME: (float %x.0.val, float %x.4.val)
 #[autodiff_forward(df1, Dual, Dual)]
@@ -58,7 +58,7 @@ fn f1(x: &[f32; 2]) -> f32 {
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal { float, float }
 // debug-SAME: (ptr %f, float %x, float %dret)
-// release-NEXT: define internal fastcc float
+// release-NEXT: define internal fastcc noundef float
 // release-SAME: (float noundef %x)
 
 // CHECK-LABEL: ; abi_handling::f2
@@ -77,13 +77,13 @@ fn f2(f: fn(f32) -> f32, x: f32) -> f32 {
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal { float, float }
 // debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0, ptr align 4 %y, ptr align 4 %by_0)
-// release-NEXT: define internal fastcc { float, float }
+// release-NEXT: define internal fastcc float
 // release-SAME: (float %x.0.val)
 
 // CHECK-LABEL: ; abi_handling::f3
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal float
-// debug-SAME: (ptr align 4 %x, ptr align 4 %y)
+// debug-SAME: (ptr {{.*}}%x, ptr {{.*}}%y)
 // release-NEXT: define internal fastcc noundef float
 // release-SAME: (float %x.0.val)
 #[autodiff_forward(df3, Dual, Dual, Dual)]
@@ -160,7 +160,7 @@ fn f6(i: NestedInput) -> f32 {
 // CHECK-LABEL: ; abi_handling::f7
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal float
-// debug-SAME: (ptr align 4 %x.0, ptr align 4 %x.1)
+// debug-SAME: (ptr {{.*}}%x.0, ptr {{.*}}%x.1)
 // release-NEXT: define internal fastcc noundef float
 // release-SAME: (float %x.0.0.val, float %x.1.0.val)
 #[autodiff_forward(df7, Dual, Dual)]

From b6d567c12cb16a0c02928dfea0db24523ddb9c5f Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Fri, 23 Jan 2026 23:18:52 -0800
Subject: [PATCH 181/583] Shorten the autodiff batching test, to make it more
 reliable

---
 tests/codegen-llvm/autodiff/batched.rs | 83 +++++---------------------
 1 file changed, 16 insertions(+), 67 deletions(-)

diff --git a/tests/codegen-llvm/autodiff/batched.rs b/tests/codegen-llvm/autodiff/batched.rs
index 0ff6134bc07d..5a723ff04183 100644
--- a/tests/codegen-llvm/autodiff/batched.rs
+++ b/tests/codegen-llvm/autodiff/batched.rs
@@ -1,13 +1,11 @@
 //@ compile-flags: -Zautodiff=Enable,NoTT,NoPostopt -C opt-level=3  -Clto=fat
 //@ no-prefer-dynamic
 //@ needs-enzyme
-//
-// In Enzyme, we test against a large range of LLVM versions (5+) and don't have overly many
-// breakages. One benefit is that we match the IR generated by Enzyme only after running it
-// through LLVM's O3 pipeline, which will remove most of the noise.
-// However, our integration test could also be affected by changes in how rustc lowers MIR into
-// LLVM-IR, which could cause additional noise and thus breakages. If that's the case, we should
-// reduce this test to only match the first lines and the ret instructions.
+
+// This test combines two features of Enzyme, automatic differentiation and batching. As such, it is
+// especially prone to breakages. I reduced it therefore to a minimal check matches argument/return
+// types. Based on the original batching author, implementing the batching feature over MLIR instead
+// of LLVM should give significantly more reliable performance.
 
 #![feature(autodiff)]
 
@@ -22,69 +20,20 @@ fn square(x: &f32) -> f32 {
     x * x
 }
 
+// The base ("scalar") case d_square3, without batching.
+// CHECK: define internal fastcc float @fwddiffesquare(float %x.0.val, float %"x'.0.val")
+// CHECK:   %0 = fadd fast float %"x'.0.val", %"x'.0.val"
+// CHECK-NEXT:   %1 = fmul fast float %0, %x.0.val
+// CHECK-NEXT:   ret float %1
+// CHECK-NEXT: }
+
 // d_square2
-// CHECK: define internal [4 x float] @fwddiffe4square(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'")
-// CHECK-NEXT: start:
-// CHECK-NEXT:   %0 = extractvalue [4 x ptr] %"x'", 0
-// CHECK-NEXT:   %"_2'ipl" = load float, ptr %0, align 4
-// CHECK-NEXT:   %1 = extractvalue [4 x ptr] %"x'", 1
-// CHECK-NEXT:   %"_2'ipl1" = load float, ptr %1, align 4
-// CHECK-NEXT:   %2 = extractvalue [4 x ptr] %"x'", 2
-// CHECK-NEXT:   %"_2'ipl2" = load float, ptr %2, align 4
-// CHECK-NEXT:   %3 = extractvalue [4 x ptr] %"x'", 3
-// CHECK-NEXT:   %"_2'ipl3" = load float, ptr %3, align 4
-// CHECK-NEXT:   %_2 = load float, ptr %x, align 4
-// CHECK-NEXT:   %4 = fmul fast float %"_2'ipl", %_2
-// CHECK-NEXT:   %5 = fmul fast float %"_2'ipl1", %_2
-// CHECK-NEXT:   %6 = fmul fast float %"_2'ipl2", %_2
-// CHECK-NEXT:   %7 = fmul fast float %"_2'ipl3", %_2
-// CHECK-NEXT:   %8 = fmul fast float %"_2'ipl", %_2
-// CHECK-NEXT:   %9 = fmul fast float %"_2'ipl1", %_2
-// CHECK-NEXT:   %10 = fmul fast float %"_2'ipl2", %_2
-// CHECK-NEXT:   %11 = fmul fast float %"_2'ipl3", %_2
-// CHECK-NEXT:   %12 = fadd fast float %4, %8
-// CHECK-NEXT:   %13 = insertvalue [4 x float] undef, float %12, 0
-// CHECK-NEXT:   %14 = fadd fast float %5, %9
-// CHECK-NEXT:   %15 = insertvalue [4 x float] %13, float %14, 1
-// CHECK-NEXT:   %16 = fadd fast float %6, %10
-// CHECK-NEXT:   %17 = insertvalue [4 x float] %15, float %16, 2
-// CHECK-NEXT:   %18 = fadd fast float %7, %11
-// CHECK-NEXT:   %19 = insertvalue [4 x float] %17, float %18, 3
-// CHECK-NEXT:   ret [4 x float] %19
+// CHECK: define internal fastcc [4 x float] @fwddiffe4square(float %x.0.val, [4 x ptr] %"x'")
+// CHECK:   ret [4 x float]
 // CHECK-NEXT:   }
 
-// d_square3, the extra float is the original return value (x * x)
-// CHECK: define internal { float, [4 x float] } @fwddiffe4square.1(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'")
-// CHECK-NEXT: start:
-// CHECK-NEXT:   %0 = extractvalue [4 x ptr] %"x'", 0
-// CHECK-NEXT:   %"_2'ipl" = load float, ptr %0, align 4
-// CHECK-NEXT:   %1 = extractvalue [4 x ptr] %"x'", 1
-// CHECK-NEXT:   %"_2'ipl1" = load float, ptr %1, align 4
-// CHECK-NEXT:   %2 = extractvalue [4 x ptr] %"x'", 2
-// CHECK-NEXT:   %"_2'ipl2" = load float, ptr %2, align 4
-// CHECK-NEXT:   %3 = extractvalue [4 x ptr] %"x'", 3
-// CHECK-NEXT:   %"_2'ipl3" = load float, ptr %3, align 4
-// CHECK-NEXT:   %_2 = load float, ptr %x, align 4
-// CHECK-NEXT:   %_0 = fmul float %_2, %_2
-// CHECK-NEXT:   %4 = fmul fast float %"_2'ipl", %_2
-// CHECK-NEXT:   %5 = fmul fast float %"_2'ipl1", %_2
-// CHECK-NEXT:   %6 = fmul fast float %"_2'ipl2", %_2
-// CHECK-NEXT:   %7 = fmul fast float %"_2'ipl3", %_2
-// CHECK-NEXT:   %8 = fmul fast float %"_2'ipl", %_2
-// CHECK-NEXT:   %9 = fmul fast float %"_2'ipl1", %_2
-// CHECK-NEXT:   %10 = fmul fast float %"_2'ipl2", %_2
-// CHECK-NEXT:   %11 = fmul fast float %"_2'ipl3", %_2
-// CHECK-NEXT:   %12 = fadd fast float %4, %8
-// CHECK-NEXT:   %13 = insertvalue [4 x float] undef, float %12, 0
-// CHECK-NEXT:   %14 = fadd fast float %5, %9
-// CHECK-NEXT:   %15 = insertvalue [4 x float] %13, float %14, 1
-// CHECK-NEXT:   %16 = fadd fast float %6, %10
-// CHECK-NEXT:   %17 = insertvalue [4 x float] %15, float %16, 2
-// CHECK-NEXT:   %18 = fadd fast float %7, %11
-// CHECK-NEXT:   %19 = insertvalue [4 x float] %17, float %18, 3
-// CHECK-NEXT:   %20 = insertvalue { float, [4 x float] } undef, float %_0, 0
-// CHECK-NEXT:   %21 = insertvalue { float, [4 x float] } %20, [4 x float] %19, 1
-// CHECK-NEXT:   ret { float, [4 x float] } %21
+// CHECK: define internal fastcc { float, [4 x float] } @fwddiffe4square.{{.*}}(float %x.0.val, [4 x ptr] %"x'")
+// CHECK:   ret { float, [4 x float] }
 // CHECK-NEXT:   }
 
 fn main() {

From fcbb046ced6d4f93fffa724430a2feb3db0de08a Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Sat, 24 Jan 2026 00:47:39 +0100
Subject: [PATCH 182/583] Add a marker to tell users that there are hidden
 (deprecated) items in the search results

---
 src/librustdoc/html/static/css/rustdoc.css | 11 ++++++++++-
 src/librustdoc/html/static/js/search.js    | 12 +++++++++++-
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 486ca9b22539..b62d6b15cf36 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -341,7 +341,8 @@ summary.hideme,
 .rustdoc-breadcrumbs,
 .search-switcher,
 /* This selector is for the items listed in the "all items" page. */
-ul.all-items {
+ul.all-items,
+.deprecated-count {
 	font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
 }
 
@@ -2652,6 +2653,14 @@ However, it's not needed with smaller screen width because the doc/code block is
 	display: none;
 }
 
+.deprecated-count {
+	display: none;
+}
+.hide-deprecated-items .deprecated-count:not(:empty) {
+	display: block;
+	margin: 10px 0;
+}
+
 /*
 WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
 If you update this line, then you also need to update the line with the same warning
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 9961c1447ec2..55ff48846dcb 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -158,6 +158,7 @@ const REGEX_INVALID_TYPE_FILTER = /[^a-z]/ui;
 
 const MAX_RESULTS = 200;
 const NO_TYPE_FILTER = -1;
+const DEPRECATED_COUNT_SELECTOR = "deprecated-count";
 
 /**
  * The [edit distance] is a metric for measuring the difference between two strings.
@@ -4904,7 +4905,12 @@ async function addTab(results, query, display, finishedCallback, isTypeSearch) {
     let output = document.createElement("ul");
     output.className = "search-results " + extraClass;
 
+    const deprecatedCountElem = document.createElement("span");
+    deprecatedCountElem.className = DEPRECATED_COUNT_SELECTOR;
+    output.appendChild(deprecatedCountElem);
+
     let count = 0;
+    let deprecatedCount = 0;
 
     /** @type {Promise[]} */
     const descList = [];
@@ -4922,6 +4928,10 @@ async function addTab(results, query, display, finishedCallback, isTypeSearch) {
         link.className = "result-" + type;
         if (obj.item.deprecated) {
             link.className += " deprecated";
+            deprecatedCount += 1;
+            const plural = deprecatedCount > 1 ? "s" : "";
+            deprecatedCountElem.innerText =
+                `${deprecatedCount} deprecated item${plural} hidden by setting`;
         }
         link.href = obj.href;
 
@@ -5411,7 +5421,7 @@ function registerSearchEvents() {
             const active = document.activeElement;
             if (active) {
                 const previous = active.previousElementSibling;
-                if (previous) {
+                if (previous && previous.className !== DEPRECATED_COUNT_SELECTOR) {
                     // @ts-expect-error
                     previous.focus();
                 } else {

From 0e240d32325bdb491f8894b4dc1e720ec756c4f7 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Sat, 24 Jan 2026 00:48:37 +0100
Subject: [PATCH 183/583] Add GUI regression test for the display and content
 of the "hidden deprecated item marker"

---
 tests/rustdoc-gui/setting-hide-deprecated.goml | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/tests/rustdoc-gui/setting-hide-deprecated.goml b/tests/rustdoc-gui/setting-hide-deprecated.goml
index 6dc5a6bff175..0fefe00f9457 100644
--- a/tests/rustdoc-gui/setting-hide-deprecated.goml
+++ b/tests/rustdoc-gui/setting-hide-deprecated.goml
@@ -69,7 +69,7 @@ wait-for-css: ("details" + |deprecated_class|, {"display": "block"}, ALL)
 
 // And now we check with the search results.
 call-function: ("perform-search", {"query": "deprecated::depr"})
-// There should at least 7 results.
+// There should be at least 7 results.
 store-count: ("#results ul.search-results.active > a", nb_search_results)
 assert: |nb_search_results| >= 7
 // There should be at least 5 deprecated items.
@@ -77,6 +77,12 @@ store-count: ("#results ul.search-results.active > a" + |deprecated_class|, nb_d
 assert: |nb_search_results| >= 5
 // Deprecated items should all be displayed.
 assert-css: ("#results ul.search-results.active > a" + |deprecated_class|, {"display": "grid"}, ALL)
+// The "X deprecated items hidden by setting" element should not be displayed.
+assert-text: (
+    "#results ul.search-results.active .deprecated-count",
+    "5 deprecated items hidden by setting",
+)
+assert-css: ("#results ul.search-results.active .deprecated-count", {"display": "none"})
 // We enable the "hide deprecated items" setting.
 call-function: ("open-settings-menu", {})
 click: "#hide-deprecated-items"
@@ -86,11 +92,16 @@ wait-for-css: (
     {"display": "none"},
     ALL,
 )
+// The "X deprecated items hidden by setting" element should be displayed.
+assert-css: ("#results ul.search-results.active .deprecated-count", {"display": "block"})
 
 // Finally we check that the future deprecated item doesn't have the deprecated class in the search
-// and therefore isn't impact by the setting.
-call-function: ("perform-search", {"query": "deprecated::future_deprecated"})
+// and therefore isn't impacted by the setting.
+call-function: ("perform-search", {"query": '"future_deprecated_fn"'})
 assert-text: (
     "#results ul.search-results.active > a:not(" + |deprecated_class| + ") .path",
     " lib2::deprecated::NonDeprecatedStruct::future_deprecated_fn",
 )
+// The "X deprecated items hidden by setting" element should now be empty, and therefore not displayed.
+assert-text: ("#results ul.search-results.active .deprecated-count", "")
+assert-css: ("#results ul.search-results.active .deprecated-count", {"display": "none"})

From 3f3959310695b6bb7b55d980ddb3ff8fcdbdecba Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Sat, 24 Jan 2026 12:42:33 +0800
Subject: [PATCH 184/583] fix: Fix incorrect continue for
 convert_range_for_to_while

Example
---
```rust
fn foo() {
    $0for mut i in 3..7 {
        foo(i);
        continue;
        bar(i);
    }
}
```

**Before this PR**

This may cause an infinite loop

```rust
fn foo() {
    let mut i = 3;
    while i < 7 {
        foo(i);
        continue;
        bar(i);
        i += 1;
    }
}
```

**After this PR**

```rust
fn foo() {
    let mut i = 3;
    while i < 7 {
        'cont: {
            foo(i);
            break 'cont;
            bar(i);
        }
        i += 1;
    }
}
```
---
 .../handlers/convert_range_for_to_while.rs    | 157 +++++++++++++++++-
 1 file changed, 151 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
index ba577b217df7..2e649f14be26 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
@@ -1,13 +1,15 @@
 use ide_db::assists::AssistId;
 use itertools::Itertools;
 use syntax::{
-    AstNode, T,
+    AstNode, SyntaxElement,
+    SyntaxKind::WHITESPACE,
+    T,
     algo::previous_non_trivia_token,
     ast::{
         self, HasArgList, HasLoopBody, HasName, RangeItem, edit::AstNodeEdit, make,
         syntax_factory::SyntaxFactory,
     },
-    syntax_editor::{Element, Position},
+    syntax_editor::{Element, Position, SyntaxEditor},
 };
 
 use crate::assist_context::{AssistContext, Assists};
@@ -40,8 +42,8 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<
     let iterable = for_.iterable()?;
     let (start, end, step, inclusive) = extract_range(&iterable)?;
     let name = pat.name()?;
-    let body = for_.loop_body()?;
-    let last = previous_non_trivia_token(body.stmt_list()?.r_curly_token()?)?;
+    let body = for_.loop_body()?.stmt_list()?;
+    let label = for_.label();
 
     let description = if end.is_some() {
         "Replace with while expression"
@@ -90,8 +92,10 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<
             );
 
             let op = ast::BinaryOp::Assignment { op: Some(ast::ArithOp::Add) };
-            edit.insert_all(
-                Position::after(last),
+            process_loop_body(
+                body,
+                label,
+                &mut edit,
                 vec![
                     make.whitespace(&format!("\n{}", indent + 1)).syntax_element(),
                     make.expr_bin(var_expr, op, step).syntax().syntax_element(),
@@ -121,6 +125,86 @@ fn extract_range(iterable: &ast::Expr) -> Option<(ast::Expr, Option,
     })
 }
 
+fn process_loop_body(
+    body: ast::StmtList,
+    label: Option,
+    edit: &mut SyntaxEditor,
+    incrementer: Vec,
+) -> Option<()> {
+    let last = previous_non_trivia_token(body.r_curly_token()?)?.syntax_element();
+
+    let new_body = body.indent(1.into()).clone_subtree();
+    let mut continues = vec![];
+    collect_continue_to(
+        &mut continues,
+        &label.and_then(|it| it.lifetime()),
+        new_body.syntax(),
+        false,
+    );
+
+    if continues.is_empty() {
+        edit.insert_all(Position::after(last), incrementer);
+        return Some(());
+    }
+
+    let mut children = body
+        .syntax()
+        .children_with_tokens()
+        .filter(|it| !matches!(it.kind(), WHITESPACE | T!['{'] | T!['}']));
+    let first = children.next()?;
+    let block_content = first.clone()..=children.last().unwrap_or(first);
+
+    let continue_label = make::lifetime("'cont");
+    let break_expr = make::expr_break(Some(continue_label.clone()), None).clone_for_update();
+    let mut new_edit = SyntaxEditor::new(new_body.syntax().clone());
+    for continue_expr in &continues {
+        new_edit.replace(continue_expr.syntax(), break_expr.syntax());
+    }
+    let new_body = new_edit.finish().new_root().clone();
+    let elements = itertools::chain(
+        [
+            continue_label.syntax().clone_for_update().syntax_element(),
+            make::token(T![:]).syntax_element(),
+            make::tokens::single_space().syntax_element(),
+            new_body.syntax_element(),
+        ],
+        incrementer,
+    );
+    edit.replace_all(block_content, elements.collect());
+
+    Some(())
+}
+
+fn collect_continue_to(
+    acc: &mut Vec,
+    label: &Option,
+    node: &syntax::SyntaxNode,
+    only_label: bool,
+) {
+    let match_label = |it: &Option, label: &Option| match (it, label)
+    {
+        (None, _) => !only_label,
+        (Some(a), Some(b)) if a.text() == b.text() => true,
+        _ => false,
+    };
+    if let Some(expr) = ast::ContinueExpr::cast(node.clone())
+        && match_label(&expr.lifetime(), label)
+    {
+        acc.push(expr);
+    } else if let Some(any_loop) = ast::AnyHasLoopBody::cast(node.clone()) {
+        if match_label(label, &any_loop.label().and_then(|it| it.lifetime())) {
+            return;
+        }
+        for children in node.children() {
+            collect_continue_to(acc, label, &children, true);
+        }
+    } else {
+        for children in node.children() {
+            collect_continue_to(acc, label, &children, only_label);
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};
@@ -219,6 +303,67 @@ fn foo() {
         );
     }
 
+    #[test]
+    fn test_convert_range_for_to_while_with_continue() {
+        check_assist(
+            convert_range_for_to_while,
+            "
+fn foo() {
+    $0for mut i in 3..7 {
+        foo(i);
+        continue;
+        loop { break; continue }
+        bar(i);
+    }
+}
+            ",
+            "
+fn foo() {
+    let mut i = 3;
+    while i < 7 {
+        'cont: {
+            foo(i);
+            break 'cont;
+            loop { break; continue }
+            bar(i);
+        }
+        i += 1;
+    }
+}
+            ",
+        );
+
+        check_assist(
+            convert_range_for_to_while,
+            "
+fn foo() {
+    'x: $0for mut i in 3..7 {
+        foo(i);
+        continue 'x;
+        loop { break; continue 'x }
+        'x: loop { continue 'x }
+        bar(i);
+    }
+}
+            ",
+            "
+fn foo() {
+    let mut i = 3;
+    'x: while i < 7 {
+        'cont: {
+            foo(i);
+            break 'cont;
+            loop { break; break 'cont }
+            'x: loop { continue 'x }
+            bar(i);
+        }
+        i += 1;
+    }
+}
+            ",
+        );
+    }
+
     #[test]
     fn test_convert_range_for_to_while_step_by() {
         check_assist(

From 91e3e2a37f4a71dab6092e98785d54143fe8ca1f Mon Sep 17 00:00:00 2001
From: Urgau 
Date: Thu, 22 Jan 2026 22:39:31 +0100
Subject: [PATCH 185/583] Add the remapping path `documentation` scope for
 rustdoc usage

---
 compiler/rustc_session/src/config.rs          | 12 ++++++++--
 compiler/rustc_span/src/lib.rs                |  2 ++
 src/doc/rustc/src/remap-source-paths.md       |  2 +-
 tests/run-make/remap-path-prefix/rmake.rs     | 24 +++++++++++++++++++
 .../remap-path-scope-documentation.rs         |  7 ++++++
 .../remap-path-scope-documentation.stderr     |  2 ++
 6 files changed, 46 insertions(+), 3 deletions(-)
 create mode 100644 tests/ui/feature-gates/remap-path-scope-documentation.rs
 create mode 100644 tests/ui/feature-gates/remap-path-scope-documentation.stderr

diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 995dd5b29474..5e2671ef4ef6 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1322,6 +1322,7 @@ impl OutputFilenames {
 pub(crate) fn parse_remap_path_scope(
     early_dcx: &EarlyDiagCtxt,
     matches: &getopts::Matches,
+    unstable_opts: &UnstableOptions,
 ) -> RemapPathScopeComponents {
     if let Some(v) = matches.opt_str("remap-path-scope") {
         let mut slot = RemapPathScopeComponents::empty();
@@ -1329,11 +1330,18 @@ pub(crate) fn parse_remap_path_scope(
             slot |= match s {
                 "macro" => RemapPathScopeComponents::MACRO,
                 "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS,
+                "documentation" => {
+                    if !unstable_opts.unstable_options {
+                        early_dcx.early_fatal("remapping `documentation` path scope requested but `-Zunstable-options` not specified");
+                    }
+
+                    RemapPathScopeComponents::DOCUMENTATION
+                },
                 "debuginfo" => RemapPathScopeComponents::DEBUGINFO,
                 "coverage" => RemapPathScopeComponents::COVERAGE,
                 "object" => RemapPathScopeComponents::OBJECT,
                 "all" => RemapPathScopeComponents::all(),
-                _ => early_dcx.early_fatal("argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `debuginfo`, `coverage`, `object`, `all`"),
+                _ => early_dcx.early_fatal("argument for `--remap-path-scope` must be a comma separated list of scopes: `macro`, `diagnostics`, `documentation`, `debuginfo`, `coverage`, `object`, `all`"),
             }
         }
         slot
@@ -2677,7 +2685,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let externs = parse_externs(early_dcx, matches, &unstable_opts);
 
     let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts);
-    let remap_path_scope = parse_remap_path_scope(early_dcx, matches);
+    let remap_path_scope = parse_remap_path_scope(early_dcx, matches, &unstable_opts);
 
     let pretty = parse_pretty(early_dcx, &unstable_opts);
 
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 747cb2c17eb9..69f91cfab9e4 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -233,6 +233,8 @@ bitflags::bitflags! {
         const DEBUGINFO = 1 << 3;
         /// Apply remappings to coverage information
         const COVERAGE = 1 << 4;
+        /// Apply remappings to documentation information
+        const DOCUMENTATION = 1 << 5;
 
         /// An alias for `macro`, `debuginfo` and `coverage`. This ensures all paths in compiled
         /// executables, libraries and objects are remapped but not elsewhere.
diff --git a/src/doc/rustc/src/remap-source-paths.md b/src/doc/rustc/src/remap-source-paths.md
index 9912f353774b..1967981d5600 100644
--- a/src/doc/rustc/src/remap-source-paths.md
+++ b/src/doc/rustc/src/remap-source-paths.md
@@ -52,7 +52,7 @@ The valid scopes are:
 - `debuginfo` - apply remappings to debug information
 - `coverage` - apply remappings to coverage information
 - `object` - apply remappings to all paths in compiled executables or libraries, but not elsewhere. Currently an alias for `macro,coverage,debuginfo`.
-- `all` (default) - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`.
+- `all` (default) - an alias for all of the above (and unstable scopes), also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`.
 
 The scopes accepted by `--remap-path-scope` are not exhaustive - new scopes may be added in future releases for eventual stabilisation.
 This implies that the `all` scope can correspond to different scopes between releases.
diff --git a/tests/run-make/remap-path-prefix/rmake.rs b/tests/run-make/remap-path-prefix/rmake.rs
index 22ffd4f1f0d1..3b866476e950 100644
--- a/tests/run-make/remap-path-prefix/rmake.rs
+++ b/tests/run-make/remap-path-prefix/rmake.rs
@@ -12,7 +12,9 @@ fn main() {
     let mut out_simple = rustc();
     let mut out_object = rustc();
     let mut out_macro = rustc();
+    let mut out_doc = rustc();
     let mut out_diagobj = rustc();
+    let mut out_diagdocobj = rustc();
     out_simple
         .remap_path_prefix("auxiliary", "/the/aux")
         .crate_type("lib")
@@ -28,11 +30,21 @@ fn main() {
         .crate_type("lib")
         .emit("metadata")
         .input("auxiliary/lib.rs");
+    out_doc
+        .remap_path_prefix("auxiliary", "/the/aux")
+        .crate_type("lib")
+        .emit("metadata")
+        .input("auxiliary/lib.rs");
     out_diagobj
         .remap_path_prefix("auxiliary", "/the/aux")
         .crate_type("lib")
         .emit("metadata")
         .input("auxiliary/lib.rs");
+    out_diagdocobj
+        .remap_path_prefix("auxiliary", "/the/aux")
+        .crate_type("lib")
+        .emit("metadata")
+        .input("auxiliary/lib.rs");
 
     out_simple.run();
     rmeta_contains("/the/aux/lib.rs");
@@ -40,11 +52,17 @@ fn main() {
 
     out_object.arg("--remap-path-scope=object");
     out_macro.arg("--remap-path-scope=macro");
+    out_doc.arg("--remap-path-scope=documentation").arg("-Zunstable-options");
     out_diagobj.arg("--remap-path-scope=diagnostics,object");
+    out_diagdocobj
+        .arg("--remap-path-scope=diagnostics,documentation,object")
+        .arg("-Zunstable-options");
     if is_darwin() {
         out_object.arg("-Csplit-debuginfo=off");
         out_macro.arg("-Csplit-debuginfo=off");
+        out_doc.arg("-Csplit-debuginfo=off");
         out_diagobj.arg("-Csplit-debuginfo=off");
+        out_diagdocobj.arg("-Csplit-debuginfo=off");
     }
 
     out_object.run();
@@ -53,8 +71,14 @@ fn main() {
     out_macro.run();
     rmeta_contains("/the/aux/lib.rs");
     rmeta_contains("auxiliary");
+    out_doc.run();
+    rmeta_contains("/the/aux/lib.rs");
+    rmeta_contains("auxiliary");
     out_diagobj.run();
     rmeta_contains("/the/aux/lib.rs");
+    rmeta_contains("auxiliary");
+    out_diagdocobj.run();
+    rmeta_contains("/the/aux/lib.rs");
     rmeta_not_contains("auxiliary");
 }
 
diff --git a/tests/ui/feature-gates/remap-path-scope-documentation.rs b/tests/ui/feature-gates/remap-path-scope-documentation.rs
new file mode 100644
index 000000000000..55610c037628
--- /dev/null
+++ b/tests/ui/feature-gates/remap-path-scope-documentation.rs
@@ -0,0 +1,7 @@
+// Test that documentation scope is unstable
+
+//@ compile-flags: --remap-path-scope=documentation
+
+//~? ERROR remapping `documentation` path scope requested but `-Zunstable-options` not specified
+
+fn main() {}
diff --git a/tests/ui/feature-gates/remap-path-scope-documentation.stderr b/tests/ui/feature-gates/remap-path-scope-documentation.stderr
new file mode 100644
index 000000000000..03316ad86a54
--- /dev/null
+++ b/tests/ui/feature-gates/remap-path-scope-documentation.stderr
@@ -0,0 +1,2 @@
+error: remapping `documentation` path scope requested but `-Zunstable-options` not specified
+

From fbab2c0e92608c93bb353622023fbe955aca44c8 Mon Sep 17 00:00:00 2001
From: Urgau 
Date: Thu, 22 Jan 2026 22:43:27 +0100
Subject: [PATCH 186/583] Make rustdoc use the documentation remapping scope
 and cleanups

---
 src/librustdoc/clean/types.rs                   |  2 +-
 src/librustdoc/doctest.rs                       | 16 ++++++++++++----
 src/librustdoc/doctest/extracted.rs             |  3 ++-
 src/librustdoc/html/render/context.rs           |  8 ++++++--
 src/librustdoc/passes/calculate_doc_coverage.rs |  6 ++----
 5 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 393e56256f74..c145929534d9 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -152,7 +152,7 @@ impl ExternalCrate {
             FileName::Real(ref p) => {
                 match p
                     .local_path()
-                    .or(Some(p.path(RemapPathScopeComponents::MACRO)))
+                    .or(Some(p.path(RemapPathScopeComponents::DOCUMENTATION)))
                     .unwrap()
                     .parent()
                 {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 25868e24ce16..6cd7d2c628d5 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -957,8 +957,10 @@ impl ScrapedDocTest {
         if !item_path.is_empty() {
             item_path.push(' ');
         }
-        let name =
-            format!("{} - {item_path}(line {line})", filename.prefer_remapped_unconditionally());
+        let name = format!(
+            "{} - {item_path}(line {line})",
+            filename.display(RemapPathScopeComponents::DOCUMENTATION)
+        );
 
         Self { filename, line, langstr, text, name, span, global_crate_attrs }
     }
@@ -969,9 +971,12 @@ impl ScrapedDocTest {
     fn no_run(&self, opts: &RustdocOptions) -> bool {
         self.langstr.no_run || opts.no_run
     }
+
     fn path(&self) -> PathBuf {
         match &self.filename {
-            FileName::Real(name) => name.path(RemapPathScopeComponents::DIAGNOSTICS).to_path_buf(),
+            FileName::Real(name) => {
+                name.path(RemapPathScopeComponents::DOCUMENTATION).to_path_buf()
+            }
             _ => PathBuf::from(r"doctest.rs"),
         }
     }
@@ -1016,9 +1021,12 @@ impl CreateRunnableDocTests {
 
     fn add_test(&mut self, scraped_test: ScrapedDocTest, dcx: Option>) {
         // For example `module/file.rs` would become `module_file_rs`
+        //
+        // Note that we are kind-of extending the definition of the MACRO scope here, but
+        // after all `#[doc]` is kind-of a macro.
         let file = scraped_test
             .filename
-            .prefer_local_unconditionally()
+            .display(RemapPathScopeComponents::MACRO)
             .to_string_lossy()
             .chars()
             .map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs
index 3d046ec1835d..a3eb03f7c839 100644
--- a/src/librustdoc/doctest/extracted.rs
+++ b/src/librustdoc/doctest/extracted.rs
@@ -3,6 +3,7 @@
 //! This module contains the logic to extract doctests and output a JSON containing this
 //! information.
 
+use rustc_span::RemapPathScopeComponents;
 use rustc_span::edition::Edition;
 use serde::Serialize;
 
@@ -62,7 +63,7 @@ impl ExtractedDocTests {
             Some(&opts.crate_name),
         );
         self.doctests.push(ExtractedDocTest {
-            file: filename.prefer_remapped_unconditionally().to_string(),
+            file: filename.display(RemapPathScopeComponents::DOCUMENTATION).to_string(),
             line,
             doctest_attributes: langstr.into(),
             doctest_code: match wrapped {
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 4d9e352595a1..b0ea8776425f 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -367,7 +367,7 @@ impl<'tcx> Context<'tcx> {
         let file = match span.filename(self.sess()) {
             FileName::Real(ref path) => path
                 .local_path()
-                .unwrap_or(path.path(RemapPathScopeComponents::MACRO))
+                .unwrap_or(path.path(RemapPathScopeComponents::DOCUMENTATION))
                 .to_path_buf(),
             _ => return None,
         };
@@ -503,7 +503,11 @@ impl<'tcx> Context<'tcx> {
 
         let src_root = match krate.src(tcx) {
             FileName::Real(ref p) => {
-                match p.local_path().unwrap_or(p.path(RemapPathScopeComponents::MACRO)).parent() {
+                match p
+                    .local_path()
+                    .unwrap_or(p.path(RemapPathScopeComponents::DOCUMENTATION))
+                    .parent()
+                {
                     Some(p) => p.to_path_buf(),
                     None => PathBuf::new(),
                 }
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 2782baae5e20..77b3a2e9c9f2 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -124,7 +124,7 @@ impl CoverageCalculator<'_, '_> {
             &self
                 .items
                 .iter()
-                .map(|(k, v)| (k.prefer_local_unconditionally().to_string(), v))
+                .map(|(k, v)| (k.display(RemapPathScopeComponents::COVERAGE).to_string(), v))
                 .collect::>(),
         )
         .expect("failed to convert JSON data to string")
@@ -168,9 +168,7 @@ impl CoverageCalculator<'_, '_> {
             if let Some(percentage) = count.percentage() {
                 print_table_record(
                     &limit_filename_len(
-                        file.display(RemapPathScopeComponents::DIAGNOSTICS)
-                            .to_string_lossy()
-                            .into(),
+                        file.display(RemapPathScopeComponents::COVERAGE).to_string(),
                     ),
                     count,
                     percentage,

From 3d6a5b51581ddd3532603001064d76eaae749ee8 Mon Sep 17 00:00:00 2001
From: Urgau 
Date: Sat, 24 Jan 2026 12:43:23 +0100
Subject: [PATCH 187/583] Make sure the `documentation` scope doesn't imply
 other scopes

---
 tests/ui/errors/auxiliary/file-doc.rs         | 13 ++++++++++++
 tests/ui/errors/auxiliary/trait-doc.rs        |  4 ++++
 ...prefix-diagnostics.only-doc-in-deps.stderr | 20 +++++++++++++++++++
 .../errors/remap-path-prefix-diagnostics.rs   | 19 +++++++++++++-----
 ...prefix-diagnostics.with-doc-in-deps.stderr | 20 +++++++++++++++++++
 ...h-prefix-macro.only-doc-in-deps.run.stdout |  3 +++
 tests/ui/errors/remap-path-prefix-macro.rs    | 11 ++++++++--
 ...h-prefix-macro.with-doc-in-deps.run.stdout |  3 +++
 8 files changed, 86 insertions(+), 7 deletions(-)
 create mode 100644 tests/ui/errors/auxiliary/file-doc.rs
 create mode 100644 tests/ui/errors/auxiliary/trait-doc.rs
 create mode 100644 tests/ui/errors/remap-path-prefix-diagnostics.only-doc-in-deps.stderr
 create mode 100644 tests/ui/errors/remap-path-prefix-diagnostics.with-doc-in-deps.stderr
 create mode 100644 tests/ui/errors/remap-path-prefix-macro.only-doc-in-deps.run.stdout
 create mode 100644 tests/ui/errors/remap-path-prefix-macro.with-doc-in-deps.run.stdout

diff --git a/tests/ui/errors/auxiliary/file-doc.rs b/tests/ui/errors/auxiliary/file-doc.rs
new file mode 100644
index 000000000000..4e9733bd5a1c
--- /dev/null
+++ b/tests/ui/errors/auxiliary/file-doc.rs
@@ -0,0 +1,13 @@
+//@ compile-flags: --remap-path-prefix={{src-base}}=remapped
+//@ compile-flags: --remap-path-scope=documentation -Zunstable-options
+
+#[macro_export]
+macro_rules! my_file {
+    () => {
+        file!()
+    };
+}
+
+pub fn file() -> &'static str {
+    file!()
+}
diff --git a/tests/ui/errors/auxiliary/trait-doc.rs b/tests/ui/errors/auxiliary/trait-doc.rs
new file mode 100644
index 000000000000..451437de5f11
--- /dev/null
+++ b/tests/ui/errors/auxiliary/trait-doc.rs
@@ -0,0 +1,4 @@
+//@ compile-flags: --remap-path-prefix={{src-base}}=remapped
+//@ compile-flags: --remap-path-scope=documentation -Zunstable-options
+
+pub trait Trait: std::fmt::Display {}
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.only-doc-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.only-doc-in-deps.stderr
new file mode 100644
index 000000000000..e1b93a1042f5
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.only-doc-in-deps.stderr
@@ -0,0 +1,20 @@
+error[E0277]: `A` doesn't implement `std::fmt::Display`
+  --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
+   |
+LL | impl r#trait::Trait for A {}
+   |                         ^ unsatisfied trait bound
+   |
+help: the trait `std::fmt::Display` is not implemented for `A`
+  --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
+   |
+LL | struct A;
+   | ^^^^^^^^
+note: required by a bound in `Trait`
+  --> $DIR/auxiliary/trait-doc.rs:LL:COL
+   |
+LL | pub trait Trait: std::fmt::Display {}
+   |                  ^^^^^^^^^^^^^^^^^ required by this bound in `Trait`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.rs b/tests/ui/errors/remap-path-prefix-diagnostics.rs
index 54dbcfd64711..8976106ab347 100644
--- a/tests/ui/errors/remap-path-prefix-diagnostics.rs
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.rs
@@ -3,26 +3,30 @@
 // We test different combinations with/without remap in deps, with/without remap in this
 // crate but always in deps and always here but never in deps.
 
-//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps
-//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps
+//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps with-doc-in-deps
+//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps only-doc-in-deps
 //@ revisions: not-diag-in-deps
 
 //@[with-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
 //@[with-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
 //@[with-debuginfo-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
+//@[with-doc-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
 //@[not-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
 
 //@[with-diag-in-deps] compile-flags: --remap-path-scope=diagnostics
 //@[with-macro-in-deps] compile-flags: --remap-path-scope=macro
 //@[with-debuginfo-in-deps] compile-flags: --remap-path-scope=debuginfo
+//@[with-doc-in-deps] compile-flags: --remap-path-scope=documentation -Zunstable-options
 //@[not-diag-in-deps] compile-flags: --remap-path-scope=diagnostics
 
 //@[with-diag-in-deps] aux-build:trait-diag.rs
 //@[with-macro-in-deps] aux-build:trait-macro.rs
 //@[with-debuginfo-in-deps] aux-build:trait-debuginfo.rs
+//@[with-doc-in-deps] aux-build:trait-doc.rs
 //@[only-diag-in-deps] aux-build:trait-diag.rs
 //@[only-macro-in-deps] aux-build:trait-macro.rs
 //@[only-debuginfo-in-deps] aux-build:trait-debuginfo.rs
+//@[only-doc-in-deps] aux-build:trait-doc.rs
 //@[not-diag-in-deps] aux-build:trait.rs
 
 // The $SRC_DIR*.rs:LL:COL normalisation doesn't kick in automatically
@@ -39,6 +43,9 @@ extern crate trait_macro as r#trait;
 #[cfg(any(with_debuginfo_in_deps, only_debuginfo_in_deps))]
 extern crate trait_debuginfo as r#trait;
 
+#[cfg(any(with_doc_in_deps, only_doc_in_deps))]
+extern crate trait_doc as r#trait;
+
 #[cfg(not_diag_in_deps)]
 extern crate r#trait as r#trait;
 
@@ -47,9 +54,11 @@ struct A;
 impl r#trait::Trait for A {}
 //[with-macro-in-deps]~^ ERROR `A` doesn't implement `std::fmt::Display`
 //[with-debuginfo-in-deps]~^^ ERROR `A` doesn't implement `std::fmt::Display`
-//[only-diag-in-deps]~^^^ ERROR `A` doesn't implement `std::fmt::Display`
-//[only-macro-in-deps]~^^^^ ERROR `A` doesn't implement `std::fmt::Display`
-//[only-debuginfo-in-deps]~^^^^^ ERROR `A` doesn't implement `std::fmt::Display`
+//[with-doc-in-deps]~^^^ ERROR `A` doesn't implement `std::fmt::Display`
+//[only-diag-in-deps]~^^^^ ERROR `A` doesn't implement `std::fmt::Display`
+//[only-macro-in-deps]~^^^^^ ERROR `A` doesn't implement `std::fmt::Display`
+//[only-debuginfo-in-deps]~^^^^^^ ERROR `A` doesn't implement `std::fmt::Display`
+//[only-doc-in-deps]~^^^^^^^ ERROR `A` doesn't implement `std::fmt::Display`
 
 //[with-diag-in-deps]~? ERROR `A` doesn't implement `std::fmt::Display`
 //[not-diag-in-deps]~? ERROR `A` doesn't implement `std::fmt::Display`
diff --git a/tests/ui/errors/remap-path-prefix-diagnostics.with-doc-in-deps.stderr b/tests/ui/errors/remap-path-prefix-diagnostics.with-doc-in-deps.stderr
new file mode 100644
index 000000000000..e1b93a1042f5
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix-diagnostics.with-doc-in-deps.stderr
@@ -0,0 +1,20 @@
+error[E0277]: `A` doesn't implement `std::fmt::Display`
+  --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
+   |
+LL | impl r#trait::Trait for A {}
+   |                         ^ unsatisfied trait bound
+   |
+help: the trait `std::fmt::Display` is not implemented for `A`
+  --> $DIR/remap-path-prefix-diagnostics.rs:LL:COL
+   |
+LL | struct A;
+   | ^^^^^^^^
+note: required by a bound in `Trait`
+  --> $DIR/auxiliary/trait-doc.rs:LL:COL
+   |
+LL | pub trait Trait: std::fmt::Display {}
+   |                  ^^^^^^^^^^^^^^^^^ required by this bound in `Trait`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/errors/remap-path-prefix-macro.only-doc-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.only-doc-in-deps.run.stdout
new file mode 100644
index 000000000000..0026efdb7e58
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix-macro.only-doc-in-deps.run.stdout
@@ -0,0 +1,3 @@
+file::my_file!() = $DIR/remap-path-prefix-macro.rs
+file::file() = $DIR/auxiliary/file-doc.rs
+file!() = $DIR/remap-path-prefix-macro.rs
diff --git a/tests/ui/errors/remap-path-prefix-macro.rs b/tests/ui/errors/remap-path-prefix-macro.rs
index 1f895aeeb6b4..89e2379b575c 100644
--- a/tests/ui/errors/remap-path-prefix-macro.rs
+++ b/tests/ui/errors/remap-path-prefix-macro.rs
@@ -6,26 +6,30 @@
 //@ run-pass
 //@ check-run-results
 
-//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps
-//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps
+//@ revisions: with-diag-in-deps with-macro-in-deps with-debuginfo-in-deps with-doc-in-deps
+//@ revisions: only-diag-in-deps only-macro-in-deps only-debuginfo-in-deps only-doc-in-deps
 //@ revisions: not-macro-in-deps
 
 //@[with-diag-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
 //@[with-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
 //@[with-debuginfo-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
+//@[with-doc-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
 //@[not-macro-in-deps] compile-flags: --remap-path-prefix={{src-base}}=remapped
 
 //@[with-diag-in-deps] compile-flags: --remap-path-scope=diagnostics
 //@[with-macro-in-deps] compile-flags: --remap-path-scope=macro
 //@[with-debuginfo-in-deps] compile-flags: --remap-path-scope=debuginfo
+//@[with-doc-in-deps] compile-flags: --remap-path-scope=documentation -Zunstable-options
 //@[not-macro-in-deps] compile-flags: --remap-path-scope=macro
 
 //@[with-diag-in-deps] aux-build:file-diag.rs
 //@[with-macro-in-deps] aux-build:file-macro.rs
 //@[with-debuginfo-in-deps] aux-build:file-debuginfo.rs
+//@[with-doc-in-deps] aux-build:file-doc.rs
 //@[only-diag-in-deps] aux-build:file-diag.rs
 //@[only-macro-in-deps] aux-build:file-macro.rs
 //@[only-debuginfo-in-deps] aux-build:file-debuginfo.rs
+//@[only-doc-in-deps] aux-build:file-doc.rs
 //@[not-macro-in-deps] aux-build:file.rs
 
 #[cfg(any(with_diag_in_deps, only_diag_in_deps))]
@@ -37,6 +41,9 @@ extern crate file_macro as file;
 #[cfg(any(with_debuginfo_in_deps, only_debuginfo_in_deps))]
 extern crate file_debuginfo as file;
 
+#[cfg(any(with_doc_in_deps, only_doc_in_deps))]
+extern crate file_doc as file;
+
 #[cfg(not_macro_in_deps)]
 extern crate file;
 
diff --git a/tests/ui/errors/remap-path-prefix-macro.with-doc-in-deps.run.stdout b/tests/ui/errors/remap-path-prefix-macro.with-doc-in-deps.run.stdout
new file mode 100644
index 000000000000..0026efdb7e58
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix-macro.with-doc-in-deps.run.stdout
@@ -0,0 +1,3 @@
+file::my_file!() = $DIR/remap-path-prefix-macro.rs
+file::file() = $DIR/auxiliary/file-doc.rs
+file!() = $DIR/remap-path-prefix-macro.rs

From dd84a4fc0fa88f9cbd2d0cce804037a5492d23ad Mon Sep 17 00:00:00 2001
From: Urgau 
Date: Sat, 24 Jan 2026 13:59:48 +0100
Subject: [PATCH 188/583] Add documentation for `--remap-path-prefix` and `doc`
 scope in rustdoc

---
 src/doc/rustdoc/src/unstable-features.md | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index 08465ea541b8..ae007c4c13bb 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -751,6 +751,22 @@ pass `--doctest-build-arg ARG` for each argument `ARG`.
 
 This flag enables the generation of toggles to expand macros in the HTML source code pages.
 
+## `--remap-path-prefix`: Remap source code paths in output
+
+This flag is the equivalent flag from `rustc` `--remap-path-prefix`.
+
+it permits remapping source path prefixes in all output, including compiler diagnostics,
+debug information, macro expansions, etc. It takes a value of the form `FROM=TO`
+where a path prefix equal to `FROM` is rewritten to the value `TO`.
+
+### `documentation` scope
+
+`rustdoc` (and by extension `rustc`) have a special `documentation` remapping scope, it
+permits remapping source paths that ends up in the generated documentation.
+
+Currently the scope can only be specified from `rustc`, due to the lack of an equivalent
+`--remap-path-scope` flag in `rustc`.
+
 ## `#[doc(cfg)]` and `#[doc(auto_cfg)]`
 
 This feature aims at providing rustdoc users the possibility to add visual markers to the rendered documentation to know under which conditions an item is available (currently possible through the following unstable feature: `doc_cfg`).

From a72f68e80154a208b85a3b80cea744b84b7b5d18 Mon Sep 17 00:00:00 2001
From: Andreas Liljeqvist 
Date: Sat, 24 Jan 2026 20:05:01 +0100
Subject: [PATCH 189/583] Fix is_ascii performance on x86_64 with explicit SSE2
 intrinsics

Use explicit SSE2 intrinsics to avoid LLVM's broken AVX-512
auto-vectorization which generates ~31 kshiftrd instructions.

Performance
- AVX-512: 34-48x faster
- SSE2: 1.5-2x faster

Improves on earlier pr
---
 library/core/src/slice/ascii.rs       | 59 +++++++++------------------
 tests/assembly-llvm/slice-is-ascii.rs |  5 +++
 2 files changed, 25 insertions(+), 39 deletions(-)

diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs
index 459c826f4064..de89d77e5e2c 100644
--- a/library/core/src/slice/ascii.rs
+++ b/library/core/src/slice/ascii.rs
@@ -460,56 +460,37 @@ const fn is_ascii(s: &[u8]) -> bool {
     )
 }
 
-/// Chunk size for vectorized ASCII checking (two 16-byte SSE registers).
+/// Chunk size for SSE2 vectorized ASCII checking (4x 16-byte loads).
 #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
-const CHUNK_SIZE: usize = 32;
+const SSE2_CHUNK_SIZE: usize = 64;
 
-/// SSE2 implementation using `_mm_movemask_epi8` (compiles to `pmovmskb`) to
-/// avoid LLVM's broken AVX-512 auto-vectorization of counting loops.
-///
-/// FIXME(llvm#176906): Remove this workaround once LLVM generates efficient code.
 #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
 fn is_ascii_sse2(bytes: &[u8]) -> bool {
     use crate::arch::x86_64::{__m128i, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128};
 
-    let mut i = 0;
-
-    while i + CHUNK_SIZE <= bytes.len() {
-        // SAFETY: We have verified that `i + CHUNK_SIZE <= bytes.len()`.
-        let ptr = unsafe { bytes.as_ptr().add(i) };
-
-        // Load two 16-byte chunks and combine them.
-        // SAFETY: We verified `i + 32 <= len`, so ptr is valid for 32 bytes.
-        // `_mm_loadu_si128` allows unaligned loads.
-        let chunk1 = unsafe { _mm_loadu_si128(ptr as *const __m128i) };
-        // SAFETY: Same as above - ptr.add(16) is within the valid 32-byte range.
-        let chunk2 = unsafe { _mm_loadu_si128(ptr.add(16) as *const __m128i) };
-
-        // OR them together - if any byte has the high bit set, the result will too.
-        // SAFETY: SSE2 is guaranteed by the cfg predicate.
-        let combined = unsafe { _mm_or_si128(chunk1, chunk2) };
-
-        // Create a mask from the MSBs of each byte.
-        // If any byte is >= 128, its MSB is 1, so the mask will be non-zero.
-        // SAFETY: SSE2 is guaranteed by the cfg predicate.
-        let mask = unsafe { _mm_movemask_epi8(combined) };
+    let (chunks, rest) = bytes.as_chunks::();
 
+    for chunk in chunks {
+        let ptr = chunk.as_ptr();
+        // SAFETY: chunk is 64 bytes. SSE2 is baseline on x86_64.
+        let mask = unsafe {
+            let a1 = _mm_loadu_si128(ptr as *const __m128i);
+            let a2 = _mm_loadu_si128(ptr.add(16) as *const __m128i);
+            let b1 = _mm_loadu_si128(ptr.add(32) as *const __m128i);
+            let b2 = _mm_loadu_si128(ptr.add(48) as *const __m128i);
+            // OR all chunks - if any byte has high bit set, combined will too.
+            let combined = _mm_or_si128(_mm_or_si128(a1, a2), _mm_or_si128(b1, b2));
+            // Create a mask from the MSBs of each byte.
+            // If any byte is >= 128, its MSB is 1, so the mask will be non-zero.
+            _mm_movemask_epi8(combined)
+        };
         if mask != 0 {
             return false;
         }
-
-        i += CHUNK_SIZE;
     }
 
-    // Handle remaining bytes with simple loop
-    while i < bytes.len() {
-        if !bytes[i].is_ascii() {
-            return false;
-        }
-        i += 1;
-    }
-
-    true
+    // Handle remaining bytes
+    rest.iter().all(|b| b.is_ascii())
 }
 
 /// ASCII test optimized to use the `pmovmskb` instruction on `x86-64`.
@@ -529,7 +510,7 @@ const fn is_ascii(bytes: &[u8]) -> bool {
             is_ascii_simple(bytes)
         } else {
             // For small inputs, use usize-at-a-time processing to avoid SSE2 call overhead.
-            if bytes.len() < CHUNK_SIZE {
+            if bytes.len() < SSE2_CHUNK_SIZE {
                 let chunks = bytes.chunks_exact(USIZE_SIZE);
                 let remainder = chunks.remainder();
                 for chunk in chunks {
diff --git a/tests/assembly-llvm/slice-is-ascii.rs b/tests/assembly-llvm/slice-is-ascii.rs
index d01b321bf460..3b782ab2cd82 100644
--- a/tests/assembly-llvm/slice-is-ascii.rs
+++ b/tests/assembly-llvm/slice-is-ascii.rs
@@ -22,6 +22,11 @@
 // X86_64-LABEL: test_is_ascii
 // X86_64-NOT: kshiftrd
 // X86_64-NOT: kshiftrq
+// Verify explicit SSE2/AVX intrinsics are used:
+// - pmovmskb/vpmovmskb: efficient mask extraction from the MSBs
+// - vpor/por: OR-combining of 4x 16-byte loads (2x unrolled, 64-byte chunks)
+// X86_64: {{vpmovmskb|pmovmskb}}
+// X86_64: {{vpor|por}}
 
 // LA64-LABEL: test_is_ascii
 // LA64: vmskltz.b

From 74e70765ad397418294d357b97f6b87d8a13fe7f Mon Sep 17 00:00:00 2001
From: Trevor Gross 
Date: Sat, 24 Jan 2026 13:28:39 -0600
Subject: [PATCH 190/583] hint: Add a recommendation to benchmark with
 `cold_path`

Other hints have a note recommending benchmarks to ensure they actually
do what is intended. This is also applicable for `cold_path`, so add a
note here.
---
 library/core/src/hint.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index 4c050b49bf7e..bf8bbd12d82a 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -708,6 +708,10 @@ pub const fn unlikely(b: bool) -> bool {
 /// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may
 /// choose to optimize paths that are not cold at the expense of paths that are cold.
 ///
+/// Note that like all hints, the exact effect to codegen is not guaranteed. Using `cold_path`
+/// can actually *decrease* performance if the branch is called more than expected. It is advisable
+/// to perform benchmarks to tell if this function is useful.
+///
 /// # Examples
 ///
 /// ```

From 4b8fc13da047092b2684f09cd5d879721bf2c180 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Wed, 21 Jan 2026 13:16:33 +1100
Subject: [PATCH 191/583] Remove `Deref` from `QueryCtxt`

Explicitly writing `self.tcx` is easy enough, and lets us remove a bit of
non-essential deref magic.
---
 compiler/rustc_query_impl/src/plumbing.rs | 39 +++++++++++------------
 1 file changed, 18 insertions(+), 21 deletions(-)

diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 7479a992e297..de11cd82eb1d 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -35,6 +35,8 @@ use rustc_span::def_id::LOCAL_CRATE;
 
 use crate::QueryConfigRestored;
 
+/// Implements [`QueryContext`] for use by [`rustc_query_system`], since that
+/// crate does not have direct access to [`TyCtxt`].
 #[derive(Copy, Clone)]
 pub struct QueryCtxt<'tcx> {
     pub tcx: TyCtxt<'tcx>,
@@ -47,15 +49,6 @@ impl<'tcx> QueryCtxt<'tcx> {
     }
 }
 
-impl<'tcx> std::ops::Deref for QueryCtxt<'tcx> {
-    type Target = TyCtxt<'tcx>;
-
-    #[inline]
-    fn deref(&self) -> &Self::Target {
-        &self.tcx
-    }
-}
-
 impl<'tcx> HasDepContext for QueryCtxt<'tcx> {
     type Deps = rustc_middle::dep_graph::DepsType;
     type DepContext = TyCtxt<'tcx>;
@@ -69,14 +62,16 @@ impl<'tcx> HasDepContext for QueryCtxt<'tcx> {
 impl QueryContext for QueryCtxt<'_> {
     #[inline]
     fn jobserver_proxy(&self) -> &Proxy {
-        &*self.jobserver_proxy
+        &self.tcx.jobserver_proxy
     }
 
     #[inline]
     fn next_job_id(self) -> QueryJobId {
         QueryJobId(
-            NonZero::new(self.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
-                .unwrap(),
+            NonZero::new(
+                self.tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
+            )
+            .unwrap(),
         )
     }
 
@@ -113,7 +108,8 @@ impl QueryContext for QueryCtxt<'_> {
         self,
         prev_dep_node_index: SerializedDepNodeIndex,
     ) -> Option {
-        self.query_system
+        self.tcx
+            .query_system
             .on_disk_cache
             .as_ref()
             .and_then(|c| c.load_side_effect(self.tcx, prev_dep_node_index))
@@ -122,7 +118,7 @@ impl QueryContext for QueryCtxt<'_> {
     #[inline(never)]
     #[cold]
     fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect) {
-        if let Some(c) = self.query_system.on_disk_cache.as_ref() {
+        if let Some(c) = self.tcx.query_system.on_disk_cache.as_ref() {
             c.store_side_effect(dep_node_index, side_effect)
         }
     }
@@ -140,7 +136,9 @@ impl QueryContext for QueryCtxt<'_> {
         // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes
         // when accessing the `ImplicitCtxt`.
         tls::with_related_context(self.tcx, move |current_icx| {
-            if depth_limit && !self.recursion_limit().value_within_limit(current_icx.query_depth) {
+            if depth_limit
+                && !self.tcx.recursion_limit().value_within_limit(current_icx.query_depth)
+            {
                 self.depth_limit_error(token);
             }
 
@@ -161,16 +159,16 @@ impl QueryContext for QueryCtxt<'_> {
         let query_map = self.collect_active_jobs(true).expect("failed to collect active queries");
         let (info, depth) = job.find_dep_kind_root(query_map);
 
-        let suggested_limit = match self.recursion_limit() {
+        let suggested_limit = match self.tcx.recursion_limit() {
             Limit(0) => Limit(2),
             limit => limit * 2,
         };
 
-        self.sess.dcx().emit_fatal(QueryOverflow {
+        self.tcx.sess.dcx().emit_fatal(QueryOverflow {
             span: info.job.span,
             note: QueryOverflowNote { desc: info.query.description, depth },
             suggested_limit,
-            crate_name: self.crate_name(LOCAL_CRATE),
+            crate_name: self.tcx.crate_name(LOCAL_CRATE),
         });
     }
 }
@@ -367,7 +365,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>(
     Q: super::QueryConfigRestored<'tcx>,
     Q::RestoredValue: Encodable>,
 {
-    let _timer = qcx.profiler().generic_activity_with_arg("encode_query_results_for", query.name());
+    let _timer = qcx.tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name());
 
     assert!(query.query_state(qcx).all_inactive());
     let cache = query.query_cache(qcx);
@@ -389,8 +387,7 @@ pub(crate) fn query_key_hash_verify<'tcx>(
     query: impl QueryConfig>,
     qcx: QueryCtxt<'tcx>,
 ) {
-    let _timer =
-        qcx.profiler().generic_activity_with_arg("query_key_hash_verify_for", query.name());
+    let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name());
 
     let mut map = UnordMap::default();
 

From cb603ad5906baa50bf4e1410a7e6a13ba5da2051 Mon Sep 17 00:00:00 2001
From: Pavan Kumar Sunkara 
Date: Sun, 25 Jan 2026 06:37:15 +0530
Subject: [PATCH 192/583] internal: Use parser expect where possible

---
 .../crates/parser/src/grammar/attributes.rs            | 10 +++-------
 .../test_data/parser/err/0002_duplicate_shebang.rast   |  2 +-
 .../test_data/parser/err/0005_attribute_recover.rast   |  2 +-
 .../parser/err/0032_match_arms_inner_attrs.rast        |  6 +++---
 4 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
index ccb556b2ccac..54b5c8a275a8 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
@@ -24,15 +24,11 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
         p.bump(T![!]);
     }
 
-    if p.eat(T!['[']) {
+    if p.expect(T!['[']) {
         meta(p);
-
-        if !p.eat(T![']']) {
-            p.error("expected `]`");
-        }
-    } else {
-        p.error("expected `[`");
+        p.expect(T![']']);
     }
+
     attr.complete(p, ATTR);
 }
 
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast
index 7ee1ecfbb159..60cc690f7c98 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast
@@ -28,7 +28,7 @@ SOURCE_FILE
         NAME_REF
           IDENT "rusti"
   WHITESPACE "\n"
-error 23: expected `[`
+error 23: expected L_BRACK
 error 23: expected an item
 error 27: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
 error 28: expected SEMICOLON
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast
index 6ff072e207cd..77b4d06321d5 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast
@@ -58,5 +58,5 @@ SOURCE_FILE
           R_CURLY "}"
   WHITESPACE "\n"
 error 53: expected R_PAREN
-error 53: expected `]`
+error 53: expected R_BRACK
 error 53: expected an item
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
index 327bf94a49e6..b657e9834156 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
@@ -192,14 +192,14 @@ SOURCE_FILE
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
-error 52: expected `[`
+error 52: expected L_BRACK
 error 52: expected pattern
 error 53: expected FAT_ARROW
 error 78: expected `,`
-error 161: expected `[`
+error 161: expected L_BRACK
 error 161: expected pattern
 error 162: expected FAT_ARROW
-error 232: expected `[`
+error 232: expected L_BRACK
 error 232: expected pattern
 error 233: expected FAT_ARROW
 error 250: expected `,`

From 9e80b1ba7e751a8e16ce2ef1286278b3e66f728b Mon Sep 17 00:00:00 2001
From: zakie 
Date: Sun, 25 Jan 2026 17:24:08 +0900
Subject: [PATCH 193/583] Fix broken WASIp1 reference link

---
 src/doc/rustc/src/platform-support/wasm32-wasip1.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md
index 958a34a86928..eb74edda22de 100644
--- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md
+++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md
@@ -20,7 +20,7 @@ focused on the Component Model-based definition of WASI. At this point the
 `wasm32-wasip1` Rust target is intended for historical compatibility with
 [WASIp1] set of syscalls.
 
-[WASIp1]: https://github.com/WebAssembly/WASI/tree/main/legacy/preview1
+[WASIp1]: https://github.com/WebAssembly/WASI/tree/wasi-0.1/preview1
 [Component Model]: https://github.com/webassembly/component-model
 
 Today the `wasm32-wasip1` target will generate core WebAssembly modules

From cbcd8694c6e549c658901f010644fddcb7ffbce8 Mon Sep 17 00:00:00 2001
From: Andreas Liljeqvist 
Date: Sun, 25 Jan 2026 09:44:04 +0100
Subject: [PATCH 194/583] Remove x86_64 assembly test for is_ascii

The SSE2 helper function is not inlined across crate boundaries,
so we cannot verify the codegen in an assembly test. The fix is
still verified by the absence of performance regression.
---
 tests/assembly-llvm/slice-is-ascii.rs | 22 +---------------------
 1 file changed, 1 insertion(+), 21 deletions(-)

diff --git a/tests/assembly-llvm/slice-is-ascii.rs b/tests/assembly-llvm/slice-is-ascii.rs
index 3b782ab2cd82..00deb23e9a6c 100644
--- a/tests/assembly-llvm/slice-is-ascii.rs
+++ b/tests/assembly-llvm/slice-is-ascii.rs
@@ -1,32 +1,12 @@
-//@ revisions: X86_64 LA64
+//@ revisions: LA64
 //@ assembly-output: emit-asm
 //@ compile-flags: -C opt-level=3
 //
-//@ [X86_64] only-x86_64
-//@ [X86_64] compile-flags: -C target-cpu=znver4
-//@ [X86_64] compile-flags: -C llvm-args=-x86-asm-syntax=intel
-//
 //@ [LA64] only-loongarch64
 
 #![crate_type = "lib"]
 
-/// Verify `is_ascii` generates efficient code on different architectures:
-///
-/// - x86_64: Must NOT use `kshiftrd`/`kshiftrq` (broken AVX-512 auto-vectorization).
-///   The fix uses explicit SSE2 intrinsics (`pmovmskb`/`vpmovmskb`).
-///   See: https://github.com/llvm/llvm-project/issues/176906
-///
 /// - loongarch64: Should use `vmskltz.b` instruction for the fast-path.
-///   This architecture still relies on LLVM auto-vectorization.
-
-// X86_64-LABEL: test_is_ascii
-// X86_64-NOT: kshiftrd
-// X86_64-NOT: kshiftrq
-// Verify explicit SSE2/AVX intrinsics are used:
-// - pmovmskb/vpmovmskb: efficient mask extraction from the MSBs
-// - vpor/por: OR-combining of 4x 16-byte loads (2x unrolled, 64-byte chunks)
-// X86_64: {{vpmovmskb|pmovmskb}}
-// X86_64: {{vpor|por}}
 
 // LA64-LABEL: test_is_ascii
 // LA64: vmskltz.b

From 36fb9f08293b182ee213ed9d64d53da12387da4b Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Sun, 25 Jan 2026 16:12:13 +0100
Subject: [PATCH 195/583] Update `sysinfo` version to `0.38.0`

---
 Cargo.lock                    |  81 +++++++++++++++----
 src/bootstrap/Cargo.lock      | 142 +++++++++++++++++++++++++++-------
 src/bootstrap/Cargo.toml      |   2 +-
 src/tools/opt-dist/Cargo.toml |   2 +-
 4 files changed, 183 insertions(+), 44 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 1bc531ba916c..41cce3d54ddb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -857,7 +857,7 @@ dependencies = [
  "tracing-subscriber",
  "unified-diff",
  "walkdir",
- "windows",
+ "windows 0.61.3",
 ]
 
 [[package]]
@@ -2038,7 +2038,7 @@ dependencies = [
  "serde",
  "tempfile",
  "uuid",
- "windows",
+ "windows 0.61.3",
 ]
 
 [[package]]
@@ -3698,7 +3698,7 @@ dependencies = [
  "thorin-dwp",
  "tracing",
  "wasm-encoder 0.219.2",
- "windows",
+ "windows 0.61.3",
 ]
 
 [[package]]
@@ -3756,7 +3756,7 @@ dependencies = [
  "tempfile",
  "thin-vec",
  "tracing",
- "windows",
+ "windows 0.61.3",
 ]
 
 [[package]]
@@ -3822,7 +3822,7 @@ dependencies = [
  "serde_json",
  "shlex",
  "tracing",
- "windows",
+ "windows 0.61.3",
 ]
 
 [[package]]
@@ -3873,7 +3873,7 @@ dependencies = [
  "serde_json",
  "termize",
  "tracing",
- "windows",
+ "windows 0.61.3",
 ]
 
 [[package]]
@@ -4664,7 +4664,7 @@ dependencies = [
  "rustc_target",
  "termize",
  "tracing",
- "windows",
+ "windows 0.61.3",
 ]
 
 [[package]]
@@ -5438,14 +5438,14 @@ dependencies = [
 
 [[package]]
 name = "sysinfo"
-version = "0.37.2"
+version = "0.38.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f"
+checksum = "fe840c5b1afe259a5657392a4dbb74473a14c8db999c3ec2f4ae812e028a94da"
 dependencies = [
  "libc",
  "objc2-core-foundation",
  "objc2-io-kit",
- "windows",
+ "windows 0.62.2",
 ]
 
 [[package]]
@@ -6398,11 +6398,23 @@ version = "0.61.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
 dependencies = [
- "windows-collections",
+ "windows-collections 0.2.0",
  "windows-core 0.61.2",
- "windows-future",
+ "windows-future 0.2.1",
  "windows-link 0.1.3",
- "windows-numerics",
+ "windows-numerics 0.2.0",
+]
+
+[[package]]
+name = "windows"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
+dependencies = [
+ "windows-collections 0.3.2",
+ "windows-core 0.62.2",
+ "windows-future 0.3.2",
+ "windows-numerics 0.3.1",
 ]
 
 [[package]]
@@ -6413,7 +6425,7 @@ checksum = "9b4e97b01190d32f268a2dfbd3f006f77840633746707fbe40bcee588108a231"
 dependencies = [
  "serde",
  "serde_json",
- "windows-threading",
+ "windows-threading 0.1.0",
 ]
 
 [[package]]
@@ -6425,6 +6437,15 @@ dependencies = [
  "windows-core 0.61.2",
 ]
 
+[[package]]
+name = "windows-collections"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
+dependencies = [
+ "windows-core 0.62.2",
+]
+
 [[package]]
 name = "windows-core"
 version = "0.61.2"
@@ -6459,7 +6480,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
 dependencies = [
  "windows-core 0.61.2",
  "windows-link 0.1.3",
- "windows-threading",
+ "windows-threading 0.1.0",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
+dependencies = [
+ "windows-core 0.62.2",
+ "windows-link 0.2.1",
+ "windows-threading 0.2.1",
 ]
 
 [[package]]
@@ -6506,6 +6538,16 @@ dependencies = [
  "windows-link 0.1.3",
 ]
 
+[[package]]
+name = "windows-numerics"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
+dependencies = [
+ "windows-core 0.62.2",
+ "windows-link 0.2.1",
+]
+
 [[package]]
 name = "windows-result"
 version = "0.3.4"
@@ -6611,6 +6653,15 @@ dependencies = [
  "windows-link 0.1.3",
 ]
 
+[[package]]
+name = "windows-threading"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.52.6"
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index 884f67e91e68..960f003992fb 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -70,7 +70,7 @@ dependencies = [
  "tracing-chrome",
  "tracing-subscriber",
  "walkdir",
- "windows",
+ "windows 0.61.1",
  "xz2",
 ]
 
@@ -472,18 +472,18 @@ dependencies = [
 
 [[package]]
 name = "objc2-core-foundation"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
+checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "objc2-io-kit"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a"
+checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
 dependencies = [
  "libc",
  "objc2-core-foundation",
@@ -743,16 +743,16 @@ dependencies = [
 
 [[package]]
 name = "sysinfo"
-version = "0.37.0"
+version = "0.38.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753"
+checksum = "fe840c5b1afe259a5657392a4dbb74473a14c8db999c3ec2f4ae812e028a94da"
 dependencies = [
  "libc",
  "memchr",
  "ntapi",
  "objc2-core-foundation",
  "objc2-io-kit",
- "windows",
+ "windows 0.62.2",
 ]
 
 [[package]]
@@ -958,11 +958,23 @@ version = "0.61.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
 dependencies = [
- "windows-collections",
- "windows-core",
- "windows-future",
- "windows-link",
- "windows-numerics",
+ "windows-collections 0.2.0",
+ "windows-core 0.61.0",
+ "windows-future 0.2.0",
+ "windows-link 0.1.1",
+ "windows-numerics 0.2.0",
+]
+
+[[package]]
+name = "windows"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
+dependencies = [
+ "windows-collections 0.3.2",
+ "windows-core 0.62.2",
+ "windows-future 0.3.2",
+ "windows-numerics 0.3.1",
 ]
 
 [[package]]
@@ -971,7 +983,16 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
 dependencies = [
- "windows-core",
+ "windows-core 0.61.0",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
+dependencies = [
+ "windows-core 0.62.2",
 ]
 
 [[package]]
@@ -982,9 +1003,22 @@ checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
 dependencies = [
  "windows-implement",
  "windows-interface",
- "windows-link",
- "windows-result",
- "windows-strings",
+ "windows-link 0.1.1",
+ "windows-result 0.3.2",
+ "windows-strings 0.4.0",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link 0.2.1",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
 ]
 
 [[package]]
@@ -993,15 +1027,26 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
 dependencies = [
- "windows-core",
- "windows-link",
+ "windows-core 0.61.0",
+ "windows-link 0.1.1",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
+dependencies = [
+ "windows-core 0.62.2",
+ "windows-link 0.2.1",
+ "windows-threading",
 ]
 
 [[package]]
 name = "windows-implement"
-version = "0.60.0"
+version = "0.60.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1010,9 +1055,9 @@ dependencies = [
 
 [[package]]
 name = "windows-interface"
-version = "0.59.1"
+version = "0.59.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1025,14 +1070,30 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
 
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
 [[package]]
 name = "windows-numerics"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
 dependencies = [
- "windows-core",
- "windows-link",
+ "windows-core 0.61.0",
+ "windows-link 0.1.1",
+]
+
+[[package]]
+name = "windows-numerics"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
+dependencies = [
+ "windows-core 0.62.2",
+ "windows-link 0.2.1",
 ]
 
 [[package]]
@@ -1041,7 +1102,16 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
 dependencies = [
- "windows-link",
+ "windows-link 0.1.1",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link 0.2.1",
 ]
 
 [[package]]
@@ -1050,7 +1120,16 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
 dependencies = [
- "windows-link",
+ "windows-link 0.1.1",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link 0.2.1",
 ]
 
 [[package]]
@@ -1103,6 +1182,15 @@ dependencies = [
  "windows_x86_64_msvc 0.53.0",
 ]
 
+[[package]]
+name = "windows-threading"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.52.6"
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index e66c0576e9ee..008e0d37642b 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -57,7 +57,7 @@ walkdir = "2.4"
 xz2 = "0.1"
 
 # Dependencies needed by the build-metrics feature
-sysinfo = { version = "0.37.0", default-features = false, optional = true, features = ["system"] }
+sysinfo = { version = "0.38.0", default-features = false, optional = true, features = ["system"] }
 
 # Dependencies needed by the `tracing` feature
 chrono = { version = "0.4", default-features = false, optional = true, features = ["now", "std"] }
diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml
index 66c19183f343..9ca1729e2e8b 100644
--- a/src/tools/opt-dist/Cargo.toml
+++ b/src/tools/opt-dist/Cargo.toml
@@ -10,7 +10,7 @@ log = "0.4"
 anyhow = "1"
 humantime = "2"
 humansize = "2"
-sysinfo = { version = "0.37.0", default-features = false, features = ["disk"] }
+sysinfo = { version = "0.38.0", default-features = false, features = ["disk"] }
 fs_extra = "1"
 camino = "1"
 tar = "0.4"

From 163a4b4caf059f662f0eec6d49ec153939560a40 Mon Sep 17 00:00:00 2001
From: Marijn Schouten 
Date: Mon, 6 Oct 2025 12:19:50 +0000
Subject: [PATCH 196/583] From: cleanup

---
 library/core/src/convert/num.rs | 87 ++++++++++++++-------------------
 1 file changed, 38 insertions(+), 49 deletions(-)

diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs
index c69026c5c9f3..75c94af30000 100644
--- a/library/core/src/convert/num.rs
+++ b/library/core/src/convert/num.rs
@@ -39,62 +39,51 @@ impl_float_to_int!(f32 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i12
 impl_float_to_int!(f64 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
 impl_float_to_int!(f128 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
 
-// Conversion traits for primitive integer and float types
-// Conversions T -> T are covered by a blanket impl and therefore excluded
-// Some conversions from and to usize/isize are not implemented due to portability concerns
-macro_rules! impl_from {
-    (bool => $Int:ty $(,)?) => {
-        impl_from!(
-            bool => $Int,
-            #[stable(feature = "from_bool", since = "1.28.0")],
-            concat!(
-                "Converts a [`bool`] to [`", stringify!($Int), "`] losslessly.\n",
-                "The resulting value is `0` for `false` and `1` for `true` values.\n",
-                "\n",
-                "# Examples\n",
-                "\n",
-                "```\n",
-                "assert_eq!(", stringify!($Int), "::from(true), 1);\n",
-                "assert_eq!(", stringify!($Int), "::from(false), 0);\n",
-                "```\n",
-            ),
-        );
-    };
-    ($Small:ty => $Large:ty, #[$attr:meta] $(,)?) => {
-        impl_from!(
-            $Small => $Large,
-            #[$attr],
-            concat!("Converts [`", stringify!($Small), "`] to [`", stringify!($Large), "`] losslessly."),
-        );
-    };
-    ($Small:ty => $Large:ty, #[$attr:meta], $doc:expr $(,)?) => {
-        #[$attr]
+/// Implement `From` for integers
+macro_rules! impl_from_bool {
+    ($($int:ty)*) => {$(
+        #[stable(feature = "from_bool", since = "1.28.0")]
         #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
-        impl const From<$Small> for $Large {
-            // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
-            // Rustdocs on functions do not.
-            #[doc = $doc]
+        impl const From for $int {
+            /// Converts from [`bool`] to
+            #[doc = concat!("[`", stringify!($int), "`]")]
+            /// , by turning `false` into `0` and `true` into `1`.
+            ///
+            /// # Examples
+            ///
+            /// ```
+            #[doc = concat!("assert_eq!(", stringify!($int), "::from(false), 0);")]
+            ///
+            #[doc = concat!("assert_eq!(", stringify!($int), "::from(true), 1);")]
+            /// ```
             #[inline(always)]
-            fn from(small: $Small) -> Self {
-                small as Self
+            fn from(b: bool) -> Self {
+                b as Self
             }
         }
-    };
+    )*}
 }
 
 // boolean -> integer
-impl_from!(bool => u8);
-impl_from!(bool => u16);
-impl_from!(bool => u32);
-impl_from!(bool => u64);
-impl_from!(bool => u128);
-impl_from!(bool => usize);
-impl_from!(bool => i8);
-impl_from!(bool => i16);
-impl_from!(bool => i32);
-impl_from!(bool => i64);
-impl_from!(bool => i128);
-impl_from!(bool => isize);
+impl_from_bool!(u8 u16 u32 u64 u128 usize);
+impl_from_bool!(i8 i16 i32 i64 i128 isize);
+
+/// Implement `From<$small>` for `$large`
+macro_rules! impl_from {
+    ($small:ty => $large:ty, #[$attr:meta]) => {
+        #[$attr]
+        #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
+        impl const From<$small> for $large {
+            #[doc = concat!("Converts from [`", stringify!($small), "`] to [`", stringify!($large), "`] losslessly.")]
+            #[inline(always)]
+            fn from(small: $small) -> Self {
+                debug_assert!(<$large>::MIN as i128 <= <$small>::MIN as i128);
+                debug_assert!(<$small>::MAX as u128 <= <$large>::MAX as u128);
+                small as Self
+            }
+        }
+    }
+}
 
 // unsigned integer -> unsigned integer
 impl_from!(u8 => u16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);

From f12288ec263233265ea19b5e8147f4b87837d514 Mon Sep 17 00:00:00 2001
From: Marijn Schouten 
Date: Mon, 6 Oct 2025 16:27:02 +0000
Subject: [PATCH 197/583] TryFrom for bool

---
 library/core/src/convert/num.rs               | 36 +++++++++++++++++++
 tests/ui/try-trait/bad-interconversion.stderr |  2 +-
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs
index 75c94af30000..6e82e3356410 100644
--- a/library/core/src/convert/num.rs
+++ b/library/core/src/convert/num.rs
@@ -327,12 +327,48 @@ macro_rules! impl_try_from_both_bounded {
     )*}
 }
 
+/// Implement `TryFrom` for `bool`
+macro_rules! impl_try_from_integer_for_bool {
+    ($($int:ty)+) => {$(
+        #[stable(feature = "try_from", since = "1.34.0")]
+        #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
+        impl const TryFrom<$int> for bool {
+            type Error = TryFromIntError;
+
+            /// Tries to create a bool from an integer type.
+            /// Returns an error if the integer is not 0 or 1.
+            ///
+            /// # Examples
+            ///
+            /// ```
+            #[doc = concat!("assert_eq!(0_", stringify!($int), ".try_into(), Ok(false));")]
+            ///
+            #[doc = concat!("assert_eq!(1_", stringify!($int), ".try_into(), Ok(true));")]
+            ///
+            #[doc = concat!("assert!(<", stringify!($int), " as TryInto>::try_into(2).is_err());")]
+            /// ```
+            #[inline]
+            fn try_from(i: $int) -> Result {
+                match i {
+                    0 => Ok(false),
+                    1 => Ok(true),
+                    _ => Err(TryFromIntError(())),
+                }
+            }
+        }
+    )*}
+}
+
 macro_rules! rev {
     ($mac:ident, $source:ty => $($target:ty),+) => {$(
         $mac!($target => $source);
     )*}
 }
 
+// integer -> bool
+impl_try_from_integer_for_bool!(u128 u64 u32 u16 u8);
+impl_try_from_integer_for_bool!(i128 i64 i32 i16 i8);
+
 // unsigned integer -> unsigned integer
 impl_try_from_upper_bounded!(u16 => u8);
 impl_try_from_upper_bounded!(u32 => u8, u16);
diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr
index f8c0deba99ba..a566800da53e 100644
--- a/tests/ui/try-trait/bad-interconversion.stderr
+++ b/tests/ui/try-trait/bad-interconversion.stderr
@@ -22,7 +22,7 @@ help: the following other types implement trait `From`
   ::: $SRC_DIR/core/src/ascii/ascii_char.rs:LL:COL
    |
    = note: in this macro invocation
-   = note: this error originates in the macro `impl_from` which comes from the expansion of the macro `into_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this error originates in the macro `impl_from_bool` which comes from the expansion of the macro `into_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result`
   --> $DIR/bad-interconversion.rs:9:12

From dbc870afec91308b2e6a6c6ba16e8f3bb085e338 Mon Sep 17 00:00:00 2001
From: Andreas Liljeqvist 
Date: Sun, 25 Jan 2026 20:03:32 +0100
Subject: [PATCH 198/583] Mark is_ascii_sse2 as #[inline]

---
 library/core/src/slice/ascii.rs       |  1 +
 tests/assembly-llvm/slice-is-ascii.rs | 17 ++++++++++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs
index de89d77e5e2c..ae641871279b 100644
--- a/library/core/src/slice/ascii.rs
+++ b/library/core/src/slice/ascii.rs
@@ -465,6 +465,7 @@ const fn is_ascii(s: &[u8]) -> bool {
 const SSE2_CHUNK_SIZE: usize = 64;
 
 #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))]
+#[inline]
 fn is_ascii_sse2(bytes: &[u8]) -> bool {
     use crate::arch::x86_64::{__m128i, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128};
 
diff --git a/tests/assembly-llvm/slice-is-ascii.rs b/tests/assembly-llvm/slice-is-ascii.rs
index 00deb23e9a6c..b9a520505498 100644
--- a/tests/assembly-llvm/slice-is-ascii.rs
+++ b/tests/assembly-llvm/slice-is-ascii.rs
@@ -1,13 +1,28 @@
-//@ revisions: LA64
+//@ revisions: X86_64 LA64
 //@ assembly-output: emit-asm
 //@ compile-flags: -C opt-level=3
 //
+//@ [X86_64] only-x86_64
+//@ [X86_64] compile-flags: -C target-cpu=znver4
+//@ [X86_64] compile-flags: -C llvm-args=-x86-asm-syntax=intel
+//
 //@ [LA64] only-loongarch64
 
 #![crate_type = "lib"]
 
+/// Verify `is_ascii` generates efficient code on different architectures:
+///
+/// - x86_64: Must NOT use `kshiftrd`/`kshiftrq` (broken AVX-512 auto-vectorization).
+///   Good version uses explicit SSE2 intrinsics (`pmovmskb`/`vpmovmskb`).
+///
 /// - loongarch64: Should use `vmskltz.b` instruction for the fast-path.
 
+// X86_64-LABEL: test_is_ascii
+// X86_64-NOT: kshiftrd
+// X86_64-NOT: kshiftrq
+// X86_64: {{vpor|por}}
+// X86_64: {{vpmovmskb|pmovmskb}}
+
 // LA64-LABEL: test_is_ascii
 // LA64: vmskltz.b
 

From 46483e6a4672188b8434c0c7e5f144552505229d Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Sun, 25 Jan 2026 16:45:46 -0500
Subject: [PATCH 199/583] Add a new subsection for contributors

---
 src/doc/rustc-dev-guide/src/SUMMARY.md        |  1 +
 .../src/offload/contributing.md               | 32 +++++++++++++++++++
 2 files changed, 33 insertions(+)
 create mode 100644 src/doc/rustc-dev-guide/src/offload/contributing.md

diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index daaaef42d909..3f863ebf4c6d 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -106,6 +106,7 @@
 - [GPU offload internals](./offload/internals.md)
     - [Installation](./offload/installation.md)
     - [Usage](./offload/usage.md)
+    - [Contributing](./offload/contributing.md)
 - [Autodiff internals](./autodiff/internals.md)
     - [Installation](./autodiff/installation.md)
     - [How to debug](./autodiff/debugging.md)
diff --git a/src/doc/rustc-dev-guide/src/offload/contributing.md b/src/doc/rustc-dev-guide/src/offload/contributing.md
new file mode 100644
index 000000000000..f3a1ed2150a1
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/offload/contributing.md
@@ -0,0 +1,32 @@
+# Contributing
+
+Contributions are always welcome. This project is experimental, so the documentation and code are likely incomplete. Please ask on [Zulip](https://rust-lang.zulipchat.com/#narrow/channel/422870-t-compiler.2Fgpgpu-backend) (preferred) or the Rust Community Discord for help if you get stuck or if our documentation is unclear.
+
+We generally try to automate as much of the compilation process as possible for users. However, as a contributor it might sometimes be easier to directly rewrite and compile the LLVM-IR modules (.ll) to quickly iterate on changes, without needing to repeatedly recompile rustc. For people familiar with LLVM we therefore have the shell script below. Only when you are then happy with the IR changes you can work on updating rustc to generate the new, desired output.
+
+```sh
+set -e
+# set -e to avoid continuing on errors, which would likely use stale artifacts
+# inputs:
+# lib.ll (host code) + host.out (device)
+
+# You only need to run the first three commands once to generate lib.ll and host.out from your rust code.
+
+# RUSTFLAGS="-Ctarget-cpu=gfx90a --emit=llvm-bc,llvm-ir -Zoffload=Device -Csave-temps -Zunstable-options" cargo +offload build -Zunstable-options -v --target amdgcn-amd-amdhsa -Zbuild-std=core -r
+#
+# RUSTFLAGS="--emit=llvm-bc,llvm-ir -Csave-temps -Zoffload=Host=/absolute/path/to/project/target/amdgcn-amd-amdhsa/release/deps/host.out -Zunstable-options" cargo +offload build -r
+#
+# cp target/release/deps/.ll lib.ll
+
+opt lib.ll -o lib.bc
+
+"clang-21" "-cc1" "-triple" "x86_64-unknown-linux-gnu" "-S" "-save-temps=cwd" "-disable-free" "-clear-ast-before-backend" "-main-file-name" "lib.rs" "-mrelocation-model" "pic" "-pic-level" "2" "-pic-is-pie" "-mframe-pointer=all" "-fmath-errno" "-ffp-contract=on" "-fno-rounding-math" "-mconstructor-aliases" "-funwind-tables=2" "-target-cpu" "x86-64" "-tune-cpu" "generic" "-resource-dir" "//rust/build/x86_64-unknown-linux-gnu/llvm/lib/clang/21" "-ferror-limit" "19" "-fopenmp" "-fopenmp-offload-mandatory" "-fgnuc-version=4.2.1" "-fskip-odr-check-in-gmf" "-fembed-offload-object=host.out" "-fopenmp-targets=amdgcn-amd-amdhsa" "-faddrsig" "-D__GCC_HAVE_DWARF2_CFI_ASM=1" "-o" "host.s" "-x" "ir" "lib.bc"
+
+"clang-21" "-cc1as" "-triple" "x86_64-unknown-linux-gnu" "-filetype" "obj" "-main-file-name" "lib.rs" "-target-cpu" "x86-64" "-mrelocation-model" "pic" "-o" "host.o" "host.s"
+
+"//rust/build/x86_64-unknown-linux-gnu/llvm/bin/clang-linker-wrapper" "--should-extract=gfx90a" "--device-compiler=amdgcn-amd-amdhsa=-g" "--device-compiler=amdgcn-amd-amdhsa=-save-temps=cwd" "--device-linker=amdgcn-amd-amdhsa=-lompdevice" "--host-triple=x86_64-unknown-linux-gnu" "--save-temps" "--linker-path=//rust/build/x86_64-unknown-linux-gnu/lld/bin/ld.lld" "--hash-style=gnu" "--eh-frame-hdr" "-m" "elf_x86_64" "-pie" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "a.out" "/lib/../lib64/Scrt1.o" "/lib/../lib64/crti.o" "/opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/12/crtbeginS.o" "-L//rust/build/x86_64-unknown-linux-gnu/llvm/bin/../lib/x86_64-unknown-linux-gnu" "-L//rust/build/x86_64-unknown-linux-gnu/llvm/lib/clang/21/lib/x86_64-unknown-linux-gnu" "-L/opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/12" "-L/opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/12/../../../../lib64" "-L/lib/../lib64" "-L/usr/lib64" "-L/lib" "-L/usr/lib" "host.o" "-lstdc++" "-lm" "-lomp" "-lomptarget" "-L//rust/build/x86_64-unknown-linux-gnu/llvm/lib" "-lgcc_s" "-lgcc" "-lpthread" "-lc" "-lgcc_s" "-lgcc" "/opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/12/crtendS.o" "/lib/../lib64/crtn.o"
+
+LIBOMPTARGET_INFO=-1 OFFLOAD_TRACK_ALLOCATION_TRACES=true ./a.out
+```
+
+Please update the `` placeholders on the `clang-linker-wrapper` invocation. You will likely also need to adjust the library paths. See the linked usage section for details: [usage](usage.md#compile-instructions)

From 97b05786e8e99ea81eeac106021cf34edc8fd696 Mon Sep 17 00:00:00 2001
From: LorrensP-2158466 
Date: Fri, 31 Oct 2025 15:19:32 +0100
Subject: [PATCH 200/583] use CmResolver instead of &mut CmResolver

---
 compiler/rustc_resolve/src/ident.rs   |  6 +++---
 compiler/rustc_resolve/src/imports.rs |  2 +-
 compiler/rustc_resolve/src/lib.rs     | 10 +++++-----
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 79d08828ccc4..78c6cf39b630 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -58,7 +58,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         orig_ctxt: Span,
         derive_fallback_lint_id: Option,
         mut visitor: impl FnMut(
-            &mut CmResolver<'r, 'ra, 'tcx>,
+            CmResolver<'_, 'ra, 'tcx>,
             Scope<'ra>,
             UsePrelude,
             Span,
@@ -165,7 +165,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             if visit {
                 let use_prelude = if use_prelude { UsePrelude::Yes } else { UsePrelude::No };
                 if let ControlFlow::Break(break_result) =
-                    visitor(&mut self, scope, use_prelude, ctxt)
+                    visitor(self.reborrow(), scope, use_prelude, ctxt)
                 {
                     return Some(break_result);
                 }
@@ -438,7 +438,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             parent_scope,
             orig_ident.span,
             derive_fallback_lint_id,
-            |this, scope, use_prelude, ctxt| {
+            |mut this, scope, use_prelude, ctxt| {
                 let ident = Ident::new(orig_ident.name, ctxt);
                 // The passed `ctxt` is already normalized, so avoid expensive double normalization.
                 let ident = Macros20NormalizedIdent(ident);
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 016fc407daab..f5933afd0134 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -893,7 +893,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         };
 
         let mut indeterminate_count = 0;
-        self.per_ns_cm(|this, ns| {
+        self.per_ns_cm(|mut this, ns| {
             if !type_ns_only || ns == TypeNS {
                 if bindings[ns].get() != PendingDecl::Pending {
                     return;
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 52b016b6bdca..c60b7aa50313 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1831,13 +1831,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         f(self, MacroNS);
     }
 
-    fn per_ns_cm<'r, F: FnMut(&mut CmResolver<'r, 'ra, 'tcx>, Namespace)>(
+    fn per_ns_cm<'r, F: FnMut(CmResolver<'_, 'ra, 'tcx>, Namespace)>(
         mut self: CmResolver<'r, 'ra, 'tcx>,
         mut f: F,
     ) {
-        f(&mut self, TypeNS);
-        f(&mut self, ValueNS);
-        f(&mut self, MacroNS);
+        f(self.reborrow(), TypeNS);
+        f(self.reborrow(), ValueNS);
+        f(self, MacroNS);
     }
 
     fn is_builtin_macro(&self, res: Res) -> bool {
@@ -1902,7 +1902,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
 
         let scope_set = ScopeSet::All(TypeNS);
-        self.cm().visit_scopes(scope_set, parent_scope, ctxt, None, |this, scope, _, _| {
+        self.cm().visit_scopes(scope_set, parent_scope, ctxt, None, |mut this, scope, _, _| {
             match scope {
                 Scope::ModuleNonGlobs(module, _) => {
                     this.get_mut().traits_in_module(module, assoc_item, &mut found_traits);

From 0b28b1320209cf2c7254a5d4f6de77fd84bbf4b2 Mon Sep 17 00:00:00 2001
From: Jonathan Brouwer 
Date: Sun, 25 Jan 2026 22:57:18 +0100
Subject: [PATCH 201/583] Add info about unused_attributes

---
 src/doc/rustc-dev-guide/src/tests/ui.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md
index 8e3c4c133a3c..b7cf446c4a57 100644
--- a/src/doc/rustc-dev-guide/src/tests/ui.md
+++ b/src/doc/rustc-dev-guide/src/tests/ui.md
@@ -636,6 +636,7 @@ By default, test suites under UI test mode (`tests/ui`, `tests/ui-fulldeps`,
 but not `tests/rustdoc-ui`) will specify
 
 - `-A unused`
+- `-W unused_attributes` (since these tend to be interesting for ui tests)
 - `-A internal_features`
 
 If:

From 54fc546f203ac11b4a7b41171b4359844d0cd706 Mon Sep 17 00:00:00 2001
From: Usman Akinyemi 
Date: Sun, 18 Jan 2026 13:35:19 +0530
Subject: [PATCH 202/583] Recover from struct literals with placeholder or
 empty path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Based on earlier work by León Orell Valerian Liehr.

Co-authored-by: León Orell Valerian Liehr 
Signed-off-by: Usman Akinyemi 
---
 compiler/rustc_parse/messages.ftl             | 10 ++++
 compiler/rustc_parse/src/errors.rs            | 19 ++++++++
 compiler/rustc_parse/src/parser/expr.rs       | 45 +++++++++++++++++
 tests/ui/parser/bare-struct-body.stderr       | 10 ++--
 .../struct-lit-placeholder-or-empty-path.rs   | 14 ++++++
 ...truct-lit-placeholder-or-empty-path.stderr | 48 +++++++++++++++++++
 .../struct-lit-placeholder-path.rs            | 21 ++++++++
 .../struct-lit-placeholder-path.stderr        | 26 ++++++++++
 8 files changed, 187 insertions(+), 6 deletions(-)
 create mode 100644 tests/ui/parser/struct-lit-placeholder-or-empty-path.rs
 create mode 100644 tests/ui/parser/struct-lit-placeholder-or-empty-path.stderr
 create mode 100644 tests/ui/suggestions/struct-lit-placeholder-path.rs
 create mode 100644 tests/ui/suggestions/struct-lit-placeholder-path.stderr

diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 1331d99c01ea..449d0b964fd4 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -822,9 +822,19 @@ parse_struct_literal_body_without_path =
     struct literal body without path
     .suggestion = you might have forgotten to add the struct literal inside the block
 
+parse_struct_literal_body_without_path_late =
+    struct literal body without path
+    .label = struct name missing for struct literal
+    .suggestion = add the correct type
+
 parse_struct_literal_not_allowed_here = struct literals are not allowed here
     .suggestion = surround the struct literal with parentheses
 
+parse_struct_literal_placeholder_path =
+    placeholder `_` is not allowed for the path in struct literals
+    .label = not allowed in struct literals
+    .suggestion = replace it with the correct type
+
 parse_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
     .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 60e4a240c85e..42327c7e343d 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -3684,3 +3684,22 @@ pub(crate) struct ImplReuseInherentImpl {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(parse_struct_literal_placeholder_path)]
+pub(crate) struct StructLiteralPlaceholderPath {
+    #[primary_span]
+    #[label]
+    #[suggestion(applicability = "has-placeholders", code = "/* Type */", style = "verbose")]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_struct_literal_body_without_path_late)]
+pub(crate) struct StructLiteralWithoutPathLate {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[suggestion(applicability = "has-placeholders", code = "/* Type */ ", style = "verbose")]
+    pub suggestion_span: Span,
+}
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 8835fba1adcd..c31a4798b471 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1468,6 +1468,9 @@ impl<'a> Parser<'a> {
             } else if this.check(exp!(OpenParen)) {
                 this.parse_expr_tuple_parens(restrictions)
             } else if this.check(exp!(OpenBrace)) {
+                if let Some(expr) = this.maybe_recover_bad_struct_literal_path(false)? {
+                    return Ok(expr);
+                }
                 this.parse_expr_block(None, lo, BlockCheckMode::Default)
             } else if this.check(exp!(Or)) || this.check(exp!(OrOr)) {
                 this.parse_expr_closure().map_err(|mut err| {
@@ -1542,6 +1545,9 @@ impl<'a> Parser<'a> {
             } else if this.check_keyword(exp!(Let)) {
                 this.parse_expr_let(restrictions)
             } else if this.eat_keyword(exp!(Underscore)) {
+                if let Some(expr) = this.maybe_recover_bad_struct_literal_path(true)? {
+                    return Ok(expr);
+                }
                 Ok(this.mk_expr(this.prev_token.span, ExprKind::Underscore))
             } else if this.token_uninterpolated_span().at_least_rust_2018() {
                 // `Span::at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
@@ -3698,6 +3704,45 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn maybe_recover_bad_struct_literal_path(
+        &mut self,
+        is_underscore_entry_point: bool,
+    ) -> PResult<'a, Option>> {
+        if self.may_recover()
+            && self.check_noexpect(&token::OpenBrace)
+            && (!self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
+                && self.is_likely_struct_lit())
+        {
+            let span = if is_underscore_entry_point {
+                self.prev_token.span
+            } else {
+                self.token.span.shrink_to_lo()
+            };
+
+            self.bump(); // {
+            let expr = self.parse_expr_struct(
+                None,
+                Path::from_ident(Ident::new(kw::Underscore, span)),
+                false,
+            )?;
+
+            let guar = if is_underscore_entry_point {
+                self.dcx().create_err(errors::StructLiteralPlaceholderPath { span }).emit()
+            } else {
+                self.dcx()
+                    .create_err(errors::StructLiteralWithoutPathLate {
+                        span: expr.span,
+                        suggestion_span: expr.span.shrink_to_lo(),
+                    })
+                    .emit()
+            };
+
+            Ok(Some(self.mk_expr_err(expr.span, guar)))
+        } else {
+            Ok(None)
+        }
+    }
+
     pub(super) fn parse_struct_fields(
         &mut self,
         pth: ast::Path,
diff --git a/tests/ui/parser/bare-struct-body.stderr b/tests/ui/parser/bare-struct-body.stderr
index 7d17ea59647e..1b817e2b9ee2 100644
--- a/tests/ui/parser/bare-struct-body.stderr
+++ b/tests/ui/parser/bare-struct-body.stderr
@@ -21,14 +21,12 @@ LL |       let x = {
    |  _____________^
 LL | |         val: (),
 LL | |     };
-   | |_____^
+   | |_____^ struct name missing for struct literal
    |
-help: you might have forgotten to add the struct literal inside the block
-   |
-LL ~     let x = { SomeStruct {
-LL |         val: (),
-LL ~     } };
+help: add the correct type
    |
+LL |     let x = /* Type */ {
+   |             ++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/bare-struct-body.rs:11:14
diff --git a/tests/ui/parser/struct-lit-placeholder-or-empty-path.rs b/tests/ui/parser/struct-lit-placeholder-or-empty-path.rs
new file mode 100644
index 000000000000..8f91eb68b2eb
--- /dev/null
+++ b/tests/ui/parser/struct-lit-placeholder-or-empty-path.rs
@@ -0,0 +1,14 @@
+fn main() {
+    let _ = {foo: (), bar: {} }; //~ ERROR struct literal body without path
+    //~| NOTE struct name missing for struct literal
+    //~| HELP add the correct type
+    let _ = _ {foo: (), bar: {} }; //~ ERROR placeholder `_` is not allowed for the path in struct literals
+    //~| NOTE not allowed in struct literals
+    //~| HELP replace it with the correct type
+    let _ = {foo: ()}; //~ ERROR struct literal body without path
+    //~| NOTE struct name missing for struct literal
+    //~| HELP add the correct type
+    let _ = _ {foo: ()}; //~ ERROR placeholder `_` is not allowed for the path in struct literals
+    //~| NOTE not allowed in struct literals
+    //~| HELP replace it with the correct type
+}
diff --git a/tests/ui/parser/struct-lit-placeholder-or-empty-path.stderr b/tests/ui/parser/struct-lit-placeholder-or-empty-path.stderr
new file mode 100644
index 000000000000..62a417aefc1e
--- /dev/null
+++ b/tests/ui/parser/struct-lit-placeholder-or-empty-path.stderr
@@ -0,0 +1,48 @@
+error: struct literal body without path
+  --> $DIR/struct-lit-placeholder-or-empty-path.rs:2:13
+   |
+LL |     let _ = {foo: (), bar: {} };
+   |             ^^^^^^^^^^^^^^^^^^^ struct name missing for struct literal
+   |
+help: add the correct type
+   |
+LL |     let _ = /* Type */ {foo: (), bar: {} };
+   |             ++++++++++
+
+error: placeholder `_` is not allowed for the path in struct literals
+  --> $DIR/struct-lit-placeholder-or-empty-path.rs:5:13
+   |
+LL |     let _ = _ {foo: (), bar: {} };
+   |             ^ not allowed in struct literals
+   |
+help: replace it with the correct type
+   |
+LL -     let _ = _ {foo: (), bar: {} };
+LL +     let _ = /* Type */ {foo: (), bar: {} };
+   |
+
+error: struct literal body without path
+  --> $DIR/struct-lit-placeholder-or-empty-path.rs:8:13
+   |
+LL |     let _ = {foo: ()};
+   |             ^^^^^^^^^ struct name missing for struct literal
+   |
+help: add the correct type
+   |
+LL |     let _ = /* Type */ {foo: ()};
+   |             ++++++++++
+
+error: placeholder `_` is not allowed for the path in struct literals
+  --> $DIR/struct-lit-placeholder-or-empty-path.rs:11:13
+   |
+LL |     let _ = _ {foo: ()};
+   |             ^ not allowed in struct literals
+   |
+help: replace it with the correct type
+   |
+LL -     let _ = _ {foo: ()};
+LL +     let _ = /* Type */ {foo: ()};
+   |
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/suggestions/struct-lit-placeholder-path.rs b/tests/ui/suggestions/struct-lit-placeholder-path.rs
new file mode 100644
index 000000000000..190440dd1569
--- /dev/null
+++ b/tests/ui/suggestions/struct-lit-placeholder-path.rs
@@ -0,0 +1,21 @@
+// Regression test for issue #98282.
+
+mod blah {
+    pub struct Stuff { x: i32 }
+    pub fn do_stuff(_: Stuff) {}
+}
+
+fn main() {
+    blah::do_stuff(_ { x: 10 });
+    //~^ ERROR placeholder `_` is not allowed for the path in struct literals
+    //~| NOTE not allowed in struct literals
+    //~| HELP replace it with the correct type
+}
+
+#[cfg(FALSE)]
+fn disabled() {
+    blah::do_stuff(_ { x: 10 });
+    //~^ ERROR placeholder `_` is not allowed for the path in struct literals
+    //~| NOTE not allowed in struct literals
+    //~| HELP replace it with the correct type
+}
diff --git a/tests/ui/suggestions/struct-lit-placeholder-path.stderr b/tests/ui/suggestions/struct-lit-placeholder-path.stderr
new file mode 100644
index 000000000000..8cd24b6f268d
--- /dev/null
+++ b/tests/ui/suggestions/struct-lit-placeholder-path.stderr
@@ -0,0 +1,26 @@
+error: placeholder `_` is not allowed for the path in struct literals
+  --> $DIR/struct-lit-placeholder-path.rs:9:20
+   |
+LL |     blah::do_stuff(_ { x: 10 });
+   |                    ^ not allowed in struct literals
+   |
+help: replace it with the correct type
+   |
+LL -     blah::do_stuff(_ { x: 10 });
+LL +     blah::do_stuff(/* Type */ { x: 10 });
+   |
+
+error: placeholder `_` is not allowed for the path in struct literals
+  --> $DIR/struct-lit-placeholder-path.rs:17:20
+   |
+LL |     blah::do_stuff(_ { x: 10 });
+   |                    ^ not allowed in struct literals
+   |
+help: replace it with the correct type
+   |
+LL -     blah::do_stuff(_ { x: 10 });
+LL +     blah::do_stuff(/* Type */ { x: 10 });
+   |
+
+error: aborting due to 2 previous errors
+

From f4731a17f696cdde1650d9e660236c3077f06a3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= 
Date: Mon, 26 Jan 2026 00:06:59 +0100
Subject: [PATCH 203/583] Bump `std`'s `backtrace`'s `rustc-demangle`

---
 library/Cargo.lock     | 4 ++--
 library/std/Cargo.toml | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/library/Cargo.lock b/library/Cargo.lock
index f6c14bc58a04..92dbedb6457a 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -274,9 +274,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.26"
+version = "0.1.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
+checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
 dependencies = [
  "rustc-std-workspace-core",
 ]
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 5c9ae52d9e6c..80dd0801333b 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -26,7 +26,7 @@ hashbrown = { version = "0.16.1", default-features = false, features = [
 std_detect = { path = "../std_detect", public = true }
 
 # Dependencies of the `backtrace` crate
-rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] }
+rustc-demangle = { version = "0.1.27", features = ['rustc-dep-of-std'] }
 
 [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
 miniz_oxide = { version = "0.8.0", optional = true, default-features = false }

From 2b32446c7cdda434a4aed521dd6ec63891feb0b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Sun, 25 Jan 2026 23:00:18 +0000
Subject: [PATCH 204/583] Suggest changing `iter`/`into_iter` when the other
 was meant

When encountering a call to `iter` that should have been `into_iter` and vice-versa, provide a structured suggestion:

```
error[E0271]: type mismatch resolving ` as IntoIterator>::Item == &{integer}`
  --> $DIR/into_iter-when-iter-was-intended.rs:5:37
   |
LL |     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
   |                               ----- ^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer
   |                               |
   |                               required by a bound introduced by this call
   |
note: the method call chain might not have had the expected associated types
  --> $DIR/into_iter-when-iter-was-intended.rs:5:47
   |
LL |     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
   |                                     --------- ^^^^^^^^^^^ `IntoIterator::Item` is `{integer}` here
   |                                     |
   |                                     this expression has type `[{integer}; 3]`
note: required by a bound in `std::iter::Iterator::chain`
  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
help: consider not consuming the `[{integer}, 3]` to construct the `Iterator`
   |
LL -     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
LL +     let _a = [0, 1, 2].iter().chain([3, 4, 5].iter());
   |
```
---
 compiler/rustc_span/src/symbol.rs             |  1 +
 .../src/error_reporting/traits/suggestions.rs | 49 +++++++++++++++++--
 library/core/src/iter/traits/collect.rs       |  1 +
 .../into_iter-when-iter-was-intended.fixed    | 10 ++++
 .../into_iter-when-iter-was-intended.rs       | 10 ++++
 .../into_iter-when-iter-was-intended.stderr   | 48 ++++++++++++++++++
 6 files changed, 116 insertions(+), 3 deletions(-)
 create mode 100644 tests/ui/iterators/into_iter-when-iter-was-intended.fixed
 create mode 100644 tests/ui/iterators/into_iter-when-iter-was-intended.rs
 create mode 100644 tests/ui/iterators/into_iter-when-iter-was-intended.stderr

diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8fd228211f3c..c4391c3ec4fc 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -270,6 +270,7 @@ symbols! {
         Into,
         IntoFuture,
         IntoIterator,
+        IntoIteratorItem,
         IoBufRead,
         IoLines,
         IoRead,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 1c08b5e33142..d54f3812350d 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -4390,6 +4390,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         path_segment: &hir::PathSegment<'_>,
         args: &[hir::Expr<'_>],
+        prev_ty: Ty<'_>,
         err: &mut Diag<'_, G>,
     ) {
         let tcx = self.tcx;
@@ -4403,6 +4404,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 let TypeError::Sorts(expected_found) = diff else {
                     continue;
                 };
+                if tcx.is_diagnostic_item(sym::IntoIteratorItem, *def_id)
+                    && path_segment.ident.name == sym::iter
+                    && self.can_eq(
+                        param_env,
+                        Ty::new_ref(
+                            tcx,
+                            tcx.lifetimes.re_erased,
+                            expected_found.found,
+                            ty::Mutability::Not,
+                        ),
+                        *ty,
+                    )
+                    && let [] = args
+                {
+                    // Used `.iter()` when `.into_iter()` was likely meant.
+                    err.span_suggestion_verbose(
+                        path_segment.ident.span,
+                        format!("consider consuming the `{prev_ty}` to construct the `Iterator`"),
+                        "into_iter".to_string(),
+                        Applicability::MachineApplicable,
+                    );
+                }
+                if tcx.is_diagnostic_item(sym::IntoIteratorItem, *def_id)
+                    && path_segment.ident.name == sym::into_iter
+                    && self.can_eq(
+                        param_env,
+                        expected_found.found,
+                        Ty::new_ref(tcx, tcx.lifetimes.re_erased, *ty, ty::Mutability::Not),
+                    )
+                    && let [] = args
+                {
+                    // Used `.into_iter()` when `.iter()` was likely meant.
+                    err.span_suggestion_verbose(
+                        path_segment.ident.span,
+                        format!(
+                            "consider not consuming the `{prev_ty}` to construct the `Iterator`"
+                        ),
+                        "iter".to_string(),
+                        Applicability::MachineApplicable,
+                    );
+                }
                 if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
                     && path_segment.ident.name == sym::map
                     && self.can_eq(param_env, expected_found.found, *ty)
@@ -4515,6 +4557,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             expr = rcvr_expr;
             let assocs_in_this_method =
                 self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
+            prev_ty = self.resolve_vars_if_possible(
+                typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
+            );
             self.look_for_iterator_item_mistakes(
                 &assocs_in_this_method,
                 typeck_results,
@@ -4522,12 +4567,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 param_env,
                 path_segment,
                 args,
+                prev_ty,
                 err,
             );
             assocs.push(assocs_in_this_method);
-            prev_ty = self.resolve_vars_if_possible(
-                typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
-            );
 
             if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
                 && let hir::Path { res: Res::Local(hir_id), .. } = path
diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs
index cdf81385bdaf..c3b9a0f0b7a4 100644
--- a/library/core/src/iter/traits/collect.rs
+++ b/library/core/src/iter/traits/collect.rs
@@ -281,6 +281,7 @@ pub trait FromIterator: Sized {
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait IntoIterator {
     /// The type of the elements being iterated over.
+    #[rustc_diagnostic_item = "IntoIteratorItem"]
     #[stable(feature = "rust1", since = "1.0.0")]
     type Item;
 
diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.fixed b/tests/ui/iterators/into_iter-when-iter-was-intended.fixed
new file mode 100644
index 000000000000..e841b1605f11
--- /dev/null
+++ b/tests/ui/iterators/into_iter-when-iter-was-intended.fixed
@@ -0,0 +1,10 @@
+//@ run-rustfix
+//@ edition:2021
+// Suggest using the right `IntoIterator` method. #68095
+fn main() {
+    let _a = [0, 1, 2].iter().chain([3, 4, 5].iter()); //~ ERROR E0271
+    let _b = [0, 1, 2].into_iter().chain([3, 4, 5].into_iter()); //~ ERROR E0271
+    // These don't have appropriate suggestions yet.
+    // let c = [0, 1, 2].iter().chain([3, 4, 5]);
+    // let d = [0, 1, 2].iter().chain(vec![3, 4, 5]);
+}
diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.rs b/tests/ui/iterators/into_iter-when-iter-was-intended.rs
new file mode 100644
index 000000000000..8d4376aa0ae6
--- /dev/null
+++ b/tests/ui/iterators/into_iter-when-iter-was-intended.rs
@@ -0,0 +1,10 @@
+//@ run-rustfix
+//@ edition:2021
+// Suggest using the right `IntoIterator` method. #68095
+fn main() {
+    let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter()); //~ ERROR E0271
+    let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter()); //~ ERROR E0271
+    // These don't have appropriate suggestions yet.
+    // let c = [0, 1, 2].iter().chain([3, 4, 5]);
+    // let d = [0, 1, 2].iter().chain(vec![3, 4, 5]);
+}
diff --git a/tests/ui/iterators/into_iter-when-iter-was-intended.stderr b/tests/ui/iterators/into_iter-when-iter-was-intended.stderr
new file mode 100644
index 000000000000..f26db9781b13
--- /dev/null
+++ b/tests/ui/iterators/into_iter-when-iter-was-intended.stderr
@@ -0,0 +1,48 @@
+error[E0271]: type mismatch resolving ` as IntoIterator>::Item == &{integer}`
+  --> $DIR/into_iter-when-iter-was-intended.rs:5:37
+   |
+LL |     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
+   |                               ----- ^^^^^^^^^^^^^^^^^^^^^ expected `&{integer}`, found integer
+   |                               |
+   |                               required by a bound introduced by this call
+   |
+note: the method call chain might not have had the expected associated types
+  --> $DIR/into_iter-when-iter-was-intended.rs:5:47
+   |
+LL |     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
+   |                                     --------- ^^^^^^^^^^^ `IntoIterator::Item` is `{integer}` here
+   |                                     |
+   |                                     this expression has type `[{integer}; 3]`
+note: required by a bound in `std::iter::Iterator::chain`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider not consuming the `[{integer}; 3]` to construct the `Iterator`
+   |
+LL -     let _a = [0, 1, 2].iter().chain([3, 4, 5].into_iter());
+LL +     let _a = [0, 1, 2].iter().chain([3, 4, 5].iter());
+   |
+
+error[E0271]: type mismatch resolving ` as IntoIterator>::Item == {integer}`
+  --> $DIR/into_iter-when-iter-was-intended.rs:6:42
+   |
+LL |     let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter());
+   |                                    ----- ^^^^^^^^^^^^^^^^ expected integer, found `&{integer}`
+   |                                    |
+   |                                    required by a bound introduced by this call
+   |
+note: the method call chain might not have had the expected associated types
+  --> $DIR/into_iter-when-iter-was-intended.rs:6:52
+   |
+LL |     let _b = [0, 1, 2].into_iter().chain([3, 4, 5].iter());
+   |                                          --------- ^^^^^^ `IntoIterator::Item` is `&{integer}` here
+   |                                          |
+   |                                          this expression has type `[{integer}; 3]`
+note: required by a bound in `std::iter::Iterator::chain`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider consuming the `&[{integer}]` to construct the `Iterator`
+   |
+LL |     let _b = [0, 1, 2].into_iter().chain([3, 4, 5].into_iter());
+   |                                                    +++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0271`.

From 2a3614b5557b89da3d92b17c3497ac6e954360b8 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 26 Jan 2026 05:44:04 +0530
Subject: [PATCH 205/583] correct ungrammar path in patch

---
 src/tools/rust-analyzer/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 04b513b38b58..2288933a96cc 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -42,7 +42,7 @@ debug = 2
 # lsp-server = { path = "lib/lsp-server" }
 
 
-# ungrammar = { path = "lin/ungrammar" }
+# ungrammar = { path = "lib/ungrammar" }
 
 # salsa = { path = "../salsa" }
 # salsa-macros = { path = "../salsa/components/salsa-macros" }

From 3a33ab0595044615affae9d11ba45df98e5c0dee Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sat, 24 Jan 2026 15:05:36 -0800
Subject: [PATCH 206/583] GVN: Elide more intermediate transmutes

---
 compiler/rustc_mir_transform/src/gvn.rs       |  61 ++++++--
 ...n.DataflowConstProp.32bit.panic-abort.diff |   8 +-
 ....DataflowConstProp.32bit.panic-unwind.diff |   8 +-
 ...n.DataflowConstProp.64bit.panic-abort.diff |   8 +-
 ....DataflowConstProp.64bit.panic-unwind.diff |   8 +-
 ...oxed_slice.main.GVN.32bit.panic-abort.diff |  13 +-
 ...xed_slice.main.GVN.32bit.panic-unwind.diff |  13 +-
 ...oxed_slice.main.GVN.64bit.panic-abort.diff |  13 +-
 ...xed_slice.main.GVN.64bit.panic-unwind.diff |  13 +-
 .../gvn.fn_pointers.GVN.panic-abort.diff      |  18 +--
 .../gvn.fn_pointers.GVN.panic-unwind.diff     |  18 +--
 tests/mir-opt/gvn.rs                          |  20 ++-
 ..._then_transmute_again.GVN.panic-abort.diff | 148 +++++++++++++-----
 ...then_transmute_again.GVN.panic-unwind.diff | 148 +++++++++++++-----
 .../gvn_ptr_eq_with_constant.main.GVN.diff    |   6 +-
 15 files changed, 328 insertions(+), 175 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 820998eed100..476ae059649a 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -1591,10 +1591,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
                     (Transmute, PtrToPtr) if self.pointers_have_same_metadata(from, to) => {
                         Some(Transmute)
                     }
-                    // If would be legal to always do this, but we don't want to hide information
+                    // It would be legal to always do this, but we don't want to hide information
                     // from the backend that it'd otherwise be able to use for optimizations.
                     (Transmute, Transmute)
-                        if !self.type_may_have_niche_of_interest_to_backend(from) =>
+                        if !self.transmute_may_have_niche_of_interest_to_backend(
+                            inner_from, from, to,
+                        ) =>
                     {
                         Some(Transmute)
                     }
@@ -1642,24 +1644,65 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
         }
     }
 
-    /// Returns `false` if we know for sure that this type has no interesting niche,
-    /// and thus we can skip transmuting through it without worrying.
+    /// Returns `false` if we're confident that the middle type doesn't have an
+    /// interesting niche so we can skip that step when transmuting.
     ///
     /// The backend will emit `assume`s when transmuting between types with niches,
     /// so we want to preserve `i32 -> char -> u32` so that that data is around,
     /// but it's fine to skip whole-range-is-value steps like `A -> u32 -> B`.
-    fn type_may_have_niche_of_interest_to_backend(&self, ty: Ty<'tcx>) -> bool {
-        let Ok(layout) = self.ecx.layout_of(ty) else {
+    fn transmute_may_have_niche_of_interest_to_backend(
+        &self,
+        from_ty: Ty<'tcx>,
+        middle_ty: Ty<'tcx>,
+        to_ty: Ty<'tcx>,
+    ) -> bool {
+        let Ok(middle_layout) = self.ecx.layout_of(middle_ty) else {
             // If it's too generic or something, then assume it might be interesting later.
             return true;
         };
 
-        if layout.uninhabited {
+        if middle_layout.uninhabited {
             return true;
         }
 
-        match layout.backend_repr {
-            BackendRepr::Scalar(a) => !a.is_always_valid(&self.ecx),
+        match middle_layout.backend_repr {
+            BackendRepr::Scalar(mid) => {
+                if mid.is_always_valid(&self.ecx) {
+                    // With no niche it's never interesting, so don't bother
+                    // looking at the layout of the other two types.
+                    false
+                } else if let Ok(from_layout) = self.ecx.layout_of(from_ty)
+                    && !from_layout.uninhabited
+                    && from_layout.size == middle_layout.size
+                    && let BackendRepr::Scalar(from_a) = from_layout.backend_repr
+                    && let mid_range = mid.valid_range(&self.ecx)
+                    && let from_range = from_a.valid_range(&self.ecx)
+                    && mid_range.contains_range(from_range, middle_layout.size)
+                {
+                    // The `from_range` is a (non-strict) subset of `mid_range`
+                    // such as if we're doing `bool` -> `ascii::Char` -> `_`,
+                    // where `from_range: 0..=1` and `mid_range: 0..=127`,
+                    // and thus the middle doesn't tell us anything we don't
+                    // already know from the initial type.
+                    false
+                } else if let Ok(to_layout) = self.ecx.layout_of(to_ty)
+                    && !to_layout.uninhabited
+                    && to_layout.size == middle_layout.size
+                    && let BackendRepr::Scalar(to_a) = to_layout.backend_repr
+                    && let mid_range = mid.valid_range(&self.ecx)
+                    && let to_range = to_a.valid_range(&self.ecx)
+                    && mid_range.contains_range(to_range, middle_layout.size)
+                {
+                    // The `to_range` is a (non-strict) subset of `mid_range`
+                    // such as if we're doing `_` -> `ascii::Char` -> `bool`,
+                    // where `mid_range: 0..=127` and `to_range: 0..=1`,
+                    // and thus the middle doesn't tell us anything we don't
+                    // already know from the final type.
+                    false
+                } else {
+                    true
+                }
+            }
             BackendRepr::ScalarPair(a, b) => {
                 !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx)
             }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
index 7a60070b7074..5a3342a45cd3 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,11 +44,8 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
+          _6 = const {0x1 as *const [bool; 0]};
           _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
           _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
index d13d0d962a69..2d1b05e6e987 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,11 +44,8 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
+          _6 = const {0x1 as *const [bool; 0]};
           _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
           _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
index 8701e879e959..695a06e29d3d 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,11 +44,8 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
+          _6 = const {0x1 as *const [bool; 0]};
           _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
           _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
index ac1c8d627baa..17714feb1432 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,11 +44,8 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
+          _6 = const {0x1 as *const [bool; 0]};
           _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
           _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
index 0205d0cc3d16..7d66d3129115 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,14 +44,10 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute);
-+         _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
+-         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: copy _6 };
++         _6 = const {0x1 as *const [bool; 0]};
 +         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
 -         _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
 +         _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
index f6babe35b5a0..cc00bd300a3c 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,14 +44,10 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute);
-+         _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
+-         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: copy _6 };
++         _6 = const {0x1 as *const [bool; 0]};
 +         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
 -         _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
 +         _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
index 204e59415c6b..9380cdd6ccb4 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,14 +44,10 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute);
-+         _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
+-         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: copy _6 };
++         _6 = const {0x1 as *const [bool; 0]};
 +         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
 -         _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
 +         _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
index 0cf3f43c0464..bea564376274 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,14 +44,10 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute);
-+         _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
+-         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: copy _6 };
++         _6 = const {0x1 as *const [bool; 0]};
 +         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
 -         _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
 +         _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
index 90920dd0be8f..a29d3331f380 100644
--- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
+++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
@@ -8,10 +8,10 @@
       let mut _3: fn(u8) -> u8;
       let _5: ();
       let mut _6: fn(u8) -> u8;
-      let mut _9: {closure@$DIR/gvn.rs:617:19: 617:21};
+      let mut _9: {closure@$DIR/gvn.rs:618:19: 618:21};
       let _10: ();
       let mut _11: fn();
-      let mut _13: {closure@$DIR/gvn.rs:617:19: 617:21};
+      let mut _13: {closure@$DIR/gvn.rs:618:19: 618:21};
       let _14: ();
       let mut _15: fn();
       scope 1 {
@@ -19,7 +19,7 @@
           let _4: fn(u8) -> u8;
           scope 2 {
               debug g => _4;
-              let _7: {closure@$DIR/gvn.rs:617:19: 617:21};
+              let _7: {closure@$DIR/gvn.rs:618:19: 618:21};
               scope 3 {
                   debug closure => _7;
                   let _8: fn();
@@ -62,16 +62,16 @@
           StorageDead(_6);
           StorageDead(_5);
 -         StorageLive(_7);
--         _7 = {closure@$DIR/gvn.rs:617:19: 617:21};
+-         _7 = {closure@$DIR/gvn.rs:618:19: 618:21};
 -         StorageLive(_8);
 +         nop;
-+         _7 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
++         _7 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
 +         nop;
           StorageLive(_9);
 -         _9 = copy _7;
 -         _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
-+         _9 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
-+         _8 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
++         _9 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
++         _8 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
           StorageDead(_9);
           StorageLive(_10);
           StorageLive(_11);
@@ -88,8 +88,8 @@
           StorageLive(_13);
 -         _13 = copy _7;
 -         _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
-+         _13 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
-+         _12 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
++         _13 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
++         _12 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
           StorageDead(_13);
           StorageLive(_14);
           StorageLive(_15);
diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
index 0aca8e508f5c..b716fcd4e74b 100644
--- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
+++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
@@ -8,10 +8,10 @@
       let mut _3: fn(u8) -> u8;
       let _5: ();
       let mut _6: fn(u8) -> u8;
-      let mut _9: {closure@$DIR/gvn.rs:617:19: 617:21};
+      let mut _9: {closure@$DIR/gvn.rs:618:19: 618:21};
       let _10: ();
       let mut _11: fn();
-      let mut _13: {closure@$DIR/gvn.rs:617:19: 617:21};
+      let mut _13: {closure@$DIR/gvn.rs:618:19: 618:21};
       let _14: ();
       let mut _15: fn();
       scope 1 {
@@ -19,7 +19,7 @@
           let _4: fn(u8) -> u8;
           scope 2 {
               debug g => _4;
-              let _7: {closure@$DIR/gvn.rs:617:19: 617:21};
+              let _7: {closure@$DIR/gvn.rs:618:19: 618:21};
               scope 3 {
                   debug closure => _7;
                   let _8: fn();
@@ -62,16 +62,16 @@
           StorageDead(_6);
           StorageDead(_5);
 -         StorageLive(_7);
--         _7 = {closure@$DIR/gvn.rs:617:19: 617:21};
+-         _7 = {closure@$DIR/gvn.rs:618:19: 618:21};
 -         StorageLive(_8);
 +         nop;
-+         _7 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
++         _7 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
 +         nop;
           StorageLive(_9);
 -         _9 = copy _7;
 -         _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
-+         _9 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
-+         _8 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
++         _9 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
++         _8 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
           StorageDead(_9);
           StorageLive(_10);
           StorageLive(_11);
@@ -88,8 +88,8 @@
           StorageLive(_13);
 -         _13 = copy _7;
 -         _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
-+         _13 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
-+         _12 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
++         _13 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
++         _12 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
           StorageDead(_13);
           StorageLive(_14);
           StorageLive(_15);
diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs
index 3c3241fefe22..837e8ac4d9e3 100644
--- a/tests/mir-opt/gvn.rs
+++ b/tests/mir-opt/gvn.rs
@@ -9,6 +9,7 @@
 #![feature(freeze)]
 #![allow(ambiguous_wide_pointer_comparisons)]
 #![allow(unconditional_panic)]
+#![allow(unnecessary_transmutes)]
 #![allow(unused)]
 
 use std::intrinsics::mir::*;
@@ -985,7 +986,14 @@ unsafe fn aggregate_struct_then_transmute(id: u16, thin: *const u8) {
     opaque(std::intrinsics::transmute::<_, *const u8>(j));
 }
 
-unsafe fn transmute_then_transmute_again(a: u32, c: char) {
+#[repr(u8)]
+enum ZeroOneTwo {
+    Zero,
+    One,
+    Two,
+}
+
+unsafe fn transmute_then_transmute_again(a: u32, c: char, b: bool, d: u8) {
     // CHECK: [[TEMP1:_[0-9]+]] = copy _1 as char (Transmute);
     // CHECK: [[TEMP2:_[0-9]+]] = copy [[TEMP1]] as i32 (Transmute);
     // CHECK: opaque::(move [[TEMP2]])
@@ -996,6 +1004,16 @@ unsafe fn transmute_then_transmute_again(a: u32, c: char) {
     // CHECK: opaque::(move [[TEMP]])
     let x = std::intrinsics::transmute::(c);
     opaque(std::intrinsics::transmute::(x));
+
+    // CHECK: [[TEMP:_[0-9]+]] = copy _3 as u8 (Transmute);
+    // CHECK: opaque::(move [[TEMP]])
+    let x = std::intrinsics::transmute::(b);
+    opaque(std::intrinsics::transmute::(x));
+
+    // CHECK: [[TEMP:_[0-9]+]] = copy _4 as bool (Transmute);
+    // CHECK: opaque::(move [[TEMP]])
+    let x = std::intrinsics::transmute::(d);
+    opaque(std::intrinsics::transmute::(x));
 }
 
 // Transmuting can skip a pointer cast so long as it wasn't a fat-to-thin cast.
diff --git a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff
index 962fecd2586e..caed065536e3 100644
--- a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff
+++ b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff
@@ -1,71 +1,135 @@
 - // MIR for `transmute_then_transmute_again` before GVN
 + // MIR for `transmute_then_transmute_again` after GVN
   
-  fn transmute_then_transmute_again(_1: u32, _2: char) -> () {
+  fn transmute_then_transmute_again(_1: u32, _2: char, _3: bool, _4: u8) -> () {
       debug a => _1;
       debug c => _2;
+      debug b => _3;
+      debug d => _4;
       let mut _0: ();
-      let _3: char;
-      let mut _4: u32;
-      let _5: ();
-      let mut _6: i32;
-      let mut _7: char;
+      let _5: char;
+      let mut _6: u32;
+      let _7: ();
+      let mut _8: i32;
       let mut _9: char;
-      let _10: ();
-      let mut _11: i32;
-      let mut _12: u32;
+      let mut _11: char;
+      let _12: ();
+      let mut _13: i32;
+      let mut _14: u32;
+      let mut _16: bool;
+      let _17: ();
+      let mut _18: u8;
+      let mut _19: ZeroOneTwo;
+      let mut _21: u8;
+      let _22: ();
+      let mut _23: bool;
+      let mut _24: ZeroOneTwo;
       scope 1 {
-          debug x => _3;
-          let _8: u32;
+          debug x => _5;
+          let _10: u32;
           scope 2 {
-              debug x => _8;
+              debug x => _10;
+              let _15: ZeroOneTwo;
+              scope 3 {
+                  debug x => _15;
+                  let _20: ZeroOneTwo;
+                  scope 4 {
+                      debug x => _20;
+                  }
+              }
           }
       }
   
       bb0: {
--         StorageLive(_3);
+-         StorageLive(_5);
 +         nop;
-          StorageLive(_4);
-          _4 = copy _1;
--         _3 = move _4 as char (Transmute);
-+         _3 = copy _1 as char (Transmute);
-          StorageDead(_4);
-          StorageLive(_5);
           StorageLive(_6);
+          _6 = copy _1;
+-         _5 = move _6 as char (Transmute);
++         _5 = copy _1 as char (Transmute);
+          StorageDead(_6);
           StorageLive(_7);
-          _7 = copy _3;
--         _6 = move _7 as i32 (Transmute);
-+         _6 = copy _3 as i32 (Transmute);
-          StorageDead(_7);
-          _5 = opaque::(move _6) -> [return: bb1, unwind unreachable];
+          StorageLive(_8);
+          StorageLive(_9);
+          _9 = copy _5;
+-         _8 = move _9 as i32 (Transmute);
++         _8 = copy _5 as i32 (Transmute);
+          StorageDead(_9);
+          _7 = opaque::(move _8) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
-          StorageDead(_6);
-          StorageDead(_5);
--         StorageLive(_8);
+          StorageDead(_8);
+          StorageDead(_7);
+-         StorageLive(_10);
 +         nop;
-          StorageLive(_9);
-          _9 = copy _2;
--         _8 = move _9 as u32 (Transmute);
-+         _8 = copy _2 as u32 (Transmute);
-          StorageDead(_9);
-          StorageLive(_10);
           StorageLive(_11);
+          _11 = copy _2;
+-         _10 = move _11 as u32 (Transmute);
++         _10 = copy _2 as u32 (Transmute);
+          StorageDead(_11);
           StorageLive(_12);
-          _12 = copy _8;
--         _11 = move _12 as i32 (Transmute);
-+         _11 = copy _2 as i32 (Transmute);
-          StorageDead(_12);
-          _10 = opaque::(move _11) -> [return: bb2, unwind unreachable];
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = copy _10;
+-         _13 = move _14 as i32 (Transmute);
++         _13 = copy _2 as i32 (Transmute);
+          StorageDead(_14);
+          _12 = opaque::(move _13) -> [return: bb2, unwind unreachable];
       }
   
       bb2: {
-          StorageDead(_11);
-          StorageDead(_10);
+          StorageDead(_13);
+          StorageDead(_12);
+-         StorageLive(_15);
++         nop;
+          StorageLive(_16);
+          _16 = copy _3;
+-         _15 = move _16 as ZeroOneTwo (Transmute);
++         _15 = copy _3 as ZeroOneTwo (Transmute);
+          StorageDead(_16);
+          StorageLive(_17);
+          StorageLive(_18);
+          StorageLive(_19);
+-         _19 = move _15;
+-         _18 = move _19 as u8 (Transmute);
++         _19 = copy _15;
++         _18 = copy _3 as u8 (Transmute);
+          StorageDead(_19);
+          _17 = opaque::(move _18) -> [return: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+          StorageDead(_18);
+          StorageDead(_17);
+-         StorageLive(_20);
++         nop;
+          StorageLive(_21);
+          _21 = copy _4;
+-         _20 = move _21 as ZeroOneTwo (Transmute);
++         _20 = copy _4 as ZeroOneTwo (Transmute);
+          StorageDead(_21);
+          StorageLive(_22);
+          StorageLive(_23);
+          StorageLive(_24);
+-         _24 = move _20;
+-         _23 = move _24 as bool (Transmute);
++         _24 = copy _20;
++         _23 = copy _4 as bool (Transmute);
+          StorageDead(_24);
+          _22 = opaque::(move _23) -> [return: bb4, unwind unreachable];
+      }
+  
+      bb4: {
+          StorageDead(_23);
+          StorageDead(_22);
           _0 = const ();
--         StorageDead(_8);
--         StorageDead(_3);
+-         StorageDead(_20);
+-         StorageDead(_15);
+-         StorageDead(_10);
+-         StorageDead(_5);
++         nop;
++         nop;
 +         nop;
 +         nop;
           return;
diff --git a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff
index e32397c1aed0..3b25dd362cd5 100644
--- a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff
+++ b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff
@@ -1,71 +1,135 @@
 - // MIR for `transmute_then_transmute_again` before GVN
 + // MIR for `transmute_then_transmute_again` after GVN
   
-  fn transmute_then_transmute_again(_1: u32, _2: char) -> () {
+  fn transmute_then_transmute_again(_1: u32, _2: char, _3: bool, _4: u8) -> () {
       debug a => _1;
       debug c => _2;
+      debug b => _3;
+      debug d => _4;
       let mut _0: ();
-      let _3: char;
-      let mut _4: u32;
-      let _5: ();
-      let mut _6: i32;
-      let mut _7: char;
+      let _5: char;
+      let mut _6: u32;
+      let _7: ();
+      let mut _8: i32;
       let mut _9: char;
-      let _10: ();
-      let mut _11: i32;
-      let mut _12: u32;
+      let mut _11: char;
+      let _12: ();
+      let mut _13: i32;
+      let mut _14: u32;
+      let mut _16: bool;
+      let _17: ();
+      let mut _18: u8;
+      let mut _19: ZeroOneTwo;
+      let mut _21: u8;
+      let _22: ();
+      let mut _23: bool;
+      let mut _24: ZeroOneTwo;
       scope 1 {
-          debug x => _3;
-          let _8: u32;
+          debug x => _5;
+          let _10: u32;
           scope 2 {
-              debug x => _8;
+              debug x => _10;
+              let _15: ZeroOneTwo;
+              scope 3 {
+                  debug x => _15;
+                  let _20: ZeroOneTwo;
+                  scope 4 {
+                      debug x => _20;
+                  }
+              }
           }
       }
   
       bb0: {
--         StorageLive(_3);
+-         StorageLive(_5);
 +         nop;
-          StorageLive(_4);
-          _4 = copy _1;
--         _3 = move _4 as char (Transmute);
-+         _3 = copy _1 as char (Transmute);
-          StorageDead(_4);
-          StorageLive(_5);
           StorageLive(_6);
+          _6 = copy _1;
+-         _5 = move _6 as char (Transmute);
++         _5 = copy _1 as char (Transmute);
+          StorageDead(_6);
           StorageLive(_7);
-          _7 = copy _3;
--         _6 = move _7 as i32 (Transmute);
-+         _6 = copy _3 as i32 (Transmute);
-          StorageDead(_7);
-          _5 = opaque::(move _6) -> [return: bb1, unwind continue];
+          StorageLive(_8);
+          StorageLive(_9);
+          _9 = copy _5;
+-         _8 = move _9 as i32 (Transmute);
++         _8 = copy _5 as i32 (Transmute);
+          StorageDead(_9);
+          _7 = opaque::(move _8) -> [return: bb1, unwind continue];
       }
   
       bb1: {
-          StorageDead(_6);
-          StorageDead(_5);
--         StorageLive(_8);
+          StorageDead(_8);
+          StorageDead(_7);
+-         StorageLive(_10);
 +         nop;
-          StorageLive(_9);
-          _9 = copy _2;
--         _8 = move _9 as u32 (Transmute);
-+         _8 = copy _2 as u32 (Transmute);
-          StorageDead(_9);
-          StorageLive(_10);
           StorageLive(_11);
+          _11 = copy _2;
+-         _10 = move _11 as u32 (Transmute);
++         _10 = copy _2 as u32 (Transmute);
+          StorageDead(_11);
           StorageLive(_12);
-          _12 = copy _8;
--         _11 = move _12 as i32 (Transmute);
-+         _11 = copy _2 as i32 (Transmute);
-          StorageDead(_12);
-          _10 = opaque::(move _11) -> [return: bb2, unwind continue];
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = copy _10;
+-         _13 = move _14 as i32 (Transmute);
++         _13 = copy _2 as i32 (Transmute);
+          StorageDead(_14);
+          _12 = opaque::(move _13) -> [return: bb2, unwind continue];
       }
   
       bb2: {
-          StorageDead(_11);
-          StorageDead(_10);
+          StorageDead(_13);
+          StorageDead(_12);
+-         StorageLive(_15);
++         nop;
+          StorageLive(_16);
+          _16 = copy _3;
+-         _15 = move _16 as ZeroOneTwo (Transmute);
++         _15 = copy _3 as ZeroOneTwo (Transmute);
+          StorageDead(_16);
+          StorageLive(_17);
+          StorageLive(_18);
+          StorageLive(_19);
+-         _19 = move _15;
+-         _18 = move _19 as u8 (Transmute);
++         _19 = copy _15;
++         _18 = copy _3 as u8 (Transmute);
+          StorageDead(_19);
+          _17 = opaque::(move _18) -> [return: bb3, unwind continue];
+      }
+  
+      bb3: {
+          StorageDead(_18);
+          StorageDead(_17);
+-         StorageLive(_20);
++         nop;
+          StorageLive(_21);
+          _21 = copy _4;
+-         _20 = move _21 as ZeroOneTwo (Transmute);
++         _20 = copy _4 as ZeroOneTwo (Transmute);
+          StorageDead(_21);
+          StorageLive(_22);
+          StorageLive(_23);
+          StorageLive(_24);
+-         _24 = move _20;
+-         _23 = move _24 as bool (Transmute);
++         _24 = copy _20;
++         _23 = copy _4 as bool (Transmute);
+          StorageDead(_24);
+          _22 = opaque::(move _23) -> [return: bb4, unwind continue];
+      }
+  
+      bb4: {
+          StorageDead(_23);
+          StorageDead(_22);
           _0 = const ();
--         StorageDead(_8);
--         StorageDead(_3);
+-         StorageDead(_20);
+-         StorageDead(_15);
+-         StorageDead(_10);
+-         StorageDead(_5);
++         nop;
++         nop;
 +         nop;
 +         nop;
           return;
diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
index f56af33ea603..74bbdeb8f744 100644
--- a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
+++ b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
@@ -7,7 +7,7 @@
       let mut _2: *mut u8;
       scope 1 (inlined dangling_mut::) {
           scope 2 (inlined NonNull::::dangling) {
-              let mut _3: std::num::NonZero;
+              let _3: std::ptr::Alignment;
               scope 3 {
                   scope 5 (inlined std::ptr::Alignment::as_nonzero) {
                   }
@@ -40,9 +40,9 @@
           StorageLive(_1);
           StorageLive(_2);
           StorageLive(_3);
--         _3 = const std::ptr::Alignment::of::::{constant#0} as std::num::NonZero (Transmute);
+-         _3 = const std::ptr::Alignment::of::::{constant#0};
 -         _2 = copy _3 as *mut u8 (Transmute);
-+         _3 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
++         _3 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0);
 +         _2 = const {0x1 as *mut u8};
           StorageDead(_3);
           StorageLive(_4);

From 929e2809734a1b3df4428912d9c8a36ac2546225 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sun, 25 Jan 2026 17:05:09 -0800
Subject: [PATCH 207/583] Update `ptr::Alignment` to go through transmuting

---
 library/core/src/ptr/alignment.rs             |  5 +++-
 ...ace.PreCodegen.after.32bit.panic-abort.mir | 24 +++++++++----------
 ...ce.PreCodegen.after.32bit.panic-unwind.mir | 24 +++++++++----------
 ...ace.PreCodegen.after.64bit.panic-abort.mir | 24 +++++++++----------
 ...ce.PreCodegen.after.64bit.panic-unwind.mir | 24 +++++++++----------
 tests/mir-opt/pre-codegen/drop_boxed_slice.rs |  5 ++--
 6 files changed, 54 insertions(+), 52 deletions(-)

diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs
index 42c95e6c9cd2..c3187c9a5c2b 100644
--- a/library/core/src/ptr/alignment.rs
+++ b/library/core/src/ptr/alignment.rs
@@ -166,7 +166,10 @@ impl Alignment {
     #[unstable(feature = "ptr_alignment_type", issue = "102070")]
     #[inline]
     pub const fn as_usize(self) -> usize {
-        self.0 as usize
+        // Going through `as_nonzero` helps this be more clearly the inverse of
+        // `new_unchecked`, letting MIR optimizations fold it away.
+
+        self.as_nonzero().get()
     }
 
     /// Returns the alignment as a [NonZero]<[usize]>.
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir
index 6ffadd4c51db..0adc66951e78 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir
@@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
             let _2: std::ptr::NonNull<[T]>;
             let mut _3: *mut [T];
             let mut _4: *const [T];
-            let _11: ();
+            let _10: ();
             scope 3 {
-                let _8: std::ptr::alignment::AlignmentEnum;
                 scope 4 {
                     scope 17 (inlined Layout::size) {
                     }
@@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
                     scope 23 (inlined ::deallocate) {
                         scope 24 (inlined std::alloc::Global::deallocate_impl) {
                             scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
-                                let mut _9: *mut u8;
+                                let mut _8: *mut u8;
                                 scope 26 (inlined Layout::size) {
                                 }
                                 scope 27 (inlined NonNull::::as_ptr) {
                                 }
                                 scope 28 (inlined std::alloc::dealloc) {
-                                    let mut _10: usize;
+                                    let mut _9: usize;
                                     scope 29 (inlined Layout::size) {
                                     }
                                     scope 30 (inlined Layout::align) {
                                         scope 31 (inlined std::ptr::Alignment::as_usize) {
+                                            scope 32 (inlined std::ptr::Alignment::as_nonzero) {
+                                            }
+                                            scope 33 (inlined NonZero::::get) {
+                                            }
                                         }
                                     }
                                 }
@@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         StorageLive(_4);
         _3 = copy _2 as *mut [T] (Transmute);
         _4 = copy _2 as *const [T] (Transmute);
-        StorageLive(_7);
         _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
     }
 
@@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         _6 = const ::ALIGN;
         _7 = copy _6 as std::ptr::Alignment (Transmute);
         StorageDead(_6);
-        _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum);
-        StorageDead(_7);
         StorageDead(_4);
         switchInt(copy _5) -> [0: bb4, otherwise: bb2];
     }
 
     bb2: {
+        StorageLive(_8);
+        _8 = copy _3 as *mut u8 (PtrToPtr);
         StorageLive(_9);
-        _9 = copy _3 as *mut u8 (PtrToPtr);
-        StorageLive(_10);
-        _10 = discriminant(_8);
-        _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable];
+        _9 = copy _7 as usize (Transmute);
+        _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageDead(_10);
         StorageDead(_9);
+        StorageDead(_8);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir
index 6ffadd4c51db..0adc66951e78 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir
@@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
             let _2: std::ptr::NonNull<[T]>;
             let mut _3: *mut [T];
             let mut _4: *const [T];
-            let _11: ();
+            let _10: ();
             scope 3 {
-                let _8: std::ptr::alignment::AlignmentEnum;
                 scope 4 {
                     scope 17 (inlined Layout::size) {
                     }
@@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
                     scope 23 (inlined ::deallocate) {
                         scope 24 (inlined std::alloc::Global::deallocate_impl) {
                             scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
-                                let mut _9: *mut u8;
+                                let mut _8: *mut u8;
                                 scope 26 (inlined Layout::size) {
                                 }
                                 scope 27 (inlined NonNull::::as_ptr) {
                                 }
                                 scope 28 (inlined std::alloc::dealloc) {
-                                    let mut _10: usize;
+                                    let mut _9: usize;
                                     scope 29 (inlined Layout::size) {
                                     }
                                     scope 30 (inlined Layout::align) {
                                         scope 31 (inlined std::ptr::Alignment::as_usize) {
+                                            scope 32 (inlined std::ptr::Alignment::as_nonzero) {
+                                            }
+                                            scope 33 (inlined NonZero::::get) {
+                                            }
                                         }
                                     }
                                 }
@@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         StorageLive(_4);
         _3 = copy _2 as *mut [T] (Transmute);
         _4 = copy _2 as *const [T] (Transmute);
-        StorageLive(_7);
         _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
     }
 
@@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         _6 = const ::ALIGN;
         _7 = copy _6 as std::ptr::Alignment (Transmute);
         StorageDead(_6);
-        _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum);
-        StorageDead(_7);
         StorageDead(_4);
         switchInt(copy _5) -> [0: bb4, otherwise: bb2];
     }
 
     bb2: {
+        StorageLive(_8);
+        _8 = copy _3 as *mut u8 (PtrToPtr);
         StorageLive(_9);
-        _9 = copy _3 as *mut u8 (PtrToPtr);
-        StorageLive(_10);
-        _10 = discriminant(_8);
-        _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable];
+        _9 = copy _7 as usize (Transmute);
+        _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageDead(_10);
         StorageDead(_9);
+        StorageDead(_8);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir
index 6ffadd4c51db..0adc66951e78 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir
@@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
             let _2: std::ptr::NonNull<[T]>;
             let mut _3: *mut [T];
             let mut _4: *const [T];
-            let _11: ();
+            let _10: ();
             scope 3 {
-                let _8: std::ptr::alignment::AlignmentEnum;
                 scope 4 {
                     scope 17 (inlined Layout::size) {
                     }
@@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
                     scope 23 (inlined ::deallocate) {
                         scope 24 (inlined std::alloc::Global::deallocate_impl) {
                             scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
-                                let mut _9: *mut u8;
+                                let mut _8: *mut u8;
                                 scope 26 (inlined Layout::size) {
                                 }
                                 scope 27 (inlined NonNull::::as_ptr) {
                                 }
                                 scope 28 (inlined std::alloc::dealloc) {
-                                    let mut _10: usize;
+                                    let mut _9: usize;
                                     scope 29 (inlined Layout::size) {
                                     }
                                     scope 30 (inlined Layout::align) {
                                         scope 31 (inlined std::ptr::Alignment::as_usize) {
+                                            scope 32 (inlined std::ptr::Alignment::as_nonzero) {
+                                            }
+                                            scope 33 (inlined NonZero::::get) {
+                                            }
                                         }
                                     }
                                 }
@@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         StorageLive(_4);
         _3 = copy _2 as *mut [T] (Transmute);
         _4 = copy _2 as *const [T] (Transmute);
-        StorageLive(_7);
         _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
     }
 
@@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         _6 = const ::ALIGN;
         _7 = copy _6 as std::ptr::Alignment (Transmute);
         StorageDead(_6);
-        _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum);
-        StorageDead(_7);
         StorageDead(_4);
         switchInt(copy _5) -> [0: bb4, otherwise: bb2];
     }
 
     bb2: {
+        StorageLive(_8);
+        _8 = copy _3 as *mut u8 (PtrToPtr);
         StorageLive(_9);
-        _9 = copy _3 as *mut u8 (PtrToPtr);
-        StorageLive(_10);
-        _10 = discriminant(_8);
-        _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable];
+        _9 = copy _7 as usize (Transmute);
+        _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageDead(_10);
         StorageDead(_9);
+        StorageDead(_8);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir
index 6ffadd4c51db..0adc66951e78 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir
@@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
             let _2: std::ptr::NonNull<[T]>;
             let mut _3: *mut [T];
             let mut _4: *const [T];
-            let _11: ();
+            let _10: ();
             scope 3 {
-                let _8: std::ptr::alignment::AlignmentEnum;
                 scope 4 {
                     scope 17 (inlined Layout::size) {
                     }
@@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
                     scope 23 (inlined ::deallocate) {
                         scope 24 (inlined std::alloc::Global::deallocate_impl) {
                             scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
-                                let mut _9: *mut u8;
+                                let mut _8: *mut u8;
                                 scope 26 (inlined Layout::size) {
                                 }
                                 scope 27 (inlined NonNull::::as_ptr) {
                                 }
                                 scope 28 (inlined std::alloc::dealloc) {
-                                    let mut _10: usize;
+                                    let mut _9: usize;
                                     scope 29 (inlined Layout::size) {
                                     }
                                     scope 30 (inlined Layout::align) {
                                         scope 31 (inlined std::ptr::Alignment::as_usize) {
+                                            scope 32 (inlined std::ptr::Alignment::as_nonzero) {
+                                            }
+                                            scope 33 (inlined NonZero::::get) {
+                                            }
                                         }
                                     }
                                 }
@@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         StorageLive(_4);
         _3 = copy _2 as *mut [T] (Transmute);
         _4 = copy _2 as *const [T] (Transmute);
-        StorageLive(_7);
         _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
     }
 
@@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         _6 = const ::ALIGN;
         _7 = copy _6 as std::ptr::Alignment (Transmute);
         StorageDead(_6);
-        _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum);
-        StorageDead(_7);
         StorageDead(_4);
         switchInt(copy _5) -> [0: bb4, otherwise: bb2];
     }
 
     bb2: {
+        StorageLive(_8);
+        _8 = copy _3 as *mut u8 (PtrToPtr);
         StorageLive(_9);
-        _9 = copy _3 as *mut u8 (PtrToPtr);
-        StorageLive(_10);
-        _10 = discriminant(_8);
-        _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable];
+        _9 = copy _7 as usize (Transmute);
+        _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageDead(_10);
         StorageDead(_9);
+        StorageDead(_8);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs
index dddcffdd4843..cad7251421eb 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs
@@ -11,8 +11,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) {
     // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]>
     // CHECK: [[ALIGN:_.+]] = const ::ALIGN;
     // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute);
-    // CHECK: [[C:_.+]] = copy ([[B]].0: std::ptr::alignment::AlignmentEnum);
-    // CHECK: [[D:_.+]] = discriminant([[C]]);
-    // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) ->
+    // CHECK: [[C:_.+]] = copy [[B]] as usize (Transmute);
+    // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[C]]) ->
     std::ptr::drop_in_place(ptr)
 }

From 9288c208a2ee5ef34dbaad65d3a4aca65d1d62d8 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sun, 25 Jan 2026 16:35:34 -0800
Subject: [PATCH 208/583] Adjust `Alignment` to emphasize that we don't look at
 its field

---
 library/core/src/ptr/alignment.rs                        | 9 +++++++--
 ...hout_updating_operand.test.GVN.32bit.panic-abort.diff | 8 ++++----
 ...hout_updating_operand.test.GVN.64bit.panic-abort.diff | 8 ++++----
 tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff     | 2 +-
 ...rint_invalid_constant.main.GVN.32bit.panic-abort.diff | 6 +++---
 ...int_invalid_constant.main.GVN.32bit.panic-unwind.diff | 6 +++---
 ...rint_invalid_constant.main.GVN.64bit.panic-abort.diff | 6 +++---
 ...int_invalid_constant.main.GVN.64bit.panic-unwind.diff | 6 +++---
 8 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs
index c3187c9a5c2b..7c34b026e14b 100644
--- a/library/core/src/ptr/alignment.rs
+++ b/library/core/src/ptr/alignment.rs
@@ -13,7 +13,12 @@ use crate::{cmp, fmt, hash, mem, num};
 #[unstable(feature = "ptr_alignment_type", issue = "102070")]
 #[derive(Copy, Clone, PartialEq, Eq)]
 #[repr(transparent)]
-pub struct Alignment(AlignmentEnum);
+pub struct Alignment {
+    // This field is never used directly (nor is the enum),
+    // as it's just there to convey the validity invariant.
+    // (Hopefully it'll eventually be a pattern type instead.)
+    _inner_repr_trick: AlignmentEnum,
+}
 
 // Alignment is `repr(usize)`, but via extra steps.
 const _: () = assert!(size_of::() == size_of::());
@@ -37,7 +42,7 @@ impl Alignment {
     /// assert_eq!(Alignment::MIN.as_usize(), 1);
     /// ```
     #[unstable(feature = "ptr_alignment_type", issue = "102070")]
-    pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0);
+    pub const MIN: Self = Self::new(1).unwrap();
 
     /// Returns the alignment for a type.
     ///
diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
index 2e428b778504..cde4d281f687 100644
--- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
@@ -111,7 +111,7 @@
   
       bb3: {
 -         _22 = handle_alloc_error(move _18) -> unwind unreachable;
-+         _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable;
++         _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}) -> unwind unreachable;
       }
   
       bb4: {
@@ -190,12 +190,12 @@
           StorageLive(_24);
 -         _24 = copy _17 as std::ptr::Alignment (Transmute);
 -         _18 = Layout { size: copy _16, align: move _24 };
-+         _24 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0);
-+         _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }};
++         _24 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }};
++         _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }};
           StorageDead(_24);
           StorageLive(_19);
 -         _19 = std::alloc::Global::alloc_impl_runtime(copy _18, const false) -> [return: bb7, unwind unreachable];
-+         _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable];
++         _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}, const false) -> [return: bb7, unwind unreachable];
       }
   
       bb7: {
diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
index 4531720ee501..ea5622eb9a2f 100644
--- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
@@ -111,7 +111,7 @@
   
       bb3: {
 -         _22 = handle_alloc_error(move _18) -> unwind unreachable;
-+         _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable;
++         _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}) -> unwind unreachable;
       }
   
       bb4: {
@@ -190,12 +190,12 @@
           StorageLive(_24);
 -         _24 = copy _17 as std::ptr::Alignment (Transmute);
 -         _18 = Layout { size: copy _16, align: move _24 };
-+         _24 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0);
-+         _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }};
++         _24 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }};
++         _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }};
           StorageDead(_24);
           StorageLive(_19);
 -         _19 = std::alloc::Global::alloc_impl_runtime(copy _18, const false) -> [return: bb7, unwind unreachable];
-+         _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable];
++         _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}, const false) -> [return: bb7, unwind unreachable];
       }
   
       bb7: {
diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
index 74bbdeb8f744..9e543699da70 100644
--- a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
+++ b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
@@ -42,7 +42,7 @@
           StorageLive(_3);
 -         _3 = const std::ptr::Alignment::of::::{constant#0};
 -         _2 = copy _3 as *mut u8 (Transmute);
-+         _3 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0);
++         _3 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }};
 +         _2 = const {0x1 as *mut u8};
           StorageDead(_3);
           StorageLive(_4);
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
index d0fda06c115c..b48e6fc56f43 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
@@ -63,7 +63,7 @@
   
       bb3: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
-+         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
++         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }};
           StorageDead(_8);
           StorageDead(_2);
           StorageLive(_3);
@@ -73,8 +73,8 @@
           StorageLive(_7);
 -         _7 = copy _1;
 -         _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable];
-+         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
-+         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable];
++         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }};
++         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
index 485ff902a7b9..d0e59c06ff99 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
@@ -64,7 +64,7 @@
   
       bb4: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
-+         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
++         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }};
           StorageDead(_8);
           StorageDead(_2);
           StorageLive(_3);
@@ -74,8 +74,8 @@
           StorageLive(_7);
 -         _7 = copy _1;
 -         _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb5, unwind continue];
-+         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
-+         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
++         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }};
++         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb5, unwind continue];
       }
   
       bb5: {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
index b45a0f4a9bdd..18fc4ac0d87f 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
@@ -63,7 +63,7 @@
   
       bb3: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
-+         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
++         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }};
           StorageDead(_8);
           StorageDead(_2);
           StorageLive(_3);
@@ -73,8 +73,8 @@
           StorageLive(_7);
 -         _7 = copy _1;
 -         _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable];
-+         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
-+         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable];
++         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }};
++         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
index beee899dafe6..c109fe735e89 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
@@ -64,7 +64,7 @@
   
       bb4: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
-+         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
++         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }};
           StorageDead(_8);
           StorageDead(_2);
           StorageLive(_3);
@@ -74,8 +74,8 @@
           StorageLive(_7);
 -         _7 = copy _1;
 -         _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb5, unwind continue];
-+         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
-+         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
++         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }};
++         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb5, unwind continue];
       }
   
       bb5: {

From 841d78181e9e8e93193e038614d5c0a04fcb6210 Mon Sep 17 00:00:00 2001
From: Jieyou Xu 
Date: Sun, 18 Jan 2026 18:29:13 +0800
Subject: [PATCH 209/583] compiletest: implied `needs-target-std` directive for
 `codegen` mode tests by default

A `codegen-llvm` test (and other codegen test mode tests) will now by
default have an implied `//@ needs-target-std` directive, *unless* the
test explicitly has an `#![no_std]`/`#![no_core]` attribute which
disables this implied behavior.

- When a test has both `#![no_std]`/`#![no_core]` and `//@
  needs-target-std`, the explicit `//@ needs-target-std` directive will
  cause the test to be ignored for targets that do not support std
  still.

This is to make it easier to test out-of-tree targets / custom targets
(and targets not tested in r-l/r CI) without requiring target
maintainers to do a bunch of manual `//@ needs-target-std` busywork.

Co-authored-by: Edoardo Marangoni 
---
 .../rustc-dev-guide/src/tests/compiletest.md  | 10 +-
 .../rustc-dev-guide/src/tests/directives.md   |  2 +
 src/tools/compiletest/src/directives.rs       | 15 ++-
 src/tools/compiletest/src/directives/file.rs  | 19 +++-
 src/tools/compiletest/src/directives/tests.rs | 96 ++++++++++++++++++-
 5 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md
index 91a09db7009b..64276a9ea451 100644
--- a/src/doc/rustc-dev-guide/src/tests/compiletest.md
+++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md
@@ -311,14 +311,20 @@ For example, `./x test tests/debuginfo -- --debugger gdb` will only test GDB com
 
 ### Codegen tests
 
-The tests in [`tests/codegen-llvm`] test LLVM code generation. They compile the test
-with the `--emit=llvm-ir` flag to emit LLVM IR. They then run the LLVM
+The tests in [`tests/codegen-llvm`] test LLVM code generation. They compile the
+test with the `--emit=llvm-ir` flag to emit LLVM IR. They then run the LLVM
 [FileCheck] tool. The test is annotated with various `// CHECK` comments to
 check the generated code. See the [FileCheck] documentation for a tutorial and
 more information.
 
 See also the [assembly tests](#assembly-tests) for a similar set of tests.
 
+By default, codegen tests will have `//@ needs-target-std` *implied* (that the
+target needs to support std), *unless* the `#![no_std]`/`#![no_core]` attribute
+was specified in the test source. You can override this behavior and explicitly
+write `//@ needs-target-std` to only run the test when target supports std, even
+if the test is `#![no_std]`/`#![no_core]`.
+
 If you need to work with `#![no_std]` cross-compiling tests, consult the
 [`minicore` test auxiliary](./minicore.md) chapter.
 
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 81c421bc92c4..08371a779e11 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -200,6 +200,8 @@ The following directives will check rustc build settings and target settings:
   on `wasm32-unknown-unknown` target because the target does not support the
   `proc-macro` crate type.
 - `needs-target-std` — ignores if target platform does not have std support.
+  - See also [`#![no_std]`/`#![no_core]` and implied `needs-target-std` for
+    codegen tests](./compiletest.md#codegen-tests).
 - `ignore-backends` — ignores the listed backends, separated by whitespace characters.
   Please note
   that this directive can be overriden with the `--bypass-ignore-backends=[BACKEND]` command line
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index 0263b91f50b8..462d9ae626b0 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -206,7 +206,7 @@ pub(crate) struct TestProps {
     pub add_minicore: bool,
     /// Add these flags to the build of `minicore`.
     pub minicore_compile_flags: Vec,
-    /// Whether line annotatins are required for the given error kind.
+    /// Whether line annotations are required for the given error kind.
     pub dont_require_annotations: HashSet,
     /// Whether pretty printers should be disabled in gdb.
     pub disable_gdb_pretty_printers: bool,
@@ -600,6 +600,19 @@ fn iter_directives(
         }
     }
 
+    // Note: affects all codegen test suites under test mode `codegen`, e.g. `codegen-llvm`.
+    //
+    // Codegen tests automatically receive implied `//@ needs-target-std`, unless
+    // `#![no_std]`/`#![no_core]` attribute was explicitly seen. The rationale is basically to avoid
+    // having to manually maintain a bunch of `//@ needs-target-std` directives esp. for targets
+    // tested/built out-of-tree.
+    if mode == TestMode::Codegen && !file_directives.has_explicit_no_std_core_attribute {
+        let implied_needs_target_std_line =
+            line_directive(testfile, LineNumber::ZERO, "//@ needs-target-std")
+                .expect("valid `needs-target-std` directive line");
+        it(&implied_needs_target_std_line);
+    }
+
     for directive_line in &file_directives.lines {
         it(directive_line);
     }
diff --git a/src/tools/compiletest/src/directives/file.rs b/src/tools/compiletest/src/directives/file.rs
index 57186faa56b8..bde935729121 100644
--- a/src/tools/compiletest/src/directives/file.rs
+++ b/src/tools/compiletest/src/directives/file.rs
@@ -6,20 +6,37 @@ use crate::directives::line::{DirectiveLine, line_directive};
 pub(crate) struct FileDirectives<'a> {
     pub(crate) path: &'a Utf8Path,
     pub(crate) lines: Vec>,
+
+    /// Whether the test source file contains an explicit `#![no_std]`/`#![no_core]` attribute.
+    pub(crate) has_explicit_no_std_core_attribute: bool,
 }
 
 impl<'a> FileDirectives<'a> {
     pub(crate) fn from_file_contents(path: &'a Utf8Path, file_contents: &'a str) -> Self {
         let mut lines = vec![];
+        let mut has_explicit_no_std_core_attribute = false;
 
         for (line_number, ln) in LineNumber::enumerate().zip(file_contents.lines()) {
             let ln = ln.trim();
 
+            // Perform a naive check for lines starting with `#![no_std]`/`#![no_core]`, which
+            // suppresses the implied `//@ needs-target-std` in codegen tests. This ignores
+            // occurrences in ordinary comments.
+            //
+            // This check is imperfect in some edge cases, but we can generally trust our own test
+            // suite to not hit those edge cases (e.g. `#![no_std]`/`#![no_core]` in multi-line
+            // comments or string literals). Tests can write `//@ needs-target-std` manually if
+            // needed.
+            if ln.starts_with("#![no_std]") || ln.starts_with("#![no_core]") {
+                has_explicit_no_std_core_attribute = true;
+                continue;
+            }
+
             if let Some(directive_line) = line_directive(path, line_number, ln) {
                 lines.push(directive_line);
             }
         }
 
-        Self { path, lines }
+        Self { path, lines, has_explicit_no_std_core_attribute }
     }
 }
diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs
index 21d1940d5673..4cd75fcfa511 100644
--- a/src/tools/compiletest/src/directives/tests.rs
+++ b/src/tools/compiletest/src/directives/tests.rs
@@ -105,6 +105,7 @@ fn test_parse_normalize_rule() {
 #[derive(Default)]
 struct ConfigBuilder {
     mode: Option,
+    suite: Option,
     channel: Option,
     edition: Option,
     host: Option,
@@ -126,6 +127,11 @@ impl ConfigBuilder {
         self
     }
 
+    fn suite(&mut self, s: &str) -> &mut Self {
+        self.suite = Some(s.to_owned());
+        self
+    }
+
     fn channel(&mut self, s: &str) -> &mut Self {
         self.channel = Some(s.to_owned());
         self
@@ -196,7 +202,8 @@ impl ConfigBuilder {
             "compiletest",
             "--mode",
             self.mode.as_deref().unwrap_or("ui"),
-            "--suite=ui",
+            "--suite",
+            self.suite.as_deref().unwrap_or("ui"),
             "--compile-lib-path=",
             "--run-lib-path=",
             "--python=",
@@ -1019,6 +1026,93 @@ fn test_needs_target_std() {
     assert!(!check_ignore(&config, "//@ needs-target-std"));
 }
 
+#[test]
+fn implied_needs_target_std() {
+    let config = cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-none").build();
+    // Implied `needs-target-std` due to no `#![no_std]`/`#![no_core]`.
+    assert!(check_ignore(&config, ""));
+    assert!(check_ignore(&config, "//@ needs-target-std"));
+    assert!(!check_ignore(&config, "#![no_std]"));
+    assert!(!check_ignore(&config, "#![no_core]"));
+    // Make sure that `//@ needs-target-std` takes precedence.
+    assert!(check_ignore(
+        &config,
+        r#"
+        //@ needs-target-std
+        #![no_std]
+        "#
+    ));
+    assert!(check_ignore(
+        &config,
+        r#"
+        //@ needs-target-std
+        #![no_core]
+        "#
+    ));
+
+    let config =
+        cfg().mode("codegen").suite("codegen-llvm").target("x86_64-unknown-linux-gnu").build();
+    assert!(!check_ignore(&config, ""));
+    assert!(!check_ignore(&config, "//@ needs-target-std"));
+    assert!(!check_ignore(&config, "#![no_std]"));
+    assert!(!check_ignore(&config, "#![no_core]"));
+    assert!(!check_ignore(
+        &config,
+        r#"
+        //@ needs-target-std
+        #![no_std]
+        "#
+    ));
+    assert!(!check_ignore(
+        &config,
+        r#"
+        //@ needs-target-std
+        #![no_core]
+        "#
+    ));
+
+    let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-none").build();
+    // The implied `//@ needs-target-std` is only applicable for mode=codegen tests.
+    assert!(!check_ignore(&config, ""));
+    assert!(check_ignore(&config, "//@ needs-target-std"));
+    assert!(!check_ignore(&config, "#![no_std]"));
+    assert!(!check_ignore(&config, "#![no_core]"));
+    assert!(check_ignore(
+        &config,
+        r#"
+        //@ needs-target-std
+        #![no_std]
+        "#
+    ));
+    assert!(check_ignore(
+        &config,
+        r#"
+        //@ needs-target-std
+        #![no_core]
+        "#
+    ));
+
+    let config = cfg().mode("ui").suite("ui").target("x86_64-unknown-linux-gnu").build();
+    assert!(!check_ignore(&config, ""));
+    assert!(!check_ignore(&config, "//@ needs-target-std"));
+    assert!(!check_ignore(&config, "#![no_std]"));
+    assert!(!check_ignore(&config, "#![no_core]"));
+    assert!(!check_ignore(
+        &config,
+        r#"
+        //@ needs-target-std
+        #![no_std]
+        "#
+    ));
+    assert!(!check_ignore(
+        &config,
+        r#"
+        //@ needs-target-std
+        #![no_core]
+        "#
+    ));
+}
+
 fn parse_edition_range(line: &str) -> Option {
     let config = cfg().build();
 

From a694b502d4772c6a83e97617270dbe177a93631e Mon Sep 17 00:00:00 2001
From: Trevor Gross 
Date: Sat, 24 Jan 2026 15:30:27 -0600
Subject: [PATCH 210/583] hint: Document that `cold_path` can be used to
 implement `(un)likely`

---
 library/core/src/hint.rs | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index bf8bbd12d82a..be3800956042 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -736,6 +736,38 @@ pub const fn unlikely(b: bool) -> bool {
 ///     }
 /// }
 /// ```
+///
+/// This can also be used to implement `likely` and `unlikely` helpers to hint the condition rather
+/// than the branch:
+///
+/// ```
+/// #![feature(cold_path)]
+/// use core::hint::cold_path;
+///
+/// #[inline(always)]
+/// pub const fn likely(b: bool) -> bool {
+///     if !b {
+///         cold_path();
+///     }
+///     b
+/// }
+///
+/// #[inline(always)]
+/// pub const fn unlikely(b: bool) -> bool {
+///     if b {
+///         cold_path();
+///     }
+///     b
+/// }
+///
+/// fn foo(x: i32) {
+///     if likely(x > 0) {
+///         println!("this branch is likely to be taken");
+///     } else {
+///         println!("this branch is unlikely to be taken");
+///     }
+/// }
+/// ```
 #[unstable(feature = "cold_path", issue = "136873")]
 #[inline(always)]
 pub const fn cold_path() {

From 6c0ae9322258e3fe3121561492d041d2d9f3a982 Mon Sep 17 00:00:00 2001
From: Trevor Gross 
Date: Sat, 24 Jan 2026 15:26:12 -0600
Subject: [PATCH 211/583] hint: Update the tracking issue for `likely_unlikely`

These were split from the `cold_path` tracking issue.
---
 library/core/src/hint.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index be3800956042..f4544304a4a4 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -649,7 +649,7 @@ pub const fn must_use(value: T) -> T {
 ///     }
 /// }
 /// ```
-#[unstable(feature = "likely_unlikely", issue = "136873")]
+#[unstable(feature = "likely_unlikely", issue = "151619")]
 #[inline(always)]
 pub const fn likely(b: bool) -> bool {
     crate::intrinsics::likely(b)
@@ -699,7 +699,7 @@ pub const fn likely(b: bool) -> bool {
 ///     }
 /// }
 /// ```
-#[unstable(feature = "likely_unlikely", issue = "136873")]
+#[unstable(feature = "likely_unlikely", issue = "151619")]
 #[inline(always)]
 pub const fn unlikely(b: bool) -> bool {
     crate::intrinsics::unlikely(b)

From 29596f87becfc3ec7dc3408c848850f45f05b37a Mon Sep 17 00:00:00 2001
From: Juho Kahala <57393910+quaternic@users.noreply.github.com>
Date: Mon, 26 Jan 2026 06:28:42 +0200
Subject: [PATCH 212/583] rename uN::{gather,scatter}_bits to
 uN::{extract,deposit}_bits

---
 library/core/src/num/int_bits.rs              | 20 ++--
 library/core/src/num/uint_macros.rs           | 16 ++--
 library/coretests/benches/num/int_bits/mod.rs |  4 +-
 library/coretests/tests/num/uint_macros.rs    | 96 +++++++++----------
 4 files changed, 68 insertions(+), 68 deletions(-)

diff --git a/library/core/src/num/int_bits.rs b/library/core/src/num/int_bits.rs
index 44be6de47327..7e5459192235 100644
--- a/library/core/src/num/int_bits.rs
+++ b/library/core/src/num/int_bits.rs
@@ -1,12 +1,12 @@
-//! Implementations for `uN::gather_bits` and `uN::scatter_bits`
+//! Implementations for `uN::extract_bits` and `uN::deposit_bits`
 //!
 //! For the purposes of this implementation, the operations can be thought
 //! of as operating on the input bits as a list, starting from the least
-//! significant bit. Gathering is like `Vec::retain` that deletes bits
-//! where the mask has a zero. Scattering is like doing the inverse by
-//! inserting the zeros that gathering would delete.
+//! significant bit. Extraction is like `Vec::retain` that deletes bits
+//! where the mask has a zero. Deposition is like doing the inverse by
+//! inserting the zeros that extraction would delete.
 //!
-//! Key observation: Each bit that is gathered/scattered needs to be
+//! Key observation: Each extracted or deposited bit needs to be
 //! shifted by the count of zeros up to the corresponding mask bit.
 //!
 //! With that in mind, the general idea is to decompose the operation into
@@ -14,7 +14,7 @@
 //! of the bits by `n = 1 << stage`. The masks for each stage are computed
 //! via prefix counts of zeros in the mask.
 //!
-//! # Gathering
+//! # Extraction
 //!
 //! Consider the input as a sequence of runs of data (bitstrings A,B,C,...),
 //! split by fixed-width groups of zeros ('.'), initially at width `n = 1`.
@@ -36,9 +36,9 @@
 //! ........abbbcccccddeghh
 //! ```
 //!
-//! # Scattering
+//! # Deposition
 //!
-//! For `scatter_bits`, the stages are reversed. We start with a single run of
+//! For `deposit_bits`, the stages are reversed. We start with a single run of
 //! data in the low bits. Each stage then splits each run of data in two by
 //! shifting part of it left by `n`, which is halved each stage.
 //! ```text
@@ -100,7 +100,7 @@ macro_rules! uint_impl {
             }
 
             #[inline(always)]
-            pub(in super::super) const fn gather_impl(mut x: $U, sparse: $U) -> $U {
+            pub(in super::super) const fn extract_impl(mut x: $U, sparse: $U) -> $U {
                 let masks = prepare(sparse);
                 x &= sparse;
                 let mut stage = 0;
@@ -131,7 +131,7 @@ macro_rules! uint_impl {
                 x
             }
             #[inline(always)]
-            pub(in super::super) const fn scatter_impl(mut x: $U, sparse: $U) -> $U {
+            pub(in super::super) const fn deposit_impl(mut x: $U, sparse: $U) -> $U {
                 let masks = prepare(sparse);
                 let mut stage = STAGES;
                 while stage > 0 {
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 57f0cd48fbe8..c48320c0eab3 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -507,15 +507,15 @@ macro_rules! uint_impl {
         /// #![feature(uint_gather_scatter_bits)]
         #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1011_1100;")]
         ///
-        /// assert_eq!(n.gather_bits(0b0010_0100), 0b0000_0011);
-        /// assert_eq!(n.gather_bits(0xF0), 0b0000_1011);
+        /// assert_eq!(n.extract_bits(0b0010_0100), 0b0000_0011);
+        /// assert_eq!(n.extract_bits(0xF0), 0b0000_1011);
         /// ```
         #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
-        pub const fn gather_bits(self, mask: Self) -> Self {
-            crate::num::int_bits::$ActualT::gather_impl(self as $ActualT, mask as $ActualT) as $SelfT
+        pub const fn extract_bits(self, mask: Self) -> Self {
+            crate::num::int_bits::$ActualT::extract_impl(self as $ActualT, mask as $ActualT) as $SelfT
         }
 
         /// Returns an integer with the least significant bits of `self`
@@ -524,15 +524,15 @@ macro_rules! uint_impl {
         /// #![feature(uint_gather_scatter_bits)]
         #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1010_1101;")]
         ///
-        /// assert_eq!(n.scatter_bits(0b0101_0101), 0b0101_0001);
-        /// assert_eq!(n.scatter_bits(0xF0), 0b1101_0000);
+        /// assert_eq!(n.deposit_bits(0b0101_0101), 0b0101_0001);
+        /// assert_eq!(n.deposit_bits(0xF0), 0b1101_0000);
         /// ```
         #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
-        pub const fn scatter_bits(self, mask: Self) -> Self {
-            crate::num::int_bits::$ActualT::scatter_impl(self as $ActualT, mask as $ActualT) as $SelfT
+        pub const fn deposit_bits(self, mask: Self) -> Self {
+            crate::num::int_bits::$ActualT::deposit_impl(self as $ActualT, mask as $ActualT) as $SelfT
         }
 
         /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit,
diff --git a/library/coretests/benches/num/int_bits/mod.rs b/library/coretests/benches/num/int_bits/mod.rs
index c6ec51f248ba..65ba48609e80 100644
--- a/library/coretests/benches/num/int_bits/mod.rs
+++ b/library/coretests/benches/num/int_bits/mod.rs
@@ -50,8 +50,8 @@ macro_rules! bench_mask_kind {
     ($mask_kind:ident, $mask:expr) => {
         mod $mask_kind {
             use super::{Data, ITERATIONS, U};
-            bench_template!(U::gather_bits, gather_bits, $mask);
-            bench_template!(U::scatter_bits, scatter_bits, $mask);
+            bench_template!(U::extract_bits, extract_bits, $mask);
+            bench_template!(U::deposit_bits, deposit_bits, $mask);
         }
     };
 }
diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs
index 72d500e575fa..7c4fb22599c0 100644
--- a/library/coretests/tests/num/uint_macros.rs
+++ b/library/coretests/tests/num/uint_macros.rs
@@ -127,50 +127,50 @@ macro_rules! uint_module {
                 assert_eq_const_safe!($T: _1.swap_bytes(), _1);
             }
 
-            fn test_gather_bits() {
-                assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0011), 0b_0001);
-                assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_0110), 0b_0010);
-                assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0000_1100), 0b_0001);
-                assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0001_1000), 0b_0000);
-                assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0011_0000), 0b_0010);
-                assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b0110_0000), 0b_0001);
-                assert_eq_const_safe!($T: $T::gather_bits(0b1010_0101, 0b1100_0000), 0b_0010);
+            fn test_extract_bits() {
+                assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_0011), 0b_0001);
+                assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_0110), 0b_0010);
+                assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_1100), 0b_0001);
+                assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0001_1000), 0b_0000);
+                assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0011_0000), 0b_0010);
+                assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0110_0000), 0b_0001);
+                assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b1100_0000), 0b_0010);
 
-                assert_eq_const_safe!($T: A.gather_bits(_0), 0);
-                assert_eq_const_safe!($T: B.gather_bits(_0), 0);
-                assert_eq_const_safe!($T: C.gather_bits(_0), 0);
-                assert_eq_const_safe!($T: _0.gather_bits(A), 0);
-                assert_eq_const_safe!($T: _0.gather_bits(B), 0);
-                assert_eq_const_safe!($T: _0.gather_bits(C), 0);
+                assert_eq_const_safe!($T: A.extract_bits(_0), 0);
+                assert_eq_const_safe!($T: B.extract_bits(_0), 0);
+                assert_eq_const_safe!($T: C.extract_bits(_0), 0);
+                assert_eq_const_safe!($T: _0.extract_bits(A), 0);
+                assert_eq_const_safe!($T: _0.extract_bits(B), 0);
+                assert_eq_const_safe!($T: _0.extract_bits(C), 0);
 
-                assert_eq_const_safe!($T: A.gather_bits(_1), A);
-                assert_eq_const_safe!($T: B.gather_bits(_1), B);
-                assert_eq_const_safe!($T: C.gather_bits(_1), C);
-                assert_eq_const_safe!($T: _1.gather_bits(0b0010_0001), 0b0000_0011);
-                assert_eq_const_safe!($T: _1.gather_bits(0b0010_1100), 0b0000_0111);
-                assert_eq_const_safe!($T: _1.gather_bits(0b0111_1001), 0b0001_1111);
+                assert_eq_const_safe!($T: A.extract_bits(_1), A);
+                assert_eq_const_safe!($T: B.extract_bits(_1), B);
+                assert_eq_const_safe!($T: C.extract_bits(_1), C);
+                assert_eq_const_safe!($T: _1.extract_bits(0b0010_0001), 0b0000_0011);
+                assert_eq_const_safe!($T: _1.extract_bits(0b0010_1100), 0b0000_0111);
+                assert_eq_const_safe!($T: _1.extract_bits(0b0111_1001), 0b0001_1111);
             }
 
-            fn test_scatter_bits() {
-                assert_eq_const_safe!($T: $T::scatter_bits(0b1111, 0b1001_0110), 0b1001_0110);
-                assert_eq_const_safe!($T: $T::scatter_bits(0b0001, 0b1001_0110), 0b0000_0010);
-                assert_eq_const_safe!($T: $T::scatter_bits(0b0010, 0b1001_0110), 0b0000_0100);
-                assert_eq_const_safe!($T: $T::scatter_bits(0b0100, 0b1001_0110), 0b0001_0000);
-                assert_eq_const_safe!($T: $T::scatter_bits(0b1000, 0b1001_0110), 0b1000_0000);
+            fn test_deposit_bits() {
+                assert_eq_const_safe!($T: $T::deposit_bits(0b1111, 0b1001_0110), 0b1001_0110);
+                assert_eq_const_safe!($T: $T::deposit_bits(0b0001, 0b1001_0110), 0b0000_0010);
+                assert_eq_const_safe!($T: $T::deposit_bits(0b0010, 0b1001_0110), 0b0000_0100);
+                assert_eq_const_safe!($T: $T::deposit_bits(0b0100, 0b1001_0110), 0b0001_0000);
+                assert_eq_const_safe!($T: $T::deposit_bits(0b1000, 0b1001_0110), 0b1000_0000);
 
-                assert_eq_const_safe!($T: A.scatter_bits(_0), 0);
-                assert_eq_const_safe!($T: B.scatter_bits(_0), 0);
-                assert_eq_const_safe!($T: C.scatter_bits(_0), 0);
-                assert_eq_const_safe!($T: _0.scatter_bits(A), 0);
-                assert_eq_const_safe!($T: _0.scatter_bits(B), 0);
-                assert_eq_const_safe!($T: _0.scatter_bits(C), 0);
+                assert_eq_const_safe!($T: A.deposit_bits(_0), 0);
+                assert_eq_const_safe!($T: B.deposit_bits(_0), 0);
+                assert_eq_const_safe!($T: C.deposit_bits(_0), 0);
+                assert_eq_const_safe!($T: _0.deposit_bits(A), 0);
+                assert_eq_const_safe!($T: _0.deposit_bits(B), 0);
+                assert_eq_const_safe!($T: _0.deposit_bits(C), 0);
 
-                assert_eq_const_safe!($T: A.scatter_bits(_1), A);
-                assert_eq_const_safe!($T: B.scatter_bits(_1), B);
-                assert_eq_const_safe!($T: C.scatter_bits(_1), C);
-                assert_eq_const_safe!($T: _1.scatter_bits(A), A);
-                assert_eq_const_safe!($T: _1.scatter_bits(B), B);
-                assert_eq_const_safe!($T: _1.scatter_bits(C), C);
+                assert_eq_const_safe!($T: A.deposit_bits(_1), A);
+                assert_eq_const_safe!($T: B.deposit_bits(_1), B);
+                assert_eq_const_safe!($T: C.deposit_bits(_1), C);
+                assert_eq_const_safe!($T: _1.deposit_bits(A), A);
+                assert_eq_const_safe!($T: _1.deposit_bits(B), B);
+                assert_eq_const_safe!($T: _1.deposit_bits(C), C);
             }
 
             fn test_reverse_bits() {
@@ -389,7 +389,7 @@ macro_rules! uint_module {
 
         #[cfg(not(miri))] // Miri is too slow
         #[test]
-        fn test_lots_of_gather_scatter() {
+        fn test_lots_of_extract_deposit() {
             // Generate a handful of bit patterns to use as inputs
             let xs = {
                 let mut xs = vec![];
@@ -414,7 +414,7 @@ macro_rules! uint_module {
 
             for sparse in sparse_masks {
                 // Collect the set bits to sequential low bits
-                let dense = sparse.gather_bits(sparse);
+                let dense = sparse.extract_bits(sparse);
                 let count = sparse.count_ones();
                 assert_eq!(count, dense.count_ones());
                 assert_eq!(count, dense.trailing_ones());
@@ -424,27 +424,27 @@ macro_rules! uint_module {
                 let mut bit = 1 as $T;
                 for _ in 0..count {
                     let lowest_one = t.isolate_lowest_one();
-                    assert_eq!(lowest_one, bit.scatter_bits(sparse));
-                    assert_eq!(bit, lowest_one.gather_bits(sparse));
+                    assert_eq!(lowest_one, bit.deposit_bits(sparse));
+                    assert_eq!(bit, lowest_one.extract_bits(sparse));
                     t ^= lowest_one;
                     bit <<= 1;
                 }
                 // Other bits are ignored
-                assert_eq!(0, bit.wrapping_neg().scatter_bits(sparse));
-                assert_eq!(0, (!sparse).gather_bits(sparse));
+                assert_eq!(0, bit.wrapping_neg().deposit_bits(sparse));
+                assert_eq!(0, (!sparse).extract_bits(sparse));
 
                 for &x in &xs {
                     // Gather bits from `x & sparse` to `dense`
-                    let dx = x.gather_bits(sparse);
+                    let dx = x.extract_bits(sparse);
                     assert_eq!(dx & !dense, 0);
 
                     // Scatter bits from `x & dense` to `sparse`
-                    let sx = x.scatter_bits(sparse);
+                    let sx = x.deposit_bits(sparse);
                     assert_eq!(sx & !sparse, 0);
 
                     // The other recovers the input (within the mask)
-                    assert_eq!(dx.scatter_bits(sparse), x & sparse);
-                    assert_eq!(sx.gather_bits(sparse), x & dense);
+                    assert_eq!(dx.deposit_bits(sparse), x & sparse);
+                    assert_eq!(sx.extract_bits(sparse), x & dense);
                 }
             }
         }

From 79549a053d0fff919a1e51c20e2fd7328e5a9ad0 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Mon, 26 Jan 2026 04:35:30 +0000
Subject: [PATCH 213/583] Prepare for merging from rust-lang/rust

This updates the rust-version file to 873d4682c7d285540b8f28bfe637006cef8918a6.
---
 src/doc/rustc-dev-guide/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index d6e02b0dd2b2..ccc0b55d4dc5 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-5c49c4f7c8393c861b849441d27f5d40e0f1e33b
+873d4682c7d285540b8f28bfe637006cef8918a6

From 9fd291ed7ee4d1313cfdaa5a5651c77b0a57fd33 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Mon, 19 Jan 2026 20:09:30 +0800
Subject: [PATCH 214/583] Fix not complete 'else' before tuple

Example
---
```rust
fn foo() -> (i32, i32) {
    if foo {} el$0
    (2, 3)
}
```

**Before this PR**

```rust
...
kw crate::
kw false
kw for
...
```

**After this PR**

```rust
...
kw crate::
kw else
kw else if
kw false
kw for
...
```
---
 .../ide-completion/src/context/analysis.rs    |  7 ++---
 .../ide-completion/src/tests/expression.rs    | 26 +++++++++++++++++++
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 0db93b0837cd..8842d29c8d90 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -2030,9 +2030,10 @@ fn is_after_if_expr(node: SyntaxNode) -> bool {
         Some(stmt) => stmt.syntax().clone(),
         None => node,
     };
-    let prev_sibling =
-        non_trivia_sibling(node.into(), Direction::Prev).and_then(NodeOrToken::into_node);
-    iter::successors(prev_sibling, |it| it.last_child_or_token()?.into_node())
+    let Some(prev_token) = previous_non_trivia_token(node) else { return false };
+    prev_token
+        .parent_ancestors()
+        .take_while(|it| it.text_range().end() == prev_token.text_range().end())
         .find_map(ast::IfExpr::cast)
         .is_some()
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index ff005a29218b..df39591a3346 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -2182,6 +2182,32 @@ fn foo() { match () { () => if foo {} $0, _ => (), } }
             kw ref
         "#]],
     );
+    check(
+        r#"
+fn foo() -> (i32, i32) { if foo {} el$0 (2, 3) }
+"#,
+        expect![[r#"
+            fn foo fn() -> (i32, i32)
+            bt u32                u32
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+            ex foo()
+        "#]],
+    );
     // FIXME: support else completion after ast::RecordExprField
 }
 

From 30e41dec0c7d43397b27fac4f5eb9958138f7b41 Mon Sep 17 00:00:00 2001
From: Martin Nordholts 
Date: Mon, 26 Jan 2026 06:00:20 +0100
Subject: [PATCH 215/583] compiletest: Parse aux `proc-macro` directive into
 struct

To minimize the diff when adding features the aux `proc-macro`
directive.
---
 .../compiletest/src/directives/auxiliary.rs   | 19 +++++++++++++++----
 src/tools/compiletest/src/runtest.rs          |  6 +++---
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs
index 1d5b7926a8e3..14cbab640eb6 100644
--- a/src/tools/compiletest/src/directives/auxiliary.rs
+++ b/src/tools/compiletest/src/directives/auxiliary.rs
@@ -25,6 +25,13 @@ pub struct AuxCrate {
     pub path: String,
 }
 
+/// The value of a `proc-macro` directive.
+#[derive(Clone, Debug, Default)]
+pub(crate) struct ProcMacro {
+    /// With `proc-macro: bar.rs` this will be `bar.rs`.
+    pub path: String,
+}
+
 /// Properties parsed from `aux-*` test directives.
 #[derive(Clone, Debug, Default)]
 pub(crate) struct AuxProps {
@@ -37,7 +44,7 @@ pub(crate) struct AuxProps {
     /// to build and pass with the `--extern` flag.
     pub(crate) crates: Vec,
     /// Same as `builds`, but for proc-macros.
-    pub(crate) proc_macros: Vec,
+    pub(crate) proc_macros: Vec,
     /// Similar to `builds`, but also uses the resulting dylib as a
     /// `-Zcodegen-backend` when compiling the test file.
     pub(crate) codegen_backend: Option,
@@ -53,7 +60,7 @@ impl AuxProps {
             .chain(builds.iter().map(String::as_str))
             .chain(bins.iter().map(String::as_str))
             .chain(crates.iter().map(|c| c.path.as_str()))
-            .chain(proc_macros.iter().map(String::as_str))
+            .chain(proc_macros.iter().map(|p| p.path.as_str()))
             .chain(codegen_backend.iter().map(String::as_str))
     }
 }
@@ -74,8 +81,8 @@ pub(super) fn parse_and_update_aux(
     config.push_name_value_directive(ln, AUX_BUILD, &mut aux.builds, |r| r.trim().to_string());
     config.push_name_value_directive(ln, AUX_BIN, &mut aux.bins, |r| r.trim().to_string());
     config.push_name_value_directive(ln, AUX_CRATE, &mut aux.crates, parse_aux_crate);
-    config
-        .push_name_value_directive(ln, PROC_MACRO, &mut aux.proc_macros, |r| r.trim().to_string());
+    config.push_name_value_directive(ln, PROC_MACRO, &mut aux.proc_macros, parse_proc_macro);
+
     if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
         aux.codegen_backend = Some(r.trim().to_owned());
     }
@@ -99,3 +106,7 @@ fn parse_aux_crate(r: String) -> AuxCrate {
 
     AuxCrate { extern_modifiers: modifiers, name, path }
 }
+
+fn parse_proc_macro(r: String) -> ProcMacro {
+    ProcMacro { path: r.trim().to_string() }
+}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index dfbe84d5da72..2bfb73f05d16 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1298,13 +1298,13 @@ impl<'test> TestCx<'test> {
         }
 
         for proc_macro in &self.props.aux.proc_macros {
-            self.build_auxiliary(proc_macro, &aux_dir, Some(AuxType::ProcMacro));
-            let crate_name = path_to_crate_name(proc_macro);
+            self.build_auxiliary(&proc_macro.path, &aux_dir, Some(AuxType::ProcMacro));
+            let crate_name = path_to_crate_name(&proc_macro.path);
             add_extern(
                 rustc,
                 None, // `extern_modifiers`
                 &crate_name,
-                proc_macro,
+                &proc_macro.path,
                 AuxType::ProcMacro,
             );
         }

From 2f49df3140280f1cdcaeb653c3fb9774de080f19 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Mon, 26 Jan 2026 15:41:43 +0800
Subject: [PATCH 216/583] Improve filter predicate to length cond

---
 .../crates/ide-assists/src/handlers/extract_function.rs         | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 2230c391cbad..f2363c6f7ba2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -257,7 +257,7 @@ fn make_function_name(
 
     let mut name = body
         .suggest_name()
-        .filter(|name| name.contains('_'))
+        .filter(|name| name.len() > 2)
         .unwrap_or_else(|| default_name.to_owned());
     let mut counter = 0;
     while names_in_scope.contains(&name) {

From 38bb09eeff34272c36dfbaeeb31e62f9e0674698 Mon Sep 17 00:00:00 2001
From: Edwin Cheng 
Date: Mon, 26 Jan 2026 16:45:48 +0800
Subject: [PATCH 217/583] Fix rust-src installation command in FAQ

Correct the command to install rust-src in the FAQ.
---
 src/tools/rust-analyzer/docs/book/src/faq.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/docs/book/src/faq.md b/src/tools/rust-analyzer/docs/book/src/faq.md
index 8c143ab94935..9eeb2ae55539 100644
--- a/src/tools/rust-analyzer/docs/book/src/faq.md
+++ b/src/tools/rust-analyzer/docs/book/src/faq.md
@@ -4,7 +4,7 @@
 
 rust-analyzer fails to resolve `None`, and thinks you are binding to a variable
 named `None`. That's usually a sign of a corrupted sysroot. Try removing and re-installing
-it: `rustup component remove rust-src` then `rustup component install rust-src`.
+it: `rustup component remove rust-src` then `rustup component add rust-src`.
 
 ### Rust Analyzer and Cargo compete over the build lock
 

From f6efe7e1d52abab87cd897fb96ccc6bad68c64b3 Mon Sep 17 00:00:00 2001
From: Adwin White 
Date: Mon, 26 Jan 2026 17:12:32 +0800
Subject: [PATCH 218/583] don't return incorrectly constrained opaques in
 `method_autoderef_steps`

---
 compiler/rustc_hir_typeck/src/method/probe.rs | 36 ++++++++++++----
 .../src/infer/canonical/query_response.rs     |  6 ++-
 .../opaques/method_autoderef_constraints.rs   | 38 ++++++++++++++++
 .../method_autoderef_constraints.stderr       | 43 +++++++++++++++++++
 4 files changed, 113 insertions(+), 10 deletions(-)
 create mode 100644 tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs
 create mode 100644 tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr

diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 31ca0e46091c..2a92a4b48edc 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -415,10 +415,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     infcx.instantiate_canonical(span, &query_input.canonical);
                 let query::MethodAutoderefSteps { predefined_opaques_in_body: _, self_ty } = value;
                 debug!(?self_ty, ?query_input, "probe_op: Mode::Path");
+                let prev_opaque_entries = self.inner.borrow_mut().opaque_types().num_entries();
                 MethodAutoderefStepsResult {
                     steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
-                        self_ty: self
-                            .make_query_response_ignoring_pending_obligations(var_values, self_ty),
+                        self_ty: self.make_query_response_ignoring_pending_obligations(
+                            var_values,
+                            self_ty,
+                            prev_opaque_entries,
+                        ),
                         self_ty_is_opaque: false,
                         autoderefs: 0,
                         from_unsafe_deref: false,
@@ -607,6 +611,7 @@ pub(crate) fn method_autoderef_steps<'tcx>(
             debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`");
         }
     }
+    let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries();
 
     // We accept not-yet-defined opaque types in the autoderef
     // chain to support recursive calls. We do error if the final
@@ -650,8 +655,11 @@ pub(crate) fn method_autoderef_steps<'tcx>(
             .zip(reachable_via_deref)
             .map(|((ty, d), reachable_via_deref)| {
                 let step = CandidateStep {
-                    self_ty: infcx
-                        .make_query_response_ignoring_pending_obligations(inference_vars, ty),
+                    self_ty: infcx.make_query_response_ignoring_pending_obligations(
+                        inference_vars,
+                        ty,
+                        prev_opaque_entries,
+                    ),
                     self_ty_is_opaque: self_ty_is_opaque(ty),
                     autoderefs: d,
                     from_unsafe_deref: reached_raw_pointer,
@@ -671,8 +679,11 @@ pub(crate) fn method_autoderef_steps<'tcx>(
             .by_ref()
             .map(|(ty, d)| {
                 let step = CandidateStep {
-                    self_ty: infcx
-                        .make_query_response_ignoring_pending_obligations(inference_vars, ty),
+                    self_ty: infcx.make_query_response_ignoring_pending_obligations(
+                        inference_vars,
+                        ty,
+                        prev_opaque_entries,
+                    ),
                     self_ty_is_opaque: self_ty_is_opaque(ty),
                     autoderefs: d,
                     from_unsafe_deref: reached_raw_pointer,
@@ -692,11 +703,19 @@ pub(crate) fn method_autoderef_steps<'tcx>(
     let opt_bad_ty = match final_ty.kind() {
         ty::Infer(ty::TyVar(_)) if !self_ty_is_opaque(final_ty) => Some(MethodAutoderefBadTy {
             reached_raw_pointer,
-            ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
+            ty: infcx.make_query_response_ignoring_pending_obligations(
+                inference_vars,
+                final_ty,
+                prev_opaque_entries,
+            ),
         }),
         ty::Error(_) => Some(MethodAutoderefBadTy {
             reached_raw_pointer,
-            ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
+            ty: infcx.make_query_response_ignoring_pending_obligations(
+                inference_vars,
+                final_ty,
+                prev_opaque_entries,
+            ),
         }),
         ty::Array(elem_ty, _) => {
             let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1;
@@ -704,6 +723,7 @@ pub(crate) fn method_autoderef_steps<'tcx>(
                 self_ty: infcx.make_query_response_ignoring_pending_obligations(
                     inference_vars,
                     Ty::new_slice(infcx.tcx, *elem_ty),
+                    prev_opaque_entries,
                 ),
                 self_ty_is_opaque: false,
                 autoderefs,
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 65b0f8211432..846123b8aad9 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -24,7 +24,8 @@ use crate::infer::canonical::{
 };
 use crate::infer::region_constraints::RegionConstraintData;
 use crate::infer::{
-    DefineOpaqueTypes, InferCtxt, InferOk, InferResult, SubregionOrigin, TypeOutlivesConstraint,
+    DefineOpaqueTypes, InferCtxt, InferOk, InferResult, OpaqueTypeStorageEntries, SubregionOrigin,
+    TypeOutlivesConstraint,
 };
 use crate::traits::query::NoSolution;
 use crate::traits::{ObligationCause, PredicateObligations, ScrubbedTraitError, TraitEngine};
@@ -81,6 +82,7 @@ impl<'tcx> InferCtxt<'tcx> {
         &self,
         inference_vars: CanonicalVarValues<'tcx>,
         answer: T,
+        prev_entries: OpaqueTypeStorageEntries,
     ) -> Canonical<'tcx, QueryResponse<'tcx, T>>
     where
         T: Debug + TypeFoldable>,
@@ -96,7 +98,7 @@ impl<'tcx> InferCtxt<'tcx> {
             self.inner
                 .borrow_mut()
                 .opaque_type_storage
-                .iter_opaque_types()
+                .opaque_types_added_since(prev_entries)
                 .map(|(k, v)| (k, v.ty))
                 .collect()
         } else {
diff --git a/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs
new file mode 100644
index 000000000000..d8375a62bb37
--- /dev/null
+++ b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.rs
@@ -0,0 +1,38 @@
+//@ compile-flags: -Znext-solver
+
+// Regression test for trait-system-refactor-initiative/issues/263
+// Previously `method_auto_deref_steps` would also return opaque
+// types which have already been defined in the parent context.
+//
+// We then handled these opaque types by emitting `AliasRelate` goals
+// when instantiating its result, assuming that operation to be infallible.
+// By returning opaque type constraints from the parent context and
+// constraining the hidden type without reproving the item bounds of
+// the opaque, this ended up causing ICE.
+
+use std::ops::Deref;
+trait Trait {}
+struct Inv(*mut T);
+impl Trait for i32 {}
+impl Deref for Inv {
+    type Target = u32;
+    fn deref(&self) -> &Self::Target {
+        todo!()
+    }
+}
+
+fn mk() -> T { todo!() }
+fn foo() -> Inv {
+    //~^ ERROR: the trait bound `u32: Trait` is not satisfied [E0277]
+    let mut x: Inv<_> = mk();
+    if false {
+        return x;
+        //~^ ERROR: the trait bound `u32: Trait` is not satisfied [E0277]
+    }
+
+    x.count_ones();
+    x
+    //~^ ERROR: mismatched types [E0308]
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr
new file mode 100644
index 000000000000..c421222309a0
--- /dev/null
+++ b/tests/ui/traits/next-solver/opaques/method_autoderef_constraints.stderr
@@ -0,0 +1,43 @@
+error[E0277]: the trait bound `u32: Trait` is not satisfied
+  --> $DIR/method_autoderef_constraints.rs:29:16
+   |
+LL |         return x;
+   |                ^ the trait `Trait` is not implemented for `u32`
+   |
+help: the trait `Trait` is implemented for `i32`
+  --> $DIR/method_autoderef_constraints.rs:16:1
+   |
+LL | impl Trait for i32 {}
+   | ^^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/method_autoderef_constraints.rs:34:5
+   |
+LL | fn foo() -> Inv {
+   |             ---------------
+   |             |   |
+   |             |   the expected opaque type
+   |             expected `Inv` because of return type
+...
+LL |     x
+   |     ^ types differ
+   |
+   = note: expected struct `Inv`
+              found struct `Inv`
+
+error[E0277]: the trait bound `u32: Trait` is not satisfied
+  --> $DIR/method_autoderef_constraints.rs:25:1
+   |
+LL | fn foo() -> Inv {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `u32`
+   |
+help: the trait `Trait` is implemented for `i32`
+  --> $DIR/method_autoderef_constraints.rs:16:1
+   |
+LL | impl Trait for i32 {}
+   | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.

From dab7c0923e7b43c95c5a64259a4e020ae38827ed Mon Sep 17 00:00:00 2001
From: Boxy 
Date: Tue, 20 Jan 2026 14:26:30 +0000
Subject: [PATCH 219/583] Misc cleanups to borrowck crate

---
 .../src/type_check/canonical.rs               | 15 +++-
 .../src/type_check/input_output.rs            | 77 +++++++++++++------
 compiler/rustc_borrowck/src/type_check/mod.rs | 23 +++---
 compiler/rustc_middle/src/ty/util.rs          |  5 +-
 .../normalization-generality-2.rs             |  3 +
 .../normalization-generality.rs               |  3 +
 .../higher-ranked/trait-bounds/issue-88446.rs |  3 +
 .../normalize-under-binder/issue-89436.rs     |  3 +
 .../normalize-under-binder/issue-90638.rs     |  3 +
 .../issue-112604-closure-output-normalize.rs  |  3 +
 10 files changed, 100 insertions(+), 38 deletions(-)

diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index aece0bda3469..21ea2c70fbb3 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -343,8 +343,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             return;
         }
 
-        // FIXME: Ideally MIR types are normalized, but this is not always true.
-        let mir_ty = self.normalize(mir_ty, Locations::All(span));
+        // This is a hack. `body.local_decls` are not necessarily normalized in the old
+        // solver due to not deeply normalizing in writeback. So we must re-normalize here.
+        //
+        // I am not sure of a test case where this actually matters. There is a similar
+        // hack in `equate_inputs_and_outputs` which does have associated test cases.
+        let mir_ty = match self.infcx.next_trait_solver() {
+            true => mir_ty,
+            false => self.normalize(mir_ty, Locations::All(span)),
+        };
 
         let cause = ObligationCause::dummy_with_span(span);
         let param_env = self.infcx.param_env;
@@ -353,6 +360,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             ConstraintCategory::Boring,
             type_op::custom::CustomTypeOp::new(
                 |ocx| {
+                    // The `AscribeUserType` query would normally emit a wf
+                    // obligation for the unnormalized user_ty here. This is
+                    // where the "incorrectly skips the WF checks we normally do"
+                    // happens
                     let user_ty = ocx.normalize(&cause, param_env, user_ty);
                     ocx.eq(&cause, param_env, user_ty, mir_ty)?;
                     Ok(())
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index f3b9dcc90a84..3bce78b4e2e2 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -126,6 +126,31 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         );
     }
 
+    //  FIXME(BoxyUwU): This should probably be part of a larger borrowck dev-guide chapter
+    //
+    /// Enforce that the types of the locals corresponding to the inputs and output of
+    /// the body are equal to those of the (normalized) signature.
+    ///
+    /// This is necessary for two reasons:
+    /// - Locals in the MIR all start out with `'erased` regions and then are replaced
+    ///    with unconstrained nll vars. If we have a function returning `&'a u32` then
+    ///    the local `_0: &'?10 u32` needs to have its region var equated with the nll
+    ///    var representing `'a`. i.e. borrow check must uphold that `'?10 = 'a`.
+    /// - When computing the normalized signature we may introduce new unconstrained nll
+    ///    vars due to higher ranked where clauses ([#136547]). We then wind up with implied
+    ///    bounds involving these vars.
+    ///
+    ///    For this reason it is important that we equate with the *normalized* signature
+    ///    which was produced when computing implied bounds. If we do not do so then we will
+    ///    wind up with implied bounds on nll vars which cannot actually be used as the nll
+    ///    var never gets related to anything.
+    ///
+    /// For 'closure-like' bodies this function effectively relates the *inferred* signature
+    /// of the closure against the locals corresponding to the closure's inputs/output. It *does
+    /// not* relate the user provided types for the signature to the locals, this is handled
+    /// separately by: [`TypeChecker::check_signature_annotation`].
+    ///
+    /// [#136547]: 
     #[instrument(skip(self), level = "debug")]
     pub(super) fn equate_inputs_and_outputs(&mut self, normalized_inputs_and_output: &[Ty<'tcx>]) {
         let (&normalized_output_ty, normalized_input_tys) =
@@ -173,38 +198,44 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             );
         }
 
-        // Return types are a bit more complex. They may contain opaque `impl Trait` types.
-        let mir_output_ty = self.body.local_decls[RETURN_PLACE].ty;
+        // Equate expected output ty with the type of the RETURN_PLACE in MIR
+        let mir_output_ty = self.body.return_ty();
         let output_span = self.body.local_decls[RETURN_PLACE].source_info.span;
         self.equate_normalized_input_or_output(normalized_output_ty, mir_output_ty, output_span);
     }
 
     #[instrument(skip(self), level = "debug")]
     fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
+        if self.infcx.next_trait_solver() {
+            return self
+                .eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
+                .unwrap_or_else(|terr| {
+                    span_mirbug!(
+                        self,
+                        Location::START,
+                        "equate_normalized_input_or_output: `{a:?}=={b:?}` failed with `{terr:?}`",
+                    );
+                });
+        }
+
+        // This is a hack. `body.local_decls` are not necessarily normalized in the old
+        // solver due to not deeply normalizing in writeback. So we must re-normalize here.
+        //
+        // However, in most cases normalizing is unnecessary so we only do so if it may be
+        // necessary for type equality to hold. This leads to some (very minor) performance
+        // wins.
         if let Err(_) =
             self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
         {
-            // FIXME(jackh726): This is a hack. It's somewhat like
-            // `rustc_traits::normalize_after_erasing_regions`. Ideally, we'd
-            // like to normalize *before* inserting into `local_decls`, but
-            // doing so ends up causing some other trouble.
             let b = self.normalize(b, Locations::All(span));
-
-            // Note: if we have to introduce new placeholders during normalization above, then we
-            // won't have added those universes to the universe info, which we would want in
-            // `relate_tys`.
-            if let Err(terr) =
-                self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
-            {
-                span_mirbug!(
-                    self,
-                    Location::START,
-                    "equate_normalized_input_or_output: `{:?}=={:?}` failed with `{:?}`",
-                    a,
-                    b,
-                    terr
-                );
-            }
-        }
+            self.eq_types(a, b, Locations::All(span), ConstraintCategory::BoringNoLocation)
+                .unwrap_or_else(|terr| {
+                    span_mirbug!(
+                        self,
+                        Location::START,
+                        "equate_normalized_input_or_output: `{a:?}=={b:?}` failed with `{terr:?}`",
+                    );
+                });
+        };
     }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index d2464c7e99ee..676b45e9974c 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -123,16 +123,19 @@ pub(crate) fn type_check<'tcx>(
         known_type_outlives_obligations,
     } = free_region_relations::create(infcx, universal_regions, &mut constraints);
 
-    let pre_obligations = infcx.take_registered_region_obligations();
-    assert!(
-        pre_obligations.is_empty(),
-        "there should be no incoming region obligations = {pre_obligations:#?}",
-    );
-    let pre_assumptions = infcx.take_registered_region_assumptions();
-    assert!(
-        pre_assumptions.is_empty(),
-        "there should be no incoming region assumptions = {pre_assumptions:#?}",
-    );
+    {
+        // Scope these variables so it's clear they're not used later
+        let pre_obligations = infcx.take_registered_region_obligations();
+        assert!(
+            pre_obligations.is_empty(),
+            "there should be no incoming region obligations = {pre_obligations:#?}",
+        );
+        let pre_assumptions = infcx.take_registered_region_assumptions();
+        assert!(
+            pre_assumptions.is_empty(),
+            "there should be no incoming region assumptions = {pre_assumptions:#?}",
+        );
+    }
 
     debug!(?normalized_inputs_and_output);
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 2797f2fcdb72..c4212eee8e40 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -609,9 +609,8 @@ impl<'tcx> TyCtxt<'tcx> {
     /// have the same `DefKind`.
     ///
     /// Note that closures have a `DefId`, but the closure *expression* also has a
-    /// `HirId` that is located within the context where the closure appears (and, sadly,
-    /// a corresponding `NodeId`, since those are not yet phased out). The parent of
-    /// the closure's `DefId` will also be the context where it appears.
+    /// `HirId` that is located within the context where the closure appears. The
+    /// parent of the closure's `DefId` will also be the context where it appears.
     pub fn is_closure_like(self, def_id: DefId) -> bool {
         matches!(self.def_kind(def_id), DefKind::Closure)
     }
diff --git a/tests/ui/associated-types/normalization-generality-2.rs b/tests/ui/associated-types/normalization-generality-2.rs
index 50287f9ee9b8..2a50f7e449ad 100644
--- a/tests/ui/associated-types/normalization-generality-2.rs
+++ b/tests/ui/associated-types/normalization-generality-2.rs
@@ -1,4 +1,7 @@
 //@ build-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 // Ensures that we don't regress on "implementation is not general enough" when
 // normalizating under binders. Unlike `normalization-generality.rs`, this also produces
diff --git a/tests/ui/associated-types/normalization-generality.rs b/tests/ui/associated-types/normalization-generality.rs
index 35fcf53b6414..fca70bc7ec67 100644
--- a/tests/ui/associated-types/normalization-generality.rs
+++ b/tests/ui/associated-types/normalization-generality.rs
@@ -1,4 +1,7 @@
 //@ build-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 // Ensures that we don't regress on "implementation is not general enough" when
 // normalizating under binders.
diff --git a/tests/ui/higher-ranked/trait-bounds/issue-88446.rs b/tests/ui/higher-ranked/trait-bounds/issue-88446.rs
index 0ca8387776a4..8e42465b929a 100644
--- a/tests/ui/higher-ranked/trait-bounds/issue-88446.rs
+++ b/tests/ui/higher-ranked/trait-bounds/issue-88446.rs
@@ -1,4 +1,7 @@
 //@ check-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 trait Yokeable<'a> {
     type Output: 'a;
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs
index d85c6999e26f..226bd48f0e4e 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-89436.rs
@@ -1,4 +1,7 @@
 //@ check-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 #![allow(unused)]
 
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs
index b3feda4a531f..0dcef54ed69c 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-90638.rs
@@ -1,4 +1,7 @@
 //@check-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 trait Yokeable<'a>: 'static {
     type Output: 'a;
diff --git a/tests/ui/nll/issue-112604-closure-output-normalize.rs b/tests/ui/nll/issue-112604-closure-output-normalize.rs
index 117e1d91e341..c99900759917 100644
--- a/tests/ui/nll/issue-112604-closure-output-normalize.rs
+++ b/tests/ui/nll/issue-112604-closure-output-normalize.rs
@@ -1,4 +1,7 @@
 //@check-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
 
 use higher_kinded_types::*;
 mod higher_kinded_types {

From 8ff928ed4d4fe1eb74c6ce42a5e4387db49394ff Mon Sep 17 00:00:00 2001
From: Jamie Hill-Daniel 
Date: Mon, 26 Jan 2026 10:30:54 +0000
Subject: [PATCH 220/583] Fix lockfile update script

---
 src/tools/update-lockfile.sh | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/tools/update-lockfile.sh b/src/tools/update-lockfile.sh
index a968d83d8152..123481b6b681 100755
--- a/src/tools/update-lockfile.sh
+++ b/src/tools/update-lockfile.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 # Updates the workspaces in `.`, `library` and `src/tools/rustbook`
 # Logs are written to `cargo_update.log`
@@ -6,13 +6,13 @@
 
 set -euo pipefail
 
-echo -e "\ncompiler & tools dependencies:" > cargo_update.log
+printf "\ncompiler & tools dependencies:" > cargo_update.log
 # Remove first line that always just says "Updating crates.io index"
 cargo update 2>&1 | sed '/crates.io index/d' | \
     tee -a cargo_update.log
-echo -e "\nlibrary dependencies:" >> cargo_update.log
+printf "\nlibrary dependencies:" >> cargo_update.log
 cargo update --manifest-path library/Cargo.toml 2>&1 | sed '/crates.io index/d' | \
     tee -a cargo_update.log
-echo -e "\nrustbook dependencies:" >> cargo_update.log
+printf "\nrustbook dependencies:" >> cargo_update.log
 cargo update --manifest-path src/tools/rustbook/Cargo.toml 2>&1 | sed '/crates.io index/d' | \
     tee -a cargo_update.log

From 8a32fcee2fbbb778c3032278aa6be8a6d9425c2f Mon Sep 17 00:00:00 2001
From: Chris Denton 
Date: Mon, 26 Jan 2026 10:53:42 +0000
Subject: [PATCH 221/583] Update backtrace

---
 library/backtrace | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/backtrace b/library/backtrace
index b65ab935fb2e..28ec93b503bf 160000
--- a/library/backtrace
+++ b/library/backtrace
@@ -1 +1 @@
-Subproject commit b65ab935fb2e0d59dba8966ffca09c9cc5a5f57c
+Subproject commit 28ec93b503bf0410745bc3d571bf3dc1caac3019

From aaeb550f6ff84d9086dfd53d5827f3dc3128adf1 Mon Sep 17 00:00:00 2001
From: Chris Denton 
Date: Mon, 26 Jan 2026 10:54:00 +0000
Subject: [PATCH 222/583] Update windows bindings in std

---
 Cargo.lock                                    |   6 +-
 library/Cargo.lock                            |  14 +-
 library/Cargo.toml                            |   2 +-
 library/std/Cargo.toml                        |   6 +-
 library/std/src/sys/alloc/windows.rs          |   8 +-
 library/std/src/sys/pal/windows/c.rs          |  14 +-
 .../std/src/sys/pal/windows/c/bindings.txt    |   2 +-
 .../std/src/sys/pal/windows/c/windows_sys.rs  | 282 +++++++++---------
 .../Cargo.toml                                |   4 +-
 .../src/lib.rs                                |   0
 src/tools/generate-windows-sys/Cargo.toml     |   2 +-
 src/tools/generate-windows-sys/src/main.rs    |   2 +-
 src/tools/tidy/src/pal.rs                     |   2 +-
 13 files changed, 172 insertions(+), 172 deletions(-)
 rename library/{windows_targets => windows_link}/Cargo.toml (63%)
 rename library/{windows_targets => windows_link}/src/lib.rs (100%)

diff --git a/Cargo.lock b/Cargo.lock
index 41cce3d54ddb..11d4fa025946 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6419,13 +6419,13 @@ dependencies = [
 
 [[package]]
 name = "windows-bindgen"
-version = "0.61.1"
+version = "0.66.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b4e97b01190d32f268a2dfbd3f006f77840633746707fbe40bcee588108a231"
+checksum = "81b7ec123a4eadd44d1f44f76804316b477b2537abed9a2ab950b3c54afa1fcf"
 dependencies = [
  "serde",
  "serde_json",
- "windows-threading 0.1.0",
+ "windows-threading 0.2.1",
 ]
 
 [[package]]
diff --git a/library/Cargo.lock b/library/Cargo.lock
index f6c14bc58a04..608e7e412c3b 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -346,7 +346,7 @@ dependencies = [
  "vex-sdk",
  "wasi 0.11.1+wasi-snapshot-preview1",
  "wasi 0.14.4+wasi-0.2.4",
- "windows-targets 0.0.0",
+ "windows-link 0.0.0",
 ]
 
 [[package]]
@@ -427,6 +427,10 @@ dependencies = [
  "wit-bindgen",
 ]
 
+[[package]]
+name = "windows-link"
+version = "0.0.0"
+
 [[package]]
 name = "windows-link"
 version = "0.2.1"
@@ -439,20 +443,16 @@ version = "0.60.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
 dependencies = [
- "windows-targets 0.53.5",
+ "windows-targets",
 ]
 
-[[package]]
-name = "windows-targets"
-version = "0.0.0"
-
 [[package]]
 name = "windows-targets"
 version = "0.53.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
 dependencies = [
- "windows-link",
+ "windows-link 0.2.1",
  "windows_aarch64_gnullvm",
  "windows_aarch64_msvc",
  "windows_i686_gnu",
diff --git a/library/Cargo.toml b/library/Cargo.toml
index b26e5f41c931..87df966bb6b8 100644
--- a/library/Cargo.toml
+++ b/library/Cargo.toml
@@ -12,7 +12,7 @@ members = [
 exclude = [
   # stdarch has its own Cargo workspace
   "stdarch",
-  "windows_targets"
+  "windows_link"
 ]
 
 [profile.release.package.compiler_builtins]
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 5c9ae52d9e6c..87887f113b79 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -55,8 +55,8 @@ object = { version = "0.37.1", default-features = false, optional = true, featur
     'archive',
 ] }
 
-[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-targets]
-path = "../windows_targets"
+[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-link]
+path = "../windows_link"
 
 [dev-dependencies]
 rand = { version = "0.9.0", default-features = false, features = ["alloc"] }
@@ -130,7 +130,7 @@ llvm_enzyme = ["core/llvm_enzyme"]
 
 # Enable using raw-dylib for Windows imports.
 # This will eventually be the default.
-windows_raw_dylib = ["windows-targets/windows_raw_dylib"]
+windows_raw_dylib = ["windows-link/windows_raw_dylib"]
 
 [package.metadata.fortanix-sgx]
 # Maximum possible number of threads when testing
diff --git a/library/std/src/sys/alloc/windows.rs b/library/std/src/sys/alloc/windows.rs
index 7e2402afab97..90da0b7e9965 100644
--- a/library/std/src/sys/alloc/windows.rs
+++ b/library/std/src/sys/alloc/windows.rs
@@ -20,7 +20,7 @@ const HEAP_ZERO_MEMORY: u32 = 0x00000008;
 // always return the same handle, which remains valid for the entire lifetime of the process.
 //
 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap
-windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE);
+windows_link::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE);
 
 // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`.
 // The allocated memory may be uninitialized, or zeroed if `dwFlags` is
@@ -36,7 +36,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE)
 // Note that `dwBytes` is allowed to be zero, contrary to some other allocators.
 //
 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc
-windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dwflags: u32, dwbytes: usize) -> *mut c_void);
+windows_link::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dwflags: u32, dwbytes: usize) -> *mut c_void);
 
 // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`,
 // to a block of at least `dwBytes` bytes, either shrinking the block in place,
@@ -57,7 +57,7 @@ windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dw
 // Note that `dwBytes` is allowed to be zero, contrary to some other allocators.
 //
 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc
-windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc(
+windows_link::link!("kernel32.dll" "system" fn HeapReAlloc(
     hheap: c::HANDLE,
     dwflags : u32,
     lpmem: *const c_void,
@@ -78,7 +78,7 @@ windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc(
 // Note that `lpMem` is allowed to be null, which will not cause the operation to fail.
 //
 // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree
-windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap: c::HANDLE, dwflags: u32, lpmem: *const c_void) -> c::BOOL);
+windows_link::link!("kernel32.dll" "system" fn HeapFree(hheap: c::HANDLE, dwflags: u32, lpmem: *const c_void) -> c::BOOL);
 
 fn get_process_heap() -> *mut c_void {
     // SAFETY: GetProcessHeap simply returns a valid handle or NULL so is always safe to call.
diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs
index 0f54f80d72ee..9f1a51c7dd3f 100644
--- a/library/std/src/sys/pal/windows/c.rs
+++ b/library/std/src/sys/pal/windows/c.rs
@@ -109,7 +109,7 @@ unsafe extern "system" {
     pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL;
 }
 
-windows_targets::link!("ntdll.dll" "system" fn NtCreateNamedPipeFile(
+windows_link::link!("ntdll.dll" "system" fn NtCreateNamedPipeFile(
     filehandle: *mut HANDLE,
     desiredaccess: FILE_ACCESS_RIGHTS,
     objectattributes: *const OBJECT_ATTRIBUTES,
@@ -229,15 +229,15 @@ compat_fn_with_fallback! {
 
 cfg_select! {
     target_vendor = "uwp" => {
-        windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS);
-        windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS);
-        windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
-        windows_targets::link_raw_dylib!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
-        windows_targets::link_raw_dylib!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32);
+        windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS);
+        windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS);
+        windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
+        windows_link::link_raw_dylib!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
+        windows_link::link_raw_dylib!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32);
     }
     _ => {}
 }
 
 // Only available starting with Windows 8.
 #[cfg(not(target_vendor = "win7"))]
-windows_targets::link!("ws2_32.dll" "system" fn GetHostNameW(name : PWSTR, namelen : i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn GetHostNameW(name : PWSTR, namelen : i32) -> i32);
diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt
index 12babcb84ccf..c21d1de81341 100644
--- a/library/std/src/sys/pal/windows/c/bindings.txt
+++ b/library/std/src/sys/pal/windows/c/bindings.txt
@@ -2,7 +2,7 @@
 --flat
 --sys
 --no-deps
---link windows_targets
+--link windows_link
 --filter
 !INVALID_HANDLE_VALUE
 ABOVE_NORMAL_PRIORITY_CLASS
diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs
index edc9d2d11f7c..eb54efd1c1fe 100644
--- a/library/std/src/sys/pal/windows/c/windows_sys.rs
+++ b/library/std/src/sys/pal/windows/c/windows_sys.rs
@@ -1,147 +1,147 @@
-// Bindings generated by `windows-bindgen` 0.61.1
+// Bindings generated by `windows-bindgen` 0.66.0
 
 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
 
-windows_targets::link!("kernel32.dll" "system" fn AcquireSRWLockExclusive(srwlock : *mut SRWLOCK));
-windows_targets::link!("kernel32.dll" "system" fn AcquireSRWLockShared(srwlock : *mut SRWLOCK));
-windows_targets::link!("kernel32.dll" "system" fn AddVectoredExceptionHandler(first : u32, handler : PVECTORED_EXCEPTION_HANDLER) -> *mut core::ffi::c_void);
-windows_targets::link!("kernel32.dll" "system" fn CancelIo(hfile : HANDLE) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn CompareStringOrdinal(lpstring1 : PCWSTR, cchcount1 : i32, lpstring2 : PCWSTR, cchcount2 : i32, bignorecase : BOOL) -> COMPARESTRING_RESULT);
-windows_targets::link!("kernel32.dll" "system" fn CopyFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, lpprogressroutine : LPPROGRESS_ROUTINE, lpdata : *const core::ffi::c_void, pbcancel : *mut BOOL, dwcopyflags : COPYFILE_FLAGS) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn CreateDirectoryW(lppathname : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes : *const SECURITY_ATTRIBUTES, bmanualreset : BOOL, binitialstate : BOOL, lpname : PCWSTR) -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn CreateFileW(lpfilename : PCWSTR, dwdesiredaccess : u32, dwsharemode : FILE_SHARE_MODE, lpsecurityattributes : *const SECURITY_ATTRIBUTES, dwcreationdisposition : FILE_CREATION_DISPOSITION, dwflagsandattributes : FILE_FLAGS_AND_ATTRIBUTES, htemplatefile : HANDLE) -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn CreateHardLinkW(lpfilename : PCWSTR, lpexistingfilename : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn CreateNamedPipeW(lpname : PCWSTR, dwopenmode : FILE_FLAGS_AND_ATTRIBUTES, dwpipemode : NAMED_PIPE_MODE, nmaxinstances : u32, noutbuffersize : u32, ninbuffersize : u32, ndefaulttimeout : u32, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn CreatePipe(hreadpipe : *mut HANDLE, hwritepipe : *mut HANDLE, lppipeattributes : *const SECURITY_ATTRIBUTES, nsize : u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn CreateProcessW(lpapplicationname : PCWSTR, lpcommandline : PWSTR, lpprocessattributes : *const SECURITY_ATTRIBUTES, lpthreadattributes : *const SECURITY_ATTRIBUTES, binherithandles : BOOL, dwcreationflags : PROCESS_CREATION_FLAGS, lpenvironment : *const core::ffi::c_void, lpcurrentdirectory : PCWSTR, lpstartupinfo : *const STARTUPINFOW, lpprocessinformation : *mut PROCESS_INFORMATION) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn CreateSymbolicLinkW(lpsymlinkfilename : PCWSTR, lptargetfilename : PCWSTR, dwflags : SYMBOLIC_LINK_FLAGS) -> bool);
-windows_targets::link!("kernel32.dll" "system" fn CreateThread(lpthreadattributes : *const SECURITY_ATTRIBUTES, dwstacksize : usize, lpstartaddress : LPTHREAD_START_ROUTINE, lpparameter : *const core::ffi::c_void, dwcreationflags : THREAD_CREATION_FLAGS, lpthreadid : *mut u32) -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn CreateWaitableTimerExW(lptimerattributes : *const SECURITY_ATTRIBUTES, lptimername : PCWSTR, dwflags : u32, dwdesiredaccess : u32) -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn DeleteFileW(lpfilename : PCWSTR) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn DeleteProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST));
-windows_targets::link!("kernel32.dll" "system" fn DeviceIoControl(hdevice : HANDLE, dwiocontrolcode : u32, lpinbuffer : *const core::ffi::c_void, ninbuffersize : u32, lpoutbuffer : *mut core::ffi::c_void, noutbuffersize : u32, lpbytesreturned : *mut u32, lpoverlapped : *mut OVERLAPPED) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn DuplicateHandle(hsourceprocesshandle : HANDLE, hsourcehandle : HANDLE, htargetprocesshandle : HANDLE, lptargethandle : *mut HANDLE, dwdesiredaccess : u32, binherithandle : BOOL, dwoptions : DUPLICATE_HANDLE_OPTIONS) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn ExitProcess(uexitcode : u32) -> !);
-windows_targets::link!("kernel32.dll" "system" fn FindClose(hfindfile : HANDLE) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn FindFirstFileExW(lpfilename : PCWSTR, finfolevelid : FINDEX_INFO_LEVELS, lpfindfiledata : *mut core::ffi::c_void, fsearchop : FINDEX_SEARCH_OPS, lpsearchfilter : *const core::ffi::c_void, dwadditionalflags : FIND_FIRST_EX_FLAGS) -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn FindNextFileW(hfindfile : HANDLE, lpfindfiledata : *mut WIN32_FIND_DATAW) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn FlushFileBuffers(hfile : HANDLE) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn FreeEnvironmentStringsW(penv : PCWSTR) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn GetActiveProcessorCount(groupnumber : u16) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetCommandLineW() -> PCWSTR);
-windows_targets::link!("kernel32.dll" "system" fn GetConsoleMode(hconsolehandle : HANDLE, lpmode : *mut CONSOLE_MODE) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn GetConsoleOutputCP() -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferlength : u32, lpbuffer : PWSTR) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetCurrentThread() -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn GetCurrentThreadId() -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentStringsW() -> PWSTR);
-windows_targets::link!("kernel32.dll" "system" fn GetEnvironmentVariableW(lpname : PCWSTR, lpbuffer : PWSTR, nsize : u32) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetExitCodeProcess(hprocess : HANDLE, lpexitcode : *mut u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn GetFileAttributesW(lpfilename : PCWSTR) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetFileInformationByHandle(hfile : HANDLE, lpfileinformation : *mut BY_HANDLE_FILE_INFORMATION) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn GetFileInformationByHandleEx(hfile : HANDLE, fileinformationclass : FILE_INFO_BY_HANDLE_CLASS, lpfileinformation : *mut core::ffi::c_void, dwbuffersize : u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn GetFileSizeEx(hfile : HANDLE, lpfilesize : *mut i64) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE);
-windows_targets::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR);
-windows_targets::link!("kernel32.dll" "system" fn GetModuleFileNameW(hmodule : HMODULE, lpfilename : PWSTR, nsize : u32) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleA(lpmodulename : PCSTR) -> HMODULE);
-windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleW(lpmodulename : PCWSTR) -> HMODULE);
-windows_targets::link!("kernel32.dll" "system" fn GetOverlappedResult(hfile : HANDLE, lpoverlapped : *const OVERLAPPED, lpnumberofbytestransferred : *mut u32, bwait : BOOL) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC);
-windows_targets::link!("kernel32.dll" "system" fn GetProcessId(process : HANDLE) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetStdHandle(nstdhandle : STD_HANDLE) -> HANDLE);
-windows_targets::link!("kernel32.dll" "system" fn GetSystemDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn GetSystemInfo(lpsysteminfo : *mut SYSTEM_INFO));
-windows_targets::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME));
-windows_targets::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME));
-windows_targets::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32);
-windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : *mut INIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL);
-windows_targets::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : LOCK_FILE_FLAGS, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : MOVE_FILE_FLAGS) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : MULTI_BYTE_TO_WIDE_CHAR_FLAGS, lpmultibytestr : PCSTR, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32);
-windows_targets::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS);
-windows_targets::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS);
-windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
-windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
-windows_targets::link!("advapi32.dll" "system" fn OpenProcessToken(processhandle : HANDLE, desiredaccess : TOKEN_ACCESS_MASK, tokenhandle : *mut HANDLE) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceCounter(lpperformancecount : *mut i64) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceFrequency(lpfrequency : *mut i64) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn ReadConsoleW(hconsoleinput : HANDLE, lpbuffer : *mut core::ffi::c_void, nnumberofcharstoread : u32, lpnumberofcharsread : *mut u32, pinputcontrol : *const CONSOLE_READCONSOLE_CONTROL) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn ReadFile(hfile : HANDLE, lpbuffer : *mut u8, nnumberofbytestoread : u32, lpnumberofbytesread : *mut u32, lpoverlapped : *mut OVERLAPPED) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn ReadFileEx(hfile : HANDLE, lpbuffer : *mut u8, nnumberofbytestoread : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn ReleaseSRWLockExclusive(srwlock : *mut SRWLOCK));
-windows_targets::link!("kernel32.dll" "system" fn ReleaseSRWLockShared(srwlock : *mut SRWLOCK));
-windows_targets::link!("kernel32.dll" "system" fn RemoveDirectoryW(lppathname : PCWSTR) -> BOOL);
-windows_targets::link!("advapi32.dll" "system" "SystemFunction036" fn RtlGenRandom(randombuffer : *mut core::ffi::c_void, randombufferlength : u32) -> bool);
-windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn SetCurrentDirectoryW(lppathname : PCWSTR) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SetEnvironmentVariableW(lpname : PCWSTR, lpvalue : PCWSTR) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SetFileAttributesW(lpfilename : PCWSTR, dwfileattributes : FILE_FLAGS_AND_ATTRIBUTES) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SetFileInformationByHandle(hfile : HANDLE, fileinformationclass : FILE_INFO_BY_HANDLE_CLASS, lpfileinformation : *const core::ffi::c_void, dwbuffersize : u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SetFilePointerEx(hfile : HANDLE, lidistancetomove : i64, lpnewfilepointer : *mut i64, dwmovemethod : SET_FILE_POINTER_MOVE_METHOD) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SetFileTime(hfile : HANDLE, lpcreationtime : *const FILETIME, lplastaccesstime : *const FILETIME, lplastwritetime : *const FILETIME) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SetHandleInformation(hobject : HANDLE, dwmask : u32, dwflags : HANDLE_FLAGS) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SetLastError(dwerrcode : WIN32_ERROR));
-windows_targets::link!("kernel32.dll" "system" fn SetThreadStackGuarantee(stacksizeinbytes : *mut u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SetWaitableTimer(htimer : HANDLE, lpduetime : *const i64, lperiod : i32, pfncompletionroutine : PTIMERAPCROUTINE, lpargtocompletionroutine : *const core::ffi::c_void, fresume : BOOL) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn Sleep(dwmilliseconds : u32));
-windows_targets::link!("kernel32.dll" "system" fn SleepConditionVariableSRW(conditionvariable : *mut CONDITION_VARIABLE, srwlock : *mut SRWLOCK, dwmilliseconds : u32, flags : u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn SleepEx(dwmilliseconds : u32, balertable : BOOL) -> u32);
-windows_targets::link!("kernel32.dll" "system" fn SwitchToThread() -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn TerminateProcess(hprocess : HANDLE, uexitcode : u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn TlsAlloc() -> u32);
-windows_targets::link!("kernel32.dll" "system" fn TlsFree(dwtlsindex : u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn TlsGetValue(dwtlsindex : u32) -> *mut core::ffi::c_void);
-windows_targets::link!("kernel32.dll" "system" fn TlsSetValue(dwtlsindex : u32, lptlsvalue : *const core::ffi::c_void) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> bool);
-windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(srwlock : *mut SRWLOCK) -> bool);
-windows_targets::link!("kernel32.dll" "system" fn UnlockFile(hfile : HANDLE, dwfileoffsetlow : u32, dwfileoffsethigh : u32, nnumberofbytestounlocklow : u32, nnumberofbytestounlockhigh : u32) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn UpdateProcThreadAttribute(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwflags : u32, attribute : usize, lpvalue : *const core::ffi::c_void, cbsize : usize, lppreviousvalue : *mut core::ffi::c_void, lpreturnsize : *const usize) -> BOOL);
-windows_targets::link!("ws2_32.dll" "system" fn WSACleanup() -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn WSADuplicateSocketW(s : SOCKET, dwprocessid : u32, lpprotocolinfo : *mut WSAPROTOCOL_INFOW) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn WSAGetLastError() -> WSA_ERROR);
-windows_targets::link!("ws2_32.dll" "system" fn WSARecv(s : SOCKET, lpbuffers : *const WSABUF, dwbuffercount : u32, lpnumberofbytesrecvd : *mut u32, lpflags : *mut u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn WSASend(s : SOCKET, lpbuffers : *const WSABUF, dwbuffercount : u32, lpnumberofbytessent : *mut u32, dwflags : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn WSASocketW(af : i32, r#type : i32, protocol : i32, lpprotocolinfo : *const WSAPROTOCOL_INFOW, g : u32, dwflags : u32) -> SOCKET);
-windows_targets::link!("ws2_32.dll" "system" fn WSAStartup(wversionrequested : u16, lpwsadata : *mut WSADATA) -> i32);
-windows_targets::link!("kernel32.dll" "system" fn WaitForMultipleObjects(ncount : u32, lphandles : *const HANDLE, bwaitall : BOOL, dwmilliseconds : u32) -> WAIT_EVENT);
-windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
-windows_targets::link!("kernel32.dll" "system" fn WakeAllConditionVariable(conditionvariable : *mut CONDITION_VARIABLE));
-windows_targets::link!("kernel32.dll" "system" fn WakeConditionVariable(conditionvariable : *mut CONDITION_VARIABLE));
-windows_targets::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : u32, dwflags : u32, lpwidecharstr : PCWSTR, cchwidechar : i32, lpmultibytestr : PSTR, cbmultibyte : i32, lpdefaultchar : PCSTR, lpuseddefaultchar : *mut BOOL) -> i32);
-windows_targets::link!("kernel32.dll" "system" fn WriteConsoleW(hconsoleoutput : HANDLE, lpbuffer : PCWSTR, nnumberofcharstowrite : u32, lpnumberofcharswritten : *mut u32, lpreserved : *const core::ffi::c_void) -> BOOL);
-windows_targets::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL);
-windows_targets::link!("ws2_32.dll" "system" fn accept(s : SOCKET, addr : *mut SOCKADDR, addrlen : *mut i32) -> SOCKET);
-windows_targets::link!("ws2_32.dll" "system" fn bind(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn closesocket(s : SOCKET) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn connect(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn freeaddrinfo(paddrinfo : *const ADDRINFOA));
-windows_targets::link!("ws2_32.dll" "system" fn getaddrinfo(pnodename : PCSTR, pservicename : PCSTR, phints : *const ADDRINFOA, ppresult : *mut *mut ADDRINFOA) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn getpeername(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn ioctlsocket(s : SOCKET, cmd : i32, argp : *mut u32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn listen(s : SOCKET, backlog : i32) -> i32);
-windows_targets::link!("kernel32.dll" "system" fn lstrlenW(lpstring : PCWSTR) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn recv(s : SOCKET, buf : PSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn recvfrom(s : SOCKET, buf : PSTR, len : i32, flags : i32, from : *mut SOCKADDR, fromlen : *mut i32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn select(nfds : i32, readfds : *mut FD_SET, writefds : *mut FD_SET, exceptfds : *mut FD_SET, timeout : *const TIMEVAL) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn send(s : SOCKET, buf : PCSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn sendto(s : SOCKET, buf : PCSTR, len : i32, flags : i32, to : *const SOCKADDR, tolen : i32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn setsockopt(s : SOCKET, level : i32, optname : i32, optval : PCSTR, optlen : i32) -> i32);
-windows_targets::link!("ws2_32.dll" "system" fn shutdown(s : SOCKET, how : WINSOCK_SHUTDOWN_HOW) -> i32);
+windows_link::link!("kernel32.dll" "system" fn AcquireSRWLockExclusive(srwlock : *mut SRWLOCK));
+windows_link::link!("kernel32.dll" "system" fn AcquireSRWLockShared(srwlock : *mut SRWLOCK));
+windows_link::link!("kernel32.dll" "system" fn AddVectoredExceptionHandler(first : u32, handler : PVECTORED_EXCEPTION_HANDLER) -> *mut core::ffi::c_void);
+windows_link::link!("kernel32.dll" "system" fn CancelIo(hfile : HANDLE) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn CompareStringOrdinal(lpstring1 : PCWSTR, cchcount1 : i32, lpstring2 : PCWSTR, cchcount2 : i32, bignorecase : BOOL) -> COMPARESTRING_RESULT);
+windows_link::link!("kernel32.dll" "system" fn CopyFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, lpprogressroutine : LPPROGRESS_ROUTINE, lpdata : *const core::ffi::c_void, pbcancel : *mut BOOL, dwcopyflags : COPYFILE_FLAGS) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn CreateDirectoryW(lppathname : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes : *const SECURITY_ATTRIBUTES, bmanualreset : BOOL, binitialstate : BOOL, lpname : PCWSTR) -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn CreateFileW(lpfilename : PCWSTR, dwdesiredaccess : u32, dwsharemode : FILE_SHARE_MODE, lpsecurityattributes : *const SECURITY_ATTRIBUTES, dwcreationdisposition : FILE_CREATION_DISPOSITION, dwflagsandattributes : FILE_FLAGS_AND_ATTRIBUTES, htemplatefile : HANDLE) -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn CreateHardLinkW(lpfilename : PCWSTR, lpexistingfilename : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn CreateNamedPipeW(lpname : PCWSTR, dwopenmode : FILE_FLAGS_AND_ATTRIBUTES, dwpipemode : NAMED_PIPE_MODE, nmaxinstances : u32, noutbuffersize : u32, ninbuffersize : u32, ndefaulttimeout : u32, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn CreatePipe(hreadpipe : *mut HANDLE, hwritepipe : *mut HANDLE, lppipeattributes : *const SECURITY_ATTRIBUTES, nsize : u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn CreateProcessW(lpapplicationname : PCWSTR, lpcommandline : PWSTR, lpprocessattributes : *const SECURITY_ATTRIBUTES, lpthreadattributes : *const SECURITY_ATTRIBUTES, binherithandles : BOOL, dwcreationflags : PROCESS_CREATION_FLAGS, lpenvironment : *const core::ffi::c_void, lpcurrentdirectory : PCWSTR, lpstartupinfo : *const STARTUPINFOW, lpprocessinformation : *mut PROCESS_INFORMATION) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn CreateSymbolicLinkW(lpsymlinkfilename : PCWSTR, lptargetfilename : PCWSTR, dwflags : SYMBOLIC_LINK_FLAGS) -> bool);
+windows_link::link!("kernel32.dll" "system" fn CreateThread(lpthreadattributes : *const SECURITY_ATTRIBUTES, dwstacksize : usize, lpstartaddress : LPTHREAD_START_ROUTINE, lpparameter : *const core::ffi::c_void, dwcreationflags : THREAD_CREATION_FLAGS, lpthreadid : *mut u32) -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn CreateWaitableTimerExW(lptimerattributes : *const SECURITY_ATTRIBUTES, lptimername : PCWSTR, dwflags : u32, dwdesiredaccess : u32) -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn DeleteFileW(lpfilename : PCWSTR) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn DeleteProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST));
+windows_link::link!("kernel32.dll" "system" fn DeviceIoControl(hdevice : HANDLE, dwiocontrolcode : u32, lpinbuffer : *const core::ffi::c_void, ninbuffersize : u32, lpoutbuffer : *mut core::ffi::c_void, noutbuffersize : u32, lpbytesreturned : *mut u32, lpoverlapped : *mut OVERLAPPED) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn DuplicateHandle(hsourceprocesshandle : HANDLE, hsourcehandle : HANDLE, htargetprocesshandle : HANDLE, lptargethandle : *mut HANDLE, dwdesiredaccess : u32, binherithandle : BOOL, dwoptions : DUPLICATE_HANDLE_OPTIONS) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn ExitProcess(uexitcode : u32) -> !);
+windows_link::link!("kernel32.dll" "system" fn FindClose(hfindfile : HANDLE) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn FindFirstFileExW(lpfilename : PCWSTR, finfolevelid : FINDEX_INFO_LEVELS, lpfindfiledata : *mut core::ffi::c_void, fsearchop : FINDEX_SEARCH_OPS, lpsearchfilter : *const core::ffi::c_void, dwadditionalflags : FIND_FIRST_EX_FLAGS) -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn FindNextFileW(hfindfile : HANDLE, lpfindfiledata : *mut WIN32_FIND_DATAW) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn FlushFileBuffers(hfile : HANDLE) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn FormatMessageW(dwflags : FORMAT_MESSAGE_OPTIONS, lpsource : *const core::ffi::c_void, dwmessageid : u32, dwlanguageid : u32, lpbuffer : PWSTR, nsize : u32, arguments : *const *const i8) -> u32);
+windows_link::link!("kernel32.dll" "system" fn FreeEnvironmentStringsW(penv : PCWSTR) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetActiveProcessorCount(groupnumber : u16) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetCommandLineW() -> PCWSTR);
+windows_link::link!("kernel32.dll" "system" fn GetConsoleMode(hconsolehandle : HANDLE, lpmode : *mut CONSOLE_MODE) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetConsoleOutputCP() -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferlength : u32, lpbuffer : PWSTR) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetCurrentThread() -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn GetCurrentThreadId() -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetEnvironmentStringsW() -> PWSTR);
+windows_link::link!("kernel32.dll" "system" fn GetEnvironmentVariableW(lpname : PCWSTR, lpbuffer : PWSTR, nsize : u32) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetExitCodeProcess(hprocess : HANDLE, lpexitcode : *mut u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetFileAttributesW(lpfilename : PCWSTR) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetFileInformationByHandle(hfile : HANDLE, lpfileinformation : *mut BY_HANDLE_FILE_INFORMATION) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetFileInformationByHandleEx(hfile : HANDLE, fileinformationclass : FILE_INFO_BY_HANDLE_CLASS, lpfileinformation : *mut core::ffi::c_void, dwbuffersize : u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetFileSizeEx(hfile : HANDLE, lpfilesize : *mut i64) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE);
+windows_link::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetLastError() -> WIN32_ERROR);
+windows_link::link!("kernel32.dll" "system" fn GetModuleFileNameW(hmodule : HMODULE, lpfilename : PWSTR, nsize : u32) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetModuleHandleA(lpmodulename : PCSTR) -> HMODULE);
+windows_link::link!("kernel32.dll" "system" fn GetModuleHandleW(lpmodulename : PCWSTR) -> HMODULE);
+windows_link::link!("kernel32.dll" "system" fn GetOverlappedResult(hfile : HANDLE, lpoverlapped : *const OVERLAPPED, lpnumberofbytestransferred : *mut u32, bwait : BOOL) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC);
+windows_link::link!("kernel32.dll" "system" fn GetProcessId(process : HANDLE) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetStdHandle(nstdhandle : STD_HANDLE) -> HANDLE);
+windows_link::link!("kernel32.dll" "system" fn GetSystemDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32);
+windows_link::link!("kernel32.dll" "system" fn GetSystemInfo(lpsysteminfo : *mut SYSTEM_INFO));
+windows_link::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME));
+windows_link::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME));
+windows_link::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32);
+windows_link::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32);
+windows_link::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : *mut INIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL);
+windows_link::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : LOCK_FILE_FLAGS, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : MOVE_FILE_FLAGS) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : MULTI_BYTE_TO_WIDE_CHAR_FLAGS, lpmultibytestr : PCSTR, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32);
+windows_link::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS);
+windows_link::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS);
+windows_link::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
+windows_link::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
+windows_link::link!("advapi32.dll" "system" fn OpenProcessToken(processhandle : HANDLE, desiredaccess : TOKEN_ACCESS_MASK, tokenhandle : *mut HANDLE) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn QueryPerformanceCounter(lpperformancecount : *mut i64) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn QueryPerformanceFrequency(lpfrequency : *mut i64) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn ReadConsoleW(hconsoleinput : HANDLE, lpbuffer : *mut core::ffi::c_void, nnumberofcharstoread : u32, lpnumberofcharsread : *mut u32, pinputcontrol : *const CONSOLE_READCONSOLE_CONTROL) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn ReadFile(hfile : HANDLE, lpbuffer : *mut u8, nnumberofbytestoread : u32, lpnumberofbytesread : *mut u32, lpoverlapped : *mut OVERLAPPED) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn ReadFileEx(hfile : HANDLE, lpbuffer : *mut u8, nnumberofbytestoread : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn ReleaseSRWLockExclusive(srwlock : *mut SRWLOCK));
+windows_link::link!("kernel32.dll" "system" fn ReleaseSRWLockShared(srwlock : *mut SRWLOCK));
+windows_link::link!("kernel32.dll" "system" fn RemoveDirectoryW(lppathname : PCWSTR) -> BOOL);
+windows_link::link!("advapi32.dll" "system" "SystemFunction036" fn RtlGenRandom(randombuffer : *mut core::ffi::c_void, randombufferlength : u32) -> bool);
+windows_link::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32);
+windows_link::link!("kernel32.dll" "system" fn SetCurrentDirectoryW(lppathname : PCWSTR) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SetEnvironmentVariableW(lpname : PCWSTR, lpvalue : PCWSTR) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SetFileAttributesW(lpfilename : PCWSTR, dwfileattributes : FILE_FLAGS_AND_ATTRIBUTES) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SetFileInformationByHandle(hfile : HANDLE, fileinformationclass : FILE_INFO_BY_HANDLE_CLASS, lpfileinformation : *const core::ffi::c_void, dwbuffersize : u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SetFilePointerEx(hfile : HANDLE, lidistancetomove : i64, lpnewfilepointer : *mut i64, dwmovemethod : SET_FILE_POINTER_MOVE_METHOD) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SetFileTime(hfile : HANDLE, lpcreationtime : *const FILETIME, lplastaccesstime : *const FILETIME, lplastwritetime : *const FILETIME) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SetHandleInformation(hobject : HANDLE, dwmask : u32, dwflags : HANDLE_FLAGS) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SetLastError(dwerrcode : WIN32_ERROR));
+windows_link::link!("kernel32.dll" "system" fn SetThreadStackGuarantee(stacksizeinbytes : *mut u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SetWaitableTimer(htimer : HANDLE, lpduetime : *const i64, lperiod : i32, pfncompletionroutine : PTIMERAPCROUTINE, lpargtocompletionroutine : *const core::ffi::c_void, fresume : BOOL) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn Sleep(dwmilliseconds : u32));
+windows_link::link!("kernel32.dll" "system" fn SleepConditionVariableSRW(conditionvariable : *mut CONDITION_VARIABLE, srwlock : *mut SRWLOCK, dwmilliseconds : u32, flags : u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn SleepEx(dwmilliseconds : u32, balertable : BOOL) -> u32);
+windows_link::link!("kernel32.dll" "system" fn SwitchToThread() -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn TerminateProcess(hprocess : HANDLE, uexitcode : u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn TlsAlloc() -> u32);
+windows_link::link!("kernel32.dll" "system" fn TlsFree(dwtlsindex : u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn TlsGetValue(dwtlsindex : u32) -> *mut core::ffi::c_void);
+windows_link::link!("kernel32.dll" "system" fn TlsSetValue(dwtlsindex : u32, lptlsvalue : *const core::ffi::c_void) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> bool);
+windows_link::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(srwlock : *mut SRWLOCK) -> bool);
+windows_link::link!("kernel32.dll" "system" fn UnlockFile(hfile : HANDLE, dwfileoffsetlow : u32, dwfileoffsethigh : u32, nnumberofbytestounlocklow : u32, nnumberofbytestounlockhigh : u32) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn UpdateProcThreadAttribute(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwflags : u32, attribute : usize, lpvalue : *const core::ffi::c_void, cbsize : usize, lppreviousvalue : *mut core::ffi::c_void, lpreturnsize : *const usize) -> BOOL);
+windows_link::link!("ws2_32.dll" "system" fn WSACleanup() -> i32);
+windows_link::link!("ws2_32.dll" "system" fn WSADuplicateSocketW(s : SOCKET, dwprocessid : u32, lpprotocolinfo : *mut WSAPROTOCOL_INFOW) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn WSAGetLastError() -> WSA_ERROR);
+windows_link::link!("ws2_32.dll" "system" fn WSARecv(s : SOCKET, lpbuffers : *const WSABUF, dwbuffercount : u32, lpnumberofbytesrecvd : *mut u32, lpflags : *mut u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn WSASend(s : SOCKET, lpbuffers : *const WSABUF, dwbuffercount : u32, lpnumberofbytessent : *mut u32, dwflags : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn WSASocketW(af : i32, r#type : i32, protocol : i32, lpprotocolinfo : *const WSAPROTOCOL_INFOW, g : u32, dwflags : u32) -> SOCKET);
+windows_link::link!("ws2_32.dll" "system" fn WSAStartup(wversionrequested : u16, lpwsadata : *mut WSADATA) -> i32);
+windows_link::link!("kernel32.dll" "system" fn WaitForMultipleObjects(ncount : u32, lphandles : *const HANDLE, bwaitall : BOOL, dwmilliseconds : u32) -> WAIT_EVENT);
+windows_link::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
+windows_link::link!("kernel32.dll" "system" fn WakeAllConditionVariable(conditionvariable : *mut CONDITION_VARIABLE));
+windows_link::link!("kernel32.dll" "system" fn WakeConditionVariable(conditionvariable : *mut CONDITION_VARIABLE));
+windows_link::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : u32, dwflags : u32, lpwidecharstr : PCWSTR, cchwidechar : i32, lpmultibytestr : PSTR, cbmultibyte : i32, lpdefaultchar : PCSTR, lpuseddefaultchar : *mut BOOL) -> i32);
+windows_link::link!("kernel32.dll" "system" fn WriteConsoleW(hconsoleoutput : HANDLE, lpbuffer : PCWSTR, nnumberofcharstowrite : u32, lpnumberofcharswritten : *mut u32, lpreserved : *const core::ffi::c_void) -> BOOL);
+windows_link::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL);
+windows_link::link!("ws2_32.dll" "system" fn accept(s : SOCKET, addr : *mut SOCKADDR, addrlen : *mut i32) -> SOCKET);
+windows_link::link!("ws2_32.dll" "system" fn bind(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn closesocket(s : SOCKET) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn connect(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn freeaddrinfo(paddrinfo : *const ADDRINFOA));
+windows_link::link!("ws2_32.dll" "system" fn getaddrinfo(pnodename : PCSTR, pservicename : PCSTR, phints : *const ADDRINFOA, ppresult : *mut *mut ADDRINFOA) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn getpeername(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn getsockname(s : SOCKET, name : *mut SOCKADDR, namelen : *mut i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn getsockopt(s : SOCKET, level : i32, optname : i32, optval : PSTR, optlen : *mut i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn ioctlsocket(s : SOCKET, cmd : i32, argp : *mut u32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn listen(s : SOCKET, backlog : i32) -> i32);
+windows_link::link!("kernel32.dll" "system" fn lstrlenW(lpstring : PCWSTR) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn recv(s : SOCKET, buf : PSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn recvfrom(s : SOCKET, buf : PSTR, len : i32, flags : i32, from : *mut SOCKADDR, fromlen : *mut i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn select(nfds : i32, readfds : *mut FD_SET, writefds : *mut FD_SET, exceptfds : *mut FD_SET, timeout : *const TIMEVAL) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn send(s : SOCKET, buf : PCSTR, len : i32, flags : SEND_RECV_FLAGS) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn sendto(s : SOCKET, buf : PCSTR, len : i32, flags : i32, to : *const SOCKADDR, tolen : i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn setsockopt(s : SOCKET, level : i32, optname : i32, optval : PCSTR, optlen : i32) -> i32);
+windows_link::link!("ws2_32.dll" "system" fn shutdown(s : SOCKET, how : WINSOCK_SHUTDOWN_HOW) -> i32);
 pub const ABOVE_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32768u32;
 #[repr(C)]
 #[derive(Clone, Copy, Default)]
diff --git a/library/windows_targets/Cargo.toml b/library/windows_link/Cargo.toml
similarity index 63%
rename from library/windows_targets/Cargo.toml
rename to library/windows_link/Cargo.toml
index 1c804a0ab391..fc5d0a69bfb5 100644
--- a/library/windows_targets/Cargo.toml
+++ b/library/windows_link/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
-name = "windows-targets"
-description = "A drop-in replacement for the real windows-targets crate for use in std only."
+name = "windows-link"
+description = "A drop-in replacement for the real windows-link crate for use in std only."
 version = "0.0.0"
 edition = "2024"
 
diff --git a/library/windows_targets/src/lib.rs b/library/windows_link/src/lib.rs
similarity index 100%
rename from library/windows_targets/src/lib.rs
rename to library/windows_link/src/lib.rs
diff --git a/src/tools/generate-windows-sys/Cargo.toml b/src/tools/generate-windows-sys/Cargo.toml
index 152cd504abd1..5627a8555943 100644
--- a/src/tools/generate-windows-sys/Cargo.toml
+++ b/src/tools/generate-windows-sys/Cargo.toml
@@ -4,4 +4,4 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies.windows-bindgen]
-version = "0.61.0"
+version = "0.66.0"
diff --git a/src/tools/generate-windows-sys/src/main.rs b/src/tools/generate-windows-sys/src/main.rs
index 6bf47e840627..9b1d62f14bb7 100644
--- a/src/tools/generate-windows-sys/src/main.rs
+++ b/src/tools/generate-windows-sys/src/main.rs
@@ -29,7 +29,7 @@ fn main() -> Result<(), Box> {
 
     sort_bindings("bindings.txt")?;
 
-    windows_bindgen::bindgen(["--etc", "bindings.txt"]);
+    windows_bindgen::bindgen(["--etc", "bindings.txt"]).unwrap();
 
     let mut f = std::fs::File::options().append(true).open("windows_sys.rs")?;
     f.write_all(ARM32_SHIM.as_bytes())?;
diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs
index e6423aeeba99..f1ac90ba0bc5 100644
--- a/src/tools/tidy/src/pal.rs
+++ b/src/tools/tidy/src/pal.rs
@@ -38,7 +38,7 @@ use crate::walk::{filter_dirs, walk};
 const EXCEPTION_PATHS: &[&str] = &[
     "library/compiler-builtins",
     "library/std_detect",
-    "library/windows_targets",
+    "library/windows_link",
     "library/panic_abort",
     "library/panic_unwind",
     "library/unwind",

From b75c58d4b3495021b414778d8b91c12285d3d7ee Mon Sep 17 00:00:00 2001
From: kouhe <25522053+kouhe3@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:10:34 +0800
Subject: [PATCH 223/583] Implement default field values `..` syntax

- Added `RecordSpread` enum to distinguish between no spread, field defaults, and spread expressions
- Updated `FieldData` to include `default_value` field
- Modified record literal lowering to handle default field values
- Updated diagnostics to check for missing fields considering defaults
- Added methods to get matched fields for records for completions
- Enhanced hover support for struct rest patterns
---
 .../crates/hir-def/src/expr_store.rs          |  10 +-
 .../crates/hir-def/src/expr_store/lower.rs    |  14 ++-
 .../src/expr_store/lower/format_args.rs       |   5 +-
 .../crates/hir-def/src/expr_store/pretty.rs   |  20 ++-
 .../rust-analyzer/crates/hir-def/src/hir.rs   |   9 +-
 .../crates/hir-def/src/signatures.rs          |  12 +-
 .../crates/hir-ty/src/diagnostics/expr.rs     |  46 +++++--
 .../hir-ty/src/infer/closure/analysis.rs      |   4 +-
 .../crates/hir-ty/src/infer/expr.rs           |   6 +-
 .../crates/hir-ty/src/infer/mutability.rs     |  10 +-
 .../crates/hir-ty/src/mir/lower.rs            |  11 +-
 .../rust-analyzer/crates/hir/src/semantics.rs |  18 +++
 .../crates/hir/src/source_analyzer.rs         | 118 ++++++++++++++++--
 .../src/handlers/expand_rest_pattern.rs       |   6 +-
 .../ide-completion/src/completions/expr.rs    |   2 +-
 .../ide-completion/src/completions/record.rs  |  14 +--
 .../crates/ide-completion/src/tests/record.rs |  40 +++++-
 .../src/handlers/missing_fields.rs            |  77 ++++++++++++
 .../crates/ide/src/hover/render.rs            |   8 +-
 19 files changed, 355 insertions(+), 75 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
index 10cd460d1d36..edbfd42d1314 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
@@ -32,7 +32,7 @@ use crate::{
     expr_store::path::Path,
     hir::{
         Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
-        PatId, RecordFieldPat, Statement,
+        PatId, RecordFieldPat, RecordSpread, Statement,
     },
     nameres::{DefMap, block_def_map},
     type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId},
@@ -575,8 +575,8 @@ impl ExpressionStore {
                 for field in fields.iter() {
                     f(field.expr);
                 }
-                if let &Some(expr) = spread {
-                    f(expr);
+                if let RecordSpread::Expr(expr) = spread {
+                    f(*expr);
                 }
             }
             Expr::Closure { body, .. } => {
@@ -706,8 +706,8 @@ impl ExpressionStore {
                 for field in fields.iter() {
                     f(field.expr);
                 }
-                if let &Some(expr) = spread {
-                    f(expr);
+                if let RecordSpread::Expr(expr) = spread {
+                    f(*expr);
                 }
             }
             Expr::Closure { body, .. } => {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index 79222615929f..4fbf6d951779 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -47,7 +47,7 @@ use crate::{
     hir::{
         Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
         Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
-        RecordFieldPat, RecordLitField, Statement, generics::GenericParams,
+        RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams,
     },
     item_scope::BuiltinShadowMode,
     item_tree::FieldsShape,
@@ -1266,10 +1266,16 @@ impl<'db> ExprCollector<'db> {
                             Some(RecordLitField { name, expr })
                         })
                         .collect();
-                    let spread = nfl.spread().map(|s| self.collect_expr(s));
+                    let spread_expr = nfl.spread().map(|s| self.collect_expr(s));
+                    let has_spread_syntax = nfl.dotdot_token().is_some();
+                    let spread = match (spread_expr, has_spread_syntax) {
+                        (None, false) => RecordSpread::None,
+                        (None, true) => RecordSpread::FieldDefaults,
+                        (Some(expr), _) => RecordSpread::Expr(expr),
+                    };
                     Expr::RecordLit { path, fields, spread }
                 } else {
-                    Expr::RecordLit { path, fields: Box::default(), spread: None }
+                    Expr::RecordLit { path, fields: Box::default(), spread: RecordSpread::None }
                 };
 
                 self.alloc_expr(record_lit, syntax_ptr)
@@ -1995,7 +2001,7 @@ impl<'db> ExprCollector<'db> {
         }
     }
 
-    fn collect_expr_opt(&mut self, expr: Option) -> ExprId {
+    pub fn collect_expr_opt(&mut self, expr: Option) -> ExprId {
         match expr {
             Some(expr) => self.collect_expr(expr),
             None => self.missing_expr(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs
index 7ef84f27f664..51616afb3892 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs
@@ -10,7 +10,8 @@ use crate::{
     builtin_type::BuiltinUint,
     expr_store::{HygieneId, lower::ExprCollector, path::Path},
     hir::{
-        Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, Statement,
+        Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, RecordSpread,
+        Statement,
         format_args::{
             self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
             FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
@@ -869,7 +870,7 @@ impl<'db> ExprCollector<'db> {
             self.alloc_expr_desugared(Expr::RecordLit {
                 path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new),
                 fields: Box::new([position, flags, precision, width]),
-                spread: None,
+                spread: RecordSpread::None,
             })
         } else {
             let format_placeholder_new =
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
index f5ef8e1a3595..35f3cd114e36 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
@@ -16,7 +16,8 @@ use crate::{
     attrs::AttrFlags,
     expr_store::path::{GenericArg, GenericArgs},
     hir::{
-        Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement,
+        Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, RecordSpread,
+        Statement,
         generics::{GenericParams, WherePredicate},
     },
     lang_item::LangItemTarget,
@@ -139,7 +140,7 @@ pub fn print_variant_body_hir(db: &dyn DefDatabase, owner: VariantId, edition: E
     }
 
     for (_, data) in fields.fields().iter() {
-        let FieldData { name, type_ref, visibility, is_unsafe } = data;
+        let FieldData { name, type_ref, visibility, is_unsafe, default_value: _ } = data;
         match visibility {
             crate::item_tree::RawVisibility::Module(interned, _visibility_explicitness) => {
                 w!(p, "pub(in {})", interned.display(db, p.edition))
@@ -679,10 +680,17 @@ impl Printer<'_> {
                         p.print_expr(field.expr);
                         wln!(p, ",");
                     }
-                    if let Some(spread) = spread {
-                        w!(p, "..");
-                        p.print_expr(*spread);
-                        wln!(p);
+                    match spread {
+                        RecordSpread::None => {}
+                        RecordSpread::FieldDefaults => {
+                            w!(p, "..");
+                            wln!(p);
+                        }
+                        RecordSpread::Expr(spread_expr) => {
+                            w!(p, "..");
+                            p.print_expr(*spread_expr);
+                            wln!(p);
+                        }
                     }
                 });
                 w!(self, "}}");
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 53be0de7d9c3..7781a8fe54ee 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -187,6 +187,13 @@ impl From for Literal {
     }
 }
 
+#[derive(Debug, Clone, Eq, PartialEq, Copy)]
+pub enum RecordSpread {
+    None,
+    FieldDefaults,
+    Expr(ExprId),
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Expr {
     /// This is produced if the syntax tree does not have a required expression piece.
@@ -259,7 +266,7 @@ pub enum Expr {
     RecordLit {
         path: Option>,
         fields: Box<[RecordLitField]>,
-        spread: Option,
+        spread: RecordSpread,
     },
     Field {
         expr: ExprId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
index 0dd88edbfb08..37c8f762fe5d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
@@ -12,7 +12,7 @@ use intern::{Symbol, sym};
 use la_arena::{Arena, Idx};
 use rustc_abi::{IntegerType, ReprOptions};
 use syntax::{
-    NodeOrToken, SyntaxNodePtr, T,
+    AstNode, NodeOrToken, SyntaxNodePtr, T,
     ast::{self, HasGenericParams, HasName, HasVisibility, IsString},
 };
 use thin_vec::ThinVec;
@@ -754,6 +754,7 @@ pub struct FieldData {
     pub type_ref: TypeRefId,
     pub visibility: RawVisibility,
     pub is_unsafe: bool,
+    pub default_value: Option,
 }
 
 pub type LocalFieldId = Idx;
@@ -903,7 +904,14 @@ fn lower_fields(
                     .filter_map(NodeOrToken::into_token)
                     .any(|token| token.kind() == T![unsafe]);
                 let name = field_name(idx, &field);
-                arena.alloc(FieldData { name, type_ref, visibility, is_unsafe });
+
+                // Check if field has default value (only for record fields)
+                let default_value = ast::RecordField::cast(field.syntax().clone())
+                    .and_then(|rf| rf.eq_token().is_some().then_some(rf.expr()))
+                    .flatten()
+                    .map(|expr| col.collect_expr_opt(Some(expr)));
+
+                arena.alloc(FieldData { name, type_ref, visibility, is_unsafe, default_value });
                 idx += 1;
             }
             Err(cfg) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index dd1fc3b36ef8..4e1bb6f4c533 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -41,7 +41,7 @@ use crate::{
 pub(crate) use hir_def::{
     LocalFieldId, VariantId,
     expr_store::Body,
-    hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement},
+    hir::{Expr, ExprId, MatchArm, Pat, PatId, RecordSpread, Statement},
 };
 
 pub enum BodyValidationDiagnostic {
@@ -123,7 +123,7 @@ impl<'db> ExprValidator<'db> {
         }
 
         for (id, expr) in body.exprs() {
-            if let Some((variant, missed_fields, true)) =
+            if let Some((variant, missed_fields)) =
                 record_literal_missing_fields(db, self.infer, id, expr)
             {
                 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
@@ -154,7 +154,7 @@ impl<'db> ExprValidator<'db> {
         }
 
         for (id, pat) in body.pats() {
-            if let Some((variant, missed_fields, true)) =
+            if let Some((variant, missed_fields)) =
                 record_pattern_missing_fields(db, self.infer, id, pat)
             {
                 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
@@ -557,9 +557,9 @@ pub fn record_literal_missing_fields(
     infer: &InferenceResult,
     id: ExprId,
     expr: &Expr,
-) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> {
-    let (fields, exhaustive) = match expr {
-        Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()),
+) -> Option<(VariantId, Vec)> {
+    let (fields, spread) = match expr {
+        Expr::RecordLit { fields, spread, .. } => (fields, spread),
         _ => return None,
     };
 
@@ -571,15 +571,28 @@ pub fn record_literal_missing_fields(
     let variant_data = variant_def.fields(db);
 
     let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
+    // don't show missing fields if:
+    // - has ..expr
+    // - or has default value + ..
+    // - or already in code
     let missed_fields: Vec = variant_data
         .fields()
         .iter()
-        .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) })
+        .filter_map(|(f, d)| {
+            if specified_fields.contains(&d.name)
+                || matches!(spread, RecordSpread::Expr(_))
+                || (d.default_value.is_some() && matches!(spread, RecordSpread::FieldDefaults))
+            {
+                None
+            } else {
+                Some(f)
+            }
+        })
         .collect();
     if missed_fields.is_empty() {
         return None;
     }
-    Some((variant_def, missed_fields, exhaustive))
+    Some((variant_def, missed_fields))
 }
 
 pub fn record_pattern_missing_fields(
@@ -587,9 +600,9 @@ pub fn record_pattern_missing_fields(
     infer: &InferenceResult,
     id: PatId,
     pat: &Pat,
-) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> {
-    let (fields, exhaustive) = match pat {
-        Pat::Record { path: _, args, ellipsis } => (args, !ellipsis),
+) -> Option<(VariantId, Vec)> {
+    let (fields, ellipsis) = match pat {
+        Pat::Record { path: _, args, ellipsis } => (args, *ellipsis),
         _ => return None,
     };
 
@@ -601,15 +614,22 @@ pub fn record_pattern_missing_fields(
     let variant_data = variant_def.fields(db);
 
     let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
+    // don't show missing fields if:
+    // - in code
+    // - or has ..
     let missed_fields: Vec = variant_data
         .fields()
         .iter()
-        .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) })
+        .filter_map(
+            |(f, d)| {
+                if specified_fields.contains(&d.name) || ellipsis { None } else { Some(f) }
+            },
+        )
         .collect();
     if missed_fields.is_empty() {
         return None;
     }
-    Some((variant_def, missed_fields, exhaustive))
+    Some((variant_def, missed_fields))
 }
 
 fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs
index b25901cc3b99..5a3eba1a71ae 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs
@@ -8,7 +8,7 @@ use hir_def::{
     expr_store::path::Path,
     hir::{
         Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
-        Statement, UnaryOp,
+        RecordSpread, Statement, UnaryOp,
     },
     item_tree::FieldsShape,
     resolver::ValueNs,
@@ -627,7 +627,7 @@ impl<'db> InferenceContext<'_, 'db> {
                 self.consume_expr(expr);
             }
             Expr::RecordLit { fields, spread, .. } => {
-                if let &Some(expr) = spread {
+                if let RecordSpread::Expr(expr) = *spread {
                     self.consume_expr(expr);
                 }
                 self.consume_exprs(fields.iter().map(|it| it.expr));
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index c57d41cc5f73..9f2d9d25b957 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -8,7 +8,7 @@ use hir_def::{
     expr_store::path::{GenericArgs as HirGenericArgs, Path},
     hir::{
         Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId,
-        InlineAsmKind, LabelId, Literal, Pat, PatId, Statement, UnaryOp,
+        InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp,
     },
     resolver::ValueNs,
 };
@@ -657,8 +657,8 @@ impl<'db> InferenceContext<'_, 'db> {
                         }
                     }
                 }
-                if let Some(expr) = spread {
-                    self.infer_expr(*expr, &Expectation::has_type(ty), ExprIsRead::Yes);
+                if let RecordSpread::Expr(expr) = *spread {
+                    self.infer_expr(expr, &Expectation::has_type(ty), ExprIsRead::Yes);
                 }
                 ty
             }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index 729ed214daea..45fa141b6d3d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -2,7 +2,8 @@
 //! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
 
 use hir_def::hir::{
-    Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, UnaryOp,
+    Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, RecordSpread,
+    Statement, UnaryOp,
 };
 use rustc_ast_ir::Mutability;
 
@@ -132,8 +133,11 @@ impl<'db> InferenceContext<'_, 'db> {
             Expr::Become { expr } => {
                 self.infer_mut_expr(*expr, Mutability::Not);
             }
-            Expr::RecordLit { path: _, fields, spread } => {
-                self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
+            Expr::RecordLit { path: _, fields, spread, .. } => {
+                self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr));
+                if let RecordSpread::Expr(expr) = *spread {
+                    self.infer_mut_expr(expr, Mutability::Not);
+                }
             }
             &Expr::Index { base, index } => {
                 if mutability == Mutability::Mut {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 1579f00e9266..199db7a3e718 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -9,7 +9,7 @@ use hir_def::{
     expr_store::{Body, ExpressionStore, HygieneId, path::Path},
     hir::{
         ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
-        Pat, PatId, RecordFieldPat, RecordLitField,
+        Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread,
     },
     item_tree::FieldsShape,
     lang_item::LangItems,
@@ -867,16 +867,17 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
             }
             Expr::Become { .. } => not_supported!("tail-calls"),
             Expr::Yield { .. } => not_supported!("yield"),
-            Expr::RecordLit { fields, path, spread } => {
-                let spread_place = match spread {
-                    &Some(it) => {
+            Expr::RecordLit { fields, path, spread, .. } => {
+                let spread_place = match *spread {
+                    RecordSpread::Expr(it) => {
                         let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
                             return Ok(None);
                         };
                         current = c;
                         Some(p)
                     }
-                    None => None,
+                    RecordSpread::None => None,
+                    RecordSpread::FieldDefaults => not_supported!("empty record spread"),
                 };
                 let variant_id =
                     self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 98f5739600f3..4bc757da4417 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -2003,6 +2003,15 @@ impl<'db> SemanticsImpl<'db> {
             .unwrap_or_default()
     }
 
+    pub fn record_literal_matched_fields(
+        &self,
+        literal: &ast::RecordExpr,
+    ) -> Vec<(Field, Type<'db>)> {
+        self.analyze(literal.syntax())
+            .and_then(|it| it.record_literal_matched_fields(self.db, literal))
+            .unwrap_or_default()
+    }
+
     pub fn record_pattern_missing_fields(
         &self,
         pattern: &ast::RecordPat,
@@ -2012,6 +2021,15 @@ impl<'db> SemanticsImpl<'db> {
             .unwrap_or_default()
     }
 
+    pub fn record_pattern_matched_fields(
+        &self,
+        pattern: &ast::RecordPat,
+    ) -> Vec<(Field, Type<'db>)> {
+        self.analyze(pattern.syntax())
+            .and_then(|it| it.record_pattern_matched_fields(self.db, pattern))
+            .unwrap_or_default()
+    }
+
     fn with_ctx) -> T, T>(&self, f: F) -> T {
         let mut ctx = SourceToDefCtx { db: self.db, cache: &mut self.s2d_cache.borrow_mut() };
         f(&mut ctx)
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 6ba7a42c1946..4e85e299a97f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -17,7 +17,7 @@ use hir_def::{
         path::Path,
         scope::{ExprScopes, ScopeId},
     },
-    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
+    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId},
     lang_item::LangItems,
     nameres::MacroSubNs,
     resolver::{HasResolver, Resolver, TypeNs, ValueNs, resolver_for_scope},
@@ -44,6 +44,7 @@ use hir_ty::{
 };
 use intern::sym;
 use itertools::Itertools;
+use rustc_hash::FxHashSet;
 use rustc_type_ir::{
     AliasTyKind,
     inherent::{AdtDef, IntoKind, Ty as _},
@@ -1241,21 +1242,31 @@ impl<'db> SourceAnalyzer<'db> {
         let body = self.store()?;
         let infer = self.infer()?;
 
-        let expr_id = self.expr_id(literal.clone().into())?;
-        let substs = infer.expr_or_pat_ty(expr_id).as_adt()?.1;
-
-        let (variant, missing_fields, _exhaustive) = match expr_id {
-            ExprOrPatId::ExprId(expr_id) => {
-                record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?
-            }
-            ExprOrPatId::PatId(pat_id) => {
-                record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?
-            }
-        };
+        let expr_id = self.expr_id(literal.clone().into())?.as_expr()?;
+        let substs = infer.expr_ty(expr_id).as_adt()?.1;
+        let (variant, missing_fields) =
+            record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
         let res = self.missing_fields(db, substs, variant, missing_fields);
         Some(res)
     }
 
+    pub(crate) fn record_literal_matched_fields(
+        &self,
+        db: &'db dyn HirDatabase,
+        literal: &ast::RecordExpr,
+    ) -> Option)>> {
+        let body = self.store()?;
+        let infer = self.infer()?;
+
+        let expr_id = self.expr_id(literal.clone().into())?.as_expr()?;
+        let substs = infer.expr_ty(expr_id).as_adt()?.1;
+        let (variant, matched_fields) =
+            record_literal_matched_fields(db, infer, expr_id, &body[expr_id])?;
+
+        let res = self.missing_fields(db, substs, variant, matched_fields);
+        Some(res)
+    }
+
     pub(crate) fn record_pattern_missing_fields(
         &self,
         db: &'db dyn HirDatabase,
@@ -1267,12 +1278,29 @@ impl<'db> SourceAnalyzer<'db> {
         let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?;
         let substs = infer.pat_ty(pat_id).as_adt()?.1;
 
-        let (variant, missing_fields, _exhaustive) =
+        let (variant, missing_fields) =
             record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?;
         let res = self.missing_fields(db, substs, variant, missing_fields);
         Some(res)
     }
 
+    pub(crate) fn record_pattern_matched_fields(
+        &self,
+        db: &'db dyn HirDatabase,
+        pattern: &ast::RecordPat,
+    ) -> Option)>> {
+        let body = self.store()?;
+        let infer = self.infer()?;
+
+        let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?;
+        let substs = infer.pat_ty(pat_id).as_adt()?.1;
+
+        let (variant, matched_fields) =
+            record_pattern_matched_fields(db, infer, pat_id, &body[pat_id])?;
+        let res = self.missing_fields(db, substs, variant, matched_fields);
+        Some(res)
+    }
+
     fn missing_fields(
         &self,
         db: &'db dyn HirDatabase,
@@ -1810,3 +1838,67 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H
     let ctx = span_map.span_at(name.value.text_range().start()).ctx;
     HygieneId::new(ctx.opaque_and_semiopaque(db))
 }
+
+fn record_literal_matched_fields(
+    db: &dyn HirDatabase,
+    infer: &InferenceResult,
+    id: ExprId,
+    expr: &Expr,
+) -> Option<(VariantId, Vec)> {
+    let (fields, _spread) = match expr {
+        Expr::RecordLit { fields, spread, .. } => (fields, spread),
+        _ => return None,
+    };
+
+    let variant_def = infer.variant_resolution_for_expr(id)?;
+    if let VariantId::UnionId(_) = variant_def {
+        return None;
+    }
+
+    let variant_data = variant_def.fields(db);
+
+    let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
+    // suggest fields if:
+    // - not in code
+    let matched_fields: Vec = variant_data
+        .fields()
+        .iter()
+        .filter_map(|(f, d)| (!specified_fields.contains(&d.name)).then_some(f))
+        .collect();
+    if matched_fields.is_empty() {
+        return None;
+    }
+    Some((variant_def, matched_fields))
+}
+
+fn record_pattern_matched_fields(
+    db: &dyn HirDatabase,
+    infer: &InferenceResult,
+    id: PatId,
+    pat: &Pat,
+) -> Option<(VariantId, Vec)> {
+    let (fields, _ellipsis) = match pat {
+        Pat::Record { path: _, args, ellipsis } => (args, *ellipsis),
+        _ => return None,
+    };
+
+    let variant_def = infer.variant_resolution_for_pat(id)?;
+    if let VariantId::UnionId(_) = variant_def {
+        return None;
+    }
+
+    let variant_data = variant_def.fields(db);
+
+    let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
+    // suggest fields if:
+    // - not in code
+    let matched_fields: Vec = variant_data
+        .fields()
+        .iter()
+        .filter_map(|(f, d)| if !specified_fields.contains(&d.name) { Some(f) } else { None })
+        .collect();
+    if matched_fields.is_empty() {
+        return None;
+    }
+    Some((variant_def, matched_fields))
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index b746099e7279..867ac4851864 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -33,8 +33,8 @@ fn expand_record_rest_pattern(
     record_pat: ast::RecordPat,
     rest_pat: ast::RestPat,
 ) -> Option<()> {
-    let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
-    if missing_fields.is_empty() {
+    let matched_fields = ctx.sema.record_pattern_matched_fields(&record_pat);
+    if matched_fields.is_empty() {
         cov_mark::hit!(no_missing_fields);
         return None;
     }
@@ -53,7 +53,7 @@ fn expand_record_rest_pattern(
         |builder| {
             let make = SyntaxFactory::with_mappings();
             let mut editor = builder.make_editor(rest_pat.syntax());
-            let new_fields = old_field_list.fields().chain(missing_fields.iter().map(|(f, _)| {
+            let new_fields = old_field_list.fields().chain(matched_fields.iter().map(|(f, _)| {
                 make.record_pat_field_shorthand(
                     make.ident_pat(
                         false,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 77734c5d6f98..8c532e0f4d04 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -340,7 +340,7 @@ pub(crate) fn complete_expr_path(
                             let missing_fields =
                                 ctx.sema.record_literal_missing_fields(record_expr);
                             if !missing_fields.is_empty() {
-                                add_default_update(acc, ctx, ty);
+                                add_default_update(acc, ctx, ty.as_ref());
                             }
                         }
                     };
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index c5bfdcb8b734..12c564af5cba 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -36,7 +36,7 @@ pub(crate) fn complete_record_pattern_fields(
                     true => return,
                 }
             }
-            _ => ctx.sema.record_pattern_missing_fields(record_pat),
+            _ => ctx.sema.record_pattern_matched_fields(record_pat),
         };
         complete_fields(acc, ctx, missing_fields);
     }
@@ -69,14 +69,14 @@ pub(crate) fn complete_record_expr_fields(
             }
         }
         _ => {
-            let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
+            let suggest_fields = ctx.sema.record_literal_matched_fields(record_expr);
             let update_exists = record_expr
                 .record_expr_field_list()
                 .is_some_and(|list| list.dotdot_token().is_some());
 
-            if !missing_fields.is_empty() && !update_exists {
+            if !suggest_fields.is_empty() && !update_exists {
                 cov_mark::hit!(functional_update_field);
-                add_default_update(acc, ctx, ty);
+                add_default_update(acc, ctx, ty.as_ref());
             }
             if dot_prefix {
                 cov_mark::hit!(functional_update_one_dot);
@@ -90,7 +90,7 @@ pub(crate) fn complete_record_expr_fields(
                 item.add_to(acc, ctx.db);
                 return;
             }
-            missing_fields
+            suggest_fields
         }
     };
     complete_fields(acc, ctx, missing_fields);
@@ -99,11 +99,11 @@ pub(crate) fn complete_record_expr_fields(
 pub(crate) fn add_default_update(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    ty: Option>,
+    ty: Option<&hir::TypeInfo<'_>>,
 ) {
     let default_trait = ctx.famous_defs().core_default_Default();
     let impls_default_trait = default_trait
-        .zip(ty.as_ref())
+        .zip(ty)
         .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
     if impls_default_trait {
         // FIXME: This should make use of scope_def like completions so we get all the other goodies
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
index d9be6556fa5b..045b2d03b051 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
@@ -286,6 +286,24 @@ fn main() {
     );
 }
 
+#[test]
+fn functional_update_fields_completion() {
+    // Complete fields before functional update `..`
+    check(
+        r#"
+struct Point { x: i32 = 0, y: i32 = 0 }
+
+fn main() {
+    let p = Point { $0, .. };
+}
+"#,
+        expect![[r#"
+            fd x i32
+            fd y i32
+        "#]],
+    );
+}
+
 #[test]
 fn empty_union_literal() {
     check(
@@ -302,7 +320,27 @@ fn foo() {
             fd bar f32
             fd foo u32
         "#]],
-    )
+    );
+}
+
+#[test]
+fn record_pattern_field_with_rest_pat() {
+    // When .. is present, complete all unspecified fields (even those with default values)
+    check(
+        r#"
+struct UserInfo { id: i32, age: f32, email: u64 }
+
+fn foo(u1: UserInfo) {
+    let UserInfo { id, $0, .. } = u1;
+}
+"#,
+        expect![[r#"
+            fd age   f32
+            fd email u64
+            kw mut
+            kw ref
+        "#]],
+    );
 }
 
 #[test]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 2a251382d465..d5f25dfaf208 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -857,4 +857,81 @@ pub struct Claims {
         "#,
         );
     }
+
+    #[test]
+    fn test_default_field_values_basic() {
+        // This should work without errors - only field 'b' is required
+        check_diagnostics(
+            r#"
+#![feature(default_field_values)]
+struct Struct {
+    a: usize = 0,
+    b: usize,
+}
+
+fn main() {
+    Struct { b: 1, .. };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_default_field_values_missing_field_error() {
+        // This should report a missing field error because email is required
+        check_diagnostics(
+            r#"
+#![feature(default_field_values)]
+struct UserInfo {
+    id: i32,
+    age: f32 = 1.0,
+    email: String,
+}
+
+fn main() {
+    UserInfo { id: 20, .. };
+//  ^^^^^^^^💡 error: missing structure fields:
+//         |- email
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_default_field_values_requires_spread_syntax() {
+        // without `..` should report missing fields
+        check_diagnostics(
+            r#"
+#![feature(default_field_values)]
+struct Point {
+    x: i32 = 0,
+    y: i32 = 0,
+}
+
+fn main() {
+    Point { x: 0 };
+//  ^^^^^💡 error: missing structure fields:
+//      |- y
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_default_field_values_pattern_matching() {
+        check_diagnostics(
+            r#"
+#![feature(default_field_values)]
+struct Point {
+    x: i32 = 0,
+    y: i32 = 0,
+    z: i32,
+}
+
+fn main() {
+    let Point { x, .. } = Point { z: 5, .. };
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index feac5fff84a7..15ea92d1c6ec 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -272,9 +272,9 @@ pub(super) fn struct_rest_pat(
     edition: Edition,
     display_target: DisplayTarget,
 ) -> HoverResult {
-    let missing_fields = sema.record_pattern_missing_fields(pattern);
+    let matched_fields = sema.record_pattern_matched_fields(pattern);
 
-    // if there are no missing fields, the end result is a hover that shows ".."
+    // if there are no matched fields, the end result is a hover that shows ".."
     // should be left in to indicate that there are no more fields in the pattern
     // example, S {a: 1, b: 2, ..} when struct S {a: u32, b: u32}
 
@@ -285,13 +285,13 @@ pub(super) fn struct_rest_pat(
             targets.push(item);
         }
     };
-    for (_, t) in &missing_fields {
+    for (_, t) in &matched_fields {
         walk_and_push_ty(sema.db, t, &mut push_new_def);
     }
 
     res.markup = {
         let mut s = String::from(".., ");
-        for (f, _) in &missing_fields {
+        for (f, _) in &matched_fields {
             s += f.display(sema.db, display_target).to_string().as_ref();
             s += ", ";
         }

From 0df94dd94eeb0893d60aee40c9fcdf4d2b43a569 Mon Sep 17 00:00:00 2001
From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Date: Wed, 14 Jan 2026 21:20:24 +0100
Subject: [PATCH 224/583] checksum-freshness: Fix incorrect hash/file length
 values of binary dependency files

---
 .../rustc_builtin_macros/src/source_util.rs   | 10 ++++++-
 compiler/rustc_interface/src/passes.rs        | 29 +++++++++----------
 2 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 0f31de60a8a4..50db19a16d8b 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -275,7 +275,15 @@ fn load_binary_file(
         }
     };
     match cx.source_map().load_binary_file(&resolved_path) {
-        Ok(data) => Ok(data),
+        Ok(data) => {
+            cx.sess
+                .psess
+                .file_depinfo
+                .borrow_mut()
+                .insert(Symbol::intern(&resolved_path.to_string_lossy()));
+
+            Ok(data)
+        }
         Err(io_err) => {
             let mut err = cx.dcx().struct_span_err(
                 macro_span,
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index a0383b187de5..e1d9e2460443 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -9,6 +9,7 @@ use rustc_ast as ast;
 use rustc_attr_parsing::{AttributeParser, ShouldEmit};
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_codegen_ssa::{CodegenResults, CrateInfo};
+use rustc_data_structures::indexmap::IndexMap;
 use rustc_data_structures::jobserver::Proxy;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, WorkerLocal};
@@ -586,7 +587,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
     let result: io::Result<()> = try {
         // Build a list of files used to compile the output and
         // write Makefile-compatible dependency rules
-        let mut files: Vec<(String, u64, Option)> = sess
+        let mut files: IndexMap)> = sess
             .source_map()
             .files()
             .iter()
@@ -595,10 +596,12 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
             .map(|fmap| {
                 (
                     escape_dep_filename(&fmap.name.prefer_local_unconditionally().to_string()),
-                    // This needs to be unnormalized,
-                    // as external tools wouldn't know how rustc normalizes them
-                    fmap.unnormalized_source_len as u64,
-                    fmap.checksum_hash,
+                    (
+                        // This needs to be unnormalized,
+                        // as external tools wouldn't know how rustc normalizes them
+                        fmap.unnormalized_source_len as u64,
+                        fmap.checksum_hash,
+                    ),
                 )
             })
             .collect();
@@ -616,7 +619,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
         fn hash_iter_files>(
             it: impl Iterator,
             checksum_hash_algo: Option,
-        ) -> impl Iterator)> {
+        ) -> impl Iterator))> {
             it.map(move |path| {
                 match checksum_hash_algo.and_then(|algo| {
                     fs::File::open(path.as_ref())
@@ -632,8 +635,8 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
                         })
                         .ok()
                 }) {
-                    Some((file_len, checksum)) => (path, file_len, Some(checksum)),
-                    None => (path, 0, None),
+                    Some((file_len, checksum)) => (path, (file_len, Some(checksum))),
+                    None => (path, (0, None)),
                 }
             })
         }
@@ -707,18 +710,14 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
                     file,
                     "{}: {}\n",
                     path.display(),
-                    files
-                        .iter()
-                        .map(|(path, _file_len, _checksum_hash_algo)| path.as_str())
-                        .intersperse(" ")
-                        .collect::()
+                    files.keys().map(String::as_str).intersperse(" ").collect::()
                 )?;
             }
 
             // Emit a fake target for each input file to the compilation. This
             // prevents `make` from spitting out an error if a file is later
             // deleted. For more info see #28735
-            for (path, _file_len, _checksum_hash_algo) in &files {
+            for path in files.keys() {
                 writeln!(file, "{path}:")?;
             }
 
@@ -747,7 +746,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
             if sess.opts.unstable_opts.checksum_hash_algorithm().is_some() {
                 files
                     .iter()
-                    .filter_map(|(path, file_len, hash_algo)| {
+                    .filter_map(|(path, (file_len, hash_algo))| {
                         hash_algo.map(|hash_algo| (path, file_len, hash_algo))
                     })
                     .try_for_each(|(path, file_len, checksum_hash)| {

From 8a24ab39125813688fd0d4ed14f928ff26cf0269 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Mon, 26 Jan 2026 11:34:15 +0000
Subject: [PATCH 225/583] Prepare for merging from rust-lang/rust

This updates the rust-version file to 0462e8f7e51f20692b02d68efee68bb28a6f4457.
---
 src/doc/rustc-dev-guide/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index ccc0b55d4dc5..c8a3b401a6e1 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-873d4682c7d285540b8f28bfe637006cef8918a6
+0462e8f7e51f20692b02d68efee68bb28a6f4457

From c7788af89ac962245d8c3eeb30f7f4562fd70d2a Mon Sep 17 00:00:00 2001
From: Antonio Souza 
Date: Sun, 25 Jan 2026 20:51:30 -0500
Subject: [PATCH 226/583] Fix contrast ratio for Since element in rustodoc dark
 theme

Signed-off-by: Antonio Souza 
---
 src/librustdoc/html/static/css/noscript.css |  2 +-
 src/librustdoc/html/static/css/rustdoc.css  |  2 +-
 tests/rustdoc-gui/headings.goml             | 10 +++++-----
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css
index 6f44854b6914..363cec3116a9 100644
--- a/src/librustdoc/html/static/css/noscript.css
+++ b/src/librustdoc/html/static/css/noscript.css
@@ -163,7 +163,7 @@ nav.sub {
 		--headings-border-bottom-color: #d2d2d2;
 		--border-color: #e0e0e0;
 		--button-background-color: #f0f0f0;
-		--right-side-color: grey;
+		--right-side-color: #d0d0d0;
 		--code-attribute-color: #999;
 		--toggles-color: #999;
 		--toggle-filter: invert(100%);
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 6c34d31cc918..59ea478ee91e 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -3286,7 +3286,7 @@ by default.
 	--headings-border-bottom-color: #d2d2d2;
 	--border-color: #e0e0e0;
 	--button-background-color: #f0f0f0;
-	--right-side-color: grey;
+	--right-side-color: #d0d0d0;
 	--code-attribute-color: #999;
 	--toggles-color: #999;
 	--toggle-filter: invert(100%);
diff --git a/tests/rustdoc-gui/headings.goml b/tests/rustdoc-gui/headings.goml
index 94d80a3e3df5..391234b78ebd 100644
--- a/tests/rustdoc-gui/headings.goml
+++ b/tests/rustdoc-gui/headings.goml
@@ -221,14 +221,14 @@ call-function: (
 
 define-function: (
     "check-since-color",
-    [theme],
+    [theme, color],
     block {
         call-function: ("switch-theme", {"theme": |theme|})
-        assert-css: (".since", {"color": "#808080"}, ALL)
+        assert-css: (".since", {"color": |color|}, ALL)
     },
 )
 
 go-to: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
-call-function: ("check-since-color", {"theme": "ayu"})
-call-function: ("check-since-color", {"theme": "dark"})
-call-function: ("check-since-color", {"theme": "light"})
+call-function: ("check-since-color", {"theme": "ayu", "color": "#808080"})
+call-function: ("check-since-color", {"theme": "dark", "color": "#d0d0d0"})
+call-function: ("check-since-color", {"theme": "light", "color": "#808080"})

From 76b6623267dc944fd61e06741327f74ee4820a21 Mon Sep 17 00:00:00 2001
From: David Wood 
Date: Fri, 23 Jan 2026 11:06:57 +0000
Subject: [PATCH 227/583] target: fix destabilising target-spec-json

---
 compiler/rustc_target/src/spec/mod.rs                 | 3 +++
 tests/run-make/rust-lld-custom-target/rmake.rs        | 6 +++++-
 tests/run-make/rustdoc-target-spec-json-path/rmake.rs | 8 +++++++-
 tests/run-make/target-specs/rmake.rs                  | 9 ++++++++-
 tests/ui/check-cfg/values-target-json.rs              | 3 ++-
 5 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index ef475630d494..164db15d24e0 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -3352,6 +3352,9 @@ impl Target {
 
                 Err(format!("could not find specification for target {target_tuple:?}"))
             }
+            TargetTuple::TargetJson { ref contents, .. } if !unstable_options => {
+                Err("custom targets are unstable and require `-Zunstable-options`".to_string())
+            }
             TargetTuple::TargetJson { ref contents, .. } => Target::from_json(contents),
         }
     }
diff --git a/tests/run-make/rust-lld-custom-target/rmake.rs b/tests/run-make/rust-lld-custom-target/rmake.rs
index 90ba424ffe94..d281d820f47b 100644
--- a/tests/run-make/rust-lld-custom-target/rmake.rs
+++ b/tests/run-make/rust-lld-custom-target/rmake.rs
@@ -15,7 +15,11 @@ fn main() {
     // Compile to a custom target spec with rust-lld enabled by default. We'll check that by asking
     // the linker to display its version number with a link-arg.
     assert_rustc_uses_lld(
-        rustc().crate_type("cdylib").target("custom-target.json").input("lib.rs"),
+        rustc()
+            .crate_type("cdylib")
+            .target("custom-target.json")
+            .arg("-Zunstable-options")
+            .input("lib.rs"),
     );
 
     // But it can also be disabled via linker features.
diff --git a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
index d43aa9b90ac7..8660556564f1 100644
--- a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
+++ b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
@@ -5,8 +5,14 @@ use run_make_support::{cwd, rustc, rustdoc};
 
 fn main() {
     let out_dir = "rustdoc-target-spec-json-path";
-    rustc().crate_type("lib").input("dummy_core.rs").target("target.json").run();
+    rustc()
+        .arg("-Zunstable-options")
+        .crate_type("lib")
+        .input("dummy_core.rs")
+        .target("target.json")
+        .run();
     rustdoc()
+        .arg("-Zunstable-options")
         .input("my_crate.rs")
         .out_dir(out_dir)
         .library_search_path(cwd())
diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs
index 69292af5fd69..6c88f3164e9e 100644
--- a/tests/run-make/target-specs/rmake.rs
+++ b/tests/run-make/target-specs/rmake.rs
@@ -20,13 +20,20 @@ fn main() {
         .target("my-incomplete-platform.json")
         .run_fail()
         .assert_stderr_contains("missing field `llvm-target`");
-    let test_platform = rustc()
+    let _ = rustc()
         .input("foo.rs")
         .target("my-x86_64-unknown-linux-gnu-platform")
         .crate_type("lib")
         .emit("asm")
         .run_fail()
         .assert_stderr_contains("custom targets are unstable and require `-Zunstable-options`");
+    let _ = rustc()
+        .input("foo.rs")
+        .target("my-awesome-platform.json")
+        .crate_type("lib")
+        .emit("asm")
+        .run_fail()
+        .assert_stderr_contains("custom targets are unstable and require `-Zunstable-options`");
     rustc()
         .arg("-Zunstable-options")
         .env("RUST_TARGET_PATH", ".")
diff --git a/tests/ui/check-cfg/values-target-json.rs b/tests/ui/check-cfg/values-target-json.rs
index defb286ed19b..21b6af9b5560 100644
--- a/tests/ui/check-cfg/values-target-json.rs
+++ b/tests/ui/check-cfg/values-target-json.rs
@@ -4,7 +4,8 @@
 //@ check-pass
 //@ no-auto-check-cfg
 //@ needs-llvm-components: x86
-//@ compile-flags: --crate-type=lib --check-cfg=cfg() --target={{src-base}}/check-cfg/my-awesome-platform.json
+//@ compile-flags: --crate-type=lib --check-cfg=cfg()
+//@ compile-flags: -Zunstable-options --target={{src-base}}/check-cfg/my-awesome-platform.json
 //@ ignore-backends: gcc
 
 #![feature(lang_items, no_core, auto_traits, rustc_attrs)]

From 6ecb3f33f0ed410753b33e04b80bd6c504477294 Mon Sep 17 00:00:00 2001
From: Jonathan Pallant 
Date: Fri, 9 Jan 2026 08:24:13 +0000
Subject: [PATCH 228/583] Adds two new Tier 3 targets -
 `aarch64v8r-unknown-none` and `aarch64v8r-unknown-none-softfloat`.

The existing `aarch64-unknown-none` target assumes Armv8.0-A as a baseline. However, Arm recently released the Arm Cortex-R82 processor which is the first to implement the Armv8-R AArch64 mode architecture. This architecture is similar to Armv8-A AArch64, however it has a different set of mandatory features, and is based off of Armv8.4. It is largely unrelated to the existing Armv8-R architecture target (`armv8r-none-eabihf`), which only operates in AArch32 mode.

The second `aarch64v8r-unknown-none-softfloat` target allows for possible Armv8-R AArch64 CPUs with no FPU, or for use-cases where FPU register stacking is not desired. As with the existing `aarch64-unknown-none` target we have coupled FPU support and Neon support together - there is no 'has FPU but does not have NEON' target proposed even though the architecture technically allows for it.

This PR was developed by Ferrous Systems on behalf of Arm. Arm is the owner of these changes.
---
 compiler/rustc_target/src/spec/mod.rs         |   2 +
 .../spec/targets/aarch64v8r_unknown_none.rs   |  37 +++++
 .../aarch64v8r_unknown_none_softfloat.rs      |  36 +++++
 src/bootstrap/src/core/sanity.rs              |   2 +
 src/doc/rustc/src/SUMMARY.md                  |   1 +
 src/doc/rustc/src/platform-support.md         |   2 +
 .../aarch64v8r-unknown-none.md                |  84 +++++++++++
 .../platform-support/armv8r-none-eabihf.md    |   3 +
 src/tools/tidy/src/target_specific_tests.rs   |   2 +-
 tests/assembly-llvm/targets/targets-elf.rs    |   6 +
 tests/codegen-llvm/aarch64v8r-softfloat.rs    |  45 ++++++
 .../sanitizer/kasan-emits-instrumentation.rs  |   4 +-
 .../kcfi/add-cfi-normalize-integers-flag.rs   |   4 +-
 .../sanitizer/kcfi/add-kcfi-flag.rs           |   4 +-
 .../sanitizer/kcfi/add-kcfi-offset-flag.rs    |   4 +-
 ...t-kcfi-operand-bundle-attr-sanitize-off.rs |   4 +-
 ...rand-bundle-itanium-cxx-abi-generalized.rs |   4 +-
 ...-itanium-cxx-abi-normalized-generalized.rs |   4 +-
 ...erand-bundle-itanium-cxx-abi-normalized.rs |   4 +-
 ...mit-kcfi-operand-bundle-itanium-cxx-abi.rs |   4 +-
 .../kcfi/emit-kcfi-operand-bundle.rs          |   4 +-
 .../kcfi/emit-type-metadata-trait-objects.rs  |   4 +-
 .../sanitizer/kcfi/fn-ptr-reify-shim.rs       |   4 +-
 .../sanitizer/kcfi/naked-function.rs          |   4 +-
 .../sanitizer/sanitize-off-asan-kasan.rs      |   4 +-
 tests/ui/asm/aarch64v8r.rs                    | 140 ++++++++++++++++++
 26 files changed, 401 insertions(+), 15 deletions(-)
 create mode 100644 compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none.rs
 create mode 100644 compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs
 create mode 100644 src/doc/rustc/src/platform-support/aarch64v8r-unknown-none.md
 create mode 100644 tests/codegen-llvm/aarch64v8r-softfloat.rs
 create mode 100644 tests/ui/asm/aarch64v8r.rs

diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index fe4b91c53f63..14e83cf3d2de 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1709,6 +1709,8 @@ supported_targets! {
     ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat),
     ("aarch64_be-unknown-none-softfloat", aarch64_be_unknown_none_softfloat),
     ("aarch64-unknown-nuttx", aarch64_unknown_nuttx),
+    ("aarch64v8r-unknown-none", aarch64v8r_unknown_none),
+    ("aarch64v8r-unknown-none-softfloat", aarch64v8r_unknown_none_softfloat),
 
     ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx),
 
diff --git a/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none.rs b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none.rs
new file mode 100644
index 000000000000..8f8bbb4a41ca
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none.rs
@@ -0,0 +1,37 @@
+use crate::spec::{
+    Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target,
+    TargetMetadata, TargetOptions,
+};
+
+pub(crate) fn target() -> Target {
+    let opts = TargetOptions {
+        // based off the aarch64-unknown-none target at time of addition
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        linker: Some("rust-lld".into()),
+        supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
+        relocation_model: RelocModel::Static,
+        disable_redzone: true,
+        max_atomic_width: Some(128),
+        stack_probes: StackProbeType::Inline,
+        panic_strategy: PanicStrategy::Abort,
+        default_uwtable: true,
+
+        // deviations from aarch64-unknown-none: `+v8a` -> `+v8r`; `+v8r` implies `+neon`
+        features: "+v8r,+strict-align".into(),
+        ..Default::default()
+    };
+    Target {
+        llvm_target: "aarch64-unknown-none".into(),
+        metadata: TargetMetadata {
+            description: Some("Bare Armv8-R AArch64, hardfloat".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(false),
+        },
+        pointer_width: 64,
+        // $ clang-21 -S -emit-llvm -target aarch64 -mcpu=cortex-r82 stub.c
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        arch: Arch::AArch64,
+        options: opts,
+    }
+}
diff --git a/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs
new file mode 100644
index 000000000000..63f518873c60
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/aarch64v8r_unknown_none_softfloat.rs
@@ -0,0 +1,36 @@
+use crate::spec::{
+    Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType,
+    Target, TargetMetadata, TargetOptions,
+};
+
+pub(crate) fn target() -> Target {
+    let opts = TargetOptions {
+        abi: Abi::SoftFloat,
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        linker: Some("rust-lld".into()),
+        relocation_model: RelocModel::Static,
+        disable_redzone: true,
+        max_atomic_width: Some(128),
+        supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
+        stack_probes: StackProbeType::Inline,
+        panic_strategy: PanicStrategy::Abort,
+        default_uwtable: true,
+
+        // deviations from aarch64-unknown-none: `+v8a` -> `+v8r`
+        features: "+v8r,+strict-align,-neon".into(),
+        ..Default::default()
+    };
+    Target {
+        llvm_target: "aarch64-unknown-none".into(),
+        metadata: TargetMetadata {
+            description: Some("Bare Armv8-R AArch64, softfloat".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(false),
+        },
+        pointer_width: 64,
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        arch: Arch::AArch64,
+        options: opts,
+    }
+}
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index a16a2b17be17..8b6405afa892 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -46,6 +46,8 @@ const STAGE0_MISSING_TARGETS: &[&str] = &[
     "armv6-none-eabi",
     "armv6-none-eabihf",
     "thumbv6-none-eabi",
+    "aarch64v8r-unknown-none",
+    "aarch64v8r-unknown-none-softfloat",
 ];
 
 /// Minimum version threshold for libstdc++ required when using prebuilt LLVM
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 632b9e2fc431..2ec0c3648bdf 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -50,6 +50,7 @@
     - [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md)
     - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md)
     - [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md)
+    - [aarch64v8r-unknown-none*](platform-support/aarch64v8r-unknown-none.md)
     - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md)
     - [aarch64_be-unknown-linux-musl](platform-support/aarch64_be-unknown-linux-musl.md)
     - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index dff8953347ff..114f66282afd 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -275,6 +275,8 @@ target | std | host | notes
 [`aarch64-unknown-trusty`](platform-support/trusty.md) | ✓ |  |
 [`aarch64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ |  |
 [`aarch64-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  | ARM64 VxWorks OS
+[`aarch64v8r-unknown-none`](platform-support/aarch64v8r-unknown-none.md) | * | | Bare Armv8-R in AArch64 mode, hardfloat
+[`aarch64v8r-unknown-none-softfloat`](platform-support/aarch64v8r-unknown-none.md) | * | | Bare Armv8-R in AArch64 mode, softfloat
 [`aarch64_be-unknown-hermit`](platform-support/hermit.md) | ✓ |  | ARM64 Hermit (big-endian)
 `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian)
 `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI)
diff --git a/src/doc/rustc/src/platform-support/aarch64v8r-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64v8r-unknown-none.md
new file mode 100644
index 000000000000..9e232b320516
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/aarch64v8r-unknown-none.md
@@ -0,0 +1,84 @@
+# `aarch64v8r-unknown-none` and `aarch64v8r-unknown-none-softfloat`
+
+* **Tier: 3**
+* **Library Support:** core and alloc (bare-metal, `#![no_std]`)
+
+Bare-metal target for CPUs in the Armv8-R architecture family, running in
+AArch64 mode. Processors in this family include the
+[Arm Cortex-R82][cortex-r82].
+
+For Armv8-R CPUs running in AArch32 mode (such as the Arm Cortex-R52), see
+[`armv8r-none-eabihf`](armv8r-none-eabihf.md) instead.
+
+[cortex-r82]: https://developer.arm.com/processors/Cortex-R82
+
+## Target maintainers
+
+- [Rust Embedded Devices Working Group Arm Team]
+- [@rust-lang/arm-maintainers][arm_maintainers] ([rust@arm.com][arm_email])
+
+[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team
+[arm_maintainers]: https://github.com/rust-lang/team/blob/master/teams/arm-maintainers.toml
+[arm_email]: mailto:rust@arm.com
+
+## Target CPU and Target Feature options
+
+Unlike AArch64 v8-A processors, not all AArch64 v8-R processors include an FPU
+(that is, not all Armv8-R AArch64 processors implement the optional Armv8
+`FEAT_FP` extension). If you do not have an FPU, or have an FPU but wish to use
+a soft-float ABI anyway, you should use the `aarch64v8r-unknown-none-softfloat`
+target. If you wish to use the standard hard-float Arm AArch64 calling
+convention, and you have an FPU, you can use the `aarch64v8r-unknown-none`
+target.
+
+When using the `aarch64v8r-unknown-none` target, the minimum floating-point
+features assumed are the Advanced SIMD features (`FEAT_AdvSIMD`, or `+neon`),
+the implementation of which is branded Arm NEON.
+
+If your processor supports a different set of floating-point features than the
+default expectations then these should also be enabled or disabled as needed
+with [`-C target-feature=(+/-)`][target-feature]. However, note that currently
+Rust does not support building hard-float AArch64 targets with Advanced SIMD
+support disabled. It is also possible to tell Rust (or LLVM) that you have a
+specific model of Arm processor, using the [`-Ctarget-cpu`][target-cpu] option.
+Doing so may change the default set of target-features enabled.
+
+[target-feature]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-feature
+[target-cpu]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-cpu
+
+## Requirements
+
+These targets are cross-compiled and use static linking.
+
+By default, the `lld` linker included with Rust will be used; however, you may
+want to use the GNU linker instead. This can be obtained for Windows/Mac/Linux
+from the [Arm Developer Website][arm-gnu-toolchain], or possibly from your OS's
+package manager. To use it, add the following to your `.cargo/config.toml`:
+
+```toml
+[target.aarch64-unknown-none]
+linker = "aarch64-none-elf-ld"
+```
+
+The GNU linker can also be used by specifying `aarch64-none-elf-gcc` as the
+linker. This is needed when using GCC's link time optimization.
+
+These targets don't provide a linker script, so you'll need to bring your own
+according to the specific device you are using. Pass
+`-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use
+`your_script.ld` during linking.
+
+[arm-gnu-toolchain]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain
+
+## Cross-compilation toolchains and C code
+
+This target supports C code compiled with the `aarch64-none-elf` target
+triple and a suitable `-march` or `-mcpu` flag.
+
+## Start-up and Low-Level Code
+
+The [Rust Embedded Devices Working Group Arm Team] maintain the
+[`aarch64-cpu`] crate, which may be useful for writing bare-metal code using
+this target.
+
+[`aarch64-cpu`]: https://docs.rs/aarch64-cpu
diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
index 85abac3d7eb8..73d88bcb89a1 100644
--- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
+++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md
@@ -15,6 +15,9 @@ and [Cortex-R52+][cortex-r52-plus].
 See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all
 `arm-none-eabi` targets.
 
+For Armv8-R CPUs running in AArch64 mode (such as the Arm Cortex-R82), see
+[`aarch64v8r-unknown-none`](aarch64v8r-unknown-none.md) instead.
+
 [cortex-r52]: https://www.arm.com/products/silicon-ip-cpu/cortex-r/cortex-r52
 [cortex-r52-plus]: https://www.arm.com/products/silicon-ip-cpu/cortex-r/cortex-r52-plus
 
diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs
index 11138de5de76..ea365527a8cd 100644
--- a/src/tools/tidy/src/target_specific_tests.rs
+++ b/src/tools/tidy/src/target_specific_tests.rs
@@ -98,7 +98,7 @@ fn arch_to_llvm_component(arch: &str) -> String {
     // enough for the purpose of this tidy check.
     match arch {
         "amdgcn" => "amdgpu".into(),
-        "aarch64_be" | "arm64_32" | "arm64e" | "arm64ec" => "aarch64".into(),
+        "aarch64v8r" | "aarch64_be" | "arm64_32" | "arm64e" | "arm64ec" => "aarch64".into(),
         "i386" | "i586" | "i686" | "x86" | "x86_64" | "x86_64h" => "x86".into(),
         "loongarch32" | "loongarch64" => "loongarch".into(),
         "nvptx64" => "nvptx".into(),
diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs
index 0d5cd796aa48..c38e86315b27 100644
--- a/tests/assembly-llvm/targets/targets-elf.rs
+++ b/tests/assembly-llvm/targets/targets-elf.rs
@@ -67,6 +67,12 @@
 //@ revisions: aarch64_unknown_none_softfloat
 //@ [aarch64_unknown_none_softfloat] compile-flags: --target aarch64-unknown-none-softfloat
 //@ [aarch64_unknown_none_softfloat] needs-llvm-components: aarch64
+//@ revisions: aarch64v8r_unknown_none
+//@ [aarch64v8r_unknown_none] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r_unknown_none] needs-llvm-components: aarch64
+//@ revisions: aarch64v8r_unknown_none_softfloat
+//@ [aarch64v8r_unknown_none_softfloat] compile-flags: --target aarch64v8r-unknown-none-softfloat
+//@ [aarch64v8r_unknown_none_softfloat] needs-llvm-components: aarch64
 //@ revisions: aarch64_unknown_nto_qnx700
 //@ [aarch64_unknown_nto_qnx700] compile-flags: --target aarch64-unknown-nto-qnx700
 //@ [aarch64_unknown_nto_qnx700] needs-llvm-components: aarch64
diff --git a/tests/codegen-llvm/aarch64v8r-softfloat.rs b/tests/codegen-llvm/aarch64v8r-softfloat.rs
new file mode 100644
index 000000000000..5ccda4f3a0ae
--- /dev/null
+++ b/tests/codegen-llvm/aarch64v8r-softfloat.rs
@@ -0,0 +1,45 @@
+//@ add-minicore
+//@ compile-flags: --target aarch64v8r-unknown-none-softfloat -Zmerge-functions=disabled
+//@ needs-llvm-components: aarch64
+#![crate_type = "lib"]
+#![feature(no_core, lang_items)]
+#![no_core]
+
+extern crate minicore;
+use minicore::*;
+
+// CHECK: i64 @pass_f64_C(i64 {{[^,]*}})
+#[no_mangle]
+extern "C" fn pass_f64_C(x: f64) -> f64 {
+    x
+}
+
+// CHECK: i64 @pass_f32_pair_C(i64 {{[^,]*}})
+#[no_mangle]
+extern "C" fn pass_f32_pair_C(x: (f32, f32)) -> (f32, f32) {
+    x
+}
+
+// CHECK: [2 x i64] @pass_f64_pair_C([2 x i64] {{[^,]*}})
+#[no_mangle]
+extern "C" fn pass_f64_pair_C(x: (f64, f64)) -> (f64, f64) {
+    x
+}
+
+// CHECK: i64 @pass_f64_Rust(i64 {{[^,]*}})
+#[no_mangle]
+fn pass_f64_Rust(x: f64) -> f64 {
+    x
+}
+
+// CHECK: i64 @pass_f32_pair_Rust(i64 {{[^,]*}})
+#[no_mangle]
+fn pass_f32_pair_Rust(x: (f32, f32)) -> (f32, f32) {
+    x
+}
+
+// CHECK: void @pass_f64_pair_Rust(ptr {{.*}}%{{[^ ]+}}, ptr {{.*}}%{{[^ ]+}})
+#[no_mangle]
+fn pass_f64_pair_Rust(x: (f64, f64)) -> (f64, f64) {
+    x
+}
diff --git a/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs b/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs
index da0c976d8a53..f0135cdd0011 100644
--- a/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs
+++ b/tests/codegen-llvm/sanitizer/kasan-emits-instrumentation.rs
@@ -2,9 +2,11 @@
 
 //@ add-minicore
 //@ compile-flags: -Zsanitizer=kernel-address -Copt-level=0
-//@ revisions: aarch64 riscv64imac riscv64gc x86_64
+//@ revisions: aarch64 aarch64v8r riscv64imac riscv64gc x86_64
 //@[aarch64] compile-flags: --target aarch64-unknown-none
 //@[aarch64] needs-llvm-components: aarch64
+//@[aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@[aarch64v8r] needs-llvm-components: aarch64
 //@[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf
 //@[riscv64imac] needs-llvm-components: riscv
 //@[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf
diff --git a/tests/codegen-llvm/sanitizer/kcfi/add-cfi-normalize-integers-flag.rs b/tests/codegen-llvm/sanitizer/kcfi/add-cfi-normalize-integers-flag.rs
index 24c5d1be1d60..53b8c605eb73 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/add-cfi-normalize-integers-flag.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/add-cfi-normalize-integers-flag.rs
@@ -1,9 +1,11 @@
 // Verifies that "cfi-normalize-integers" module flag is added.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers
diff --git a/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-flag.rs b/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-flag.rs
index 53b1a3f2d74a..9058d5b5cfcb 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-flag.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-flag.rs
@@ -1,9 +1,11 @@
 // Verifies that "kcfi" module flag is added.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi
diff --git a/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-offset-flag.rs b/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-offset-flag.rs
index 82747351e028..6574302033c8 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-offset-flag.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/add-kcfi-offset-flag.rs
@@ -1,9 +1,11 @@
 // Verifies that "kcfi-offset" module flag is added.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Z patchable-function-entry=4,3
diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs
index ee4928053cf9..eb9ab6b8f90c 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-sanitize-off.rs
@@ -1,9 +1,11 @@
 // Verifies that KCFI operand bundles are omitted.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0
diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs
index 9b861c08ac95..f934a3bfcee7 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs
@@ -1,9 +1,11 @@
 // Verifies that generalized KCFI type metadata for functions are emitted.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-generalize-pointers
diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs
index c2410aa9f4d8..b72b6d7ce308 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs
@@ -1,9 +1,11 @@
 // Verifies that normalized and generalized KCFI type metadata for functions are emitted.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers
diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs
index fbad335286cb..064ab53a1856 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs
@@ -1,9 +1,11 @@
 // Verifies that normalized KCFI type metadata for functions are emitted.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers
diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs
index 6c7a8194ec4e..8410286e49db 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs
@@ -1,9 +1,11 @@
 // Verifies that KCFI type metadata for functions are emitted.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0
diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs
index e22a210f3dfb..3494854bcffd 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs
@@ -1,9 +1,11 @@
 // Verifies that KCFI operand bundles are emitted.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0
diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs
index 3312f12f6885..4510e70cbc35 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs
@@ -1,9 +1,11 @@
 // Verifies that type metadata identifiers for trait objects are emitted correctly.
 //
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64v8r aarch64 x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0
diff --git a/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs
index 676b2af8c8f1..8cfb6a57a4a9 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/fn-ptr-reify-shim.rs
@@ -1,7 +1,9 @@
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0
diff --git a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs
index 830689780dce..6b9d11b192b3 100644
--- a/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs
+++ b/tests/codegen-llvm/sanitizer/kcfi/naked-function.rs
@@ -1,7 +1,9 @@
 //@ add-minicore
-//@ revisions: aarch64 x86_64
+//@ revisions: aarch64 aarch64v8r x86_64
 //@ [aarch64] compile-flags: --target aarch64-unknown-none
 //@ [aarch64] needs-llvm-components: aarch64
+//@ [aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@ [aarch64v8r] needs-llvm-components: aarch64
 //@ [x86_64] compile-flags: --target x86_64-unknown-none
 //@ [x86_64] needs-llvm-components: x86
 //@ compile-flags: -Ctarget-feature=-crt-static -Zsanitizer=kcfi -Cno-prepopulate-passes -Copt-level=0
diff --git a/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs b/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs
index c5df311efae0..cef4a650e477 100644
--- a/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs
+++ b/tests/codegen-llvm/sanitizer/sanitize-off-asan-kasan.rs
@@ -3,9 +3,11 @@
 //
 //@ add-minicore
 //@ compile-flags: -Zsanitizer=kernel-address -Ctarget-feature=-crt-static -Copt-level=0
-//@ revisions: aarch64 riscv64imac riscv64gc x86_64
+//@ revisions: aarch64 aarch64v8r riscv64imac riscv64gc x86_64
 //@[aarch64] compile-flags: --target aarch64-unknown-none
 //@[aarch64] needs-llvm-components: aarch64
+//@[aarch64v8r] compile-flags: --target aarch64v8r-unknown-none
+//@[aarch64v8r] needs-llvm-components: aarch64
 //@[riscv64imac] compile-flags: --target riscv64imac-unknown-none-elf
 //@[riscv64imac] needs-llvm-components: riscv
 //@[riscv64gc] compile-flags: --target riscv64gc-unknown-none-elf
diff --git a/tests/ui/asm/aarch64v8r.rs b/tests/ui/asm/aarch64v8r.rs
new file mode 100644
index 000000000000..6b582bb730f0
--- /dev/null
+++ b/tests/ui/asm/aarch64v8r.rs
@@ -0,0 +1,140 @@
+// Codegen test of mandatory Armv8-R AArch64 extensions
+
+//@ add-minicore
+//@ revisions: hf sf
+//@ [hf] compile-flags: --target aarch64v8r-unknown-none
+//@ [hf] needs-llvm-components: aarch64
+//@ [sf] compile-flags: --target aarch64v8r-unknown-none-softfloat
+//@ [sf] needs-llvm-components: aarch64
+//@ build-pass
+//@ ignore-backends: gcc
+
+#![feature(no_core)]
+#![no_core]
+#![no_main]
+#![crate_type = "rlib"]
+#![deny(dead_code)] // ensures we call all private functions from the public one
+
+extern crate minicore;
+use minicore::*;
+
+/* # Mandatory extensions
+ *
+ * A comment indicates that the extension has no associated assembly instruction and cannot be
+ * codegen tested
+ *
+ * ## References:
+ *
+ * - Arm Architecture Reference Manual for R-profile AArch64 architecture (DDI 0628) -- has the
+ *   list of mandatory extensions
+ * - Arm Architecture Reference Manual for A-profile architecture (ARM DDI 0487) -- has the
+ *   mapping from features to instructions
+ * - Feature names in A-profile architecture (109697_0100_02_en Version 1.0) -- overview of
+ *   what each extension mean
+ * */
+pub fn mandatory_extensions() {
+    /* ## ARMv8.0 */
+    feat_aa64();
+    // FEAT_AA64EL0
+    // FEAT_AA64EL1
+    // FEAT_AA64EL2
+    feat_crc32();
+    // FEAT_EL0
+    // FEAT_EL1
+    // FEAT_EL2
+    // FEAT_IVIPT
+
+    /* ## ARMv8.1 */
+    // FEAT_HPDS
+    feat_lse();
+    feat_pan();
+
+    /* ## ARMv8.2 */
+    feat_asmv8p2();
+    feat_dpb();
+    // FEAT_Debugv8p2
+    // FEAT_PAN2
+    feat_ras();
+    // FEAT_TTCNP
+    feat_uao();
+    // FEAT_XNX
+
+    /* ## ARMv8.3 */
+    feat_lrcpc();
+    feat_pauth();
+
+    /* ## ARMv8.4 */
+    feat_dit();
+    // FEAT_Debugv8p4
+    feat_flagm();
+    // FEAT_IDST
+    feat_lrcpc2();
+    // FEAT_LSE2
+    // FEAT_S2FWB
+    feat_tlbios();
+    feat_tlbirange();
+    // FEAT_TTL
+}
+
+fn feat_aa64() {
+    // CurrentEL register only present when FEAT_AA64 is implemented
+    unsafe { asm!("mrs x0, CurrentEL") }
+}
+
+fn feat_crc32() {
+    // instruction is present when FEAT_CRC32 is implemented
+    unsafe { asm!("crc32b w0, w1, w2") }
+}
+
+fn feat_lse() {
+    // instruction is present when FEAT_LSE is implemented
+    unsafe { asm!("casp w0, w1, w2, w3, [x4]") }
+}
+
+fn feat_pan() {
+    unsafe { asm!("mrs x0, PAN") }
+}
+
+fn feat_asmv8p2() {
+    unsafe { asm!("BFC w0, #0, #1") }
+}
+
+fn feat_dpb() {
+    unsafe { asm!("DC CVAP, x0") }
+}
+
+fn feat_ras() {
+    unsafe { asm!("ESB") }
+}
+
+fn feat_uao() {
+    unsafe { asm!("mrs x0, UAO") }
+}
+
+fn feat_lrcpc() {
+    unsafe { asm!("ldaprb w0, [x1]") }
+}
+
+fn feat_pauth() {
+    unsafe { asm!("xpacd x0") }
+}
+
+fn feat_dit() {
+    unsafe { asm!("mrs x0, DIT") }
+}
+
+fn feat_flagm() {
+    unsafe { asm!("cfinv") }
+}
+
+fn feat_lrcpc2() {
+    unsafe { asm!("stlurb w0, [x1]") }
+}
+
+fn feat_tlbios() {
+    unsafe { asm!("tlbi VMALLE1OS") }
+}
+
+fn feat_tlbirange() {
+    unsafe { asm!("tlbi RVAE1IS, x0") }
+}

From 3757ce6d1f6b3f54fabaf87d4b2c9ac1db76e808 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Mon, 19 Jan 2026 13:35:05 +0100
Subject: [PATCH 229/583] Remove a string comparison and reduce number of
 clones in `Hierarchy::add_path`

---
 src/librustdoc/html/render/write_shared.rs | 52 ++++++++++++----------
 1 file changed, 28 insertions(+), 24 deletions(-)

diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index af97ae93a2b9..42fc35ded7c6 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -504,33 +504,37 @@ impl Hierarchy {
 
     fn add_path(self: &Rc, path: &Path) {
         let mut h = Rc::clone(self);
-        let mut elems = path
+        let mut components = path
             .components()
-            .filter_map(|s| match s {
-                Component::Normal(s) => Some(s.to_owned()),
-                Component::ParentDir => Some(OsString::from("..")),
-                _ => None,
-            })
+            .filter(|component| matches!(component, Component::Normal(_) | Component::ParentDir))
             .peekable();
-        loop {
-            let cur_elem = elems.next().expect("empty file path");
-            if cur_elem == ".." {
-                if let Some(parent) = h.parent.upgrade() {
-                    h = parent;
+
+        while let Some(component) = components.next() {
+            match component {
+                Component::Normal(s) => {
+                    if components.peek().is_none() {
+                        h.elems.borrow_mut().insert(s.to_owned());
+                        break;
+                    }
+                    let next_h = {
+                        let mut children = h.children.borrow_mut();
+
+                        if let Some(existing) = children.get(s) {
+                            Rc::clone(existing)
+                        } else {
+                            let new_node = Rc::new(Self::with_parent(s.to_owned(), &h));
+                            children.insert(s.to_owned(), Rc::clone(&new_node));
+                            new_node
+                        }
+                    };
+                    h = next_h;
                 }
-                continue;
-            }
-            if elems.peek().is_none() {
-                h.elems.borrow_mut().insert(cur_elem);
-                break;
-            } else {
-                let entry = Rc::clone(
-                    h.children
-                        .borrow_mut()
-                        .entry(cur_elem.clone())
-                        .or_insert_with(|| Rc::new(Self::with_parent(cur_elem, &h))),
-                );
-                h = entry;
+                Component::ParentDir => {
+                    if let Some(parent) = h.parent.upgrade() {
+                        h = parent;
+                    }
+                }
+                _ => {}
             }
         }
     }

From dd42a9f118e3d35928a5efccfca1ad692f13e93b Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Mon, 19 Jan 2026 15:00:04 +0100
Subject: [PATCH 230/583] Replace regex with find calls

---
 src/librustdoc/html/render/write_shared.rs | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 42fc35ded7c6..aa7972449e55 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -26,7 +26,6 @@ use std::str::FromStr;
 use std::{fmt, fs};
 
 use indexmap::IndexMap;
-use regex::Regex;
 use rustc_ast::join_path_syms;
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
@@ -376,12 +375,15 @@ fn hack_get_external_crate_names(
     };
     // this is only run once so it's fine not to cache it
     // !dot_matches_new_line: all crates on same line. greedy: match last bracket
-    let regex = Regex::new(r"\[.*\]").unwrap();
-    let Some(content) = regex.find(&content) else {
-        return Err(Error::new("could not find crates list in crates.js", path));
-    };
-    let content: Vec = try_err!(serde_json::from_str(content.as_str()), &path);
-    Ok(content)
+    if let Some(start) = content.find('[')
+        && let Some(end) = content[start..].find(']')
+    {
+        let content: Vec =
+            try_err!(serde_json::from_str(&content[start..=start + end]), &path);
+        Ok(content)
+    } else {
+        Err(Error::new("could not find crates list in crates.js", path))
+    }
 }
 
 #[derive(Serialize, Deserialize, Clone, Default, Debug)]

From bd453118ee2ac0195e8b5eff653cde11b9ca66e4 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Mon, 26 Jan 2026 14:59:44 +0100
Subject: [PATCH 231/583] Improve code

---
 src/librustdoc/html/render/write_shared.rs | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index aa7972449e55..1b5dbeed8de8 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -511,6 +511,7 @@ impl Hierarchy {
             .filter(|component| matches!(component, Component::Normal(_) | Component::ParentDir))
             .peekable();
 
+        assert!(components.peek().is_some(), "empty file path");
         while let Some(component) = components.next() {
             match component {
                 Component::Normal(s) => {
@@ -518,7 +519,7 @@ impl Hierarchy {
                         h.elems.borrow_mut().insert(s.to_owned());
                         break;
                     }
-                    let next_h = {
+                    h = {
                         let mut children = h.children.borrow_mut();
 
                         if let Some(existing) = children.get(s) {
@@ -529,12 +530,9 @@ impl Hierarchy {
                             new_node
                         }
                     };
-                    h = next_h;
                 }
-                Component::ParentDir => {
-                    if let Some(parent) = h.parent.upgrade() {
-                        h = parent;
-                    }
+                Component::ParentDir if let Some(parent) = h.parent.upgrade() => {
+                    h = parent;
                 }
                 _ => {}
             }

From bf1c3f6a14d41785416e89eaa909a674021e7727 Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 26 Jan 2026 11:02:50 +0000
Subject: [PATCH 232/583] move `Types` from `with_api!` to `Server`

---
 .../rustc_expand/src/proc_macro_server.rs     |   4 +-
 library/proc_macro/src/bridge/client.rs       |  13 +-
 library/proc_macro/src/bridge/mod.rs          | 121 ++++++++----------
 library/proc_macro/src/bridge/server.rs       |  59 ++++-----
 .../crates/proc-macro-srv/src/dylib.rs        |   2 +-
 .../src/server_impl/rust_analyzer_span.rs     |   4 +-
 .../src/server_impl/token_id.rs               |   4 +-
 7 files changed, 81 insertions(+), 126 deletions(-)

diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index a51aa90355bc..12490880ab0e 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -458,13 +458,11 @@ impl<'a, 'b> Rustc<'a, 'b> {
     }
 }
 
-impl server::Types for Rustc<'_, '_> {
+impl server::Server for Rustc<'_, '_> {
     type TokenStream = TokenStream;
     type Span = Span;
     type Symbol = Symbol;
-}
 
-impl server::Server for Rustc<'_, '_> {
     fn globals(&mut self) -> ExpnGlobals {
         ExpnGlobals {
             def_site: self.def_site,
diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs
index 0d87a727ae40..381c8502c853 100644
--- a/library/proc_macro/src/bridge/client.rs
+++ b/library/proc_macro/src/bridge/client.rs
@@ -67,12 +67,6 @@ impl Decode<'_, '_, S> for Span {
     }
 }
 
-// FIXME(eddyb) generate these impls by pattern-matching on the
-// names of methods - also could use the presence of `fn drop`
-// to distinguish between 'owned and 'interned, above.
-// Alternatively, special "modes" could be listed of types in with_api
-// instead of pattern matching on methods, here and in server decl.
-
 impl Clone for TokenStream {
     fn clone(&self) -> Self {
         Methods::ts_clone(self)
@@ -104,10 +98,7 @@ pub(crate) use super::symbol::Symbol;
 
 macro_rules! define_client_side {
     (
-        Methods {
-            $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
-        },
-        $($name:ident),* $(,)?
+        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
     ) => {
         impl Methods {
             $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? {
@@ -130,7 +121,7 @@ macro_rules! define_client_side {
         }
     }
 }
-with_api!(self, self, define_client_side);
+with_api!(self, define_client_side);
 
 struct Bridge<'a> {
     /// Reusable buffer (only `clear`-ed, never shrunk), primarily
diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs
index 6f7c8726f925..f454861be4ed 100644
--- a/library/proc_macro/src/bridge/mod.rs
+++ b/library/proc_macro/src/bridge/mod.rs
@@ -18,87 +18,71 @@ use crate::{Delimiter, Level, Spacing};
 /// Higher-order macro describing the server RPC API, allowing automatic
 /// generation of type-safe Rust APIs, both client-side and server-side.
 ///
-/// `with_api!(MySelf, my_self, my_macro)` expands to:
+/// `with_api!(MySelf, my_macro)` expands to:
 /// ```rust,ignore (pseudo-code)
 /// my_macro! {
-///     Methods {
-///         // ...
-///         fn lit_character(ch: char) -> MySelf::Literal;
-///         // ...
-///         fn lit_span(my_self: &MySelf::Literal) -> MySelf::Span;
-///         fn lit_set_span(my_self: &mut MySelf::Literal, span: MySelf::Span);
-///     },
-///     Literal,
-///     Span,
+///     fn lit_character(ch: char) -> MySelf::Literal;
+///     fn lit_span(lit: &MySelf::Literal) -> MySelf::Span;
+///     fn lit_set_span(lit: &mut MySelf::Literal, span: MySelf::Span);
 ///     // ...
 /// }
 /// ```
 ///
-/// The first two arguments serve to customize the arguments names
-/// and argument/return types, to enable several different usecases:
-///
-/// If `my_self` is just `self`, then each `fn` signature can be used
-/// as-is for a method. If it's anything else (`self_` in practice),
-/// then the signatures don't have a special `self` argument, and
-/// can, therefore, have a different one introduced.
+/// The first argument serves to customize the argument/return types,
+/// to enable several different usecases:
 ///
 /// If `MySelf` is just `Self`, then the types are only valid inside
 /// a trait or a trait impl, where the trait has associated types
 /// for each of the API types. If non-associated types are desired,
 /// a module name (`self` in practice) can be used instead of `Self`.
 macro_rules! with_api {
-    ($S:ident, $self:ident, $m:ident) => {
+    ($S:ident, $m:ident) => {
         $m! {
-            Methods {
-                fn injected_env_var(var: &str) -> Option;
-                fn track_env_var(var: &str, value: Option<&str>);
-                fn track_path(path: &str);
-                fn literal_from_str(s: &str) -> Result, ()>;
-                fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);
+            fn injected_env_var(var: &str) -> Option;
+            fn track_env_var(var: &str, value: Option<&str>);
+            fn track_path(path: &str);
+            fn literal_from_str(s: &str) -> Result, ()>;
+            fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);
 
-                fn ts_drop(stream: $S::TokenStream);
-                fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream;
-                fn ts_is_empty(stream: &$S::TokenStream) -> bool;
-                fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
-                fn ts_from_str(src: &str) -> $S::TokenStream;
-                fn ts_to_string(stream: &$S::TokenStream) -> String;
-                fn ts_from_token_tree(
-                    tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>,
-                ) -> $S::TokenStream;
-                fn ts_concat_trees(
-                    base: Option<$S::TokenStream>,
-                    trees: Vec>,
-                ) -> $S::TokenStream;
-                fn ts_concat_streams(
-                    base: Option<$S::TokenStream>,
-                    streams: Vec<$S::TokenStream>,
-                ) -> $S::TokenStream;
-                fn ts_into_trees(
-                    stream: $S::TokenStream
-                ) -> Vec>;
+            fn ts_drop(stream: $S::TokenStream);
+            fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream;
+            fn ts_is_empty(stream: &$S::TokenStream) -> bool;
+            fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
+            fn ts_from_str(src: &str) -> $S::TokenStream;
+            fn ts_to_string(stream: &$S::TokenStream) -> String;
+            fn ts_from_token_tree(
+                tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>,
+            ) -> $S::TokenStream;
+            fn ts_concat_trees(
+                base: Option<$S::TokenStream>,
+                trees: Vec>,
+            ) -> $S::TokenStream;
+            fn ts_concat_streams(
+                base: Option<$S::TokenStream>,
+                streams: Vec<$S::TokenStream>,
+            ) -> $S::TokenStream;
+            fn ts_into_trees(
+                stream: $S::TokenStream
+            ) -> Vec>;
 
-                fn span_debug(span: $S::Span) -> String;
-                fn span_parent(span: $S::Span) -> Option<$S::Span>;
-                fn span_source(span: $S::Span) -> $S::Span;
-                fn span_byte_range(span: $S::Span) -> Range;
-                fn span_start(span: $S::Span) -> $S::Span;
-                fn span_end(span: $S::Span) -> $S::Span;
-                fn span_line(span: $S::Span) -> usize;
-                fn span_column(span: $S::Span) -> usize;
-                fn span_file(span: $S::Span) -> String;
-                fn span_local_file(span: $S::Span) -> Option;
-                fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>;
-                fn span_subspan(span: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>;
-                fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span;
-                fn span_source_text(span: $S::Span) -> Option;
-                fn span_save_span(span: $S::Span) -> usize;
-                fn span_recover_proc_macro_span(id: usize) -> $S::Span;
+            fn span_debug(span: $S::Span) -> String;
+            fn span_parent(span: $S::Span) -> Option<$S::Span>;
+            fn span_source(span: $S::Span) -> $S::Span;
+            fn span_byte_range(span: $S::Span) -> Range;
+            fn span_start(span: $S::Span) -> $S::Span;
+            fn span_end(span: $S::Span) -> $S::Span;
+            fn span_line(span: $S::Span) -> usize;
+            fn span_column(span: $S::Span) -> usize;
+            fn span_file(span: $S::Span) -> String;
+            fn span_local_file(span: $S::Span) -> Option;
+            fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>;
+            fn span_subspan(span: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>;
+            fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span;
+            fn span_source_text(span: $S::Span) -> Option;
+            fn span_save_span(span: $S::Span) -> usize;
+            fn span_recover_proc_macro_span(id: usize) -> $S::Span;
 
-                fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>;
-            },
-            TokenStream,
-            Span,
-            Symbol,
+            fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>;
         }
     };
 }
@@ -158,10 +142,7 @@ mod api_tags {
 
     macro_rules! declare_tags {
         (
-            Methods {
-                $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
-            },
-            $($name:ident),* $(,)?
+            $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
         ) => {
             pub(super) enum Method {
                 $($method),*
@@ -169,7 +150,7 @@ mod api_tags {
             rpc_encode_decode!(enum Method { $($method),* });
         }
     }
-    with_api!(self, self, declare_tags);
+    with_api!(self, declare_tags);
 }
 
 /// Helper to wrap associated types to allow trait impl dispatch.
diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs
index b79de9984453..2fed45d3a0ab 100644
--- a/library/proc_macro/src/bridge/server.rs
+++ b/library/proc_macro/src/bridge/server.rs
@@ -5,12 +5,12 @@ use std::marker::PhantomData;
 
 use super::*;
 
-pub(super) struct HandleStore {
+pub(super) struct HandleStore {
     token_stream: handle::OwnedStore>,
     span: handle::InternedStore>,
 }
 
-impl HandleStore {
+impl HandleStore {
     fn new(handle_counters: &'static client::HandleCounters) -> Self {
         HandleStore {
             token_stream: handle::OwnedStore::new(&handle_counters.token_stream),
@@ -19,19 +19,19 @@ impl HandleStore {
     }
 }
 
-impl Encode> for Marked {
+impl Encode> for Marked {
     fn encode(self, w: &mut Writer, s: &mut HandleStore) {
         s.token_stream.alloc(self).encode(w, s);
     }
 }
 
-impl Decode<'_, '_, HandleStore> for Marked {
+impl Decode<'_, '_, HandleStore> for Marked {
     fn decode(r: &mut Reader<'_>, s: &mut HandleStore) -> Self {
         s.token_stream.take(handle::Handle::decode(r, &mut ()))
     }
 }
 
-impl<'s, S: Types> Decode<'_, 's, HandleStore>
+impl<'s, S: Server> Decode<'_, 's, HandleStore>
     for &'s Marked
 {
     fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore) -> Self {
@@ -39,32 +39,32 @@ impl<'s, S: Types> Decode<'_, 's, HandleStore>
     }
 }
 
-impl Encode> for Marked {
+impl Encode> for Marked {
     fn encode(self, w: &mut Writer, s: &mut HandleStore) {
         s.span.alloc(self).encode(w, s);
     }
 }
 
-impl Decode<'_, '_, HandleStore> for Marked {
+impl Decode<'_, '_, HandleStore> for Marked {
     fn decode(r: &mut Reader<'_>, s: &mut HandleStore) -> Self {
         s.span.copy(handle::Handle::decode(r, &mut ()))
     }
 }
 
-pub trait Types {
-    type TokenStream: 'static + Clone;
-    type Span: 'static + Copy + Eq + Hash;
-    type Symbol: 'static;
+struct Dispatcher {
+    handle_store: HandleStore,
+    server: S,
 }
 
-macro_rules! declare_server_traits {
+macro_rules! define_server_dispatcher_impl {
     (
-        Methods {
-            $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
-        },
-        $($name:ident),* $(,)?
+        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
     ) => {
-        pub trait Server: Types {
+        pub trait Server {
+            type TokenStream: 'static + Clone;
+            type Span: 'static + Copy + Eq + Hash;
+            type Symbol: 'static;
+
             fn globals(&mut self) -> ExpnGlobals;
 
             /// Intern a symbol received from RPC
@@ -75,32 +75,21 @@ macro_rules! declare_server_traits {
 
             $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)*
         }
-    }
-}
-with_api!(Self, self_, declare_server_traits);
 
-struct Dispatcher {
-    handle_store: HandleStore,
-    server: S,
-}
-
-macro_rules! define_dispatcher_impl {
-    (
-        Methods {
-            $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
-        },
-        $($name:ident),* $(,)?
-    ) => {
         // FIXME(eddyb) `pub` only for `ExecutionStrategy` below.
         pub trait DispatcherTrait {
             // HACK(eddyb) these are here to allow `Self::$name` to work below.
-            $(type $name;)*
+            type TokenStream;
+            type Span;
+            type Symbol;
 
             fn dispatch(&mut self, buf: Buffer) -> Buffer;
         }
 
         impl DispatcherTrait for Dispatcher {
-            $(type $name = Marked;)*
+            type TokenStream = Marked;
+            type Span = Marked;
+            type Symbol = Marked;
 
             fn dispatch(&mut self, mut buf: Buffer) -> Buffer {
                 let Dispatcher { handle_store, server } = self;
@@ -136,7 +125,7 @@ macro_rules! define_dispatcher_impl {
         }
     }
 }
-with_api!(Self, self_, define_dispatcher_impl);
+with_api!(Self, define_server_dispatcher_impl);
 
 pub trait ExecutionStrategy {
     fn run_bridge_and_client(
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
index 8680e9180e3a..9a65538675fe 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
@@ -48,7 +48,7 @@ impl Expander {
         callback: Option>,
     ) -> Result, PanicMessage>
     where
-         as bridge::server::Types>::TokenStream: Default,
+         as bridge::server::Server>::TokenStream: Default,
     {
         self.inner
             .proc_macros
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index ec30630c10bb..eacb100fbc9f 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -30,13 +30,11 @@ pub struct RaSpanServer<'a> {
     pub callback: Option>,
 }
 
-impl server::Types for RaSpanServer<'_> {
+impl server::Server for RaSpanServer<'_> {
     type TokenStream = crate::token_stream::TokenStream;
     type Span = Span;
     type Symbol = Symbol;
-}
 
-impl server::Server for RaSpanServer<'_> {
     fn globals(&mut self) -> ExpnGlobals {
         ExpnGlobals {
             def_site: self.def_site,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
index 3bf07290c8c0..70484c4dc28f 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -36,13 +36,11 @@ pub struct SpanIdServer<'a> {
     pub callback: Option>,
 }
 
-impl server::Types for SpanIdServer<'_> {
+impl server::Server for SpanIdServer<'_> {
     type TokenStream = crate::token_stream::TokenStream;
     type Span = Span;
     type Symbol = Symbol;
-}
 
-impl server::Server for SpanIdServer<'_> {
     fn globals(&mut self) -> ExpnGlobals {
         ExpnGlobals {
             def_site: self.def_site,

From 5fef797f6e8360b2457cb9a4133f59effb9c5d65 Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 26 Jan 2026 14:00:39 +0000
Subject: [PATCH 233/583] inline `Writer` and `Reader` type aliases

---
 library/proc_macro/src/bridge/client.rs | 10 ++--
 library/proc_macro/src/bridge/mod.rs    |  5 +-
 library/proc_macro/src/bridge/rpc.rs    | 64 ++++++++++++-------------
 library/proc_macro/src/bridge/server.rs | 10 ++--
 library/proc_macro/src/bridge/symbol.rs |  8 ++--
 5 files changed, 48 insertions(+), 49 deletions(-)

diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs
index 381c8502c853..83a3b21e3548 100644
--- a/library/proc_macro/src/bridge/client.rs
+++ b/library/proc_macro/src/bridge/client.rs
@@ -30,19 +30,19 @@ impl Drop for TokenStream {
 }
 
 impl Encode for TokenStream {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         mem::ManuallyDrop::new(self).handle.encode(w, s);
     }
 }
 
 impl Encode for &TokenStream {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.handle.encode(w, s);
     }
 }
 
 impl Decode<'_, '_, S> for TokenStream {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
+    fn decode(r: &mut &[u8], s: &mut S) -> Self {
         TokenStream { handle: handle::Handle::decode(r, s) }
     }
 }
@@ -56,13 +56,13 @@ impl !Send for Span {}
 impl !Sync for Span {}
 
 impl Encode for Span {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.handle.encode(w, s);
     }
 }
 
 impl Decode<'_, '_, S> for Span {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
+    fn decode(r: &mut &[u8], s: &mut S) -> Self {
         Span { handle: handle::Handle::decode(r, s) }
     }
 }
diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs
index f454861be4ed..006aa5f973fc 100644
--- a/library/proc_macro/src/bridge/mod.rs
+++ b/library/proc_macro/src/bridge/mod.rs
@@ -113,7 +113,7 @@ mod symbol;
 
 use buffer::Buffer;
 pub use rpc::PanicMessage;
-use rpc::{Decode, Encode, Reader, Writer};
+use rpc::{Decode, Encode};
 
 /// Configuration for establishing an active connection between a server and a
 /// client.  The server creates the bridge config (`run_server` in `server.rs`),
@@ -138,7 +138,8 @@ impl !Sync for BridgeConfig<'_> {}
 #[forbid(unsafe_code)]
 #[allow(non_camel_case_types)]
 mod api_tags {
-    use super::rpc::{Decode, Encode, Reader, Writer};
+    use super::buffer::Buffer;
+    use super::rpc::{Decode, Encode};
 
     macro_rules! declare_tags {
         (
diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs
index ed67674a74ab..254b176e19c5 100644
--- a/library/proc_macro/src/bridge/rpc.rs
+++ b/library/proc_macro/src/bridge/rpc.rs
@@ -4,28 +4,26 @@ use std::any::Any;
 use std::io::Write;
 use std::num::NonZero;
 
-pub(super) type Writer = super::buffer::Buffer;
+use super::buffer::Buffer;
 
 pub(super) trait Encode: Sized {
-    fn encode(self, w: &mut Writer, s: &mut S);
+    fn encode(self, w: &mut Buffer, s: &mut S);
 }
 
-pub(super) type Reader<'a> = &'a [u8];
-
 pub(super) trait Decode<'a, 's, S>: Sized {
-    fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self;
+    fn decode(r: &mut &'a [u8], s: &'s mut S) -> Self;
 }
 
 macro_rules! rpc_encode_decode {
     (le $ty:ty) => {
         impl Encode for $ty {
-            fn encode(self, w: &mut Writer, _: &mut S) {
+            fn encode(self, w: &mut Buffer, _: &mut S) {
                 w.extend_from_array(&self.to_le_bytes());
             }
         }
 
         impl Decode<'_, '_, S> for $ty {
-            fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
+            fn decode(r: &mut &[u8], _: &mut S) -> Self {
                 const N: usize = size_of::<$ty>();
 
                 let mut bytes = [0; N];
@@ -38,7 +36,7 @@ macro_rules! rpc_encode_decode {
     };
     (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => {
         impl),+)?> Encode for $name $(<$($T),+>)? {
-            fn encode(self, w: &mut Writer, s: &mut S) {
+            fn encode(self, w: &mut Buffer, s: &mut S) {
                 $(self.$field.encode(w, s);)*
             }
         }
@@ -46,7 +44,7 @@ macro_rules! rpc_encode_decode {
         impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S>
             for $name $(<$($T),+>)?
         {
-            fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
+            fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
                 $name {
                     $($field: Decode::decode(r, s)),*
                 }
@@ -55,7 +53,7 @@ macro_rules! rpc_encode_decode {
     };
     (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
         impl),+)?> Encode for $name $(<$($T),+>)? {
-            fn encode(self, w: &mut Writer, s: &mut S) {
+            fn encode(self, w: &mut Buffer, s: &mut S) {
                 // HACK(eddyb): `Tag` enum duplicated between the
                 // two impls as there's no other place to stash it.
                 #[repr(u8)] enum Tag { $($variant),* }
@@ -72,7 +70,7 @@ macro_rules! rpc_encode_decode {
         impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S>
             for $name $(<$($T),+>)?
         {
-            fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
+            fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
                 // HACK(eddyb): `Tag` enum duplicated between the
                 // two impls as there's no other place to stash it.
                 #[allow(non_upper_case_globals)]
@@ -95,21 +93,21 @@ macro_rules! rpc_encode_decode {
 }
 
 impl Encode for () {
-    fn encode(self, _: &mut Writer, _: &mut S) {}
+    fn encode(self, _: &mut Buffer, _: &mut S) {}
 }
 
 impl Decode<'_, '_, S> for () {
-    fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {}
+    fn decode(_: &mut &[u8], _: &mut S) -> Self {}
 }
 
 impl Encode for u8 {
-    fn encode(self, w: &mut Writer, _: &mut S) {
+    fn encode(self, w: &mut Buffer, _: &mut S) {
         w.push(self);
     }
 }
 
 impl Decode<'_, '_, S> for u8 {
-    fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
+    fn decode(r: &mut &[u8], _: &mut S) -> Self {
         let x = r[0];
         *r = &r[1..];
         x
@@ -120,13 +118,13 @@ rpc_encode_decode!(le u32);
 rpc_encode_decode!(le usize);
 
 impl Encode for bool {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         (self as u8).encode(w, s);
     }
 }
 
 impl Decode<'_, '_, S> for bool {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
+    fn decode(r: &mut &[u8], s: &mut S) -> Self {
         match u8::decode(r, s) {
             0 => false,
             1 => true,
@@ -136,31 +134,31 @@ impl Decode<'_, '_, S> for bool {
 }
 
 impl Encode for char {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         (self as u32).encode(w, s);
     }
 }
 
 impl Decode<'_, '_, S> for char {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
+    fn decode(r: &mut &[u8], s: &mut S) -> Self {
         char::from_u32(u32::decode(r, s)).unwrap()
     }
 }
 
 impl Encode for NonZero {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.get().encode(w, s);
     }
 }
 
 impl Decode<'_, '_, S> for NonZero {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
+    fn decode(r: &mut &[u8], s: &mut S) -> Self {
         Self::new(u32::decode(r, s)).unwrap()
     }
 }
 
 impl, B: Encode> Encode for (A, B) {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.0.encode(w, s);
         self.1.encode(w, s);
     }
@@ -169,20 +167,20 @@ impl, B: Encode> Encode for (A, B) {
 impl<'a, S, A: for<'s> Decode<'a, 's, S>, B: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S>
     for (A, B)
 {
-    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
+    fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
         (Decode::decode(r, s), Decode::decode(r, s))
     }
 }
 
 impl Encode for &[u8] {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.len().encode(w, s);
         w.write_all(self).unwrap();
     }
 }
 
 impl<'a, S> Decode<'a, '_, S> for &'a [u8] {
-    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
+    fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
         let len = usize::decode(r, s);
         let xs = &r[..len];
         *r = &r[len..];
@@ -191,31 +189,31 @@ impl<'a, S> Decode<'a, '_, S> for &'a [u8] {
 }
 
 impl Encode for &str {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.as_bytes().encode(w, s);
     }
 }
 
 impl<'a, S> Decode<'a, '_, S> for &'a str {
-    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
+    fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
         str::from_utf8(<&[u8]>::decode(r, s)).unwrap()
     }
 }
 
 impl Encode for String {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self[..].encode(w, s);
     }
 }
 
 impl Decode<'_, '_, S> for String {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
+    fn decode(r: &mut &[u8], s: &mut S) -> Self {
         <&str>::decode(r, s).to_string()
     }
 }
 
 impl> Encode for Vec {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.len().encode(w, s);
         for x in self {
             x.encode(w, s);
@@ -224,7 +222,7 @@ impl> Encode for Vec {
 }
 
 impl<'a, S, T: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for Vec {
-    fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
+    fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
         let len = usize::decode(r, s);
         let mut vec = Vec::with_capacity(len);
         for _ in 0..len {
@@ -278,13 +276,13 @@ impl PanicMessage {
 }
 
 impl Encode for PanicMessage {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.as_str().encode(w, s);
     }
 }
 
 impl Decode<'_, '_, S> for PanicMessage {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
+    fn decode(r: &mut &[u8], s: &mut S) -> Self {
         match Option::::decode(r, s) {
             Some(s) => PanicMessage::String(s),
             None => PanicMessage::Unknown,
diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs
index 2fed45d3a0ab..8f31d7aa9f55 100644
--- a/library/proc_macro/src/bridge/server.rs
+++ b/library/proc_macro/src/bridge/server.rs
@@ -20,13 +20,13 @@ impl HandleStore {
 }
 
 impl Encode> for Marked {
-    fn encode(self, w: &mut Writer, s: &mut HandleStore) {
+    fn encode(self, w: &mut Buffer, s: &mut HandleStore) {
         s.token_stream.alloc(self).encode(w, s);
     }
 }
 
 impl Decode<'_, '_, HandleStore> for Marked {
-    fn decode(r: &mut Reader<'_>, s: &mut HandleStore) -> Self {
+    fn decode(r: &mut &[u8], s: &mut HandleStore) -> Self {
         s.token_stream.take(handle::Handle::decode(r, &mut ()))
     }
 }
@@ -34,19 +34,19 @@ impl Decode<'_, '_, HandleStore> for Marked Decode<'_, 's, HandleStore>
     for &'s Marked
 {
-    fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore) -> Self {
+    fn decode(r: &mut &[u8], s: &'s mut HandleStore) -> Self {
         &s.token_stream[handle::Handle::decode(r, &mut ())]
     }
 }
 
 impl Encode> for Marked {
-    fn encode(self, w: &mut Writer, s: &mut HandleStore) {
+    fn encode(self, w: &mut Buffer, s: &mut HandleStore) {
         s.span.alloc(self).encode(w, s);
     }
 }
 
 impl Decode<'_, '_, HandleStore> for Marked {
-    fn decode(r: &mut Reader<'_>, s: &mut HandleStore) -> Self {
+    fn decode(r: &mut &[u8], s: &mut HandleStore) -> Self {
         s.span.copy(handle::Handle::decode(r, &mut ()))
     }
 }
diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs
index edba142bad72..02b1d351ac16 100644
--- a/library/proc_macro/src/bridge/symbol.rs
+++ b/library/proc_macro/src/bridge/symbol.rs
@@ -94,25 +94,25 @@ impl fmt::Display for Symbol {
 }
 
 impl Encode for Symbol {
-    fn encode(self, w: &mut Writer, s: &mut S) {
+    fn encode(self, w: &mut Buffer, s: &mut S) {
         self.with(|sym| sym.encode(w, s))
     }
 }
 
 impl Decode<'_, '_, server::HandleStore> for Marked {
-    fn decode(r: &mut Reader<'_>, s: &mut server::HandleStore) -> Self {
+    fn decode(r: &mut &[u8], s: &mut server::HandleStore) -> Self {
         Mark::mark(S::intern_symbol(<&str>::decode(r, s)))
     }
 }
 
 impl Encode> for Marked {
-    fn encode(self, w: &mut Writer, s: &mut server::HandleStore) {
+    fn encode(self, w: &mut Buffer, s: &mut server::HandleStore) {
         S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s))
     }
 }
 
 impl Decode<'_, '_, S> for Symbol {
-    fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
+    fn decode(r: &mut &[u8], s: &mut S) -> Self {
         Symbol::new(<&str>::decode(r, s))
     }
 }

From 2c2602bf0d1edc47cb886dbe4be24a247eb64ab5 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 26 Jan 2026 16:13:06 +0200
Subject: [PATCH 234/583] Fix macro matching of `meta` then `=>` or `==`

The parser declared it was invalid meta because it consumed the lone `=`, which is incorrect.
---
 .../src/macro_expansion_tests/mbe/matching.rs | 20 +++++++++++++++++++
 .../crates/parser/src/grammar/attributes.rs   |  2 +-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index e33a366769b0..bbadcf8794bf 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -237,3 +237,23 @@ fn test() {
 "#]],
     );
 }
+
+#[test]
+fn meta_fat_arrow() {
+    check(
+        r#"
+macro_rules! m {
+    ( $m:meta => ) => {};
+}
+
+m! { foo => }
+    "#,
+        expect![[r#"
+macro_rules! m {
+    ( $m:meta => ) => {};
+}
+
+
+    "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
index 54b5c8a275a8..c0cf43a87bf7 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
@@ -70,7 +70,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
     paths::attr_path(p);
 
     match p.current() {
-        T![=] => {
+        T![=] if !p.at(T![=>]) && !p.at(T![==]) => {
             p.bump(T![=]);
             if expressions::expr(p).is_none() {
                 p.error("expected expression");

From 8d6b2f6a49c02456002094c069ac5a4d266625d7 Mon Sep 17 00:00:00 2001
From: zakie 
Date: Mon, 26 Jan 2026 23:36:26 +0900
Subject: [PATCH 235/583] docs: fix broken Xtensa installation link

---
 src/doc/rustc/src/platform-support/xtensa.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc/src/platform-support/xtensa.md b/src/doc/rustc/src/platform-support/xtensa.md
index 8592ce7eda9d..39bcc02355cb 100644
--- a/src/doc/rustc/src/platform-support/xtensa.md
+++ b/src/doc/rustc/src/platform-support/xtensa.md
@@ -24,4 +24,4 @@ Xtensa targets that support `std` are documented in the [ESP-IDF platform suppor
 
 ## Building the targets
 
-The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of The Rust on ESP Book](https://docs.espressif.com/projects/rust/book/installation/index.html).
+The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of The Rust on ESP Book](https://docs.espressif.com/projects/rust/book/getting-started/toolchain.html).

From f8d05b6c855f64e11dc9bdec4db21bd4c3c6a969 Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 26 Jan 2026 14:43:50 +0000
Subject: [PATCH 236/583] flatten the `api_tags` module

---
 library/proc_macro/src/bridge/client.rs |  2 +-
 library/proc_macro/src/bridge/mod.rs    | 24 +++++++++---------------
 library/proc_macro/src/bridge/rpc.rs    |  6 ++++--
 library/proc_macro/src/bridge/server.rs |  4 ++--
 4 files changed, 16 insertions(+), 20 deletions(-)

diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs
index 83a3b21e3548..8f4a79b389f6 100644
--- a/library/proc_macro/src/bridge/client.rs
+++ b/library/proc_macro/src/bridge/client.rs
@@ -106,7 +106,7 @@ macro_rules! define_client_side {
                     let mut buf = bridge.cached_buffer.take();
 
                     buf.clear();
-                    api_tags::Method::$method.encode(&mut buf, &mut ());
+                    ApiTags::$method.encode(&mut buf, &mut ());
                     $($arg.encode(&mut buf, &mut ());)*
 
                     buf = bridge.dispatch.call(buf);
diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs
index 006aa5f973fc..cf6ffd86f414 100644
--- a/library/proc_macro/src/bridge/mod.rs
+++ b/library/proc_macro/src/bridge/mod.rs
@@ -135,24 +135,18 @@ pub struct BridgeConfig<'a> {
 impl !Send for BridgeConfig<'_> {}
 impl !Sync for BridgeConfig<'_> {}
 
-#[forbid(unsafe_code)]
-#[allow(non_camel_case_types)]
-mod api_tags {
-    use super::buffer::Buffer;
-    use super::rpc::{Decode, Encode};
-
-    macro_rules! declare_tags {
-        (
-            $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
-        ) => {
-            pub(super) enum Method {
-                $($method),*
-            }
-            rpc_encode_decode!(enum Method { $($method),* });
+macro_rules! declare_tags {
+    (
+        $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
+    ) => {
+        #[allow(non_camel_case_types)]
+        pub(super) enum ApiTags {
+            $($method),*
         }
+        rpc_encode_decode!(enum ApiTags { $($method),* });
     }
-    with_api!(self, declare_tags);
 }
+with_api!(self, declare_tags);
 
 /// Helper to wrap associated types to allow trait impl dispatch.
 /// That is, normally a pair of impls for `T::Foo` and `T::Bar`
diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs
index 254b176e19c5..5786c62d9464 100644
--- a/library/proc_macro/src/bridge/rpc.rs
+++ b/library/proc_macro/src/bridge/rpc.rs
@@ -56,7 +56,9 @@ macro_rules! rpc_encode_decode {
             fn encode(self, w: &mut Buffer, s: &mut S) {
                 // HACK(eddyb): `Tag` enum duplicated between the
                 // two impls as there's no other place to stash it.
-                #[repr(u8)] enum Tag { $($variant),* }
+                #[allow(non_camel_case_types)]
+                #[repr(u8)]
+                enum Tag { $($variant),* }
 
                 match self {
                     $($name::$variant $(($field))* => {
@@ -73,7 +75,7 @@ macro_rules! rpc_encode_decode {
             fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
                 // HACK(eddyb): `Tag` enum duplicated between the
                 // two impls as there's no other place to stash it.
-                #[allow(non_upper_case_globals)]
+                #[allow(non_upper_case_globals, non_camel_case_types)]
                 mod tag {
                     #[repr(u8)] enum Tag { $($variant),* }
 
diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs
index 8f31d7aa9f55..f477fa28798d 100644
--- a/library/proc_macro/src/bridge/server.rs
+++ b/library/proc_macro/src/bridge/server.rs
@@ -95,8 +95,8 @@ macro_rules! define_server_dispatcher_impl {
                 let Dispatcher { handle_store, server } = self;
 
                 let mut reader = &buf[..];
-                match api_tags::Method::decode(&mut reader, &mut ()) {
-                    $(api_tags::Method::$method => {
+                match ApiTags::decode(&mut reader, &mut ()) {
+                    $(ApiTags::$method => {
                         let mut call_method = || {
                             $(let $arg = <$arg_ty>::decode(&mut reader, handle_store).unmark();)*
                             let r = server.$method($($arg),*);

From d5328c545a0cad33053369ddbc0e5f27aa2924aa Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 26 Jan 2026 14:58:29 +0000
Subject: [PATCH 237/583] introduce `MarkedX` type aliases

---
 library/proc_macro/src/bridge/server.rs | 43 +++++++++++--------------
 library/proc_macro/src/bridge/symbol.rs |  4 +--
 2 files changed, 21 insertions(+), 26 deletions(-)

diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs
index f477fa28798d..073ddb554994 100644
--- a/library/proc_macro/src/bridge/server.rs
+++ b/library/proc_macro/src/bridge/server.rs
@@ -6,8 +6,8 @@ use std::marker::PhantomData;
 use super::*;
 
 pub(super) struct HandleStore {
-    token_stream: handle::OwnedStore>,
-    span: handle::InternedStore>,
+    token_stream: handle::OwnedStore>,
+    span: handle::InternedStore>,
 }
 
 impl HandleStore {
@@ -19,33 +19,35 @@ impl HandleStore {
     }
 }
 
-impl Encode> for Marked {
+pub(super) type MarkedTokenStream = Marked<::TokenStream, client::TokenStream>;
+pub(super) type MarkedSpan = Marked<::Span, client::Span>;
+pub(super) type MarkedSymbol = Marked<::Symbol, client::Symbol>;
+
+impl Encode> for MarkedTokenStream {
     fn encode(self, w: &mut Buffer, s: &mut HandleStore) {
         s.token_stream.alloc(self).encode(w, s);
     }
 }
 
-impl Decode<'_, '_, HandleStore> for Marked {
+impl Decode<'_, '_, HandleStore> for MarkedTokenStream {
     fn decode(r: &mut &[u8], s: &mut HandleStore) -> Self {
         s.token_stream.take(handle::Handle::decode(r, &mut ()))
     }
 }
 
-impl<'s, S: Server> Decode<'_, 's, HandleStore>
-    for &'s Marked
-{
+impl<'s, S: Server> Decode<'_, 's, HandleStore> for &'s MarkedTokenStream {
     fn decode(r: &mut &[u8], s: &'s mut HandleStore) -> Self {
         &s.token_stream[handle::Handle::decode(r, &mut ())]
     }
 }
 
-impl Encode> for Marked {
+impl Encode> for MarkedSpan {
     fn encode(self, w: &mut Buffer, s: &mut HandleStore) {
         s.span.alloc(self).encode(w, s);
     }
 }
 
-impl Decode<'_, '_, HandleStore> for Marked {
+impl Decode<'_, '_, HandleStore> for MarkedSpan {
     fn decode(r: &mut &[u8], s: &mut HandleStore) -> Self {
         s.span.copy(handle::Handle::decode(r, &mut ()))
     }
@@ -87,9 +89,9 @@ macro_rules! define_server_dispatcher_impl {
         }
 
         impl DispatcherTrait for Dispatcher {
-            type TokenStream = Marked;
-            type Span = Marked;
-            type Symbol = Marked;
+            type TokenStream = MarkedTokenStream;
+            type Span = MarkedSpan;
+            type Symbol = MarkedSymbol;
 
             fn dispatch(&mut self, mut buf: Buffer) -> Buffer {
                 let Dispatcher { handle_store, server } = self;
@@ -292,7 +294,7 @@ fn run_server<
     let globals = dispatcher.server.globals();
 
     let mut buf = Buffer::new();
-    (> as Mark>::mark(globals), input)
+    (> as Mark>::mark(globals), input)
         .encode(&mut buf, &mut dispatcher.handle_store);
 
     buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics);
@@ -317,13 +319,11 @@ impl client::Client {
             strategy,
             handle_counters,
             server,
-            >::mark(input),
+            >::mark(input),
             run,
             force_show_panics,
         )
-        .map(|s| {
-            >>::unmark(s).unwrap_or_default()
-        })
+        .map(|s| >>::unmark(s).unwrap_or_default())
     }
 }
 
@@ -345,15 +345,10 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream
             strategy,
             handle_counters,
             server,
-            (
-                >::mark(input),
-                >::mark(input2),
-            ),
+            (>::mark(input), >::mark(input2)),
             run,
             force_show_panics,
         )
-        .map(|s| {
-            >>::unmark(s).unwrap_or_default()
-        })
+        .map(|s| >>::unmark(s).unwrap_or_default())
     }
 }
diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs
index 02b1d351ac16..2a04f7d808bd 100644
--- a/library/proc_macro/src/bridge/symbol.rs
+++ b/library/proc_macro/src/bridge/symbol.rs
@@ -99,13 +99,13 @@ impl Encode for Symbol {
     }
 }
 
-impl Decode<'_, '_, server::HandleStore> for Marked {
+impl Decode<'_, '_, server::HandleStore> for server::MarkedSymbol {
     fn decode(r: &mut &[u8], s: &mut server::HandleStore) -> Self {
         Mark::mark(S::intern_symbol(<&str>::decode(r, s)))
     }
 }
 
-impl Encode> for Marked {
+impl Encode> for server::MarkedSymbol {
     fn encode(self, w: &mut Buffer, s: &mut server::HandleStore) {
         S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s))
     }

From 356107e0b46182a080ab4876282cebbb97e33b1a Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 26 Jan 2026 14:27:18 +0000
Subject: [PATCH 238/583] remove some unneeded impls

---
 library/proc_macro/src/bridge/mod.rs | 17 +------------
 library/proc_macro/src/bridge/rpc.rs | 37 ++++++----------------------
 2 files changed, 8 insertions(+), 46 deletions(-)

diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs
index cf6ffd86f414..97e36a3d70c1 100644
--- a/library/proc_macro/src/bridge/mod.rs
+++ b/library/proc_macro/src/bridge/mod.rs
@@ -13,7 +13,7 @@ use std::ops::{Bound, Range};
 use std::sync::Once;
 use std::{fmt, marker, mem, panic, thread};
 
-use crate::{Delimiter, Level, Spacing};
+use crate::{Delimiter, Level};
 
 /// Higher-order macro describing the server RPC API, allowing automatic
 /// generation of type-safe Rust APIs, both client-side and server-side.
@@ -187,12 +187,6 @@ impl<'a, T, M> Unmark for &'a Marked {
         &self.value
     }
 }
-impl<'a, T, M> Unmark for &'a mut Marked {
-    type Unmarked = &'a mut T;
-    fn unmark(self) -> Self::Unmarked {
-        &mut self.value
-    }
-}
 
 impl Mark for Vec {
     type Unmarked = Vec;
@@ -230,8 +224,6 @@ macro_rules! mark_noop {
 mark_noop! {
     (),
     bool,
-    char,
-    &'_ [u8],
     &'_ str,
     String,
     u8,
@@ -239,7 +231,6 @@ mark_noop! {
     Delimiter,
     LitKind,
     Level,
-    Spacing,
 }
 
 rpc_encode_decode!(
@@ -258,12 +249,6 @@ rpc_encode_decode!(
         Help,
     }
 );
-rpc_encode_decode!(
-    enum Spacing {
-        Alone,
-        Joint,
-    }
-);
 
 #[derive(Copy, Clone, Eq, PartialEq, Debug)]
 pub enum LitKind {
diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs
index 5786c62d9464..63329c8c0260 100644
--- a/library/proc_macro/src/bridge/rpc.rs
+++ b/library/proc_macro/src/bridge/rpc.rs
@@ -135,18 +135,6 @@ impl Decode<'_, '_, S> for bool {
     }
 }
 
-impl Encode for char {
-    fn encode(self, w: &mut Buffer, s: &mut S) {
-        (self as u32).encode(w, s);
-    }
-}
-
-impl Decode<'_, '_, S> for char {
-    fn decode(r: &mut &[u8], s: &mut S) -> Self {
-        char::from_u32(u32::decode(r, s)).unwrap()
-    }
-}
-
 impl Encode for NonZero {
     fn encode(self, w: &mut Buffer, s: &mut S) {
         self.get().encode(w, s);
@@ -174,31 +162,20 @@ impl<'a, S, A: for<'s> Decode<'a, 's, S>, B: for<'s> Decode<'a, 's, S>> Decode<'
     }
 }
 
-impl Encode for &[u8] {
-    fn encode(self, w: &mut Buffer, s: &mut S) {
-        self.len().encode(w, s);
-        w.write_all(self).unwrap();
-    }
-}
-
-impl<'a, S> Decode<'a, '_, S> for &'a [u8] {
-    fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
-        let len = usize::decode(r, s);
-        let xs = &r[..len];
-        *r = &r[len..];
-        xs
-    }
-}
-
 impl Encode for &str {
     fn encode(self, w: &mut Buffer, s: &mut S) {
-        self.as_bytes().encode(w, s);
+        let bytes = self.as_bytes();
+        bytes.len().encode(w, s);
+        w.write_all(bytes).unwrap();
     }
 }
 
 impl<'a, S> Decode<'a, '_, S> for &'a str {
     fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
-        str::from_utf8(<&[u8]>::decode(r, s)).unwrap()
+        let len = usize::decode(r, s);
+        let xs = &r[..len];
+        *r = &r[len..];
+        str::from_utf8(xs).unwrap()
     }
 }
 

From cd8fe54a1a743c0bf88f8a9c28ae8554345f3d58 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Mon, 26 Jan 2026 01:46:13 +0800
Subject: [PATCH 239/583] Fix semicolon for toggle_macro_delimiter

Example
---
```rust
macro_rules! sth {
    () => {};
}

sth!$0{ }
```

(old test `sth!{};` is a syntax error in item place)

**Before this PR**

```rust
macro_rules! sth {
    () => {};
}

sth![ ]
```

**After this PR**

```rust
macro_rules! sth {
    () => {};
}

sth![ ];
```
---
 .../src/handlers/toggle_macro_delimiter.rs    | 149 +++++++++++++++++-
 1 file changed, 145 insertions(+), 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
index bf1546986ed2..60b0797f028a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
@@ -86,7 +86,14 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>)
                 }
                 MacroDelims::LCur | MacroDelims::RCur => {
                     editor.replace(ltoken, make.token(T!['[']));
-                    editor.replace(rtoken, make.token(T![']']));
+                    if semicolon.is_some() || !needs_semicolon(token_tree) {
+                        editor.replace(rtoken, make.token(T![']']));
+                    } else {
+                        editor.replace_with_many(
+                            rtoken,
+                            vec![make.token(T![']']).into(), make.token(T![;]).into()],
+                        );
+                    }
                 }
             }
             editor.add_mappings(make.finish_with_mappings());
@@ -103,6 +110,30 @@ fn macro_semicolon(makro: &ast::MacroCall) -> Option {
     })
 }
 
+fn needs_semicolon(tt: ast::TokenTree) -> bool {
+    (|| {
+        let call = ast::MacroCall::cast(tt.syntax().parent()?)?;
+        let container = call.syntax().parent()?;
+        let kind = container.kind();
+
+        if call.semicolon_token().is_some() {
+            return Some(false);
+        }
+
+        Some(
+            ast::ItemList::can_cast(kind)
+                || ast::SourceFile::can_cast(kind)
+                || ast::AssocItemList::can_cast(kind)
+                || ast::ExternItemList::can_cast(kind)
+                || ast::MacroItems::can_cast(kind)
+                || ast::MacroExpr::can_cast(kind)
+                    && ast::ExprStmt::cast(container.parent()?)
+                        .is_some_and(|it| it.semicolon_token().is_none()),
+        )
+    })()
+    .unwrap_or(false)
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};
@@ -161,7 +192,7 @@ macro_rules! sth {
     () => {};
 }
 
-sth!$0{ };
+sth!$0{ }
             "#,
             r#"
 macro_rules! sth {
@@ -170,7 +201,117 @@ macro_rules! sth {
 
 sth![ ];
             "#,
-        )
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+fn foo() -> i32 {
+    sth!$0{ }
+    2
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+fn foo() -> i32 {
+    sth![ ];
+    2
+}
+            "#,
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() {
+    sth!$0{ };
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() {
+    sth![ ];
+}
+            "#,
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() -> i32 {
+    sth!$0{ }
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() -> i32 {
+    sth![ ]
+}
+            "#,
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+impl () {
+    sth!$0{}
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+impl () {
+    sth![];
+}
+            "#,
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() -> i32 {
+    bar(sth!$0{ })
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() -> i32 {
+    bar(sth![ ])
+}
+            "#,
+        );
     }
 
     #[test]
@@ -204,7 +345,7 @@ mod abc {
         () => {};
     }
 
-    sth!$0{ };
+    sth!$0{ }
 }
             "#,
             r#"

From 21ebd03e5ec9c62c1b582adb90d179aaec504d7b Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Mon, 26 Jan 2026 16:18:36 +0100
Subject: [PATCH 240/583] Try to reduce rustdoc GUI tests flakyness

---
 package.json                 | 2 +-
 tests/rustdoc-gui/utils.goml | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 0cba589d23f5..1fe87b181669 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "dependencies": {
-    "browser-ui-test": "^0.23.1",
+    "browser-ui-test": "^0.23.2",
     "es-check": "^9.4.4",
     "eslint": "^8.57.1",
     "typescript": "^5.8.3"
diff --git a/tests/rustdoc-gui/utils.goml b/tests/rustdoc-gui/utils.goml
index fa863ae4e96e..53ea91b929fe 100644
--- a/tests/rustdoc-gui/utils.goml
+++ b/tests/rustdoc-gui/utils.goml
@@ -110,6 +110,7 @@ define-function: (
         call-function: ("open-search", {})
         // We empty the search input in case it wasn't empty.
         set-property: (".search-input", {"value": ""})
+        focus: ".search-input"
         // We write the actual query.
         write-into: (".search-input", |query|)
         press-key: 'Enter'

From 6e7a87c5c5a20d43e254986724e4d781556d8a53 Mon Sep 17 00:00:00 2001
From: cyrgani 
Date: Mon, 26 Jan 2026 15:24:22 +0000
Subject: [PATCH 241/583] merge `Mark` and `Unmark` traits

---
 library/proc_macro/src/bridge/mod.rs | 31 ++++++----------------------
 1 file changed, 6 insertions(+), 25 deletions(-)

diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs
index 97e36a3d70c1..244ab7d81b02 100644
--- a/library/proc_macro/src/bridge/mod.rs
+++ b/library/proc_macro/src/bridge/mod.rs
@@ -155,11 +155,6 @@ with_api!(self, declare_tags);
 trait Mark {
     type Unmarked;
     fn mark(unmarked: Self::Unmarked) -> Self;
-}
-
-/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details).
-trait Unmark {
-    type Unmarked;
     fn unmark(self) -> Self::Unmarked;
 }
 
@@ -174,15 +169,15 @@ impl Mark for Marked {
     fn mark(unmarked: Self::Unmarked) -> Self {
         Marked { value: unmarked, _marker: marker::PhantomData }
     }
-}
-impl Unmark for Marked {
-    type Unmarked = T;
     fn unmark(self) -> Self::Unmarked {
         self.value
     }
 }
-impl<'a, T, M> Unmark for &'a Marked {
+impl<'a, T, M> Mark for &'a Marked {
     type Unmarked = &'a T;
+    fn mark(_: Self::Unmarked) -> Self {
+        unreachable!()
+    }
     fn unmark(self) -> Self::Unmarked {
         &self.value
     }
@@ -194,9 +189,6 @@ impl Mark for Vec {
         // Should be a no-op due to std's in-place collect optimizations.
         unmarked.into_iter().map(T::mark).collect()
     }
-}
-impl Unmark for Vec {
-    type Unmarked = Vec;
     fn unmark(self) -> Self::Unmarked {
         // Should be a no-op due to std's in-place collect optimizations.
         self.into_iter().map(T::unmark).collect()
@@ -211,9 +203,6 @@ macro_rules! mark_noop {
                 fn mark(unmarked: Self::Unmarked) -> Self {
                     unmarked
                 }
-            }
-            impl Unmark for $ty {
-                type Unmarked = Self;
                 fn unmark(self) -> Self::Unmarked {
                     self
                 }
@@ -294,13 +283,9 @@ macro_rules! mark_compound {
                     $($field: Mark::mark(unmarked.$field)),*
                 }
             }
-        }
-
-        impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
-            type Unmarked = $name <$($T::Unmarked),+>;
             fn unmark(self) -> Self::Unmarked {
                 $name {
-                    $($field: Unmark::unmark(self.$field)),*
+                    $($field: Mark::unmark(self.$field)),*
                 }
             }
         }
@@ -315,14 +300,10 @@ macro_rules! mark_compound {
                     })*
                 }
             }
-        }
-
-        impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
-            type Unmarked = $name <$($T::Unmarked),+>;
             fn unmark(self) -> Self::Unmarked {
                 match self {
                     $($name::$variant $(($field))? => {
-                        $name::$variant $((Unmark::unmark($field)))?
+                        $name::$variant $((Mark::unmark($field)))?
                     })*
                 }
             }

From 996d72bce8845a4c4dcb0d41823cf0af4c11c59a Mon Sep 17 00:00:00 2001
From: Martin Nordholts 
Date: Sat, 17 Jan 2026 17:53:10 +0100
Subject: [PATCH 242/583] compiletest: Support `--extern` modifiers with
 `proc-macro` directive

So that `pub-priv1.rs` test does not have to (ab)use the `aux-crate`
directive for this purpose.

This is very edge-casey so I don't think we should document this in
rustc-dev-guide. If someone needs to do this they will look at the code
and easily find the functionality.

This is a bit hacky since `--extern priv:pm.rs` is not valid, but we can
make our directives work however we want. And I think this is a fine
pragmatic approach. Doing it "the right way" would be a lot of work for
not much gain. Plus, that work can be done incrementally in small steps
in the future if wanted.
---
 .../compiletest/src/directives/auxiliary.rs   | 20 +++++++++++++++++--
 .../src/directives/auxiliary/tests.rs         | 16 +++++++++++++++
 src/tools/compiletest/src/runtest.rs          |  2 +-
 tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs |  5 -----
 tests/ui/privacy/pub-priv-dep/pub-priv1.rs    |  2 +-
 5 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs
index 14cbab640eb6..0e7e370adbd4 100644
--- a/src/tools/compiletest/src/directives/auxiliary.rs
+++ b/src/tools/compiletest/src/directives/auxiliary.rs
@@ -26,8 +26,12 @@ pub struct AuxCrate {
 }
 
 /// The value of a `proc-macro` directive.
-#[derive(Clone, Debug, Default)]
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
 pub(crate) struct ProcMacro {
+    /// Contains `--extern` modifiers, if any. See the tracking issue for more
+    /// info: 
+    /// With `proc-macro: noprelude:bar.rs` this will be `noprelude`.
+    pub extern_modifiers: Option,
     /// With `proc-macro: bar.rs` this will be `bar.rs`.
     pub path: String,
 }
@@ -108,5 +112,17 @@ fn parse_aux_crate(r: String) -> AuxCrate {
 }
 
 fn parse_proc_macro(r: String) -> ProcMacro {
-    ProcMacro { path: r.trim().to_string() }
+    let r = r.trim();
+
+    // Matches:
+    //   path
+    //   modifiers:path
+    let caps = static_regex!(r"^(?:(?[^=]*?):)?(?.*)$")
+        .captures(r)
+        .expect("can never fail");
+
+    let modifiers = caps.name("modifiers").map(|m| m.as_str().to_string());
+    let path = caps["path"].to_string();
+
+    ProcMacro { extern_modifiers: modifiers, path }
 }
diff --git a/src/tools/compiletest/src/directives/auxiliary/tests.rs b/src/tools/compiletest/src/directives/auxiliary/tests.rs
index ad205eaabfda..74fff630692e 100644
--- a/src/tools/compiletest/src/directives/auxiliary/tests.rs
+++ b/src/tools/compiletest/src/directives/auxiliary/tests.rs
@@ -25,3 +25,19 @@ fn test_aux_crate_value_with_modifiers() {
 fn test_aux_crate_value_invalid() {
     parse_aux_crate("foo.rs".to_string());
 }
+
+#[test]
+fn test_proc_macro_value_no_modifiers() {
+    assert_eq!(
+        ProcMacro { extern_modifiers: None, path: "foo.rs".to_string() },
+        parse_proc_macro("foo.rs".to_string())
+    );
+}
+
+#[test]
+fn test_proc_macro_value_with_modifiers() {
+    assert_eq!(
+        ProcMacro { extern_modifiers: Some("noprelude".to_string()), path: "foo.rs".to_string() },
+        parse_proc_macro("noprelude:foo.rs".to_string())
+    );
+}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 2bfb73f05d16..502db382e269 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1302,7 +1302,7 @@ impl<'test> TestCx<'test> {
             let crate_name = path_to_crate_name(&proc_macro.path);
             add_extern(
                 rustc,
-                None, // `extern_modifiers`
+                proc_macro.extern_modifiers.as_deref(),
                 &crate_name,
                 &proc_macro.path,
                 AuxType::ProcMacro,
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs
index 9e2aa898afe8..d45f2639d182 100644
--- a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs
@@ -1,8 +1,3 @@
-//@ force-host
-//@ no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
-
 extern crate proc_macro;
 use proc_macro::TokenStream;
 
diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
index eae0f9756a10..09ad59582d84 100644
--- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
+++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
@@ -1,6 +1,6 @@
 //@ aux-crate:priv:priv_dep=priv_dep.rs
 //@ aux-build:pub_dep.rs
-//@ aux-crate:priv:pm=pm.rs
+//@ proc-macro:priv:pm.rs
 //@ compile-flags: -Zunstable-options
 
 // Basic behavior check of exported_private_dependencies from either a public

From d02e33db2d5f2b9f5fd87eebd9dd883bfdf8a09b Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 26 Jan 2026 18:40:51 +0200
Subject: [PATCH 243/583] Handle `Self::EnumVariant` and `Self` on traits in
 doclinks

---
 .../rust-analyzer/crates/hir/src/attrs.rs     | 66 ++++++++++++-----
 .../crates/ide/src/hover/tests.rs             | 72 +++++++++++++++++++
 2 files changed, 120 insertions(+), 18 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index cba1b39e5254..cfb95e07c362 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -3,7 +3,8 @@
 use cfg::CfgExpr;
 use either::Either;
 use hir_def::{
-    AssocItemId, AttrDefId, FieldId, LifetimeParamId, ModuleDefId, TypeOrConstParamId,
+    AssocItemId, AttrDefId, FieldId, GenericDefId, ItemContainerId, LifetimeParamId, ModuleDefId,
+    TraitId, TypeOrConstParamId,
     attrs::{AttrFlags, Docs, IsInnerDoc},
     expr_store::path::Path,
     item_scope::ItemInNs,
@@ -22,6 +23,7 @@ use hir_ty::{
     next_solver::{DbInterner, TypingMode, infer::DbInternerInferExt},
 };
 use intern::Symbol;
+use stdx::never;
 
 use crate::{
     Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
@@ -357,13 +359,46 @@ fn resolve_assoc_or_field(
     ns: Option,
 ) -> Option {
     let path = Path::from_known_path_with_no_generic(path);
-    // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the
-    // trait itself.
     let base_def = resolver.resolve_path_in_type_ns_fully(db, &path)?;
 
+    let handle_trait = |id: TraitId| {
+        // Doc paths in this context may only resolve to an item of this trait
+        // (i.e. no items of its supertraits), so we need to handle them here
+        // independently of others.
+        id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| {
+            let def = match *assoc_id {
+                AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()),
+                AssocItemId::ConstId(it) => ModuleDef::Const(it.into()),
+                AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
+            };
+            DocLinkDef::ModuleDef(def)
+        })
+    };
     let ty = match base_def {
         TypeNs::SelfType(id) => Impl::from(id).self_ty(db),
-        TypeNs::GenericParam(_) => {
+        TypeNs::GenericParam(param) => {
+            let generic_params = db.generic_params(param.parent());
+            if generic_params[param.local_id()].is_trait_self() {
+                // `Self::assoc` in traits should refer to the trait itself.
+                let parent_trait = |container| match container {
+                    ItemContainerId::TraitId(trait_) => handle_trait(trait_),
+                    _ => {
+                        never!("container {container:?} should be a trait");
+                        None
+                    }
+                };
+                return match param.parent() {
+                    GenericDefId::TraitId(trait_) => handle_trait(trait_),
+                    GenericDefId::ConstId(it) => parent_trait(it.loc(db).container),
+                    GenericDefId::FunctionId(it) => parent_trait(it.loc(db).container),
+                    GenericDefId::TypeAliasId(it) => parent_trait(it.loc(db).container),
+                    _ => {
+                        never!("type param {param:?} should belong to a trait");
+                        None
+                    }
+                };
+            }
+
             // Even if this generic parameter has some trait bounds, rustdoc doesn't
             // resolve `name` to trait items.
             return None;
@@ -384,19 +419,7 @@ fn resolve_assoc_or_field(
             alias.ty(db)
         }
         TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db),
-        TypeNs::TraitId(id) => {
-            // Doc paths in this context may only resolve to an item of this trait
-            // (i.e. no items of its supertraits), so we need to handle them here
-            // independently of others.
-            return id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| {
-                let def = match *assoc_id {
-                    AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()),
-                    AssocItemId::ConstId(it) => ModuleDef::Const(it.into()),
-                    AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
-                };
-                DocLinkDef::ModuleDef(def)
-            });
-        }
+        TypeNs::TraitId(id) => return handle_trait(id),
         TypeNs::ModuleId(_) => {
             return None;
         }
@@ -414,7 +437,14 @@ fn resolve_assoc_or_field(
     let variant_def = match ty.as_adt()? {
         Adt::Struct(it) => it.into(),
         Adt::Union(it) => it.into(),
-        Adt::Enum(_) => return None,
+        Adt::Enum(enum_) => {
+            // Can happen on `Self::Variant` (otherwise would be fully resolved by the resolver).
+            return enum_
+                .id
+                .enum_variants(db)
+                .variant(&name)
+                .map(|variant| DocLinkDef::ModuleDef(ModuleDef::Variant(variant.into())));
+        }
     };
     resolve_field(db, variant_def, name, ns)
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 0b518021e39e..7900a0dc9991 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -11239,3 +11239,75 @@ impl Foo for T {
         "#]],
     );
 }
+
+#[test]
+fn doc_link_enum_self_variant() {
+    check(
+        r#"
+/// - [`VariantOne$0`](Self::One)
+pub enum MyEnum {
+    One,
+    Two,
+}
+    "#,
+        expect![[r#"
+            *[`VariantOne`](Self::One)*
+
+            ```rust
+            ra_test_fixture::MyEnum
+            ```
+
+            ```rust
+            One = 0
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn doc_link_trait_self() {
+    check(
+        r#"
+/// - [`do_something$0`](Self::do_something)
+pub trait MyTrait {
+    fn do_something(&self);
+}
+    "#,
+        expect![[r#"
+            *[`do_something`](Self::do_something)*
+
+            ```rust
+            ra_test_fixture::MyTrait
+            ```
+
+            ```rust
+            pub trait MyTrait
+            pub fn do_something(&self)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+pub trait MyTrait {
+    /// - [`do_something$0`](Self::do_something)
+    fn do_something(&self);
+}
+    "#,
+        expect![[r#"
+            *[`do_something`](Self::do_something)*
+
+            ```rust
+            ra_test_fixture::MyTrait
+            ```
+
+            ```rust
+            pub trait MyTrait
+            pub fn do_something(&self)
+            ```
+
+            ---
+
+            * [`do_something`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/trait.MyTrait.html#tymethod.do_something)
+        "#]],
+    );
+}

From 1f6a55d8ba5d51dda17bec2f90ac48e807de79f5 Mon Sep 17 00:00:00 2001
From: rustbot <47979223+rustbot@users.noreply.github.com>
Date: Mon, 26 Jan 2026 18:00:52 +0100
Subject: [PATCH 244/583] Update books

---
 src/doc/reference       | 2 +-
 src/doc/rust-by-example | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/doc/reference b/src/doc/reference
index 28b5a5441998..990819b86c22 160000
--- a/src/doc/reference
+++ b/src/doc/reference
@@ -1 +1 @@
-Subproject commit 28b5a54419985f03db5294de5eede71b6665b594
+Subproject commit 990819b86c22bbf538c0526f0287670f3dc1a67a
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
index 8de6ff811315..bac931ef1673 160000
--- a/src/doc/rust-by-example
+++ b/src/doc/rust-by-example
@@ -1 +1 @@
-Subproject commit 8de6ff811315ac3a96ebe01d74057382e42ffdee
+Subproject commit bac931ef1673af63fb60c3d691633034713cca20

From d541277ce18c874dd423f2550685762b351c0faa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luka=20Bla=C5=A1kovi=C4=87?= 
Date: Mon, 26 Jan 2026 17:08:00 +0000
Subject: [PATCH 245/583] os allow missing_docs

---
 library/core/src/os/mod.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library/core/src/os/mod.rs b/library/core/src/os/mod.rs
index 897f59f530ed..5ac2b637941a 100644
--- a/library/core/src/os/mod.rs
+++ b/library/core/src/os/mod.rs
@@ -1,6 +1,7 @@
 //! OS-specific functionality.
 
 #![unstable(feature = "darwin_objc", issue = "145496")]
+#![allow(missing_docs)]
 
 #[cfg(all(
     doc,

From abcd22d5ed8b8efc6ce6928a852f8b7a2659c553 Mon Sep 17 00:00:00 2001
From: xtqqczze <45661989+xtqqczze@users.noreply.github.com>
Date: Mon, 26 Jan 2026 17:31:34 +0000
Subject: [PATCH 246/583] Omit standard copyright notice

Remove copyright notices for files licensed under the standard terms (MIT OR Apache-2.0).
---
 REUSE.toml                                         | 2 +-
 compiler/rustc_codegen_gcc/example/alloc_system.rs | 3 ---
 src/doc/rustc-dev-guide/src/conventions.md         | 3 +--
 src/tools/miri/tests/pass/intrinsics/integer.rs    | 3 ---
 src/tools/miri/tests/pass/issues/issue-30530.rs    | 3 ---
 src/tools/miri/tests/pass/tag-align-dyn-u64.rs     | 3 ---
 6 files changed, 2 insertions(+), 15 deletions(-)

diff --git a/REUSE.toml b/REUSE.toml
index 6052c2df4c7a..5ee913426a14 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -53,7 +53,7 @@ path = [
 ]
 precedence = "override"
 SPDX-FileCopyrightText = "The Rust Project Developers (see https://thanks.rust-lang.org)"
-SPDX-License-Identifier = "MIT or Apache-2.0"
+SPDX-License-Identifier = "MIT OR Apache-2.0"
 
 [[annotations]]
 path = "compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp"
diff --git a/compiler/rustc_codegen_gcc/example/alloc_system.rs b/compiler/rustc_codegen_gcc/example/alloc_system.rs
index 4d70122496b7..31457185f1a8 100644
--- a/compiler/rustc_codegen_gcc/example/alloc_system.rs
+++ b/compiler/rustc_codegen_gcc/example/alloc_system.rs
@@ -1,6 +1,3 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
-
 #![no_std]
 #![feature(allocator_api, rustc_private)]
 
diff --git a/src/doc/rustc-dev-guide/src/conventions.md b/src/doc/rustc-dev-guide/src/conventions.md
index 5153f2c5711a..0e08ef9042d1 100644
--- a/src/doc/rustc-dev-guide/src/conventions.md
+++ b/src/doc/rustc-dev-guide/src/conventions.md
@@ -76,8 +76,7 @@ These use a pinned version of `ruff`, to avoid relying on the local environment.
 
 
 In the past, files began with a copyright and license notice.
-Please **omit** this notice for new files licensed under the standard terms (dual
-MIT/Apache-2.0).
+Please **omit** this notice for new files licensed under the standard terms (MIT OR Apache-2.0).
 
 All of the copyright notices should be gone by now, but if you come across one
 in the rust-lang/rust repo, feel free to open a PR to remove it.
diff --git a/src/tools/miri/tests/pass/intrinsics/integer.rs b/src/tools/miri/tests/pass/intrinsics/integer.rs
index a67c52f7b420..b56ee00422f0 100644
--- a/src/tools/miri/tests/pass/intrinsics/integer.rs
+++ b/src/tools/miri/tests/pass/intrinsics/integer.rs
@@ -1,6 +1,3 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
-
 #![feature(core_intrinsics, funnel_shifts)]
 use std::intrinsics::*;
 
diff --git a/src/tools/miri/tests/pass/issues/issue-30530.rs b/src/tools/miri/tests/pass/issues/issue-30530.rs
index af338e8032d1..3be3b54f2159 100644
--- a/src/tools/miri/tests/pass/issues/issue-30530.rs
+++ b/src/tools/miri/tests/pass/issues/issue-30530.rs
@@ -1,6 +1,3 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
-
 // Regression test for Issue #30530: alloca's created for storing
 // intermediate scratch values during brace-less match arms need to be
 // initialized with their drop-flag set to "dropped" (or else we end
diff --git a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
index e4abc3895008..58a38ff1c713 100644
--- a/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
+++ b/src/tools/miri/tests/pass/tag-align-dyn-u64.rs
@@ -1,6 +1,3 @@
-// SPDX-License-Identifier: MIT OR Apache-2.0
-// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
-
 use std::mem;
 
 enum Tag {

From 4a27be69726a9bd9badaa3b4ae1b5a4a63388235 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Sat, 8 Nov 2025 22:16:44 +0000
Subject: [PATCH 247/583] Do not mention `-Zmacro-backtrace` for std macros
 that are a wrapper around a compiler intrinsic

---
 compiler/rustc_expand/src/base.rs             | 14 ++++++++++++-
 .../tests/ui/recursive_format_impl.stderr     | 18 ----------------
 .../concurrency/windows_join_main.stderr      |  1 -
 .../dangling_primitive.stderr                 |  1 -
 .../out_of_bounds_read.stderr                 |  1 -
 .../out_of_bounds_read_neg_offset.stderr      |  1 -
 .../out_of_bounds_write.stderr                |  1 -
 .../miri/tests/fail/erroneous_const2.stderr   |  2 --
 .../return_pointer_on_unwind.stderr           |  1 -
 .../tests/fail/intrinsics/simd-scatter.stderr |  1 -
 .../tests/fail/provenance/mix-ptrs1.stderr    |  1 -
 .../tests/fail/provenance/mix-ptrs2.stderr    |  1 -
 src/tools/miri/tests/fail/rc_as_ptr.stderr    |  1 -
 .../fail/stacked_borrows/zst_slice.stderr     |  1 -
 .../parent_read_freezes_raw_mut.stderr        |  1 -
 .../tree_borrows/protector-write-lazy.stderr  |  1 -
 .../tests/pass/alloc-access-tracking.stderr   |  2 --
 .../main-alongside-macro-calls.fail.stdout    |  4 ----
 tests/ui/asm/aarch64/type-check-2.stderr      |  1 -
 tests/ui/asm/parse-error.stderr               |  8 -------
 tests/ui/asm/x86_64/type-check-2.stderr       |  1 -
 .../defaults-not-assumed-fail.stderr          |  3 ---
 .../ui/async-await/unreachable-lint-2.stderr  |  1 -
 ...y-operation-error-on-function-70724.stderr |  4 ----
 tests/ui/binop/eq-vec.stderr                  |  1 -
 .../function-comparison-errors-59488.stderr   |  6 ------
 tests/ui/binop/issue-77910-1.stderr           |  3 ---
 .../higher-ranked-outlives-for-capture.stderr |  2 --
 tests/ui/borrowck/borrowck-and-init.stderr    |  2 --
 .../borrowck-borrowed-uniq-rvalue-2.stderr    |  1 -
 .../borrowck/borrowck-break-uninit-2.stderr   |  1 -
 .../ui/borrowck/borrowck-break-uninit.stderr  |  1 -
 tests/ui/borrowck/borrowck-or-init.stderr     |  2 --
 tests/ui/borrowck/borrowck-while-break.stderr |  2 --
 .../ui/borrowck/issue-24267-flow-exit.stderr  |  2 --
 tests/ui/borrowck/issue-47646.stderr          |  2 --
 tests/ui/borrowck/issue-64453.stderr          |  1 -
 ...ownership-struct-update-moved-error.stderr |  1 -
 .../ui/borrowck/suggest-assign-rvalue.stderr  | 10 ---------
 .../diagnostics/arrays.stderr                 |  2 --
 .../diagnostics/box.stderr                    |  2 --
 .../diagnostics/repr_packed.stderr            |  1 -
 .../simple-struct-min-capture.stderr          |  2 --
 tests/ui/closures/issue-111932.stderr         |  1 -
 tests/ui/codemap_tests/bad-format-args.stderr |  2 --
 tests/ui/codemap_tests/tab_3.stderr           |  1 -
 .../vec-macro-in-static-array.stderr          |  1 -
 tests/ui/consts/const-eval/const_panic.stderr |  4 ----
 .../consts/const-eval/const_panic_2021.stderr |  4 ----
 .../const-eval/const_panic_libcore_bin.stderr |  2 --
 tests/ui/consts/const-eval/format.stderr      |  2 --
 tests/ui/consts/const-eval/issue-44578.stderr |  3 ---
 .../ui/consts/control-flow/issue-50577.stderr |  2 --
 .../min_const_fn/bad_const_fn_body_ice.stderr |  3 ---
 .../ui/consts/recursive-const-in-impl.stderr  |  1 -
 .../yield-while-ref-reborrowed.stderr         |  2 --
 .../ice-line-bounds-issue-148732.stderr       |  3 ---
 .../ui/derives/nonsense-input-to-debug.stderr |  2 --
 tests/ui/error-codes/E0010-teach.stderr       |  2 --
 tests/ui/error-codes/E0010.stderr             |  3 ---
 .../span-format_args-issue-140578.stderr      | 10 ---------
 .../expr/if/assert-macro-without-else.stderr  |  4 ----
 .../feature-gate-global-registration.stderr   |  2 --
 tests/ui/fmt/format-args-argument-span.stderr |  4 ----
 tests/ui/fmt/ifmt-bad-arg.stderr              |  2 --
 tests/ui/fmt/ifmt-unimpl.stderr               |  1 -
 tests/ui/fmt/non-source-literals.stderr       |  4 ----
 .../precise-capturing/migration-note.stderr   |  1 -
 ...745-avoid-expr-from-macro-expansion.stderr |  2 --
 tests/ui/issues/issue-42796.stderr            |  1 -
 .../iter-macro-not-async-closure.stderr       |  2 --
 .../lifetimes/borrowck-let-suggestion.stderr  |  1 -
 tests/ui/lint/dead-code/closure-bang.stderr   |  1 -
 tests/ui/lint/fn-ptr-comparisons-some.stderr  |  1 -
 tests/ui/lint/fn-ptr-comparisons-weird.stderr |  2 --
 .../ui/liveness/liveness-move-in-while.stderr |  1 -
 .../liveness/liveness-use-after-move.stderr   |  1 -
 .../liveness/liveness-use-after-send.stderr   |  1 -
 tests/ui/loops/loop-proper-liveness.stderr    |  1 -
 .../macros/assert.with-generic-asset.stderr   |  2 --
 .../assert.without-generic-asset.stderr       |  2 --
 .../failed-to-reparse-issue-139445.stderr     |  4 ----
 tests/ui/macros/format-parse-errors.stderr    |  2 --
 .../macro-expansion-empty-span-147255.stderr  |  1 -
 .../macros/macro-local-data-key-priv.stderr   |  1 -
 tests/ui/macros/vec-macro-in-pattern.stderr   |  3 ---
 .../mismatched-types-issue-126222.stderr      |  4 ----
 tests/ui/modules/issue-107649.stderr          |  1 -
 ...es-based-on-type-capture-clause-bad.stderr |  1 -
 .../nll-problem-case-3-issue-21906.nll.stderr |  2 --
 tests/ui/on-unimplemented/no-debug.stderr     |  4 ----
 ...t-mode-unimplemented-for-constblock.stderr |  3 ---
 ...fetime_errors_on_promotion_misusage.stderr |  2 --
 tests/ui/reachable/expr_again.stderr          |  1 -
 tests/ui/reachable/expr_block.stderr          |  2 --
 tests/ui/reachable/expr_if.stderr             |  2 --
 tests/ui/reachable/expr_loop.stderr           |  5 -----
 tests/ui/reachable/expr_match.stderr          |  5 -----
 .../ui/reachable/unreachable-code-ret.stderr  |  1 -
 .../underscore-bindings-disambiguators.stderr | 12 -----------
 .../diverge-causes-unreachable-code.stderr    |  7 -------
 .../dbg-macro-move-semantics.stderr           |  1 -
 .../dbg-macro-requires-debug.stderr           |  1 -
 tests/ui/span/coerce-suggestions.stderr       |  2 --
 tests/ui/span/issue-33884.stderr              |  2 --
 tests/ui/span/issue-39698.stderr              |  2 --
 tests/ui/span/slice-borrow.stderr             |  1 -
 .../statics/check-values-constraints.stderr   | 21 -------------------
 tests/ui/suggestions/bound-suggestions.stderr |  6 ------
 tests/ui/suggestions/issue-97760.stderr       |  1 -
 tests/ui/suggestions/path-display.stderr      |  2 --
 tests/ui/thread-local/no-unstable.stderr      |  4 ----
 .../ui/traits/const-traits/issue-79450.stderr |  1 -
 .../try-block-maybe-bad-lifetime.stderr       |  1 -
 tests/ui/try-block/try-block-opt-init.stderr  |  2 --
 ...ounds_from_bounds_param.edition2024.stderr |  3 ---
 tests/ui/type-alias-impl-trait/nested.stderr  |  1 -
 ...igned-block-without-tail-expression.stderr |  4 ----
 .../closure-ty-mismatch-issue-128561.stderr   |  2 --
 ...0017-format-into-help-deletes-macro.stderr |  1 -
 ...2007-leaked-writeln-macro-internals.stderr |  1 -
 ...stion-mark-operator-suggestion-span.stderr |  1 -
 ...suggest-clone-in-macro-issue-139253.stderr |  2 --
 tests/ui/uninhabited/void-branch.stderr       |  2 --
 .../use/use-after-move-based-on-type.stderr   |  1 -
 125 files changed, 13 insertions(+), 314 deletions(-)

diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 6a3c9697a939..653f2071d898 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -849,6 +849,9 @@ pub struct SyntaxExtension {
     /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other
     /// words, was the macro definition annotated with `#[collapse_debuginfo]`)?
     pub collapse_debuginfo: bool,
+    /// Suppresses the "this error originates in the macro" note when a diagnostic points at this
+    /// macro.
+    pub hide_backtrace: bool,
 }
 
 impl SyntaxExtension {
@@ -882,6 +885,7 @@ impl SyntaxExtension {
             allow_internal_unsafe: false,
             local_inner_macros: false,
             collapse_debuginfo: false,
+            hide_backtrace: false,
         }
     }
 
@@ -912,6 +916,12 @@ impl SyntaxExtension {
         collapse_table[flag as usize][attr as usize]
     }
 
+    fn get_hide_backtrace(attrs: &[hir::Attribute]) -> bool {
+        // FIXME(estebank): instead of reusing `#[rustc_diagnostic_item]` as a proxy, introduce a
+        // new attribute purely for this under the `#[diagnostic]` namespace.
+        ast::attr::find_by_name(attrs, sym::rustc_diagnostic_item).is_some()
+    }
+
     /// Constructs a syntax extension with the given properties
     /// and other properties converted from attributes.
     pub fn new(
@@ -948,6 +958,7 @@ impl SyntaxExtension {
             // Not a built-in macro
             None => (None, helper_attrs),
         };
+        let hide_backtrace = builtin_name.is_some() || Self::get_hide_backtrace(attrs);
 
         let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability);
 
@@ -982,6 +993,7 @@ impl SyntaxExtension {
             allow_internal_unsafe,
             local_inner_macros,
             collapse_debuginfo,
+            hide_backtrace,
         }
     }
 
@@ -1061,7 +1073,7 @@ impl SyntaxExtension {
             self.allow_internal_unsafe,
             self.local_inner_macros,
             self.collapse_debuginfo,
-            self.builtin_name.is_some(),
+            self.hide_backtrace,
         )
     }
 }
diff --git a/src/tools/clippy/tests/ui/recursive_format_impl.stderr b/src/tools/clippy/tests/ui/recursive_format_impl.stderr
index 31960c7193b4..4361d612bf2a 100644
--- a/src/tools/clippy/tests/ui/recursive_format_impl.stderr
+++ b/src/tools/clippy/tests/ui/recursive_format_impl.stderr
@@ -12,72 +12,54 @@ error: using `self` as `Display` in `impl Display` will cause infinite recursion
    |
 LL |         write!(f, "{}", self)
    |         ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: using `self` as `Display` in `impl Display` will cause infinite recursion
   --> tests/ui/recursive_format_impl.rs:86:9
    |
 LL |         write!(f, "{}", &self)
    |         ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: using `self` as `Debug` in `impl Debug` will cause infinite recursion
   --> tests/ui/recursive_format_impl.rs:93:9
    |
 LL |         write!(f, "{:?}", &self)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: using `self` as `Display` in `impl Display` will cause infinite recursion
   --> tests/ui/recursive_format_impl.rs:103:9
    |
 LL |         write!(f, "{}", &&&self)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: using `self` as `Display` in `impl Display` will cause infinite recursion
   --> tests/ui/recursive_format_impl.rs:178:9
    |
 LL |         write!(f, "{}", &*self)
    |         ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: using `self` as `Debug` in `impl Debug` will cause infinite recursion
   --> tests/ui/recursive_format_impl.rs:185:9
    |
 LL |         write!(f, "{:?}", &*self)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: using `self` as `Display` in `impl Display` will cause infinite recursion
   --> tests/ui/recursive_format_impl.rs:202:9
    |
 LL |         write!(f, "{}", *self)
    |         ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: using `self` as `Display` in `impl Display` will cause infinite recursion
   --> tests/ui/recursive_format_impl.rs:219:9
    |
 LL |         write!(f, "{}", **&&*self)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: using `self` as `Display` in `impl Display` will cause infinite recursion
   --> tests/ui/recursive_format_impl.rs:236:9
    |
 LL |         write!(f, "{}", &&**&&*self)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 10 previous errors
 
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
index 3d324ad0bdef..2f3e41b405bc 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
@@ -31,7 +31,6 @@ LL | |             assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_O
 LL | |         }
 LL | |     })
    | |______^
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr
index 24a807afd73d..afc2cb214842 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr
+++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr
@@ -16,7 +16,6 @@ help: ALLOC was deallocated here:
    |
 LL |     };
    |     ^
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr
index b957056ad36e..836c1bd8af89 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr
+++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read.stderr
@@ -11,7 +11,6 @@ help: ALLOC was allocated here:
    |
 LL |     let v: Vec = vec![1, 2];
    |                       ^^^^^^^^^^
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr
index 6cae87b759ee..5635bb9cc443 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr
+++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr
@@ -11,7 +11,6 @@ help: ALLOC was allocated here:
    |
 LL |     let v: Vec = vec![1, 2];
    |                       ^^^^^^^^^^
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr
index 13e83e4696cf..c18a55d30cc1 100644
--- a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr
+++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_write.stderr
@@ -11,7 +11,6 @@ help: ALLOC was allocated here:
    |
 LL |     let mut v: Vec = vec![1, 2];
    |                           ^^^^^^^^^^
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/erroneous_const2.stderr b/src/tools/miri/tests/fail/erroneous_const2.stderr
index 08d2cc124f13..d9074e6e2498 100644
--- a/src/tools/miri/tests/fail/erroneous_const2.stderr
+++ b/src/tools/miri/tests/fail/erroneous_const2.stderr
@@ -23,8 +23,6 @@ note: erroneous constant encountered
    |
 LL |     println!("{}", FOO);
    |                    ^^^
-   |
-   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
index 845b4f977ca3..364a4b4a7441 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
@@ -11,7 +11,6 @@ LL |     dbg!(x.0);
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
 ALLOC (stack variable, size: 132, align: 4) {
diff --git a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr
index 4d1dcec3b576..cd38ff4355aa 100644
--- a/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/simd-scatter.stderr
@@ -16,7 +16,6 @@ help: ALLOC was allocated here:
    |
 LL |         let mut vec: Vec = vec![10, 11, 12, 13, 14, 15, 16, 17, 18];
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr
index 92aeb6445b4f..e1d4b7d95f66 100644
--- a/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr
+++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr
@@ -6,7 +6,6 @@ LL |         assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0);
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr
index e00e72a50c6b..a77f73f81e5f 100644
--- a/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr
+++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr
@@ -6,7 +6,6 @@ LL |         assert_eq!(*ptr, 42);
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/rc_as_ptr.stderr b/src/tools/miri/tests/fail/rc_as_ptr.stderr
index d2d9d94afd44..d97878f815cc 100644
--- a/src/tools/miri/tests/fail/rc_as_ptr.stderr
+++ b/src/tools/miri/tests/fail/rc_as_ptr.stderr
@@ -16,7 +16,6 @@ help: ALLOC was deallocated here:
    |
 LL |     drop(strong);
    |     ^^^^^^^^^^^^
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr b/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr
index 91b320465624..c0e4f24a5a15 100644
--- a/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr
+++ b/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr
@@ -11,7 +11,6 @@ help:  would have been created here, but this is a zero-size retag ([0x0..0
    |
 LL |         assert_eq!(*s.as_ptr().add(1), 2);
    |                     ^^^^^^^^^^
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr b/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr
index d0cd0088c0be..791263b95248 100644
--- a/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr
@@ -24,7 +24,6 @@ help: the accessed tag  later transitioned to Frozen due to a reborrow (act
 LL |         assert_eq!(root, 0); // Parent Read
    |         ^^^^^^^^^^^^^^^^^^^
    = help: this transition corresponds to a loss of write permissions
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr b/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr
index 6ebc2dd40e29..9606aaadf33c 100644
--- a/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr
@@ -18,7 +18,6 @@ help: the accessed tag  later transitioned to Disabled due to a protector r
 LL | }
    |  ^
    = help: this transition corresponds to a loss of read and write permissions
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.stderr b/src/tools/miri/tests/pass/alloc-access-tracking.stderr
index 5dfcd0180e4c..f1c9241beded 100644
--- a/src/tools/miri/tests/pass/alloc-access-tracking.stderr
+++ b/src/tools/miri/tests/pass/alloc-access-tracking.stderr
@@ -15,8 +15,6 @@ note: read access at ALLOC[0..1]
    |
 LL |         assert_eq!(*ptr, 42);
    |         ^^^^^^^^^^^^^^^^^^^^ tracking was triggered here
-   |
-   = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: freed allocation ALLOC
   --> RUSTLIB/alloc/src/boxed.rs:LL:CC
diff --git a/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout
index 65989a8ef47c..8c01c6ff5a41 100644
--- a/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout
+++ b/tests/rustdoc-ui/doctest/main-alongside-macro-calls.fail.stdout
@@ -13,8 +13,6 @@ error: macros that expand to items must be delimited with braces or followed by
    |
 LL | println!();
    | ^^^^^^^^^^
-   |
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: macro expansion ignores `{` and any tokens following
   --> $SRC_DIR/std/src/macros.rs:LL:COL
@@ -35,8 +33,6 @@ error: macros that expand to items must be delimited with braces or followed by
    |
 LL | println!();
    | ^^^^^^^^^^
-   |
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: macro expansion ignores `{` and any tokens following
   --> $SRC_DIR/std/src/macros.rs:LL:COL
diff --git a/tests/ui/asm/aarch64/type-check-2.stderr b/tests/ui/asm/aarch64/type-check-2.stderr
index 84bc5f08b4ed..2cd767db0334 100644
--- a/tests/ui/asm/aarch64/type-check-2.stderr
+++ b/tests/ui/asm/aarch64/type-check-2.stderr
@@ -21,7 +21,6 @@ LL |         asm!("{}", in(reg) vec![0]);
    |                            ^^^^^^^
    |
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: cannot use value of type `(i32, i32, i32)` for inline assembly
   --> $DIR/type-check-2.rs:36:28
diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr
index dff85a601b73..fe6802b0c0c9 100644
--- a/tests/ui/asm/parse-error.stderr
+++ b/tests/ui/asm/parse-error.stderr
@@ -193,16 +193,12 @@ error: asm template must be a string literal
    |
 LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    |              ^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
   --> $DIR/parse-error.rs:86:21
    |
 LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    |                     ^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: _ cannot be used for input operands
   --> $DIR/parse-error.rs:88:28
@@ -357,16 +353,12 @@ error: asm template must be a string literal
    |
 LL | global_asm!(format!("{{{}}}", 0), const FOO);
    |             ^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
   --> $DIR/parse-error.rs:143:20
    |
 LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |                    ^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: the `in` operand cannot be used with `global_asm!`
   --> $DIR/parse-error.rs:146:19
diff --git a/tests/ui/asm/x86_64/type-check-2.stderr b/tests/ui/asm/x86_64/type-check-2.stderr
index d5c5a3ff1f84..e5d39b2fbd05 100644
--- a/tests/ui/asm/x86_64/type-check-2.stderr
+++ b/tests/ui/asm/x86_64/type-check-2.stderr
@@ -21,7 +21,6 @@ LL |         asm!("{}", in(reg) vec![0]);
    |                            ^^^^^^^
    |
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: cannot use value of type `(i32, i32, i32)` for inline assembly
   --> $DIR/type-check-2.rs:52:28
diff --git a/tests/ui/associated-consts/defaults-not-assumed-fail.stderr b/tests/ui/associated-consts/defaults-not-assumed-fail.stderr
index 40f5f889f361..8b38905e1c57 100644
--- a/tests/ui/associated-consts/defaults-not-assumed-fail.stderr
+++ b/tests/ui/associated-consts/defaults-not-assumed-fail.stderr
@@ -23,8 +23,6 @@ note: erroneous constant encountered
    |
 LL |     assert_eq!(<() as Tr>::B, 0);    // causes the error above
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: erroneous constant encountered
   --> $DIR/defaults-not-assumed-fail.rs:34:5
@@ -33,7 +31,6 @@ LL |     assert_eq!(<() as Tr>::B, 0);    // causes the error above
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-   = note: this note originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/async-await/unreachable-lint-2.stderr b/tests/ui/async-await/unreachable-lint-2.stderr
index cbebc9951f32..5e7b328cf523 100644
--- a/tests/ui/async-await/unreachable-lint-2.stderr
+++ b/tests/ui/async-await/unreachable-lint-2.stderr
@@ -11,7 +11,6 @@ note: the lint level is defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/binop/binary-operation-error-on-function-70724.stderr b/tests/ui/binop/binary-operation-error-on-function-70724.stderr
index 11b0e4ba19c5..2eb980764a39 100644
--- a/tests/ui/binop/binary-operation-error-on-function-70724.stderr
+++ b/tests/ui/binop/binary-operation-error-on-function-70724.stderr
@@ -6,8 +6,6 @@ LL |     assert_eq!(a, 0);
    |     |
    |     fn() -> i32 {a}
    |     {integer}
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
   --> $DIR/binary-operation-error-on-function-70724.rs:7:5
@@ -17,7 +15,6 @@ LL |     assert_eq!(a, 0);
    |
    = note: expected fn item `fn() -> i32 {a}`
                  found type `{integer}`
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug`
   --> $DIR/binary-operation-error-on-function-70724.rs:7:5
@@ -29,7 +26,6 @@ LL |     assert_eq!(a, 0);
    |     ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn() -> i32 {a}`
    |
    = help: use parentheses to call this function: `a()`
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/binop/eq-vec.stderr b/tests/ui/binop/eq-vec.stderr
index 14739752877c..158bfe625934 100644
--- a/tests/ui/binop/eq-vec.stderr
+++ b/tests/ui/binop/eq-vec.stderr
@@ -12,7 +12,6 @@ note: an implementation of `PartialEq` might be missing for `Foo`
    |
 LL |     enum Foo {
    |     ^^^^^^^^ must implement `PartialEq`
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `Foo` with `#[derive(PartialEq)]`
    |
 LL +     #[derive(PartialEq)]
diff --git a/tests/ui/binop/function-comparison-errors-59488.stderr b/tests/ui/binop/function-comparison-errors-59488.stderr
index 615458bc45b4..43ecd6a36f38 100644
--- a/tests/ui/binop/function-comparison-errors-59488.stderr
+++ b/tests/ui/binop/function-comparison-errors-59488.stderr
@@ -80,24 +80,18 @@ LL |     assert_eq!(Foo::Bar, i);
    |     |
    |     fn(usize) -> Foo {Foo::Bar}
    |     fn(usize) -> Foo {Foo::Bar}
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug`
   --> $DIR/function-comparison-errors-59488.rs:31:5
    |
 LL |     assert_eq!(Foo::Bar, i);
    |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}`
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug`
   --> $DIR/function-comparison-errors-59488.rs:31:5
    |
 LL |     assert_eq!(Foo::Bar, i);
    |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}`
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 10 previous errors
 
diff --git a/tests/ui/binop/issue-77910-1.stderr b/tests/ui/binop/issue-77910-1.stderr
index 80c384f39bd1..9503813f4eb8 100644
--- a/tests/ui/binop/issue-77910-1.stderr
+++ b/tests/ui/binop/issue-77910-1.stderr
@@ -6,8 +6,6 @@ LL |     assert_eq!(foo, y);
    |     |
    |     for<'a> fn(&'a i32) -> &'a i32 {foo}
    |     _
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `for<'a> fn(&'a i32) -> &'a i32 {foo}` doesn't implement `Debug`
   --> $DIR/issue-77910-1.rs:8:5
@@ -19,7 +17,6 @@ LL |     assert_eq!(foo, y);
    |     ^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `for<'a> fn(&'a i32) -> &'a i32 {foo}`
    |
    = help: use parentheses to call this function: `foo(/* &i32 */)`
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0381]: used binding `xs` isn't initialized
   --> $DIR/issue-77910-1.rs:3:5
diff --git a/tests/ui/borrowck/alias-liveness/higher-ranked-outlives-for-capture.stderr b/tests/ui/borrowck/alias-liveness/higher-ranked-outlives-for-capture.stderr
index b5c2b662f315..c82821b04a35 100644
--- a/tests/ui/borrowck/alias-liveness/higher-ranked-outlives-for-capture.stderr
+++ b/tests/ui/borrowck/alias-liveness/higher-ranked-outlives-for-capture.stderr
@@ -8,8 +8,6 @@ LL |     test(&vec![])
    |     argument requires that borrow lasts for `'static`
 LL | }
    | - temporary value is freed at the end of this statement
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-and-init.stderr b/tests/ui/borrowck/borrowck-and-init.stderr
index 37386f1c4651..e6d49bc6e471 100644
--- a/tests/ui/borrowck/borrowck-and-init.stderr
+++ b/tests/ui/borrowck/borrowck-and-init.stderr
@@ -8,8 +8,6 @@ LL |     println!("{}", false && { i = 5; true });
    |                               ----- binding initialized here in some conditions
 LL |     println!("{}", i);
    |                    ^ `i` used here but it is possibly-uninitialized
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr b/tests/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr
index 7f0ecf7b3596..9fd4ee9b99ba 100644
--- a/tests/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr
+++ b/tests/ui/borrowck/borrowck-borrowed-uniq-rvalue-2.stderr
@@ -8,7 +8,6 @@ LL |     let x = defer(&vec!["Goodbye", "world!"]);
 LL |     x.x[0];
    |     ------ borrow later used here
    |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider using a `let` binding to create a longer lived value
    |
 LL ~     let binding = vec!["Goodbye", "world!"];
diff --git a/tests/ui/borrowck/borrowck-break-uninit-2.stderr b/tests/ui/borrowck/borrowck-break-uninit-2.stderr
index e23ca534e745..03730e338ee0 100644
--- a/tests/ui/borrowck/borrowck-break-uninit-2.stderr
+++ b/tests/ui/borrowck/borrowck-break-uninit-2.stderr
@@ -7,7 +7,6 @@ LL |     let x: isize;
 LL |     println!("{}", x);
    |                    ^ `x` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let x: isize = 42;
diff --git a/tests/ui/borrowck/borrowck-break-uninit.stderr b/tests/ui/borrowck/borrowck-break-uninit.stderr
index 0367d224f801..6ed095f2e4a3 100644
--- a/tests/ui/borrowck/borrowck-break-uninit.stderr
+++ b/tests/ui/borrowck/borrowck-break-uninit.stderr
@@ -7,7 +7,6 @@ LL |     let x: isize;
 LL |     println!("{}", x);
    |                    ^ `x` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let x: isize = 42;
diff --git a/tests/ui/borrowck/borrowck-or-init.stderr b/tests/ui/borrowck/borrowck-or-init.stderr
index 7b43f2aee30e..63c0c982351b 100644
--- a/tests/ui/borrowck/borrowck-or-init.stderr
+++ b/tests/ui/borrowck/borrowck-or-init.stderr
@@ -8,8 +8,6 @@ LL |     println!("{}", false || { i = 5; true });
    |                               ----- binding initialized here in some conditions
 LL |     println!("{}", i);
    |                    ^ `i` used here but it is possibly-uninitialized
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/borrowck-while-break.stderr b/tests/ui/borrowck/borrowck-while-break.stderr
index e91af728b649..68333ce0a75d 100644
--- a/tests/ui/borrowck/borrowck-while-break.stderr
+++ b/tests/ui/borrowck/borrowck-while-break.stderr
@@ -8,8 +8,6 @@ LL |     while cond {
 ...
 LL |     println!("{}", v);
    |                    ^ `v` used here but it is possibly-uninitialized
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/issue-24267-flow-exit.stderr b/tests/ui/borrowck/issue-24267-flow-exit.stderr
index 216f8d49b1b0..e81f00f8c157 100644
--- a/tests/ui/borrowck/issue-24267-flow-exit.stderr
+++ b/tests/ui/borrowck/issue-24267-flow-exit.stderr
@@ -7,7 +7,6 @@ LL |     loop { x = break; }
 LL |     println!("{}", x);
    |                    ^ `x` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let x: i32 = 42;
@@ -22,7 +21,6 @@ LL |     for _ in 0..10 { x = continue; }
 LL |     println!("{}", x);
    |                    ^ `x` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let x: i32 = 42;
diff --git a/tests/ui/borrowck/issue-47646.stderr b/tests/ui/borrowck/issue-47646.stderr
index cfe6f3f39938..c8d48b76757a 100644
--- a/tests/ui/borrowck/issue-47646.stderr
+++ b/tests/ui/borrowck/issue-47646.stderr
@@ -12,8 +12,6 @@ LL |             println!("{:?}", heap);
 ...
 LL |     };
    |      - ... and the mutable borrow might be used here, when that temporary is dropped and runs the destructor for type `(Option>, ())`
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/issue-64453.stderr b/tests/ui/borrowck/issue-64453.stderr
index 29a7363705cd..9136d6a7dc33 100644
--- a/tests/ui/borrowck/issue-64453.stderr
+++ b/tests/ui/borrowck/issue-64453.stderr
@@ -8,7 +8,6 @@ note: function `format` is not const
   --> $SRC_DIR/alloc/src/fmt.rs:LL:COL
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
-   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0507]: cannot move out of static item `settings_dir`
   --> $DIR/issue-64453.rs:13:37
diff --git a/tests/ui/borrowck/ownership-struct-update-moved-error.stderr b/tests/ui/borrowck/ownership-struct-update-moved-error.stderr
index 83cfc7bb412c..f081bea76b7a 100644
--- a/tests/ui/borrowck/ownership-struct-update-moved-error.stderr
+++ b/tests/ui/borrowck/ownership-struct-update-moved-error.stderr
@@ -13,7 +13,6 @@ note: `Mine::make_string_bar` takes ownership of the receiver `self`, which move
    |
 LL |     fn make_string_bar(mut self) -> Mine {
    |                            ^^^^
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/borrowck/suggest-assign-rvalue.stderr b/tests/ui/borrowck/suggest-assign-rvalue.stderr
index daaef6e3892d..6ae893915aa9 100644
--- a/tests/ui/borrowck/suggest-assign-rvalue.stderr
+++ b/tests/ui/borrowck/suggest-assign-rvalue.stderr
@@ -19,7 +19,6 @@ LL |     let my_float: f32;
 LL |     println!("my_float: {}", my_float);
    |                              ^^^^^^^^ `my_float` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let my_float: f32 = 3.14159;
@@ -33,7 +32,6 @@ LL |     let demo: Demo;
 LL |     println!("demo: {:?}", demo);
    |                            ^^^^ `demo` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let demo: Demo = Default::default();
@@ -47,7 +45,6 @@ LL |     let demo_no: DemoNoDef;
 LL |     println!("demo_no: {:?}", demo_no);
    |                               ^^^^^^^ `demo_no` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let demo_no: DemoNoDef = /* value */;
@@ -61,7 +58,6 @@ LL |     let arr: [i32; 5];
 LL |     println!("arr: {:?}", arr);
    |                           ^^^ `arr` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let arr: [i32; 5] = [42; 5];
@@ -75,7 +71,6 @@ LL |     let foo: Vec<&str>;
 LL |     println!("foo: {:?}", foo);
    |                           ^^^ `foo` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let foo: Vec<&str> = vec![];
@@ -89,7 +84,6 @@ LL |     let my_string: String;
 LL |     println!("my_string: {}", my_string);
    |                               ^^^^^^^^^ `my_string` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let my_string: String = Default::default();
@@ -103,7 +97,6 @@ LL |     let my_int: &i32;
 LL |     println!("my_int: {}", *my_int);
    |                            ^^^^^^^ `*my_int` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let my_int: &i32 = &42;
@@ -117,7 +110,6 @@ LL |     let hello: &str;
 LL |     println!("hello: {}", hello);
    |                           ^^^^^ `hello` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let hello: &str = "";
@@ -130,8 +122,6 @@ LL |     let never: !;
    |         ----- binding declared here but left uninitialized
 LL |     println!("never: {}", never);
    |                           ^^^^^ `never` used here but it isn't initialized
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 10 previous errors
 
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr
index 97ecdfab8205..4249dea10a36 100644
--- a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr
@@ -53,8 +53,6 @@ LL |     println!("{}", arr[3]);
 ...
 LL |     c();
    |     - mutable borrow later used here
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable
   --> $DIR/arrays.rs:71:24
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr
index 2e3259e64059..09143f44dc83 100644
--- a/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr
@@ -25,8 +25,6 @@ LL |     println!("{}", e.0.0.m.x);
 LL |
 LL |     c();
    |     - mutable borrow later used here
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed
   --> $DIR/box.rs:55:5
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr
index 0e93e033c022..f59be6b7a720 100644
--- a/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr
@@ -7,7 +7,6 @@ LL |         println!("{}", foo.x);
    = note: this struct is 1-byte aligned, but the type of this field may require higher alignment
    = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
    = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr
index 68fdb3ce131f..406f7c63b734 100644
--- a/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr
@@ -13,8 +13,6 @@ LL |     println!("{:?}", p);
 LL |
 LL |     c();
    |     - mutable borrow later used here
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/closures/issue-111932.stderr b/tests/ui/closures/issue-111932.stderr
index fc3b7b0c6e66..2d10a2657aa0 100644
--- a/tests/ui/closures/issue-111932.stderr
+++ b/tests/ui/closures/issue-111932.stderr
@@ -17,7 +17,6 @@ LL |         println!("{:?}", foo);
    |                   required by this formatting parameter
    |
    = help: the trait `Sized` is not implemented for `dyn Foo`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/codemap_tests/bad-format-args.stderr b/tests/ui/codemap_tests/bad-format-args.stderr
index 8f79beaa9e1b..ef0fa5f481e5 100644
--- a/tests/ui/codemap_tests/bad-format-args.stderr
+++ b/tests/ui/codemap_tests/bad-format-args.stderr
@@ -3,8 +3,6 @@ error: requires at least a format string argument
    |
 LL |     format!();
    |     ^^^^^^^^^
-   |
-   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: expected `,`, found `1`
   --> $DIR/bad-format-args.rs:3:16
diff --git a/tests/ui/codemap_tests/tab_3.stderr b/tests/ui/codemap_tests/tab_3.stderr
index 7ae21a57052f..2a0a9e2d48f3 100644
--- a/tests/ui/codemap_tests/tab_3.stderr
+++ b/tests/ui/codemap_tests/tab_3.stderr
@@ -11,7 +11,6 @@ LL |         println!("{:?}", some_vec);
    |
 note: `into_iter` takes ownership of the receiver `self`, which moves `some_vec`
   --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: you can `clone` the value and consume it, but this might not be your desired behavior
    |
 LL |     some_vec.clone().into_iter();
diff --git a/tests/ui/const-generics/vec-macro-in-static-array.stderr b/tests/ui/const-generics/vec-macro-in-static-array.stderr
index de21f2274f3a..63d7b0c89fa1 100644
--- a/tests/ui/const-generics/vec-macro-in-static-array.stderr
+++ b/tests/ui/const-generics/vec-macro-in-static-array.stderr
@@ -6,7 +6,6 @@ LL | static VEC: [u32; 256] = vec![];
    |
    = note: expected array `[u32; 256]`
              found struct `Vec<_>`
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/const-eval/const_panic.stderr b/tests/ui/consts/const-eval/const_panic.stderr
index a2036e834b0b..8e0384b2f2af 100644
--- a/tests/ui/consts/const-eval/const_panic.stderr
+++ b/tests/ui/consts/const-eval/const_panic.stderr
@@ -21,8 +21,6 @@ error[E0080]: evaluation panicked: not implemented
    |
 LL | const X: () = std::unimplemented!();
    |               ^^^^^^^^^^^^^^^^^^^^^ evaluation of `X` failed here
-   |
-   = note: this error originates in the macro `std::unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: hello
   --> $DIR/const_panic.rs:19:15
@@ -59,8 +57,6 @@ error[E0080]: evaluation panicked: not implemented
    |
 LL | const X_CORE: () = core::unimplemented!();
    |                    ^^^^^^^^^^^^^^^^^^^^^^ evaluation of `X_CORE` failed here
-   |
-   = note: this error originates in the macro `core::unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: hello
   --> $DIR/const_panic.rs:37:20
diff --git a/tests/ui/consts/const-eval/const_panic_2021.stderr b/tests/ui/consts/const-eval/const_panic_2021.stderr
index ba771a35d036..a0181ccca391 100644
--- a/tests/ui/consts/const-eval/const_panic_2021.stderr
+++ b/tests/ui/consts/const-eval/const_panic_2021.stderr
@@ -21,8 +21,6 @@ error[E0080]: evaluation panicked: not implemented
    |
 LL | const D: () = std::unimplemented!();
    |               ^^^^^^^^^^^^^^^^^^^^^ evaluation of `D` failed here
-   |
-   = note: this error originates in the macro `std::unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: hello
   --> $DIR/const_panic_2021.rs:18:15
@@ -53,8 +51,6 @@ error[E0080]: evaluation panicked: not implemented
    |
 LL | const D_CORE: () = core::unimplemented!();
    |                    ^^^^^^^^^^^^^^^^^^^^^^ evaluation of `D_CORE` failed here
-   |
-   = note: this error originates in the macro `core::unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: hello
   --> $DIR/const_panic_2021.rs:33:20
diff --git a/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr b/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr
index e07b172d426c..a9dc43caf76a 100644
--- a/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr
+++ b/tests/ui/consts/const-eval/const_panic_libcore_bin.stderr
@@ -15,8 +15,6 @@ error[E0080]: evaluation panicked: not implemented
    |
 LL | const X: () = unimplemented!();
    |               ^^^^^^^^^^^^^^^^ evaluation of `X` failed here
-   |
-   = note: this error originates in the macro `unimplemented` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr
index 06bada8da011..67bef0ce6c35 100644
--- a/tests/ui/consts/const-eval/format.stderr
+++ b/tests/ui/consts/const-eval/format.stderr
@@ -13,7 +13,6 @@ LL |     println!("{:?}", 0);
    |     ^^^^^^^^^^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const function `std::io::_print` in constant functions
   --> $DIR/format.rs:7:5
@@ -24,7 +23,6 @@ LL |     println!("{:?}", 0);
 note: function `_print` is not const
   --> $SRC_DIR/std/src/io/stdio.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const formatting macro in constant functions
   --> $DIR/format.rs:13:5
diff --git a/tests/ui/consts/const-eval/issue-44578.stderr b/tests/ui/consts/const-eval/issue-44578.stderr
index fd0b9ae1e17d..7625664f9ed6 100644
--- a/tests/ui/consts/const-eval/issue-44578.stderr
+++ b/tests/ui/consts/const-eval/issue-44578.stderr
@@ -23,8 +23,6 @@ note: erroneous constant encountered
    |
 LL |     println!("{}",  as Foo>::AMT);
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: erroneous constant encountered
   --> $DIR/issue-44578.rs:26:20
@@ -33,7 +31,6 @@ LL |     println!("{}",  as Foo>::AMT);
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-   = note: this note originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/control-flow/issue-50577.stderr b/tests/ui/consts/control-flow/issue-50577.stderr
index 96b11f71bb3e..6f62510c8801 100644
--- a/tests/ui/consts/control-flow/issue-50577.stderr
+++ b/tests/ui/consts/control-flow/issue-50577.stderr
@@ -3,8 +3,6 @@ error[E0308]: mismatched types
    |
 LL |         Drop = assert_eq!(1, 1),
    |                ^^^^^^^^^^^^^^^^ expected `isize`, found `()`
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr b/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr
index 8e52a7aa35e1..f6ddb19f9634 100644
--- a/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr
+++ b/tests/ui/consts/min_const_fn/bad_const_fn_body_ice.stderr
@@ -3,8 +3,6 @@ error[E0010]: allocations are not allowed in constant functions
    |
 LL |     vec![1, 2, 3]
    |     ^^^^^^^^^^^^^ allocation not allowed in constant functions
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in constant functions
   --> $DIR/bad_const_fn_body_ice.rs:2:5
@@ -13,7 +11,6 @@ LL |     vec![1, 2, 3]
    |     ^^^^^^^^^^^^^
    |
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/consts/recursive-const-in-impl.stderr b/tests/ui/consts/recursive-const-in-impl.stderr
index ec5ad52fadd4..c53ce701566e 100644
--- a/tests/ui/consts/recursive-const-in-impl.stderr
+++ b/tests/ui/consts/recursive-const-in-impl.stderr
@@ -6,7 +6,6 @@ LL |     println!("{}", Thing::::X);
    |
    = help: consider increasing the recursion limit by adding a `#![recursion_limit = "14"]` attribute to your crate (`recursive_const_in_impl`)
    = note: query depth increased by 9 when simplifying constant for the type system `main::promoted[0]`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/coroutine/yield-while-ref-reborrowed.stderr b/tests/ui/coroutine/yield-while-ref-reborrowed.stderr
index 0b2cc92f4b4a..836360b87303 100644
--- a/tests/ui/coroutine/yield-while-ref-reborrowed.stderr
+++ b/tests/ui/coroutine/yield-while-ref-reborrowed.stderr
@@ -10,8 +10,6 @@ LL |     println!("{}", x);
    |                    ^ second borrow occurs here
 LL |     Pin::new(&mut b).resume(());
    |              ------ first borrow later used here
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr
index f34ac0ea306c..83cc238b5e0c 100644
--- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr
+++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr
@@ -3,8 +3,6 @@ error[E0106]: missing lifetime specifier
    |
 LL |     dbg!(b);
    |     ^^^^^^^ expected named lifetime parameter
-   |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0425]: cannot find function `a` in this scope
   --> $DIR/ice-line-bounds-issue-148732.rs:1:7
@@ -37,7 +35,6 @@ LL |     dbg!(b);
    |     ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}`
    |
    = help: use parentheses to call this function: `b()`
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/derives/nonsense-input-to-debug.stderr b/tests/ui/derives/nonsense-input-to-debug.stderr
index 7c97ca93cfc9..207d7b1969de 100644
--- a/tests/ui/derives/nonsense-input-to-debug.stderr
+++ b/tests/ui/derives/nonsense-input-to-debug.stderr
@@ -13,8 +13,6 @@ LL |     should_be_vec_t: vec![T],
    |                      expected type
    |                      in this macro invocation
    |                      this macro call doesn't expand to a type
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0392]: type parameter `T` is never used
   --> $DIR/nonsense-input-to-debug.rs:5:17
diff --git a/tests/ui/error-codes/E0010-teach.stderr b/tests/ui/error-codes/E0010-teach.stderr
index 82bbe01aef79..9318e8df7e25 100644
--- a/tests/ui/error-codes/E0010-teach.stderr
+++ b/tests/ui/error-codes/E0010-teach.stderr
@@ -5,7 +5,6 @@ LL | const CON: Vec = vec![1, 2, 3];
    |                       ^^^^^^^^^^^^^ allocation not allowed in constants
    |
    = note: The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created.
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in constants
   --> $DIR/E0010-teach.rs:5:23
@@ -14,7 +13,6 @@ LL | const CON: Vec = vec![1, 2, 3];
    |                       ^^^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/error-codes/E0010.stderr b/tests/ui/error-codes/E0010.stderr
index 87b722b5f656..d08b7f90afb4 100644
--- a/tests/ui/error-codes/E0010.stderr
+++ b/tests/ui/error-codes/E0010.stderr
@@ -3,8 +3,6 @@ error[E0010]: allocations are not allowed in constants
    |
 LL | const CON: Vec = vec![1, 2, 3];
    |                       ^^^^^^^^^^^^^ allocation not allowed in constants
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in constants
   --> $DIR/E0010.rs:3:23
@@ -13,7 +11,6 @@ LL | const CON: Vec = vec![1, 2, 3];
    |                       ^^^^^^^^^^^^^
    |
    = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/errors/span-format_args-issue-140578.stderr b/tests/ui/errors/span-format_args-issue-140578.stderr
index 6a273e5cd515..b5394b6c33af 100644
--- a/tests/ui/errors/span-format_args-issue-140578.stderr
+++ b/tests/ui/errors/span-format_args-issue-140578.stderr
@@ -3,40 +3,30 @@ error[E0282]: type annotations needed
    |
 LL |   print!("{:?} {a} {a:?}", [], a = 1 + 1);
    |                            ^^ cannot infer type
-   |
-   = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `print` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0282]: type annotations needed
   --> $DIR/span-format_args-issue-140578.rs:7:30
    |
 LL |   println!("{:?} {a} {a:?}", [], a = 1 + 1);
    |                              ^^ cannot infer type
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0282]: type annotations needed
   --> $DIR/span-format_args-issue-140578.rs:12:35
    |
 LL |   println!("{:?} {:?} {a} {a:?}", [], [], a = 1 + 1);
    |                                   ^^ cannot infer type
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0282]: type annotations needed
   --> $DIR/span-format_args-issue-140578.rs:17:41
    |
 LL |   println!("{:?} {:?} {a} {a:?} {b:?}", [], [], a = 1 + 1, b = []);
    |                                         ^^ cannot infer type
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0282]: type annotations needed
   --> $DIR/span-format_args-issue-140578.rs:26:9
    |
 LL |         [],
    |         ^^ cannot infer type
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/expr/if/assert-macro-without-else.stderr b/tests/ui/expr/if/assert-macro-without-else.stderr
index 74179ba899f5..98f22607bea7 100644
--- a/tests/ui/expr/if/assert-macro-without-else.stderr
+++ b/tests/ui/expr/if/assert-macro-without-else.stderr
@@ -11,16 +11,12 @@ error[E0308]: mismatched types
    |
 LL |     assert_eq!(1, 1)
    |     ^^^^^^^^^^^^^^^^ expected `i32`, found `()`
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
   --> $DIR/assert-macro-without-else.rs:14:5
    |
 LL |     assert_ne!(1, 2)
    |     ^^^^^^^^^^^^^^^^ expected `bool`, found `()`
-   |
-   = note: this error originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
   --> $DIR/assert-macro-without-else.rs:26:9
diff --git a/tests/ui/feature-gates/feature-gate-global-registration.stderr b/tests/ui/feature-gates/feature-gate-global-registration.stderr
index 70538ae6f317..5da1721d99dd 100644
--- a/tests/ui/feature-gates/feature-gate-global-registration.stderr
+++ b/tests/ui/feature-gates/feature-gate-global-registration.stderr
@@ -6,8 +6,6 @@ LL | todo!();
    | |
    | expected one of `!` or `::`
    | in this macro invocation
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/fmt/format-args-argument-span.stderr b/tests/ui/fmt/format-args-argument-span.stderr
index d46cfb438cf6..a486abe821b7 100644
--- a/tests/ui/fmt/format-args-argument-span.stderr
+++ b/tests/ui/fmt/format-args-argument-span.stderr
@@ -6,7 +6,6 @@ LL |     println!("{x:?} {x} {x:?}");
    |
    = help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `Option<{integer}>` doesn't implement `std::fmt::Display`
   --> $DIR/format-args-argument-span.rs:15:37
@@ -18,7 +17,6 @@ LL |     println!("{x:?} {x} {x:?}", x = Some(1));
    |
    = help: the trait `std::fmt::Display` is not implemented for `Option<{integer}>`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `DisplayOnly` doesn't implement `Debug`
   --> $DIR/format-args-argument-span.rs:18:19
@@ -28,7 +26,6 @@ LL |     println!("{x} {x:?} {x}");
    |
    = help: the trait `Debug` is not implemented for `DisplayOnly`
    = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `DisplayOnly` with `#[derive(Debug)]`
    |
 LL + #[derive(Debug)]
@@ -45,7 +42,6 @@ LL |     println!("{x} {x:?} {x}", x = DisplayOnly);
    |
    = help: the trait `Debug` is not implemented for `DisplayOnly`
    = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `DisplayOnly` with `#[derive(Debug)]`
    |
 LL + #[derive(Debug)]
diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr
index b565b836f211..9018482e7078 100644
--- a/tests/ui/fmt/ifmt-bad-arg.stderr
+++ b/tests/ui/fmt/ifmt-bad-arg.stderr
@@ -320,7 +320,6 @@ LL |     println!("{} {:.*} {}", 1, 3.2, 4);
               found reference `&{float}`
 note: associated function defined here
   --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
   --> $DIR/ifmt-bad-arg.rs:81:35
@@ -334,7 +333,6 @@ LL |     println!("{} {:07$.*} {}", 1, 3.2, 4);
               found reference `&{float}`
 note: associated function defined here
   --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 38 previous errors
 
diff --git a/tests/ui/fmt/ifmt-unimpl.stderr b/tests/ui/fmt/ifmt-unimpl.stderr
index 5e80f892dcb5..5e97e69c7cf3 100644
--- a/tests/ui/fmt/ifmt-unimpl.stderr
+++ b/tests/ui/fmt/ifmt-unimpl.stderr
@@ -17,7 +17,6 @@ LL |     format!("{:X}", "3");
              i32
            and 9 others
    = note: required for `&str` to implement `UpperHex`
-   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/fmt/non-source-literals.stderr b/tests/ui/fmt/non-source-literals.stderr
index 5f042e1e631a..953a4a64fd8d 100644
--- a/tests/ui/fmt/non-source-literals.stderr
+++ b/tests/ui/fmt/non-source-literals.stderr
@@ -10,7 +10,6 @@ help: the trait `std::fmt::Display` is not implemented for `NonDisplay`
 LL | pub struct NonDisplay;
    | ^^^^^^^^^^^^^^^^^^^^^
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `NonDisplay` doesn't implement `std::fmt::Display`
   --> $DIR/non-source-literals.rs:10:45
@@ -24,7 +23,6 @@ help: the trait `std::fmt::Display` is not implemented for `NonDisplay`
 LL | pub struct NonDisplay;
    | ^^^^^^^^^^^^^^^^^^^^^
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `NonDebug` doesn't implement `Debug`
   --> $DIR/non-source-literals.rs:11:42
@@ -34,7 +32,6 @@ LL |     let _ = format!(concat!("{:", "?}"), NonDebug);
    |
    = help: the trait `Debug` is not implemented for `NonDebug`
    = note: add `#[derive(Debug)]` to `NonDebug` or manually `impl Debug for NonDebug`
-   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `NonDebug` with `#[derive(Debug)]`
    |
 LL + #[derive(Debug)]
@@ -49,7 +46,6 @@ LL |     let _ = format!(concat!("{", "0", ":?}"), NonDebug);
    |
    = help: the trait `Debug` is not implemented for `NonDebug`
    = note: add `#[derive(Debug)]` to `NonDebug` or manually `impl Debug for NonDebug`
-   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `NonDebug` with `#[derive(Debug)]`
    |
 LL + #[derive(Debug)]
diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
index 880e7878477a..fef0a85bf1ae 100644
--- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr
+++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr
@@ -282,7 +282,6 @@ note: this call may capture more lifetimes than intended, because Rust 2024 has
    |
 LL |     let x = { let x = display_len(&mut vec![0]); x };
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: use the precise capturing `use<...>` syntax to make the captures explicit
    |
 LL | fn display_len(x: &Vec) -> impl Display + use {
diff --git a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr
index 3de317d2af6d..ff668f88d4d1 100644
--- a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr
+++ b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr
@@ -3,8 +3,6 @@ error[E0282]: type annotations needed
    |
 LL |     println!("{:?}", []);
    |                      ^^ cannot infer type
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-42796.stderr b/tests/ui/issues/issue-42796.stderr
index 670b98c77089..0e7ce9e98c4d 100644
--- a/tests/ui/issues/issue-42796.stderr
+++ b/tests/ui/issues/issue-42796.stderr
@@ -9,7 +9,6 @@ LL |     let mut s_copy = s;
 LL |     println!("{}", s);
    |                    ^ value borrowed here after move
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider cloning the value if the performance cost is acceptable
    |
 LL |     let mut s_copy = s.clone();
diff --git a/tests/ui/iterators/iter-macro-not-async-closure.stderr b/tests/ui/iterators/iter-macro-not-async-closure.stderr
index 2f0343a2d0d6..906ebd482fb6 100644
--- a/tests/ui/iterators/iter-macro-not-async-closure.stderr
+++ b/tests/ui/iterators/iter-macro-not-async-closure.stderr
@@ -35,7 +35,6 @@ note: required by a bound in `call_async_once`
    |
 LL | async fn call_async_once(f: impl AsyncFnOnce()) {
    |                                  ^^^^^^^^^^^^^ required by this bound in `call_async_once`
-   = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied
   --> $DIR/iter-macro-not-async-closure.rs:25:13
@@ -48,7 +47,6 @@ note: required by a bound in `call_async_once`
    |
 LL | async fn call_async_once(f: impl AsyncFnOnce()) {
    |                                  ^^^^^^^^^^^^^ required by this bound in `call_async_once`
-   = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied
   --> $DIR/iter-macro-not-async-closure.rs:30:5
diff --git a/tests/ui/lifetimes/borrowck-let-suggestion.stderr b/tests/ui/lifetimes/borrowck-let-suggestion.stderr
index 4703d7f10dc2..6a35f61708d2 100644
--- a/tests/ui/lifetimes/borrowck-let-suggestion.stderr
+++ b/tests/ui/lifetimes/borrowck-let-suggestion.stderr
@@ -9,7 +9,6 @@ LL |
 LL |     x.use_mut();
    |     - borrow later used here
    |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider consuming the `Vec` when turning it into an `Iterator`
    |
 LL |     let mut x = vec![1].into_iter();
diff --git a/tests/ui/lint/dead-code/closure-bang.stderr b/tests/ui/lint/dead-code/closure-bang.stderr
index a0f5962dfe02..c2f83c17179c 100644
--- a/tests/ui/lint/dead-code/closure-bang.stderr
+++ b/tests/ui/lint/dead-code/closure-bang.stderr
@@ -11,7 +11,6 @@ note: the lint level is defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lint/fn-ptr-comparisons-some.stderr b/tests/ui/lint/fn-ptr-comparisons-some.stderr
index 522c4399bce1..8674ce5e4a88 100644
--- a/tests/ui/lint/fn-ptr-comparisons-some.stderr
+++ b/tests/ui/lint/fn-ptr-comparisons-some.stderr
@@ -18,7 +18,6 @@ LL |     assert_eq!(Some::(func), Some(func as unsafe extern "C" fn()));
    = note: the address of the same function can vary between different codegen units
    = note: furthermore, different functions could have the same address after being merged together
    = note: for more information visit 
-   = note: this warning originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 warning: 2 warnings emitted
 
diff --git a/tests/ui/lint/fn-ptr-comparisons-weird.stderr b/tests/ui/lint/fn-ptr-comparisons-weird.stderr
index 2014e519c253..3c76e05016f5 100644
--- a/tests/ui/lint/fn-ptr-comparisons-weird.stderr
+++ b/tests/ui/lint/fn-ptr-comparisons-weird.stderr
@@ -61,7 +61,6 @@ LL |     let _ = assert_eq!(g, g);
    = note: the address of the same function can vary between different codegen units
    = note: furthermore, different functions could have the same address after being merged together
    = note: for more information visit 
-   = note: this warning originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
   --> $DIR/fn-ptr-comparisons-weird.rs:35:13
@@ -72,7 +71,6 @@ LL |     let _ = assert_ne!(g, g);
    = note: the address of the same function can vary between different codegen units
    = note: furthermore, different functions could have the same address after being merged together
    = note: for more information visit 
-   = note: this warning originates in the macro `assert_ne` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 warning: 7 warnings emitted
 
diff --git a/tests/ui/liveness/liveness-move-in-while.stderr b/tests/ui/liveness/liveness-move-in-while.stderr
index dc48c4cc9acf..1bb97ad68c7c 100644
--- a/tests/ui/liveness/liveness-move-in-while.stderr
+++ b/tests/ui/liveness/liveness-move-in-while.stderr
@@ -35,7 +35,6 @@ LL |         while true { while true { while true { x = y; x.clone(); } } }
    |         |            inside of this loop
    |         inside of this loop
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider cloning the value if the performance cost is acceptable
    |
 LL |         while true { while true { while true { x = y.clone(); x.clone(); } } }
diff --git a/tests/ui/liveness/liveness-use-after-move.stderr b/tests/ui/liveness/liveness-use-after-move.stderr
index eab51edca37f..a94ceae79d56 100644
--- a/tests/ui/liveness/liveness-use-after-move.stderr
+++ b/tests/ui/liveness/liveness-use-after-move.stderr
@@ -9,7 +9,6 @@ LL |
 LL |     println!("{}", *x);
    |                    ^^ value borrowed here after move
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider cloning the value if the performance cost is acceptable
    |
 LL |     let y = x.clone();
diff --git a/tests/ui/liveness/liveness-use-after-send.stderr b/tests/ui/liveness/liveness-use-after-send.stderr
index 2323451a7d2d..da682325347c 100644
--- a/tests/ui/liveness/liveness-use-after-send.stderr
+++ b/tests/ui/liveness/liveness-use-after-send.stderr
@@ -13,7 +13,6 @@ note: consider changing this parameter type in function `send` to borrow instead
    |
 LL | fn send(ch: Chan, data: T) {
    |    ---- in this function                             ^ this parameter takes ownership of the value
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider cloning the value if the performance cost is acceptable
    |
 LL |     send(ch, message.clone());
diff --git a/tests/ui/loops/loop-proper-liveness.stderr b/tests/ui/loops/loop-proper-liveness.stderr
index cd4c064bcd19..5432043c7d24 100644
--- a/tests/ui/loops/loop-proper-liveness.stderr
+++ b/tests/ui/loops/loop-proper-liveness.stderr
@@ -7,7 +7,6 @@ LL |     let x: i32;
 LL |     println!("{:?}", x);
    |                      ^ `x` used here but it isn't initialized
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider assigning a value
    |
 LL |     let x: i32 = 42;
diff --git a/tests/ui/macros/assert.with-generic-asset.stderr b/tests/ui/macros/assert.with-generic-asset.stderr
index 51d8f28a35c3..523346830662 100644
--- a/tests/ui/macros/assert.with-generic-asset.stderr
+++ b/tests/ui/macros/assert.with-generic-asset.stderr
@@ -15,8 +15,6 @@ error: macro requires a boolean expression as an argument
    |
 LL |     debug_assert!();
    |     ^^^^^^^^^^^^^^^ boolean expression required
-   |
-   = note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: expected expression, found keyword `struct`
   --> $DIR/assert.rs:8:19
diff --git a/tests/ui/macros/assert.without-generic-asset.stderr b/tests/ui/macros/assert.without-generic-asset.stderr
index 51d8f28a35c3..523346830662 100644
--- a/tests/ui/macros/assert.without-generic-asset.stderr
+++ b/tests/ui/macros/assert.without-generic-asset.stderr
@@ -15,8 +15,6 @@ error: macro requires a boolean expression as an argument
    |
 LL |     debug_assert!();
    |     ^^^^^^^^^^^^^^^ boolean expression required
-   |
-   = note: this error originates in the macro `debug_assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: expected expression, found keyword `struct`
   --> $DIR/assert.rs:8:19
diff --git a/tests/ui/macros/failed-to-reparse-issue-139445.stderr b/tests/ui/macros/failed-to-reparse-issue-139445.stderr
index 6f7d88fb3446..fc3a2645e25e 100644
--- a/tests/ui/macros/failed-to-reparse-issue-139445.stderr
+++ b/tests/ui/macros/failed-to-reparse-issue-139445.stderr
@@ -9,16 +9,12 @@ error: expected `while`, `for`, `loop` or `{` after a label
    |
 LL |     assert_eq!(3, 'a,)
    |     ^^^^^^^^^^^^^^^^^^ expected `while`, `for`, `loop` or `{` after a label
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: expected expression, found ``
   --> $DIR/failed-to-reparse-issue-139445.rs:2:5
    |
 LL |     assert_eq!(3, 'a,)
    |     ^^^^^^^^^^^^^^^^^^ expected expression
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/macros/format-parse-errors.stderr b/tests/ui/macros/format-parse-errors.stderr
index f9ea4c63377b..baa29170a7d4 100644
--- a/tests/ui/macros/format-parse-errors.stderr
+++ b/tests/ui/macros/format-parse-errors.stderr
@@ -3,8 +3,6 @@ error: requires at least a format string argument
    |
 LL |     format!();
    |     ^^^^^^^^^
-   |
-   = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: expected expression, found keyword `struct`
   --> $DIR/format-parse-errors.rs:5:13
diff --git a/tests/ui/macros/macro-expansion-empty-span-147255.stderr b/tests/ui/macros/macro-expansion-empty-span-147255.stderr
index 99396622b34e..cea691679988 100644
--- a/tests/ui/macros/macro-expansion-empty-span-147255.stderr
+++ b/tests/ui/macros/macro-expansion-empty-span-147255.stderr
@@ -8,7 +8,6 @@ LL |     println!("{}", x_str);
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/macros/macro-local-data-key-priv.stderr b/tests/ui/macros/macro-local-data-key-priv.stderr
index 8df1aec140d0..2aced92c4152 100644
--- a/tests/ui/macros/macro-local-data-key-priv.stderr
+++ b/tests/ui/macros/macro-local-data-key-priv.stderr
@@ -9,7 +9,6 @@ note: the constant `baz` is defined here
    |
 LL |     thread_local!(static baz: f64 = 0.0);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/macros/vec-macro-in-pattern.stderr b/tests/ui/macros/vec-macro-in-pattern.stderr
index 71ba0ea5ad4f..e5723efc0b2d 100644
--- a/tests/ui/macros/vec-macro-in-pattern.stderr
+++ b/tests/ui/macros/vec-macro-in-pattern.stderr
@@ -5,7 +5,6 @@ LL |         Some(vec![43]) => {}
    |              ^^^^^^^^ not a tuple struct or tuple variant
    |
    = note: function calls are not allowed in patterns: 
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0658]: usage of qualified paths in this context is experimental
   --> $DIR/vec-macro-in-pattern.rs:7:14
@@ -16,7 +15,6 @@ LL |         Some(vec![43]) => {}
    = note: see issue #86935  for more information
    = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0164]: expected tuple struct or tuple variant, found associated function `<[_]>::into_vec`
   --> $DIR/vec-macro-in-pattern.rs:7:14
@@ -25,7 +23,6 @@ LL |         Some(vec![43]) => {}
    |              ^^^^^^^^ `fn` calls are not allowed in patterns
    |
    = help: for more information, visit https://doc.rust-lang.org/book/ch19-00-patterns.html
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr b/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr
index 6843cb65a8cd..09ba15253dc5 100644
--- a/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr
+++ b/tests/ui/mismatched_types/mismatched-types-issue-126222.stderr
@@ -4,7 +4,6 @@ error[E0308]: mismatched types
 LL |             x => dbg!(x),
    |                  ^^^^^^^ expected `()`, found integer
    |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: you might have meant to return this value
    |
 LL |             x => return dbg!(x),
@@ -16,7 +15,6 @@ error[E0308]: mismatched types
 LL |                 dbg!(x)
    |                 ^^^^^^^ expected `()`, found integer
    |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: you might have meant to return this value
    |
 LL |                 return dbg!(x)
@@ -28,7 +26,6 @@ error[E0308]: mismatched types
 LL |             _ => dbg!(1)
    |                  ^^^^^^^ expected `()`, found integer
    |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: you might have meant to return this value
    |
 LL |             _ => return dbg!(1)
@@ -40,7 +37,6 @@ error[E0308]: mismatched types
 LL |             _ => {dbg!(1)}
    |                   ^^^^^^^ expected `()`, found integer
    |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: you might have meant to return this value
    |
 LL |             _ => {return dbg!(1)}
diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr
index 45cb29d10ec2..40dda93ad015 100644
--- a/tests/ui/modules/issue-107649.stderr
+++ b/tests/ui/modules/issue-107649.stderr
@@ -5,7 +5,6 @@ error[E0277]: `Dummy` doesn't implement `Debug`
     |     ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `Dummy`
     |
     = note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy`
-    = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `Dummy` with `#[derive(Debug)]`
    --> $DIR/auxiliary/dummy_lib.rs:2:1
     |
diff --git a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr
index 17049fe67318..ba729493c9b7 100644
--- a/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr
+++ b/tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr
@@ -11,7 +11,6 @@ LL |     });
 LL |     println!("{}", x);
    |                    ^ value borrowed here after move
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider cloning the value before moving it into the closure
    |
 LL ~     let value = x.clone();
diff --git a/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr b/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr
index dc38b8c127e5..e16300886b0b 100644
--- a/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr
+++ b/tests/ui/nll/polonius/nll-problem-case-3-issue-21906.nll.stderr
@@ -59,8 +59,6 @@ LL |         return Some(y);
 ...
 LL |     println!("{:?}", x.data);
    |                      ^^^^^^ immutable borrow occurs here
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0499]: cannot borrow `*vec` as mutable more than once at a time
   --> $DIR/nll-problem-case-3-issue-21906.rs:77:9
diff --git a/tests/ui/on-unimplemented/no-debug.stderr b/tests/ui/on-unimplemented/no-debug.stderr
index 1e6fa7d52fa0..3c3b8d2e2054 100644
--- a/tests/ui/on-unimplemented/no-debug.stderr
+++ b/tests/ui/on-unimplemented/no-debug.stderr
@@ -8,7 +8,6 @@ LL |     println!("{:?} {:?}", Foo, Bar);
    |
    = help: the trait `Debug` is not implemented for `Foo`
    = note: add `#[derive(Debug)]` to `Foo` or manually `impl Debug for Foo`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `Foo` with `#[derive(Debug)]`
    |
 LL + #[derive(Debug)]
@@ -24,7 +23,6 @@ LL |     println!("{:?} {:?}", Foo, Bar);
    |                    required by this formatting parameter
    |
    = help: the trait `Debug` is not implemented for `Bar`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `Foo` doesn't implement `std::fmt::Display`
   --> $DIR/no-debug.rs:11:23
@@ -40,7 +38,6 @@ help: the trait `std::fmt::Display` is not implemented for `Foo`
 LL | struct Foo;
    | ^^^^^^^^^^
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `Bar` doesn't implement `std::fmt::Display`
   --> $DIR/no-debug.rs:11:28
@@ -52,7 +49,6 @@ LL |     println!("{} {}", Foo, Bar);
    |
    = help: the trait `std::fmt::Display` is not implemented for `Bar`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/pattern/deref-patterns/ice-adjust-mode-unimplemented-for-constblock.stderr b/tests/ui/pattern/deref-patterns/ice-adjust-mode-unimplemented-for-constblock.stderr
index 43b0ad18a79b..40615633ea48 100644
--- a/tests/ui/pattern/deref-patterns/ice-adjust-mode-unimplemented-for-constblock.stderr
+++ b/tests/ui/pattern/deref-patterns/ice-adjust-mode-unimplemented-for-constblock.stderr
@@ -5,7 +5,6 @@ LL |     let vec![const { vec![] }]: Vec = vec![];
    |         ^^^^^^^^^^^^^^^^^^^^^^ not a tuple struct or tuple variant
    |
    = note: function calls are not allowed in patterns: 
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0658]: usage of qualified paths in this context is experimental
   --> $DIR/ice-adjust-mode-unimplemented-for-constblock.rs:5:9
@@ -16,7 +15,6 @@ LL |     let vec![const { vec![] }]: Vec = vec![];
    = note: see issue #86935  for more information
    = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: arbitrary expressions aren't allowed in patterns
   --> $DIR/ice-adjust-mode-unimplemented-for-constblock.rs:5:14
@@ -33,7 +31,6 @@ LL |     let vec![const { vec![] }]: Vec = vec![];
    |         ^^^^^^^^^^^^^^^^^^^^^^ `fn` calls are not allowed in patterns
    |
    = help: for more information, visit https://doc.rust-lang.org/book/ch19-00-patterns.html
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr
index 4ecc6370d3ca..347ec0a7743e 100644
--- a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr
+++ b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr
@@ -10,7 +10,6 @@ LL |     stuff(phantom_pinned)
    |           -------------- borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value
-   = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0716]: temporary value dropped while borrowed
   --> $DIR/lifetime_errors_on_promotion_misusage.rs:18:30
@@ -24,7 +23,6 @@ LL |     };
    |     - temporary value is freed at the end of this statement
    |
    = note: consider using a `let` binding to create a longer lived value
-   = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/reachable/expr_again.stderr b/tests/ui/reachable/expr_again.stderr
index 5dec512ba5de..2e00fdc7b431 100644
--- a/tests/ui/reachable/expr_again.stderr
+++ b/tests/ui/reachable/expr_again.stderr
@@ -11,7 +11,6 @@ note: the lint level is defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/reachable/expr_block.stderr b/tests/ui/reachable/expr_block.stderr
index d5f248a24910..aaca4053f27f 100644
--- a/tests/ui/reachable/expr_block.stderr
+++ b/tests/ui/reachable/expr_block.stderr
@@ -19,8 +19,6 @@ LL |         return;
    |         ------ any code following this expression is unreachable
 LL |         println!("foo");
    |         ^^^^^^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/reachable/expr_if.stderr b/tests/ui/reachable/expr_if.stderr
index ebd0b5a3ebef..662692ed6f28 100644
--- a/tests/ui/reachable/expr_if.stderr
+++ b/tests/ui/reachable/expr_if.stderr
@@ -23,8 +23,6 @@ LL |         return;
 ...
 LL |     println!("But I am.");
    |     ^^^^^^^^^^^^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/reachable/expr_loop.stderr b/tests/ui/reachable/expr_loop.stderr
index 918584686050..83b8d024c4fc 100644
--- a/tests/ui/reachable/expr_loop.stderr
+++ b/tests/ui/reachable/expr_loop.stderr
@@ -11,7 +11,6 @@ note: the lint level is defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable statement
   --> $DIR/expr_loop.rs:21:5
@@ -20,8 +19,6 @@ LL |     loop { return; }
    |            ------ any code following this expression is unreachable
 LL |     println!("I am dead.");
    |     ^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable statement
   --> $DIR/expr_loop.rs:32:5
@@ -30,8 +27,6 @@ LL |     loop { 'middle: loop { loop { break 'middle; } } }
    |     -------------------------------------------------- any code following this expression is unreachable
 LL |     println!("I am dead.");
    |     ^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/reachable/expr_match.stderr b/tests/ui/reachable/expr_match.stderr
index ae202a6e0c34..92f6d6758d99 100644
--- a/tests/ui/reachable/expr_match.stderr
+++ b/tests/ui/reachable/expr_match.stderr
@@ -11,7 +11,6 @@ note: the lint level is defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable statement
   --> $DIR/expr_match.rs:19:5
@@ -20,8 +19,6 @@ LL |     match () { () if false => return, () => return }
    |     ------------------------------------------------ any code following this `match` expression is unreachable, as all arms diverge
 LL |     println!("I am dead");
    |     ^^^^^^^^^^^^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable expression
   --> $DIR/expr_match.rs:25:25
@@ -42,8 +39,6 @@ LL | |     }
    | |_____- any code following this `match` expression is unreachable, as all arms diverge
 LL |       println!("I am dead");
    |       ^^^^^^^^^^^^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/reachable/unreachable-code-ret.stderr b/tests/ui/reachable/unreachable-code-ret.stderr
index d86def536df8..f51273eb4207 100644
--- a/tests/ui/reachable/unreachable-code-ret.stderr
+++ b/tests/ui/reachable/unreachable-code-ret.stderr
@@ -11,7 +11,6 @@ note: the lint level is defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/resolve/underscore-bindings-disambiguators.stderr b/tests/ui/resolve/underscore-bindings-disambiguators.stderr
index ec14ede03e3f..9208b84c43a2 100644
--- a/tests/ui/resolve/underscore-bindings-disambiguators.stderr
+++ b/tests/ui/resolve/underscore-bindings-disambiguators.stderr
@@ -21,48 +21,36 @@ error[E0080]: evaluation panicked: not yet implemented
    |
 LL |     const _: () = todo!();
    |                   ^^^^^^^ evaluation of `impls::_` failed here
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: not yet implemented
   --> $DIR/underscore-bindings-disambiguators.rs:20:19
    |
 LL |     const _: () = todo!();
    |                   ^^^^^^^ evaluation of `impls::_` failed here
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: not yet implemented
   --> $DIR/underscore-bindings-disambiguators.rs:21:19
    |
 LL |     const _: () = todo!();
    |                   ^^^^^^^ evaluation of `impls::_` failed here
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: not yet implemented
   --> $DIR/underscore-bindings-disambiguators.rs:22:19
    |
 LL |     const _: () = todo!();
    |                   ^^^^^^^ evaluation of `impls::_` failed here
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: not yet implemented
   --> $DIR/underscore-bindings-disambiguators.rs:23:19
    |
 LL |     const _: () = todo!();
    |                   ^^^^^^^ evaluation of `impls::_` failed here
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0080]: evaluation panicked: not yet implemented
   --> $DIR/underscore-bindings-disambiguators.rs:28:15
    |
 LL | const _: () = todo!();
    |               ^^^^^^^ evaluation of `_` failed here
-   |
-   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 8 previous errors
 
diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr
index c33a5855d506..a55d540dc7f2 100644
--- a/tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr
+++ b/tests/ui/rfcs/rfc-0000-never_patterns/diverge-causes-unreachable-code.stderr
@@ -11,7 +11,6 @@ note: the lint level is defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable statement
   --> $DIR/diverge-causes-unreachable-code.rs:16:5
@@ -20,8 +19,6 @@ LL | fn ref_never_arg(&!: &Void) -> u32 {
    |                  -- any code following a never pattern is unreachable
 LL |     println!();
    |     ^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable statement
   --> $DIR/diverge-causes-unreachable-code.rs:25:5
@@ -31,8 +28,6 @@ LL |         let ! = *ptr;
 LL |     }
 LL |     println!();
    |     ^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable statement
   --> $DIR/diverge-causes-unreachable-code.rs:34:5
@@ -42,8 +37,6 @@ LL |         match *ptr { ! };
 LL |     }
 LL |     println!();
    |     ^^^^^^^^^^ unreachable statement
-   |
-   = note: this error originates in the macro `$crate::print` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr
index fdf5115303ba..f8ef315b9cc7 100644
--- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr
+++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr
@@ -8,7 +8,6 @@ LL |     let _ = dbg!(a);
 LL |     let _ = dbg!(a);
    |             ^^^^^^^ value used here after move
    |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider borrowing instead of transferring ownership
    |
 LL |     let _ = dbg!(&a);
diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr
index 2c4ce2676b07..4cdeb184bd91 100644
--- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr
+++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr
@@ -5,7 +5,6 @@ LL |     let _: NotDebug = dbg!(NotDebug);
    |                       ^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `NotDebug`
    |
    = note: add `#[derive(Debug)]` to `NotDebug` or manually `impl Debug for NotDebug`
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider annotating `NotDebug` with `#[derive(Debug)]`
    |
 LL + #[derive(Debug)]
diff --git a/tests/ui/span/coerce-suggestions.stderr b/tests/ui/span/coerce-suggestions.stderr
index 77b01ee08b79..d0f76a23edc4 100644
--- a/tests/ui/span/coerce-suggestions.stderr
+++ b/tests/ui/span/coerce-suggestions.stderr
@@ -56,8 +56,6 @@ error[E0308]: mismatched types
    |
 LL |     s = format!("foo");
    |         ^^^^^^^^^^^^^^ expected `&mut String`, found `String`
-   |
-   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/span/issue-33884.stderr b/tests/ui/span/issue-33884.stderr
index 29490d86fffe..a5c3e9fa7c45 100644
--- a/tests/ui/span/issue-33884.stderr
+++ b/tests/ui/span/issue-33884.stderr
@@ -3,8 +3,6 @@ error[E0308]: mismatched types
    |
 LL |     stream.write_fmt(format!("message received"))
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Arguments<'_>`, found `String`
-   |
-   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/span/issue-39698.stderr b/tests/ui/span/issue-39698.stderr
index eb18969c3c0d..dd57fa061866 100644
--- a/tests/ui/span/issue-39698.stderr
+++ b/tests/ui/span/issue-39698.stderr
@@ -71,8 +71,6 @@ LL |         T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}
    |               |                                      binding initialized here in some conditions
    |               binding initialized here in some conditions
    |               binding declared here but left uninitialized
-   |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/span/slice-borrow.stderr b/tests/ui/span/slice-borrow.stderr
index 48ac20feea72..6d37019e91b4 100644
--- a/tests/ui/span/slice-borrow.stderr
+++ b/tests/ui/span/slice-borrow.stderr
@@ -10,7 +10,6 @@ LL |     y.use_ref();
    |     - borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/statics/check-values-constraints.stderr b/tests/ui/statics/check-values-constraints.stderr
index c54f4830533a..082dd3490603 100644
--- a/tests/ui/statics/check-values-constraints.stderr
+++ b/tests/ui/statics/check-values-constraints.stderr
@@ -16,8 +16,6 @@ error[E0010]: allocations are not allowed in statics
    |
 LL | static STATIC11: Vec = vec![MyOwned];
    |                                 ^^^^^^^^^^^^^ allocation not allowed in statics
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in statics
   --> $DIR/check-values-constraints.rs:81:33
@@ -27,7 +25,6 @@ LL | static STATIC11: Vec = vec![MyOwned];
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `::to_string` in statics
   --> $DIR/check-values-constraints.rs:92:38
@@ -50,8 +47,6 @@ error[E0010]: allocations are not allowed in statics
    |
 LL |     vec![MyOwned],
    |     ^^^^^^^^^^^^^ allocation not allowed in statics
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in statics
   --> $DIR/check-values-constraints.rs:96:5
@@ -61,15 +56,12 @@ LL |     vec![MyOwned],
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0010]: allocations are not allowed in statics
   --> $DIR/check-values-constraints.rs:98:5
    |
 LL |     vec![MyOwned],
    |     ^^^^^^^^^^^^^ allocation not allowed in statics
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in statics
   --> $DIR/check-values-constraints.rs:98:5
@@ -79,15 +71,12 @@ LL |     vec![MyOwned],
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0010]: allocations are not allowed in statics
   --> $DIR/check-values-constraints.rs:103:6
    |
 LL |     &vec![MyOwned],
    |      ^^^^^^^^^^^^^ allocation not allowed in statics
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in statics
   --> $DIR/check-values-constraints.rs:103:6
@@ -97,15 +86,12 @@ LL |     &vec![MyOwned],
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0010]: allocations are not allowed in statics
   --> $DIR/check-values-constraints.rs:105:6
    |
 LL |     &vec![MyOwned],
    |      ^^^^^^^^^^^^^ allocation not allowed in statics
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in statics
   --> $DIR/check-values-constraints.rs:105:6
@@ -115,15 +101,12 @@ LL |     &vec![MyOwned],
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0010]: allocations are not allowed in statics
   --> $DIR/check-values-constraints.rs:111:31
    |
 LL | static STATIC19: Vec = vec![3];
    |                               ^^^^^^^ allocation not allowed in statics
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in statics
   --> $DIR/check-values-constraints.rs:111:31
@@ -133,15 +116,12 @@ LL | static STATIC19: Vec = vec![3];
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0010]: allocations are not allowed in statics
   --> $DIR/check-values-constraints.rs:117:32
    |
 LL |         static x: Vec = vec![3];
    |                                ^^^^^^^ allocation not allowed in statics
-   |
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0015]: cannot call non-const method `slice::::into_vec::` in statics
   --> $DIR/check-values-constraints.rs:117:32
@@ -151,7 +131,6 @@ LL |         static x: Vec = vec![3];
    |
    = note: calls in statics are limited to constant functions, tuple structs and tuple variants
    = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`
-   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0507]: cannot move out of static item `x`
   --> $DIR/check-values-constraints.rs:119:9
diff --git a/tests/ui/suggestions/bound-suggestions.stderr b/tests/ui/suggestions/bound-suggestions.stderr
index ec1d23fac458..ba792578a202 100644
--- a/tests/ui/suggestions/bound-suggestions.stderr
+++ b/tests/ui/suggestions/bound-suggestions.stderr
@@ -6,7 +6,6 @@ LL |     println!("{:?}", t);
    |               |
    |               required by this formatting parameter
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider restricting opaque type `impl Sized` with trait `Debug`
    |
 LL | fn test_impl(t: impl Sized + std::fmt::Debug) {
@@ -20,7 +19,6 @@ LL |     println!("{:?}", t);
    |               |
    |               required by this formatting parameter
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider restricting type parameter `T` with trait `Debug`
    |
 LL | fn test_no_bounds(t: T) {
@@ -34,7 +32,6 @@ LL |     println!("{:?}", t);
    |               |
    |               required by this formatting parameter
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting type parameter `T` with trait `Debug`
    |
 LL | fn test_one_bound(t: T) {
@@ -48,7 +45,6 @@ LL |     println!("{:?} {:?}", x, y);
    |                    |
    |                    required by this formatting parameter
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting type parameter `Y` with trait `Debug`
    |
 LL | fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt::Debug {
@@ -62,7 +58,6 @@ LL |     println!("{:?}", x);
    |               |
    |               required by this formatting parameter
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting type parameter `X` with trait `Debug`
    |
 LL | fn test_one_bound_where(x: X) where X: Sized + std::fmt::Debug {
@@ -76,7 +71,6 @@ LL |     println!("{:?}", x);
    |               |
    |               required by this formatting parameter
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider further restricting type parameter `X` with trait `Debug`
    |
 LL | fn test_many_bounds_where(x: X) where X: Sized + std::fmt::Debug, X: Sized {
diff --git a/tests/ui/suggestions/issue-97760.stderr b/tests/ui/suggestions/issue-97760.stderr
index c3cf7e13987b..f3dc3f74efe8 100644
--- a/tests/ui/suggestions/issue-97760.stderr
+++ b/tests/ui/suggestions/issue-97760.stderr
@@ -6,7 +6,6 @@ LL |         println!("{x}");
    |
    = help: the trait `std::fmt::Display` is not implemented for `::Item`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: introduce a type parameter with a trait bound instead of using `impl Trait`
    |
 LL ~ pub fn print_values(values: &I)
diff --git a/tests/ui/suggestions/path-display.stderr b/tests/ui/suggestions/path-display.stderr
index 0c7271b3c1c3..df9e855bb714 100644
--- a/tests/ui/suggestions/path-display.stderr
+++ b/tests/ui/suggestions/path-display.stderr
@@ -10,7 +10,6 @@ LL |     println!("{}", path);
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data
    = note: required for `&Path` to implement `std::fmt::Display`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `PathBuf` doesn't implement `std::fmt::Display`
   --> $DIR/path-display.rs:9:20
@@ -23,7 +22,6 @@ LL |     println!("{}", path);
    = help: the trait `std::fmt::Display` is not implemented for `PathBuf`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/thread-local/no-unstable.stderr b/tests/ui/thread-local/no-unstable.stderr
index fbcd804d9178..438020d00b7d 100644
--- a/tests/ui/thread-local/no-unstable.stderr
+++ b/tests/ui/thread-local/no-unstable.stderr
@@ -10,7 +10,6 @@ LL | | }
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
    = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable
    = note: the `#[rustc_dummy]` attribute is used for rustc unit tests
-   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0658]: use of an internal attribute
   --> $DIR/no-unstable.rs:1:1
@@ -24,7 +23,6 @@ LL | | }
    = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
    = note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable
    = note: the `#[rustc_dummy]` attribute is used for rustc unit tests
-   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0658]: `#[used(linker)]` is currently unstable
   --> $DIR/no-unstable.rs:1:1
@@ -38,7 +36,6 @@ LL | | }
    = note: see issue #93798  for more information
    = help: add `#![feature(used_with_arg)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: `#[used]` attribute cannot be used on constants
   --> $DIR/no-unstable.rs:1:1
@@ -50,7 +47,6 @@ LL | | }
    | |_^
    |
    = help: `#[used]` can only be applied to statics
-   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/traits/const-traits/issue-79450.stderr b/tests/ui/traits/const-traits/issue-79450.stderr
index 702e93a76a8f..82d5ea34582f 100644
--- a/tests/ui/traits/const-traits/issue-79450.stderr
+++ b/tests/ui/traits/const-traits/issue-79450.stderr
@@ -7,7 +7,6 @@ LL |         println!("lul");
 note: function `_print` is not const
   --> $SRC_DIR/std/src/io/stdio.rs:LL:COL
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-   = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/try-block/try-block-maybe-bad-lifetime.stderr b/tests/ui/try-block/try-block-maybe-bad-lifetime.stderr
index 71c7e460c399..7fe151021976 100644
--- a/tests/ui/try-block/try-block-maybe-bad-lifetime.stderr
+++ b/tests/ui/try-block/try-block-maybe-bad-lifetime.stderr
@@ -22,7 +22,6 @@ LL |         };
 LL |         println!("{}", x);
    |                        ^ value borrowed here after move
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider cloning the value if the performance cost is acceptable
    |
 LL |             ::std::mem::drop(x.clone());
diff --git a/tests/ui/try-block/try-block-opt-init.stderr b/tests/ui/try-block/try-block-opt-init.stderr
index 1679fc2ac18c..b838af5d53b9 100644
--- a/tests/ui/try-block/try-block-opt-init.stderr
+++ b/tests/ui/try-block/try-block-opt-init.stderr
@@ -9,8 +9,6 @@ LL |         cfg_res = 5;
 ...
 LL |     assert_eq!(cfg_res, 5);
    |     ^^^^^^^^^^^^^^^^^^^^^^ `cfg_res` used here but it is possibly-uninitialized
-   |
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.edition2024.stderr b/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.edition2024.stderr
index a7135e8f05f4..da2099c1ed27 100644
--- a/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.edition2024.stderr
+++ b/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.edition2024.stderr
@@ -35,7 +35,6 @@ note: this call may capture more lifetimes than intended, because Rust 2024 has
    |
 LL |     let mut thing = test(&mut z);
    |                     ^^^^^^^^^^^^
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: use the precise capturing `use<...>` syntax to make the captures explicit
    |
 LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + use<> {
@@ -57,7 +56,6 @@ note: this call may capture more lifetimes than intended, because Rust 2024 has
    |
 LL |     let mut thing = test(&mut z);
    |                     ^^^^^^^^^^^^
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: use the precise capturing `use<...>` syntax to make the captures explicit
    |
 LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + use<> {
@@ -79,7 +77,6 @@ note: this call may capture more lifetimes than intended, because Rust 2024 has
    |
 LL |     let mut thing = test(&mut z);
    |                     ^^^^^^^^^^^^
-   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: use the precise capturing `use<...>` syntax to make the captures explicit
    |
 LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne + use<> {
diff --git a/tests/ui/type-alias-impl-trait/nested.stderr b/tests/ui/type-alias-impl-trait/nested.stderr
index f72830b864d1..9ac0fe5302be 100644
--- a/tests/ui/type-alias-impl-trait/nested.stderr
+++ b/tests/ui/type-alias-impl-trait/nested.stderr
@@ -20,7 +20,6 @@ LL |     println!("{:?}", bar());
    |               required by this formatting parameter
    |
    = help: the trait `Debug` is not implemented for `Bar`
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/type/binding-assigned-block-without-tail-expression.stderr b/tests/ui/type/binding-assigned-block-without-tail-expression.stderr
index ff34facf3892..ed7ff22e501c 100644
--- a/tests/ui/type/binding-assigned-block-without-tail-expression.stderr
+++ b/tests/ui/type/binding-assigned-block-without-tail-expression.stderr
@@ -11,7 +11,6 @@ LL |     println!("{}", x);
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `()` doesn't implement `std::fmt::Display`
   --> $DIR/binding-assigned-block-without-tail-expression.rs:15:20
@@ -26,7 +25,6 @@ LL |     println!("{}", y);
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `()` doesn't implement `std::fmt::Display`
   --> $DIR/binding-assigned-block-without-tail-expression.rs:16:20
@@ -41,7 +39,6 @@ LL |     println!("{}", z);
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `()` doesn't implement `std::fmt::Display`
   --> $DIR/binding-assigned-block-without-tail-expression.rs:17:20
@@ -59,7 +56,6 @@ LL |       println!("{}", s);
    |
    = help: the trait `std::fmt::Display` is not implemented for `()`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
   --> $DIR/binding-assigned-block-without-tail-expression.rs:18:18
diff --git a/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr b/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr
index 0907489f8e8a..b4bf0e00cfe2 100644
--- a/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr
+++ b/tests/ui/typeck/closure-ty-mismatch-issue-128561.stderr
@@ -14,8 +14,6 @@ error[E0308]: mismatched types
    |
 LL |     b"abc".iter().for_each(|x| dbg!(x));
    |                                ^^^^^^^ expected `()`, found `&u8`
-   |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
   --> $DIR/closure-ty-mismatch-issue-128561.rs:8:9
diff --git a/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr
index bc722cdd57a5..efd8d6e2686c 100644
--- a/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr
+++ b/tests/ui/typeck/issue-110017-format-into-help-deletes-macro.stderr
@@ -6,7 +6,6 @@ LL |      Err(format!("error: {x}"))
    |
    = note: expected struct `Box`
               found struct `String`
-   = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: call `Into::into` on this expression to convert `String` into `Box`
    |
 LL |      Err(format!("error: {x}").into())
diff --git a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr
index 30d51420b7cb..cc5a4af88064 100644
--- a/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr
+++ b/tests/ui/typeck/issue-112007-leaked-writeln-macro-internals.stderr
@@ -12,7 +12,6 @@ LL | |     }
    |
    = note: expected unit type `()`
                    found enum `Result<(), std::fmt::Error>`
-   = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider using a semicolon here
    |
 LL |     };
diff --git a/tests/ui/typeck/question-mark-operator-suggestion-span.stderr b/tests/ui/typeck/question-mark-operator-suggestion-span.stderr
index 089b3bcd1988..f567e553d8b7 100644
--- a/tests/ui/typeck/question-mark-operator-suggestion-span.stderr
+++ b/tests/ui/typeck/question-mark-operator-suggestion-span.stderr
@@ -12,7 +12,6 @@ LL | |     }
    |
    = note: expected unit type `()`
                    found enum `Result<(), std::fmt::Error>`
-   = note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider using a semicolon here
    |
 LL |     };
diff --git a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr
index 972c2ced0037..6785d48eca60 100644
--- a/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr
+++ b/tests/ui/typeck/suggestions/suggest-clone-in-macro-issue-139253.stderr
@@ -26,7 +26,6 @@ error[E0308]: mismatched types
 LL |     let c: S = dbg!(field);
    |                ^^^^^^^^^^^ expected `S`, found `&S`
    |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider using clone here
    |
 LL |     let c: S = dbg!(field).clone();
@@ -38,7 +37,6 @@ error[E0308]: mismatched types
 LL |     let c: S = dbg!(dbg!(field));
    |                ^^^^^^^^^^^^^^^^^ expected `S`, found `&S`
    |
-   = note: this error originates in the macro `$crate::macros::dbg_internal` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider using clone here
    |
 LL |     let c: S = dbg!(dbg!(field)).clone();
diff --git a/tests/ui/uninhabited/void-branch.stderr b/tests/ui/uninhabited/void-branch.stderr
index ee5efb94ed21..15693fc85f4b 100644
--- a/tests/ui/uninhabited/void-branch.stderr
+++ b/tests/ui/uninhabited/void-branch.stderr
@@ -16,7 +16,6 @@ note: the lint level is defined here
    |
 LL | #![deny(unreachable_code)]
    |         ^^^^^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unreachable expression
   --> $DIR/void-branch.rs:25:9
@@ -31,7 +30,6 @@ note: this expression has type `Infallible`, which is uninhabited
    |
 LL |         infallible();
    |         ^^^^^^^^^^^^
-   = note: this error originates in the macro `$crate::format_args` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/use/use-after-move-based-on-type.stderr b/tests/ui/use/use-after-move-based-on-type.stderr
index 02a6ed599a92..1e72b3a1e95a 100644
--- a/tests/ui/use/use-after-move-based-on-type.stderr
+++ b/tests/ui/use/use-after-move-based-on-type.stderr
@@ -8,7 +8,6 @@ LL |     let _y = x;
 LL |     println!("{}", x);
    |                    ^ value borrowed here after move
    |
-   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider cloning the value if the performance cost is acceptable
    |
 LL |     let _y = x.clone();

From 7b6f82eea6d4a34cf61e85516899b58551b58193 Mon Sep 17 00:00:00 2001
From: Folkert de Vries 
Date: Mon, 26 Jan 2026 18:23:05 +0100
Subject: [PATCH 248/583] 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 2dba5c83e112..5b3a689f3611 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 ed4d7e8a801e..639d07df7337 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 cd18a8d287d1a42f358d007ab72ec534b4cedd5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= 
Date: Wed, 12 Mar 2025 12:09:19 +0100
Subject: [PATCH 249/583] Tweak `VecCache` to improve performance

---
 .../rustc_data_structures/src/vec_cache.rs    | 72 +++++++++++++------
 .../src/vec_cache/tests.rs                    | 15 +++-
 2 files changed, 63 insertions(+), 24 deletions(-)

diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs
index 599970663db8..70524bae624a 100644
--- a/compiler/rustc_data_structures/src/vec_cache.rs
+++ b/compiler/rustc_data_structures/src/vec_cache.rs
@@ -29,8 +29,6 @@ struct Slot {
 struct SlotIndex {
     // the index of the bucket in VecCache (0 to 20)
     bucket_idx: usize,
-    // number of entries in that bucket
-    entries: usize,
     // the index of the slot within the bucket
     index_in_bucket: usize,
 }
@@ -39,12 +37,12 @@ struct SlotIndex {
 // compile-time. Visiting all powers of two is enough to hit all the buckets.
 //
 // We confirm counts are accurate in the slot_index_exhaustive test.
-const ENTRIES_BY_BUCKET: [usize; 21] = {
-    let mut entries = [0; 21];
+const ENTRIES_BY_BUCKET: [usize; BUCKETS] = {
+    let mut entries = [0; BUCKETS];
     let mut key = 0;
     loop {
         let si = SlotIndex::from_index(key);
-        entries[si.bucket_idx] = si.entries;
+        entries[si.bucket_idx] = si.entries();
         if key == 0 {
             key = 1;
         } else if key == (1 << 31) {
@@ -56,7 +54,14 @@ const ENTRIES_BY_BUCKET: [usize; 21] = {
     entries
 };
 
+const BUCKETS: usize = 21;
+
 impl SlotIndex {
+    /// The total possible number of entries in the bucket
+    const fn entries(&self) -> usize {
+        if self.bucket_idx == 0 { 1 << 12 } else { 1 << (self.bucket_idx + 11) }
+    }
+
     // This unpacks a flat u32 index into identifying which bucket it belongs to and the offset
     // within that bucket. As noted in the VecCache docs, buckets double in size with each index.
     // Typically that would mean 31 buckets (2^0 + 2^1 ... + 2^31 = u32::MAX - 1), but to reduce
@@ -70,18 +75,13 @@ impl SlotIndex {
     const fn from_index(idx: u32) -> Self {
         const FIRST_BUCKET_SHIFT: usize = 12;
         if idx < (1 << FIRST_BUCKET_SHIFT) {
-            return SlotIndex {
-                bucket_idx: 0,
-                entries: 1 << FIRST_BUCKET_SHIFT,
-                index_in_bucket: idx as usize,
-            };
+            return SlotIndex { bucket_idx: 0, index_in_bucket: idx as usize };
         }
         // We already ruled out idx 0, so this `ilog2` never panics (and the check optimizes away)
         let bucket = idx.ilog2() as usize;
         let entries = 1 << bucket;
         SlotIndex {
             bucket_idx: bucket - FIRST_BUCKET_SHIFT + 1,
-            entries,
             index_in_bucket: idx as usize - entries,
         }
     }
@@ -98,7 +98,7 @@ impl SlotIndex {
         if ptr.is_null() {
             return None;
         }
-        assert!(self.index_in_bucket < self.entries);
+        debug_assert!(self.index_in_bucket < self.entries());
         // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this
         // must be inbounds.
         let slot = unsafe { ptr.add(self.index_in_bucket) };
@@ -126,11 +126,12 @@ impl SlotIndex {
 
     fn bucket_ptr(&self, bucket: &AtomicPtr>) -> *mut Slot {
         let ptr = bucket.load(Ordering::Acquire);
-        if ptr.is_null() { self.initialize_bucket(bucket) } else { ptr }
+        if ptr.is_null() { Self::initialize_bucket(bucket, self.bucket_idx) } else { ptr }
     }
 
     #[cold]
-    fn initialize_bucket(&self, bucket: &AtomicPtr>) -> *mut Slot {
+    #[inline(never)]
+    fn initialize_bucket(bucket: &AtomicPtr>, bucket_idx: usize) -> *mut Slot {
         static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
 
         // If we are initializing the bucket, then acquire a global lock.
@@ -144,8 +145,8 @@ impl SlotIndex {
         // OK, now under the allocator lock, if we're still null then it's definitely us that will
         // initialize this bucket.
         if ptr.is_null() {
-            let bucket_layout =
-                std::alloc::Layout::array::>(self.entries as usize).unwrap();
+            let bucket_len = SlotIndex { bucket_idx, index_in_bucket: 0 }.entries();
+            let bucket_layout = std::alloc::Layout::array::>(bucket_len).unwrap();
             // This is more of a sanity check -- this code is very cold, so it's safe to pay a
             // little extra cost here.
             assert!(bucket_layout.size() > 0);
@@ -171,7 +172,7 @@ impl SlotIndex {
         let bucket = unsafe { buckets.get_unchecked(self.bucket_idx) };
         let ptr = self.bucket_ptr(bucket);
 
-        assert!(self.index_in_bucket < self.entries);
+        debug_assert!(self.index_in_bucket < self.entries());
         // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this
         // must be inbounds.
         let slot = unsafe { ptr.add(self.index_in_bucket) };
@@ -204,6 +205,31 @@ impl SlotIndex {
             Err(_) => false,
         }
     }
+
+    /// Inserts into the map, given that the slot is unique, so it won't race with other threads.
+    #[inline]
+    unsafe fn put_unique(&self, buckets: &[AtomicPtr>; 21], value: V, extra: u32) {
+        // SAFETY: `bucket_idx` is ilog2(u32).saturating_sub(11), which is at most 21, i.e.,
+        // in-bounds of buckets.
+        let bucket = unsafe { buckets.get_unchecked(self.bucket_idx) };
+        let ptr = self.bucket_ptr(bucket);
+
+        debug_assert!(self.index_in_bucket < self.entries());
+        // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this
+        // must be inbounds.
+        let slot = unsafe { ptr.add(self.index_in_bucket) };
+
+        // SAFETY: We known our slot is unique as a precondition of this function, so this can't race.
+        unsafe {
+            (&raw mut (*slot).value).write(value);
+        }
+
+        // SAFETY: initialized bucket has zeroed all memory within the bucket, so we are valid for
+        // AtomicU32 access.
+        let index_and_lock = unsafe { &(*slot).index_and_lock };
+
+        index_and_lock.store(extra.checked_add(2).unwrap(), Ordering::Release);
+    }
 }
 
 /// In-memory cache for queries whose keys are densely-numbered IDs
@@ -229,11 +255,11 @@ pub struct VecCache {
     // Bucket 19: 1073741824
     // Bucket 20: 2147483648
     // The total number of entries if all buckets are initialized is u32::MAX-1.
-    buckets: [AtomicPtr>; 21],
+    buckets: [AtomicPtr>; BUCKETS],
 
     // In the compiler's current usage these are only *read* during incremental and self-profiling.
     // They are an optimization over iterating the full buckets array.
-    present: [AtomicPtr>; 21],
+    present: [AtomicPtr>; BUCKETS],
     len: AtomicUsize,
 
     key: PhantomData<(K, I)>,
@@ -307,9 +333,11 @@ where
         let slot_idx = SlotIndex::from_index(key);
         if slot_idx.put(&self.buckets, value, index.index() as u32) {
             let present_idx = self.len.fetch_add(1, Ordering::Relaxed);
-            let slot = SlotIndex::from_index(present_idx as u32);
-            // We should always be uniquely putting due to `len` fetch_add returning unique values.
-            assert!(slot.put(&self.present, (), key));
+            let slot = SlotIndex::from_index(u32::try_from(present_idx).unwrap());
+            // SAFETY: We should always be uniquely putting due to `len` fetch_add returning unique values.
+            // We can't get here if `len` overflows because `put` will not succeed u32::MAX + 1 times
+            // as it will run out of slots.
+            unsafe { slot.put_unique(&self.present, (), key) };
         }
     }
 
diff --git a/compiler/rustc_data_structures/src/vec_cache/tests.rs b/compiler/rustc_data_structures/src/vec_cache/tests.rs
index 9b60913ec922..f588442eee62 100644
--- a/compiler/rustc_data_structures/src/vec_cache/tests.rs
+++ b/compiler/rustc_data_structures/src/vec_cache/tests.rs
@@ -68,6 +68,13 @@ fn slot_entries_table() {
     );
 }
 
+#[test]
+fn bucket_entries_matches() {
+    for i in 0..BUCKETS {
+        assert_eq!(SlotIndex { bucket_idx: i, index_in_bucket: 0 }.entries(), ENTRIES_BY_BUCKET[i]);
+    }
+}
+
 #[test]
 #[cfg(not(miri))]
 fn slot_index_exhaustive() {
@@ -81,14 +88,18 @@ fn slot_index_exhaustive() {
     let mut prev = slot_idx;
     for idx in 1..=u32::MAX {
         let slot_idx = SlotIndex::from_index(idx);
+
+        // SAFETY: Ensure indices don't go out of bounds of buckets.
+        assert!(slot_idx.index_in_bucket < slot_idx.entries());
+
         if prev.bucket_idx == slot_idx.bucket_idx {
             assert_eq!(prev.index_in_bucket + 1, slot_idx.index_in_bucket);
         } else {
             assert_eq!(slot_idx.index_in_bucket, 0);
         }
 
-        assert_eq!(buckets[slot_idx.bucket_idx], slot_idx.entries as u32);
-        assert_eq!(ENTRIES_BY_BUCKET[slot_idx.bucket_idx], slot_idx.entries, "{}", idx);
+        assert_eq!(buckets[slot_idx.bucket_idx], slot_idx.entries() as u32);
+        assert_eq!(ENTRIES_BY_BUCKET[slot_idx.bucket_idx], slot_idx.entries(), "{}", idx);
 
         prev = slot_idx;
     }

From fdad66a382cc53f284a8194e140e42df4b20222d Mon Sep 17 00:00:00 2001
From: Folkert de Vries 
Date: Mon, 26 Jan 2026 23:17:31 +0100
Subject: [PATCH 250/583] enable `target_feature_inline_always` in `core` and
 `std`

this is required for compiling `stdarch`
---
 library/core/src/lib.rs | 1 +
 library/std/src/lib.rs  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index c4d16ba633b8..e8c9d26fb3b5 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -182,6 +182,7 @@
 #![feature(staged_api)]
 #![feature(stmt_expr_attributes)]
 #![feature(strict_provenance_lints)]
+#![feature(target_feature_inline_always)]
 #![feature(trait_alias)]
 #![feature(transparent_unions)]
 #![feature(try_blocks)]
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 8bec157e4e6e..b213fa749177 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -309,6 +309,7 @@
 #![feature(staged_api)]
 #![feature(stmt_expr_attributes)]
 #![feature(strict_provenance_lints)]
+#![feature(target_feature_inline_always)]
 #![feature(thread_local)]
 #![feature(try_blocks)]
 #![feature(try_trait_v2)]

From 0385e26e7d1e6a984954d1daa448471e9ef1051e Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Tue, 20 Jan 2026 11:56:19 +1100
Subject: [PATCH 251/583] Reintroduce `QueryStackFrame` split.

I tried removing it in #151203, to replace it with something simpler.
But a couple of fuzzing failures have come up and I don't have a clear
picture on how to fix them. So I'm reverting the main part of #151203.

This commit also adds the two fuzzing tests.

Fixes #151226, #151358.
---
 compiler/rustc_middle/src/query/mod.rs        |   2 +-
 compiler/rustc_middle/src/query/plumbing.rs   |   2 +-
 compiler/rustc_middle/src/ty/print/pretty.rs  |   4 +-
 compiler/rustc_middle/src/values.rs           |   4 +-
 compiler/rustc_query_impl/src/lib.rs          |   9 +-
 compiler/rustc_query_impl/src/plumbing.rs     |  96 ++++++++-----
 .../rustc_query_system/src/query/config.rs    |   5 +-
 compiler/rustc_query_system/src/query/job.rs  | 131 ++++++++++--------
 compiler/rustc_query_system/src/query/mod.rs  | 105 ++++++++++++--
 .../rustc_query_system/src/query/plumbing.rs  |  61 ++++----
 .../query-cycle-printing-issue-151226.rs      |   8 ++
 .../query-cycle-printing-issue-151226.stderr  |  36 +++++
 .../query-cycle-printing-issue-151358.rs      |   7 +
 .../query-cycle-printing-issue-151358.stderr  |   9 ++
 tests/ui/resolve/query-cycle-issue-124901.rs  |   2 +-
 .../resolve/query-cycle-issue-124901.stderr   |   9 +-
 16 files changed, 341 insertions(+), 149 deletions(-)
 create mode 100644 tests/ui/query-system/query-cycle-printing-issue-151226.rs
 create mode 100644 tests/ui/query-system/query-cycle-printing-issue-151226.stderr
 create mode 100644 tests/ui/query-system/query-cycle-printing-issue-151358.rs
 create mode 100644 tests/ui/query-system/query-cycle-printing-issue-151358.stderr

diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 2f83f3078e89..9d17c998a8f2 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -88,7 +88,7 @@ use rustc_index::IndexVec;
 use rustc_lint_defs::LintId;
 use rustc_macros::rustc_queries;
 use rustc_query_system::ich::StableHashingContext;
-use rustc_query_system::query::{QueryMode, QueryState};
+use rustc_query_system::query::{QueryMode, QueryStackDeferred, QueryState};
 use rustc_session::Limits;
 use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
 use rustc_session::cstore::{
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index 17330f4e14be..9ee8d743e64a 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -427,7 +427,7 @@ macro_rules! define_callbacks {
         #[derive(Default)]
         pub struct QueryStates<'tcx> {
             $(
-                pub $name: QueryState<$($K)*>,
+                pub $name: QueryState<$($K)*, QueryStackDeferred<'tcx>>,
             )*
         }
 
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index de13c4f836a5..76a4f61e6714 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -159,9 +159,7 @@ pub macro with_types_for_signature($e:expr) {{
 /// Avoids running any queries during prints.
 pub macro with_no_queries($e:expr) {{
     $crate::ty::print::with_reduced_queries!($crate::ty::print::with_forced_impl_filename_line!(
-        $crate::ty::print::with_no_trimmed_paths!($crate::ty::print::with_no_visible_paths!(
-            $crate::ty::print::with_forced_impl_filename_line!($e)
-        ))
+        $crate::ty::print::with_no_trimmed_paths!($crate::ty::print::with_no_visible_paths!($e))
     ))
 }}
 
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index 8d614a535498..bc73d36216ef 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -88,7 +88,7 @@ impl<'tcx> Value> for Representability {
             if info.query.dep_kind == dep_kinds::representability
                 && let Some(field_id) = info.query.def_id
                 && let Some(field_id) = field_id.as_local()
-                && let Some(DefKind::Field) = info.query.def_kind
+                && let Some(DefKind::Field) = info.query.info.def_kind
             {
                 let parent_id = tcx.parent(field_id.to_def_id());
                 let item_id = match tcx.def_kind(parent_id) {
@@ -224,7 +224,7 @@ impl<'tcx, T> Value> for Result>
                             continue;
                         };
                         let frame_span =
-                            frame.query.default_span(cycle[(i + 1) % cycle.len()].span);
+                            frame.query.info.default_span(cycle[(i + 1) % cycle.len()].span);
                         if frame_span.is_dummy() {
                             continue;
                         }
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index 57027e937a4a..e8983bfa1ddb 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -23,7 +23,7 @@ use rustc_query_system::dep_graph::SerializedDepNodeIndex;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_query_system::query::{
     CycleError, CycleErrorHandling, HashResult, QueryCache, QueryConfig, QueryMap, QueryMode,
-    QueryState, get_query_incr, get_query_non_incr,
+    QueryStackDeferred, QueryState, get_query_incr, get_query_non_incr,
 };
 use rustc_span::{ErrorGuaranteed, Span};
 
@@ -79,7 +79,10 @@ where
     }
 
     #[inline(always)]
-    fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState
+    fn query_state<'a>(
+        self,
+        qcx: QueryCtxt<'tcx>,
+    ) -> &'a QueryState>
     where
         QueryCtxt<'tcx>: 'a,
     {
@@ -88,7 +91,7 @@ where
         unsafe {
             &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>)
                 .byte_add(self.dynamic.query_state)
-                .cast::>()
+                .cast::>>()
         }
     }
 
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 246152f5390c..847b5f901d7f 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -6,6 +6,7 @@ use std::num::NonZero;
 
 use rustc_data_structures::jobserver::Proxy;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::{DynSend, DynSync};
 use rustc_data_structures::unord::UnordMap;
 use rustc_hashes::Hash64;
 use rustc_hir::limit::Limit;
@@ -26,8 +27,8 @@ use rustc_middle::ty::{self, TyCtxt};
 use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext};
 use rustc_query_system::ich::StableHashingContext;
 use rustc_query_system::query::{
-    QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffect, QueryStackFrame,
-    force_query,
+    QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffect,
+    QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra, force_query,
 };
 use rustc_query_system::{QueryOverflow, QueryOverflowNote};
 use rustc_serialize::{Decodable, Encodable};
@@ -66,7 +67,9 @@ impl<'tcx> HasDepContext for QueryCtxt<'tcx> {
     }
 }
 
-impl QueryContext for QueryCtxt<'_> {
+impl<'tcx> QueryContext for QueryCtxt<'tcx> {
+    type QueryInfo = QueryStackDeferred<'tcx>;
+
     #[inline]
     fn jobserver_proxy(&self) -> &Proxy {
         &*self.jobserver_proxy
@@ -95,7 +98,10 @@ impl QueryContext for QueryCtxt<'_> {
     /// Prefer passing `false` to `require_complete` to avoid potential deadlocks,
     /// especially when called from within a deadlock handler, unless a
     /// complete map is needed and no deadlock is possible at this call site.
-    fn collect_active_jobs(self, require_complete: bool) -> Result {
+    fn collect_active_jobs(
+        self,
+        require_complete: bool,
+    ) -> Result>, QueryMap>> {
         let mut jobs = QueryMap::default();
         let mut complete = true;
 
@@ -108,6 +114,13 @@ impl QueryContext for QueryCtxt<'_> {
         if complete { Ok(jobs) } else { Err(jobs) }
     }
 
+    fn lift_query_info(
+        self,
+        info: &QueryStackDeferred<'tcx>,
+    ) -> rustc_query_system::query::QueryStackFrameExtra {
+        info.extract()
+    }
+
     // Interactions with on_disk_cache
     fn load_side_effect(
         self,
@@ -168,7 +181,10 @@ impl QueryContext for QueryCtxt<'_> {
 
         self.sess.dcx().emit_fatal(QueryOverflow {
             span: info.job.span,
-            note: QueryOverflowNote { desc: info.query.description, depth },
+            note: QueryOverflowNote {
+                desc: self.lift_query_info(&info.query.info).description,
+                depth,
+            },
             suggested_limit,
             crate_name: self.crate_name(LOCAL_CRATE),
         });
@@ -305,16 +321,17 @@ macro_rules! should_ever_cache_on_disk {
     };
 }
 
-pub(crate) fn create_query_frame<
-    'tcx,
-    K: Copy + Key + for<'a> HashStable>,
->(
-    tcx: TyCtxt<'tcx>,
-    do_describe: fn(TyCtxt<'tcx>, K) -> String,
-    key: K,
-    kind: DepKind,
-    name: &'static str,
-) -> QueryStackFrame {
+fn create_query_frame_extra<'tcx, K: Key + Copy + 'tcx>(
+    (tcx, key, kind, name, do_describe): (
+        TyCtxt<'tcx>,
+        K,
+        DepKind,
+        &'static str,
+        fn(TyCtxt<'tcx>, K) -> String,
+    ),
+) -> QueryStackFrameExtra {
+    let def_id = key.key_as_def_id();
+
     // If reduced queries are requested, we may be printing a query stack due
     // to a panic. Avoid using `default_span` and `def_kind` in that case.
     let reduce_queries = with_reduced_queries();
@@ -326,36 +343,49 @@ pub(crate) fn create_query_frame<
     } else {
         description
     };
-
-    let span = if reduce_queries {
+    let span = if kind == dep_graph::dep_kinds::def_span || reduce_queries {
         // The `def_span` query is used to calculate `default_span`,
         // so exit to avoid infinite recursion.
         None
     } else {
-        Some(tcx.with_reduced_queries(|| key.default_span(tcx)))
+        Some(key.default_span(tcx))
     };
 
-    let def_id = key.key_as_def_id();
-
-    let def_kind = if reduce_queries {
+    let def_kind = if kind == dep_graph::dep_kinds::def_kind || reduce_queries {
         // Try to avoid infinite recursion.
         None
     } else {
-        def_id
-            .and_then(|def_id| def_id.as_local())
-            .map(|def_id| tcx.with_reduced_queries(|| tcx.def_kind(def_id)))
+        def_id.and_then(|def_id| def_id.as_local()).map(|def_id| tcx.def_kind(def_id))
     };
+    QueryStackFrameExtra::new(description, span, def_kind)
+}
 
+pub(crate) fn create_query_frame<
+    'tcx,
+    K: Copy + DynSend + DynSync + Key + for<'a> HashStable> + 'tcx,
+>(
+    tcx: TyCtxt<'tcx>,
+    do_describe: fn(TyCtxt<'tcx>, K) -> String,
+    key: K,
+    kind: DepKind,
+    name: &'static str,
+) -> QueryStackFrame> {
+    let def_id = key.key_as_def_id();
+
+    let hash = || {
+        tcx.with_stable_hashing_context(|mut hcx| {
+            let mut hasher = StableHasher::new();
+            kind.as_usize().hash_stable(&mut hcx, &mut hasher);
+            key.hash_stable(&mut hcx, &mut hasher);
+            hasher.finish::()
+        })
+    };
     let def_id_for_ty_in_cycle = key.def_id_for_ty_in_cycle();
 
-    let hash = tcx.with_stable_hashing_context(|mut hcx| {
-        let mut hasher = StableHasher::new();
-        kind.as_usize().hash_stable(&mut hcx, &mut hasher);
-        key.hash_stable(&mut hcx, &mut hasher);
-        hasher.finish::()
-    });
+    let info =
+        QueryStackDeferred::new((tcx, key, kind, name, do_describe), create_query_frame_extra);
 
-    QueryStackFrame::new(description, span, def_id, def_kind, kind, def_id_for_ty_in_cycle, hash)
+    QueryStackFrame::new(info, kind, hash, def_id, def_id_for_ty_in_cycle)
 }
 
 pub(crate) fn encode_query_results<'a, 'tcx, Q>(
@@ -710,7 +740,7 @@ macro_rules! define_queries {
 
             pub(crate) fn collect_active_jobs<'tcx>(
                 tcx: TyCtxt<'tcx>,
-                qmap: &mut QueryMap,
+                qmap: &mut QueryMap>,
                 require_complete: bool,
             ) -> Option<()> {
                 let make_query = |tcx, key| {
@@ -794,7 +824,7 @@ macro_rules! define_queries {
         // These arrays are used for iteration and can't be indexed by `DepKind`.
 
         const COLLECT_ACTIVE_JOBS: &[
-            for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap, bool) -> Option<()>
+            for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap>, bool) -> Option<()>
         ] =
             &[$(query_impl::$name::collect_active_jobs),*];
 
diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs
index 739e8e3a8f26..66b04aa7b467 100644
--- a/compiler/rustc_query_system/src/query/config.rs
+++ b/compiler/rustc_query_system/src/query/config.rs
@@ -6,6 +6,7 @@ use std::hash::Hash;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_span::ErrorGuaranteed;
 
+use super::QueryStackFrameExtra;
 use crate::dep_graph::{DepKind, DepNode, DepNodeParams, SerializedDepNodeIndex};
 use crate::ich::StableHashingContext;
 use crate::query::caches::QueryCache;
@@ -26,7 +27,7 @@ pub trait QueryConfig: Copy {
     fn format_value(self) -> fn(&Self::Value) -> String;
 
     // Don't use this method to access query results, instead use the methods on TyCtxt
-    fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState
+    fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState
     where
         Qcx: 'a;
 
@@ -56,7 +57,7 @@ pub trait QueryConfig: Copy {
     fn value_from_cycle_error(
         self,
         tcx: Qcx::DepContext,
-        cycle_error: &CycleError,
+        cycle_error: &CycleError,
         guar: ErrorGuaranteed,
     ) -> Self::Value;
 
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 0431151c74c9..5810ce0cbe66 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -1,3 +1,4 @@
+use std::fmt::Debug;
 use std::hash::Hash;
 use std::io::Write;
 use std::iter;
@@ -11,6 +12,7 @@ use rustc_hir::def::DefKind;
 use rustc_session::Session;
 use rustc_span::{DUMMY_SP, Span};
 
+use super::QueryStackFrameExtra;
 use crate::dep_graph::DepContext;
 use crate::error::CycleStack;
 use crate::query::plumbing::CycleError;
@@ -18,45 +20,54 @@ use crate::query::{QueryContext, QueryStackFrame};
 
 /// Represents a span and a query key.
 #[derive(Clone, Debug)]
-pub struct QueryInfo {
+pub struct QueryInfo {
     /// The span corresponding to the reason for which this query was required.
     pub span: Span,
-    pub query: QueryStackFrame,
+    pub query: QueryStackFrame,
 }
 
-pub type QueryMap = FxHashMap;
+impl QueryInfo {
+    pub(crate) fn lift>(
+        &self,
+        qcx: Qcx,
+    ) -> QueryInfo {
+        QueryInfo { span: self.span, query: self.query.lift(qcx) }
+    }
+}
+
+pub type QueryMap = FxHashMap>;
 
 /// A value uniquely identifying an active query job.
 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
 pub struct QueryJobId(pub NonZero);
 
 impl QueryJobId {
-    fn query(self, map: &QueryMap) -> QueryStackFrame {
+    fn query(self, map: &QueryMap) -> QueryStackFrame {
         map.get(&self).unwrap().query.clone()
     }
 
-    fn span(self, map: &QueryMap) -> Span {
+    fn span(self, map: &QueryMap) -> Span {
         map.get(&self).unwrap().job.span
     }
 
-    fn parent(self, map: &QueryMap) -> Option {
+    fn parent(self, map: &QueryMap) -> Option {
         map.get(&self).unwrap().job.parent
     }
 
-    fn latch(self, map: &QueryMap) -> Option<&QueryLatch> {
+    fn latch(self, map: &QueryMap) -> Option<&QueryLatch> {
         map.get(&self).unwrap().job.latch.as_ref()
     }
 }
 
 #[derive(Clone, Debug)]
-pub struct QueryJobInfo {
-    pub query: QueryStackFrame,
-    pub job: QueryJob,
+pub struct QueryJobInfo {
+    pub query: QueryStackFrame,
+    pub job: QueryJob,
 }
 
 /// Represents an active query job.
 #[derive(Debug)]
-pub struct QueryJob {
+pub struct QueryJob {
     pub id: QueryJobId,
 
     /// The span corresponding to the reason for which this query was required.
@@ -66,23 +77,23 @@ pub struct QueryJob {
     pub parent: Option,
 
     /// The latch that is used to wait on this job.
-    latch: Option,
+    latch: Option>,
 }
 
-impl Clone for QueryJob {
+impl Clone for QueryJob {
     fn clone(&self) -> Self {
         Self { id: self.id, span: self.span, parent: self.parent, latch: self.latch.clone() }
     }
 }
 
-impl QueryJob {
+impl QueryJob {
     /// Creates a new query job.
     #[inline]
     pub fn new(id: QueryJobId, span: Span, parent: Option) -> Self {
         QueryJob { id, span, parent, latch: None }
     }
 
-    pub(super) fn latch(&mut self) -> QueryLatch {
+    pub(super) fn latch(&mut self) -> QueryLatch {
         if self.latch.is_none() {
             self.latch = Some(QueryLatch::new());
         }
@@ -102,12 +113,12 @@ impl QueryJob {
 }
 
 impl QueryJobId {
-    pub(super) fn find_cycle_in_stack(
+    pub(super) fn find_cycle_in_stack(
         &self,
-        query_map: QueryMap,
+        query_map: QueryMap,
         current_job: &Option,
         span: Span,
-    ) -> CycleError {
+    ) -> CycleError {
         // Find the waitee amongst `current_job` parents
         let mut cycle = Vec::new();
         let mut current_job = Option::clone(current_job);
@@ -141,7 +152,7 @@ impl QueryJobId {
 
     #[cold]
     #[inline(never)]
-    pub fn find_dep_kind_root(&self, query_map: QueryMap) -> (QueryJobInfo, usize) {
+    pub fn find_dep_kind_root(&self, query_map: QueryMap) -> (QueryJobInfo, usize) {
         let mut depth = 1;
         let info = query_map.get(&self).unwrap();
         let dep_kind = info.query.dep_kind;
@@ -161,31 +172,31 @@ impl QueryJobId {
 }
 
 #[derive(Debug)]
-struct QueryWaiter {
+struct QueryWaiter {
     query: Option,
     condvar: Condvar,
     span: Span,
-    cycle: Mutex>,
+    cycle: Mutex>>,
 }
 
 #[derive(Debug)]
-struct QueryLatchInfo {
+struct QueryLatchInfo {
     complete: bool,
-    waiters: Vec>,
+    waiters: Vec>>,
 }
 
 #[derive(Debug)]
-pub(super) struct QueryLatch {
-    info: Arc>,
+pub(super) struct QueryLatch {
+    info: Arc>>,
 }
 
-impl Clone for QueryLatch {
+impl Clone for QueryLatch {
     fn clone(&self) -> Self {
         Self { info: Arc::clone(&self.info) }
     }
 }
 
-impl QueryLatch {
+impl QueryLatch {
     fn new() -> Self {
         QueryLatch {
             info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
@@ -198,7 +209,7 @@ impl QueryLatch {
         qcx: impl QueryContext,
         query: Option,
         span: Span,
-    ) -> Result<(), CycleError> {
+    ) -> Result<(), CycleError> {
         let waiter =
             Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() });
         self.wait_on_inner(qcx, &waiter);
@@ -213,7 +224,7 @@ impl QueryLatch {
     }
 
     /// Awaits the caller on this latch by blocking the current thread.
-    fn wait_on_inner(&self, qcx: impl QueryContext, waiter: &Arc) {
+    fn wait_on_inner(&self, qcx: impl QueryContext, waiter: &Arc>) {
         let mut info = self.info.lock();
         if !info.complete {
             // We push the waiter on to the `waiters` list. It can be accessed inside
@@ -249,7 +260,7 @@ impl QueryLatch {
 
     /// Removes a single waiter from the list of waiters.
     /// This is used to break query cycles.
-    fn extract_waiter(&self, waiter: usize) -> Arc {
+    fn extract_waiter(&self, waiter: usize) -> Arc> {
         let mut info = self.info.lock();
         debug_assert!(!info.complete);
         // Remove the waiter from the list of waiters
@@ -269,7 +280,11 @@ type Waiter = (QueryJobId, usize);
 /// For visits of resumable waiters it returns Some(Some(Waiter)) which has the
 /// required information to resume the waiter.
 /// If all `visit` calls returns None, this function also returns None.
-fn visit_waiters(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option>
+fn visit_waiters(
+    query_map: &QueryMap,
+    query: QueryJobId,
+    mut visit: F,
+) -> Option>
 where
     F: FnMut(Span, QueryJobId) -> Option>,
 {
@@ -299,8 +314,8 @@ where
 /// `span` is the reason for the `query` to execute. This is initially DUMMY_SP.
 /// If a cycle is detected, this initial value is replaced with the span causing
 /// the cycle.
-fn cycle_check(
-    query_map: &QueryMap,
+fn cycle_check(
+    query_map: &QueryMap,
     query: QueryJobId,
     span: Span,
     stack: &mut Vec<(Span, QueryJobId)>,
@@ -339,8 +354,8 @@ fn cycle_check(
 /// Finds out if there's a path to the compiler root (aka. code which isn't in a query)
 /// from `query` without going through any of the queries in `visited`.
 /// This is achieved with a depth first search.
-fn connected_to_root(
-    query_map: &QueryMap,
+fn connected_to_root(
+    query_map: &QueryMap,
     query: QueryJobId,
     visited: &mut FxHashSet,
 ) -> bool {
@@ -361,7 +376,7 @@ fn connected_to_root(
 }
 
 // Deterministically pick an query from a list
-fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T
+fn pick_query<'a, I: Clone, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T
 where
     F: Fn(&T) -> (Span, QueryJobId),
 {
@@ -386,10 +401,10 @@ where
 /// the function return true.
 /// If a cycle was not found, the starting query is removed from `jobs` and
 /// the function returns false.
-fn remove_cycle(
-    query_map: &QueryMap,
+fn remove_cycle(
+    query_map: &QueryMap,
     jobs: &mut Vec,
-    wakelist: &mut Vec>,
+    wakelist: &mut Vec>>,
 ) -> bool {
     let mut visited = FxHashSet::default();
     let mut stack = Vec::new();
@@ -490,7 +505,10 @@ fn remove_cycle(
 /// uses a query latch and then resuming that waiter.
 /// There may be multiple cycles involved in a deadlock, so this searches
 /// all active queries for cycles before finally resuming all the waiters at once.
-pub fn break_query_cycles(query_map: QueryMap, registry: &rustc_thread_pool::Registry) {
+pub fn break_query_cycles(
+    query_map: QueryMap,
+    registry: &rustc_thread_pool::Registry,
+) {
     let mut wakelist = Vec::new();
     // It is OK per the comments:
     // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932
@@ -541,7 +559,7 @@ pub fn report_cycle<'a>(
 ) -> Diag<'a> {
     assert!(!stack.is_empty());
 
-    let span = stack[0].query.default_span(stack[1 % stack.len()].span);
+    let span = stack[0].query.info.default_span(stack[1 % stack.len()].span);
 
     let mut cycle_stack = Vec::new();
 
@@ -550,31 +568,31 @@ pub fn report_cycle<'a>(
 
     for i in 1..stack.len() {
         let query = &stack[i].query;
-        let span = query.default_span(stack[(i + 1) % stack.len()].span);
-        cycle_stack.push(CycleStack { span, desc: query.description.to_owned() });
+        let span = query.info.default_span(stack[(i + 1) % stack.len()].span);
+        cycle_stack.push(CycleStack { span, desc: query.info.description.to_owned() });
     }
 
     let mut cycle_usage = None;
     if let Some((span, ref query)) = *usage {
         cycle_usage = Some(crate::error::CycleUsage {
-            span: query.default_span(span),
-            usage: query.description.to_string(),
+            span: query.info.default_span(span),
+            usage: query.info.description.to_string(),
         });
     }
 
-    let alias = if stack.iter().all(|entry| matches!(entry.query.def_kind, Some(DefKind::TyAlias)))
-    {
-        Some(crate::error::Alias::Ty)
-    } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) {
-        Some(crate::error::Alias::Trait)
-    } else {
-        None
-    };
+    let alias =
+        if stack.iter().all(|entry| matches!(entry.query.info.def_kind, Some(DefKind::TyAlias))) {
+            Some(crate::error::Alias::Ty)
+        } else if stack.iter().all(|entry| entry.query.info.def_kind == Some(DefKind::TraitAlias)) {
+            Some(crate::error::Alias::Trait)
+        } else {
+            None
+        };
 
     let cycle_diag = crate::error::Cycle {
         span,
         cycle_stack,
-        stack_bottom: stack[0].query.description.to_owned(),
+        stack_bottom: stack[0].query.info.description.to_owned(),
         alias,
         cycle_usage,
         stack_count,
@@ -610,11 +628,12 @@ pub fn print_query_stack(
         let Some(query_info) = query_map.get(&query) else {
             break;
         };
+        let query_extra = qcx.lift_query_info(&query_info.query.info);
         if Some(count_printed) < limit_frames || limit_frames.is_none() {
             // Only print to stderr as many stack frames as `num_frames` when present.
             dcx.struct_failure_note(format!(
                 "#{} [{:?}] {}",
-                count_printed, query_info.query.dep_kind, query_info.query.description
+                count_printed, query_info.query.dep_kind, query_extra.description
             ))
             .with_span(query_info.job.span)
             .emit();
@@ -627,7 +646,7 @@ pub fn print_query_stack(
                 "#{} [{}] {}",
                 count_total,
                 qcx.dep_context().dep_kind_vtable(query_info.query.dep_kind).name,
-                query_info.query.description
+                query_extra.description
             );
         }
 
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs
index 796f41d41efa..3ff980fa9bc5 100644
--- a/compiler/rustc_query_system/src/query/mod.rs
+++ b/compiler/rustc_query_system/src/query/mod.rs
@@ -1,4 +1,10 @@
+use std::fmt::Debug;
+use std::marker::PhantomData;
+use std::mem::transmute;
+use std::sync::Arc;
+
 use rustc_data_structures::jobserver::Proxy;
+use rustc_data_structures::sync::{DynSend, DynSync};
 use rustc_errors::DiagInner;
 use rustc_hashes::Hash64;
 use rustc_hir::def::DefKind;
@@ -36,31 +42,59 @@ pub enum CycleErrorHandling {
 ///
 /// This is mostly used in case of cycles for error reporting.
 #[derive(Clone, Debug)]
-pub struct QueryStackFrame {
-    pub description: String,
-    span: Option,
-    pub def_id: Option,
-    pub def_kind: Option,
-    /// A def-id that is extracted from a `Ty` in a query key
-    pub def_id_for_ty_in_cycle: Option,
+pub struct QueryStackFrame {
+    /// This field initially stores a `QueryStackDeferred` during collection,
+    /// but can later be changed to `QueryStackFrameExtra` containing concrete information
+    /// by calling `lift`. This is done so that collecting query does not need to invoke
+    /// queries, instead `lift` will call queries in a more appropriate location.
+    pub info: I,
+
     pub dep_kind: DepKind,
     /// This hash is used to deterministically pick
     /// a query to remove cycles in the parallel compiler.
     hash: Hash64,
+    pub def_id: Option,
+    /// A def-id that is extracted from a `Ty` in a query key
+    pub def_id_for_ty_in_cycle: Option,
 }
 
-impl QueryStackFrame {
+impl QueryStackFrame {
     #[inline]
     pub fn new(
-        description: String,
-        span: Option,
-        def_id: Option,
-        def_kind: Option,
+        info: I,
         dep_kind: DepKind,
+        hash: impl FnOnce() -> Hash64,
+        def_id: Option,
         def_id_for_ty_in_cycle: Option,
-        hash: Hash64,
     ) -> Self {
-        Self { description, span, def_id, def_kind, def_id_for_ty_in_cycle, dep_kind, hash }
+        Self { info, def_id, dep_kind, hash: hash(), def_id_for_ty_in_cycle }
+    }
+
+    fn lift>(
+        &self,
+        qcx: Qcx,
+    ) -> QueryStackFrame {
+        QueryStackFrame {
+            info: qcx.lift_query_info(&self.info),
+            dep_kind: self.dep_kind,
+            hash: self.hash,
+            def_id: self.def_id,
+            def_id_for_ty_in_cycle: self.def_id_for_ty_in_cycle,
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct QueryStackFrameExtra {
+    pub description: String,
+    span: Option,
+    pub def_kind: Option,
+}
+
+impl QueryStackFrameExtra {
+    #[inline]
+    pub fn new(description: String, span: Option, def_kind: Option) -> Self {
+        Self { description, span, def_kind }
     }
 
     // FIXME(eddyb) Get more valid `Span`s on queries.
@@ -73,6 +107,40 @@ impl QueryStackFrame {
     }
 }
 
+/// Track a 'side effect' for a particular query.
+/// This is used to hold a closure which can create `QueryStackFrameExtra`.
+#[derive(Clone)]
+pub struct QueryStackDeferred<'tcx> {
+    _dummy: PhantomData<&'tcx ()>,
+
+    // `extract` may contain references to 'tcx, but we can't tell drop checking that it won't
+    // access it in the destructor.
+    extract: Arc QueryStackFrameExtra + DynSync + DynSend>,
+}
+
+impl<'tcx> QueryStackDeferred<'tcx> {
+    pub fn new(
+        context: C,
+        extract: fn(C) -> QueryStackFrameExtra,
+    ) -> Self {
+        let extract: Arc QueryStackFrameExtra + DynSync + DynSend + 'tcx> =
+            Arc::new(move || extract(context));
+        // SAFETY: The `extract` closure does not access 'tcx in its destructor as the only
+        // captured variable is `context` which is Copy and cannot have a destructor.
+        Self { _dummy: PhantomData, extract: unsafe { transmute(extract) } }
+    }
+
+    pub fn extract(&self) -> QueryStackFrameExtra {
+        (self.extract)()
+    }
+}
+
+impl<'tcx> Debug for QueryStackDeferred<'tcx> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str("QueryStackDeferred")
+    }
+}
+
 /// Tracks 'side effects' for a particular query.
 /// This struct is saved to disk along with the query result,
 /// and loaded from disk if we mark the query as green.
@@ -92,6 +160,8 @@ pub enum QuerySideEffect {
 }
 
 pub trait QueryContext: HasDepContext {
+    type QueryInfo: Clone;
+
     /// Gets a jobserver reference which is used to release then acquire
     /// a token while waiting on a query.
     fn jobserver_proxy(&self) -> &Proxy;
@@ -101,7 +171,12 @@ pub trait QueryContext: HasDepContext {
     /// Get the query information from the TLS context.
     fn current_query_job(self) -> Option;
 
-    fn collect_active_jobs(self, require_complete: bool) -> Result;
+    fn collect_active_jobs(
+        self,
+        require_complete: bool,
+    ) -> Result, QueryMap>;
+
+    fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra;
 
     /// Load a side effect associated to the node in the previous session.
     fn load_side_effect(
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 150ad238dad9..5be4ee145208 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -18,7 +18,7 @@ use rustc_errors::{Diag, FatalError, StashKey};
 use rustc_span::{DUMMY_SP, Span};
 use tracing::instrument;
 
-use super::QueryConfig;
+use super::{QueryConfig, QueryStackFrameExtra};
 use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams};
 use crate::ich::StableHashingContext;
 use crate::query::caches::QueryCache;
@@ -32,23 +32,23 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ {
     move |x| x.0 == *k
 }
 
-pub struct QueryState {
-    active: Sharded>,
+pub struct QueryState {
+    active: Sharded)>>,
 }
 
 /// Indicates the state of a query for a given key in a query map.
-enum QueryResult {
+enum QueryResult {
     /// An already executing query. The query job can be used to await for its completion.
-    Started(QueryJob),
+    Started(QueryJob),
 
     /// The query panicked. Queries trying to wait on this will raise a fatal error which will
     /// silently panic.
     Poisoned,
 }
 
-impl QueryResult {
+impl QueryResult {
     /// Unwraps the query job expecting that it has started.
-    fn expect_job(self) -> QueryJob {
+    fn expect_job(self) -> QueryJob {
         match self {
             Self::Started(job) => job,
             Self::Poisoned => {
@@ -58,7 +58,7 @@ impl QueryResult {
     }
 }
 
-impl QueryState
+impl QueryState
 where
     K: Eq + Hash + Copy + Debug,
 {
@@ -69,13 +69,13 @@ where
     pub fn collect_active_jobs(
         &self,
         qcx: Qcx,
-        make_query: fn(Qcx, K) -> QueryStackFrame,
-        jobs: &mut QueryMap,
+        make_query: fn(Qcx, K) -> QueryStackFrame,
+        jobs: &mut QueryMap,
         require_complete: bool,
     ) -> Option<()> {
         let mut active = Vec::new();
 
-        let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult)>>| {
+        let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult)>>| {
             for (k, v) in iter.iter() {
                 if let QueryResult::Started(ref job) = *v {
                     active.push((*k, job.clone()));
@@ -106,19 +106,19 @@ where
     }
 }
 
-impl Default for QueryState {
-    fn default() -> QueryState {
+impl Default for QueryState {
+    fn default() -> QueryState {
         QueryState { active: Default::default() }
     }
 }
 
 /// A type representing the responsibility to execute the job in the `job` field.
 /// This will poison the relevant query if dropped.
-struct JobOwner<'tcx, K>
+struct JobOwner<'tcx, K, I>
 where
     K: Eq + Hash + Copy,
 {
-    state: &'tcx QueryState,
+    state: &'tcx QueryState,
     key: K,
 }
 
@@ -159,7 +159,7 @@ where
         }
         CycleErrorHandling::Stash => {
             let guar = if let Some(root) = cycle_error.cycle.first()
-                && let Some(span) = root.query.span
+                && let Some(span) = root.query.info.span
             {
                 error.stash(span, StashKey::Cycle).unwrap()
             } else {
@@ -170,7 +170,7 @@ where
     }
 }
 
-impl<'tcx, K> JobOwner<'tcx, K>
+impl<'tcx, K, I> JobOwner<'tcx, K, I>
 where
     K: Eq + Hash + Copy,
 {
@@ -207,7 +207,7 @@ where
     }
 }
 
-impl<'tcx, K> Drop for JobOwner<'tcx, K>
+impl<'tcx, K, I> Drop for JobOwner<'tcx, K, I>
 where
     K: Eq + Hash + Copy,
 {
@@ -235,10 +235,19 @@ where
 }
 
 #[derive(Clone, Debug)]
-pub struct CycleError {
+pub struct CycleError {
     /// The query and related span that uses the cycle.
-    pub usage: Option<(Span, QueryStackFrame)>,
-    pub cycle: Vec,
+    pub usage: Option<(Span, QueryStackFrame)>,
+    pub cycle: Vec>,
+}
+
+impl CycleError {
+    fn lift>(&self, qcx: Qcx) -> CycleError {
+        CycleError {
+            usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift(qcx))),
+            cycle: self.cycle.iter().map(|info| info.lift(qcx)).collect(),
+        }
+    }
 }
 
 /// Checks whether there is already a value for this key in the in-memory
@@ -275,10 +284,10 @@ where
 {
     // Ensure there was no errors collecting all active jobs.
     // We need the complete map to ensure we find a cycle to break.
-    let query_map = qcx.collect_active_jobs(false).expect("failed to collect active queries");
+    let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries");
 
     let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span);
-    (mk_cycle(query, qcx, error), None)
+    (mk_cycle(query, qcx, error.lift(qcx)), None)
 }
 
 #[inline(always)]
@@ -287,7 +296,7 @@ fn wait_for_query(
     qcx: Qcx,
     span: Span,
     key: Q::Key,
-    latch: QueryLatch,
+    latch: QueryLatch,
     current: Option,
 ) -> (Q::Value, Option)
 where
@@ -327,7 +336,7 @@ where
 
             (v, Some(index))
         }
-        Err(cycle) => (mk_cycle(query, qcx, cycle), None),
+        Err(cycle) => (mk_cycle(query, qcx, cycle.lift(qcx)), None),
     }
 }
 
@@ -405,7 +414,7 @@ where
 fn execute_job(
     query: Q,
     qcx: Qcx,
-    state: &QueryState,
+    state: &QueryState,
     key: Q::Key,
     key_hash: u64,
     id: QueryJobId,
diff --git a/tests/ui/query-system/query-cycle-printing-issue-151226.rs b/tests/ui/query-system/query-cycle-printing-issue-151226.rs
new file mode 100644
index 000000000000..9d0a20737c9f
--- /dev/null
+++ b/tests/ui/query-system/query-cycle-printing-issue-151226.rs
@@ -0,0 +1,8 @@
+struct A(std::sync::OnceLock);
+//~^ ERROR recursive type `A` has infinite size
+//~| ERROR cycle detected when computing layout of `A<()>`
+
+static B: A<()> = todo!();
+//~^ ERROR cycle occurred during layout computation
+
+fn main() {}
diff --git a/tests/ui/query-system/query-cycle-printing-issue-151226.stderr b/tests/ui/query-system/query-cycle-printing-issue-151226.stderr
new file mode 100644
index 000000000000..7e574b5911a3
--- /dev/null
+++ b/tests/ui/query-system/query-cycle-printing-issue-151226.stderr
@@ -0,0 +1,36 @@
+error[E0072]: recursive type `A` has infinite size
+  --> $DIR/query-cycle-printing-issue-151226.rs:1:1
+   |
+LL | struct A(std::sync::OnceLock);
+   | ^^^^^^^^^^^ ------------------------- recursive without indirection
+   |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
+   |
+LL | struct A(Box>);
+   |             ++++                         +
+
+error[E0391]: cycle detected when computing layout of `A<()>`
+   |
+   = note: ...which requires computing layout of `std::sync::once_lock::OnceLock>`...
+   = note: ...which requires computing layout of `core::cell::UnsafeCell>>`...
+   = note: ...which requires computing layout of `core::mem::maybe_uninit::MaybeUninit>`...
+   = note: ...which requires computing layout of `core::mem::manually_drop::ManuallyDrop>`...
+   = note: ...which requires computing layout of `core::mem::maybe_dangling::MaybeDangling>`...
+   = note: ...which again requires computing layout of `A<()>`, completing the cycle
+note: cycle used when checking that `B` is well-formed
+  --> $DIR/query-cycle-printing-issue-151226.rs:5:1
+   |
+LL | static B: A<()> = todo!();
+   | ^^^^^^^^^^^^^^^
+   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
+
+error[E0080]: a cycle occurred during layout computation
+  --> $DIR/query-cycle-printing-issue-151226.rs:5:1
+   |
+LL | static B: A<()> = todo!();
+   | ^^^^^^^^^^^^^^^ evaluation of `B` failed here
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0072, E0080, E0391.
+For more information about an error, try `rustc --explain E0072`.
diff --git a/tests/ui/query-system/query-cycle-printing-issue-151358.rs b/tests/ui/query-system/query-cycle-printing-issue-151358.rs
new file mode 100644
index 000000000000..04d8664420be
--- /dev/null
+++ b/tests/ui/query-system/query-cycle-printing-issue-151358.rs
@@ -0,0 +1,7 @@
+//~ ERROR: cycle detected when looking up span for `Default`
+trait Default {}
+use std::num::NonZero;
+fn main() {
+    NonZero();
+    format!("{}", 0);
+}
diff --git a/tests/ui/query-system/query-cycle-printing-issue-151358.stderr b/tests/ui/query-system/query-cycle-printing-issue-151358.stderr
new file mode 100644
index 000000000000..9c1d7b1de33a
--- /dev/null
+++ b/tests/ui/query-system/query-cycle-printing-issue-151358.stderr
@@ -0,0 +1,9 @@
+error[E0391]: cycle detected when looking up span for `Default`
+   |
+   = note: ...which immediately requires looking up span for `Default` again
+   = note: cycle used when perform lints prior to AST lowering
+   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0391`.
diff --git a/tests/ui/resolve/query-cycle-issue-124901.rs b/tests/ui/resolve/query-cycle-issue-124901.rs
index ccaee0e6bc6f..6cb1e58b6258 100644
--- a/tests/ui/resolve/query-cycle-issue-124901.rs
+++ b/tests/ui/resolve/query-cycle-issue-124901.rs
@@ -1,4 +1,4 @@
-//~ ERROR: cycle detected when getting HIR ID of `Default`
+//~ ERROR: cycle detected when looking up span for `Default`
 trait Default {
     type Id;
 
diff --git a/tests/ui/resolve/query-cycle-issue-124901.stderr b/tests/ui/resolve/query-cycle-issue-124901.stderr
index 3679925c6db4..9c1d7b1de33a 100644
--- a/tests/ui/resolve/query-cycle-issue-124901.stderr
+++ b/tests/ui/resolve/query-cycle-issue-124901.stderr
@@ -1,10 +1,7 @@
-error[E0391]: cycle detected when getting HIR ID of `Default`
+error[E0391]: cycle detected when looking up span for `Default`
    |
-   = note: ...which requires getting the crate HIR...
-   = note: ...which requires perform lints prior to AST lowering...
-   = note: ...which requires looking up span for `Default`...
-   = note: ...which again requires getting HIR ID of `Default`, completing the cycle
-   = note: cycle used when getting the resolver for lowering
+   = note: ...which immediately requires looking up span for `Default` again
+   = note: cycle used when perform lints prior to AST lowering
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: aborting due to 1 previous error

From b3e93fc242cb08f3f18a295a574fdbb360ae1863 Mon Sep 17 00:00:00 2001
From: Folkert de Vries 
Date: Mon, 26 Jan 2026 21:58:18 +0100
Subject: [PATCH 252/583] 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 96881687af05..05020108b5b7 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 253/583] 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 05020108b5b7..de7efa355283 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 9b15010686b4650d2eaad0a7ca129eda16199c72 Mon Sep 17 00:00:00 2001
From: Trevor Gross 
Date: Thu, 22 Jan 2026 23:34:15 -0600
Subject: [PATCH 254/583] lint: Use rustc_apfloat for `overflowing_literals`,
 add f16 and f128

Switch to parsing float literals for overflow checks using
`rustc_apfloat` rather than host floats. This avoids small variations in
platform support and makes it possible to start checking `f16` and
`f128` as well.

Using APFloat matches what we try to do elsewhere to avoid platform
inconsistencies.
---
 Cargo.lock                               |  1 +
 compiler/rustc_lint/Cargo.toml           |  1 +
 compiler/rustc_lint/src/types/literal.rs | 35 ++++++++++-------
 library/core/src/fmt/float.rs            |  8 +++-
 tests/ui/lint/lint-type-overflow2.rs     |  6 +++
 tests/ui/lint/lint-type-overflow2.stderr | 48 ++++++++++++++++++++----
 6 files changed, 77 insertions(+), 22 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 01300d56cff9..89d0f9ed5cb6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4170,6 +4170,7 @@ version = "0.0.0"
 dependencies = [
  "bitflags",
  "rustc_abi",
+ "rustc_apfloat",
  "rustc_ast",
  "rustc_ast_pretty",
  "rustc_attr_parsing",
diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml
index 3a50aac50cb3..035d8b4903fa 100644
--- a/compiler/rustc_lint/Cargo.toml
+++ b/compiler/rustc_lint/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2024"
 # tidy-alphabetical-start
 bitflags = "2.4.1"
 rustc_abi = { path = "../rustc_abi" }
+rustc_apfloat = "0.2.0"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_attr_parsing = { path = "../rustc_attr_parsing" }
diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs
index d49f599407c2..b6c67549c47e 100644
--- a/compiler/rustc_lint/src/types/literal.rs
+++ b/compiler/rustc_lint/src/types/literal.rs
@@ -1,10 +1,12 @@
 use hir::{ExprKind, Node};
 use rustc_abi::{Integer, Size};
+use rustc_apfloat::Float;
+use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, Semantics, SingleS};
 use rustc_hir::{HirId, attrs};
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::{bug, ty};
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
 use {rustc_ast as ast, rustc_hir as hir};
 
 use crate::LateContext;
@@ -383,6 +385,13 @@ fn lint_uint_literal<'tcx>(
     }
 }
 
+/// `None` if `v` does not parse as the float type, otherwise indicates whether a literal rounds
+/// to infinity.
+fn float_is_infinite(v: Symbol) -> Option {
+    let x: IeeeFloat = v.as_str().parse().ok()?;
+    Some(x.is_infinite())
+}
+
 pub(crate) fn lint_literal<'tcx>(
     cx: &LateContext<'tcx>,
     type_limits: &TypeLimits,
@@ -405,18 +414,18 @@ pub(crate) fn lint_literal<'tcx>(
             lint_uint_literal(cx, hir_id, span, lit, t)
         }
         ty::Float(t) => {
-            let (is_infinite, sym) = match lit.node {
-                ast::LitKind::Float(v, _) => match t {
-                    // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI
-                    // issues resolved).
-                    ty::FloatTy::F16 => (Ok(false), v),
-                    ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v),
-                    ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v),
-                    ty::FloatTy::F128 => (Ok(false), v),
-                },
-                _ => bug!(),
+            let ast::LitKind::Float(v, _) = lit.node else {
+                bug!();
             };
-            if is_infinite == Ok(true) {
+
+            let is_infinite = match t {
+                ty::FloatTy::F16 => float_is_infinite::(v),
+                ty::FloatTy::F32 => float_is_infinite::(v),
+                ty::FloatTy::F64 => float_is_infinite::(v),
+                ty::FloatTy::F128 => float_is_infinite::(v),
+            };
+
+            if is_infinite == Some(true) {
                 cx.emit_span_lint(
                     OVERFLOWING_LITERALS,
                     span,
@@ -426,7 +435,7 @@ pub(crate) fn lint_literal<'tcx>(
                             .sess()
                             .source_map()
                             .span_to_snippet(lit.span)
-                            .unwrap_or_else(|_| sym.to_string()),
+                            .unwrap_or_else(|_| v.to_string()),
                     },
                 );
             }
diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs
index 556db239f249..59a74b7e0e5c 100644
--- a/library/core/src/fmt/float.rs
+++ b/library/core/src/fmt/float.rs
@@ -13,8 +13,14 @@ macro_rules! impl_general_format {
     ($($t:ident)*) => {
         $(impl GeneralFormat for $t {
             fn already_rounded_value_should_use_exponential(&self) -> bool {
+                // `max_abs` rounds to infinity for `f16`. This is fine to save us from a more
+                // complex macro, it just means a positive-exponent `f16` will never print as
+                // scientific notation by default (reasonably, the max is 65504.0).
+                #[allow(overflowing_literals)]
+                let max_abs = 1e+16;
+
                 let abs = $t::abs(*self);
-                (abs != 0.0 && abs < 1e-4) || abs >= 1e+16
+                (abs != 0.0 && abs < 1e-4) || abs >= max_abs
             }
         })*
     }
diff --git a/tests/ui/lint/lint-type-overflow2.rs b/tests/ui/lint/lint-type-overflow2.rs
index ac7420326c89..d3ff02aeb722 100644
--- a/tests/ui/lint/lint-type-overflow2.rs
+++ b/tests/ui/lint/lint-type-overflow2.rs
@@ -1,13 +1,19 @@
 //@ compile-flags: -O
 
+#![feature(f16)]
+#![feature(f128)]
 #![deny(overflowing_literals)]
 
 fn main() {
     let x2: i8 = --128; //~ ERROR literal out of range for `i8`
     //~| WARN use of a double negation
 
+    let x = -65520.0_f16; //~ ERROR literal out of range for `f16`
+    let x =  65520.0_f16; //~ ERROR literal out of range for `f16`
     let x = -3.40282357e+38_f32; //~ ERROR literal out of range for `f32`
     let x =  3.40282357e+38_f32; //~ ERROR literal out of range for `f32`
     let x = -1.7976931348623159e+308_f64; //~ ERROR literal out of range for `f64`
     let x =  1.7976931348623159e+308_f64; //~ ERROR literal out of range for `f64`
+    let x = -1.1897314953572317650857593266280075e+4932_f128; //~ ERROR literal out of range for `f128`
+    let x =  1.1897314953572317650857593266280075e+4932_f128; //~ ERROR literal out of range for `f128`
 }
diff --git a/tests/ui/lint/lint-type-overflow2.stderr b/tests/ui/lint/lint-type-overflow2.stderr
index 2cfb18e9fe92..c045d243753e 100644
--- a/tests/ui/lint/lint-type-overflow2.stderr
+++ b/tests/ui/lint/lint-type-overflow2.stderr
@@ -1,5 +1,5 @@
 warning: use of a double negation
-  --> $DIR/lint-type-overflow2.rs:6:18
+  --> $DIR/lint-type-overflow2.rs:8:18
    |
 LL |     let x2: i8 = --128;
    |                  ^^^^^
@@ -13,7 +13,7 @@ LL |     let x2: i8 = -(-128);
    |                   +    +
 
 error: literal out of range for `i8`
-  --> $DIR/lint-type-overflow2.rs:6:20
+  --> $DIR/lint-type-overflow2.rs:8:20
    |
 LL |     let x2: i8 = --128;
    |                    ^^^
@@ -21,13 +21,29 @@ LL |     let x2: i8 = --128;
    = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127`
    = help: consider using the type `u8` instead
 note: the lint level is defined here
-  --> $DIR/lint-type-overflow2.rs:3:9
+  --> $DIR/lint-type-overflow2.rs:5:9
    |
 LL | #![deny(overflowing_literals)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
+error: literal out of range for `f16`
+  --> $DIR/lint-type-overflow2.rs:11:14
+   |
+LL |     let x = -65520.0_f16;
+   |              ^^^^^^^^^^^
+   |
+   = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY`
+
+error: literal out of range for `f16`
+  --> $DIR/lint-type-overflow2.rs:12:14
+   |
+LL |     let x =  65520.0_f16;
+   |              ^^^^^^^^^^^
+   |
+   = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY`
+
 error: literal out of range for `f32`
-  --> $DIR/lint-type-overflow2.rs:9:14
+  --> $DIR/lint-type-overflow2.rs:13:14
    |
 LL |     let x = -3.40282357e+38_f32;
    |              ^^^^^^^^^^^^^^^^^^
@@ -35,7 +51,7 @@ LL |     let x = -3.40282357e+38_f32;
    = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY`
 
 error: literal out of range for `f32`
-  --> $DIR/lint-type-overflow2.rs:10:14
+  --> $DIR/lint-type-overflow2.rs:14:14
    |
 LL |     let x =  3.40282357e+38_f32;
    |              ^^^^^^^^^^^^^^^^^^
@@ -43,7 +59,7 @@ LL |     let x =  3.40282357e+38_f32;
    = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY`
 
 error: literal out of range for `f64`
-  --> $DIR/lint-type-overflow2.rs:11:14
+  --> $DIR/lint-type-overflow2.rs:15:14
    |
 LL |     let x = -1.7976931348623159e+308_f64;
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -51,12 +67,28 @@ LL |     let x = -1.7976931348623159e+308_f64;
    = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY`
 
 error: literal out of range for `f64`
-  --> $DIR/lint-type-overflow2.rs:12:14
+  --> $DIR/lint-type-overflow2.rs:16:14
    |
 LL |     let x =  1.7976931348623159e+308_f64;
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY`
 
-error: aborting due to 5 previous errors; 1 warning emitted
+error: literal out of range for `f128`
+  --> $DIR/lint-type-overflow2.rs:17:14
+   |
+LL |     let x = -1.1897314953572317650857593266280075e+4932_f128;
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the literal `1.1897314953572317650857593266280075e+4932_f128` does not fit into the type `f128` and will be converted to `f128::INFINITY`
+
+error: literal out of range for `f128`
+  --> $DIR/lint-type-overflow2.rs:18:14
+   |
+LL |     let x =  1.1897314953572317650857593266280075e+4932_f128;
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the literal `1.1897314953572317650857593266280075e+4932_f128` does not fit into the type `f128` and will be converted to `f128::INFINITY`
+
+error: aborting due to 9 previous errors; 1 warning emitted
 

From 120247a76fdf68ad9004ec9acd9c5261140e53ff Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 14 Jan 2026 12:24:54 +1100
Subject: [PATCH 255/583] Use an associated type default for `Key::Cache`.

They currently aren't used because r-a didn't support them, but r-a
support was recently merged in
https://github.com/rust-lang/rust-analyzer/pull/21243.
---
 compiler/rustc_middle/src/query/keys.rs | 116 +-----------------------
 1 file changed, 1 insertion(+), 115 deletions(-)

diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index dd9ba4325545..4fa8ca7d85bd 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -29,15 +29,7 @@ pub trait Key: Sized {
     /// constraint is not enforced here.
     ///
     /// [`QueryCache`]: rustc_query_system::query::QueryCache
-    // N.B. Most of the keys down below have `type Cache = DefaultCache;`,
-    //      it would be reasonable to use associated type defaults, to remove the duplication...
-    //
-    //      ...But r-a doesn't support them yet and using a default here causes r-a to not infer
-    //      return types of queries which is very annoying. Thus, until r-a support associated
-    //      type defaults, please restrain from using them here <3
-    //
-    //      r-a issue: 
-    type Cache;
+    type Cache = DefaultCache;
 
     /// In the event that a cycle occurs, if no explicit span has been
     /// given for a query with key `self`, what span should we use?
@@ -72,8 +64,6 @@ impl Key for () {
 }
 
 impl<'tcx> Key for ty::InstanceKind<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.def_id())
     }
@@ -89,32 +79,24 @@ impl<'tcx> AsLocalKey for ty::InstanceKind<'tcx> {
 }
 
 impl<'tcx> Key for ty::Instance<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.def_id())
     }
 }
 
 impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.instance.default_span(tcx)
     }
 }
 
 impl<'tcx> Key for (Ty<'tcx>, Option>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
@@ -184,8 +166,6 @@ impl AsLocalKey for DefId {
 }
 
 impl Key for LocalModDefId {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(*self)
     }
@@ -197,8 +177,6 @@ impl Key for LocalModDefId {
 }
 
 impl Key for ModDefId {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(*self)
     }
@@ -219,56 +197,42 @@ impl AsLocalKey for ModDefId {
 }
 
 impl Key for SimplifiedType {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl Key for (DefId, DefId) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.1.default_span(tcx)
     }
 }
 
 impl<'tcx> Key for (ty::Instance<'tcx>, LocalDefId) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }
 }
 
 impl Key for (DefId, LocalDefId) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.1.default_span(tcx)
     }
 }
 
 impl Key for (LocalDefId, DefId) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }
 }
 
 impl Key for (LocalDefId, LocalDefId) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }
 }
 
 impl Key for (DefId, Ident) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.0)
     }
@@ -280,16 +244,12 @@ impl Key for (DefId, Ident) {
 }
 
 impl Key for (LocalDefId, LocalDefId, Ident) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.1.default_span(tcx)
     }
 }
 
 impl Key for (CrateNum, DefId) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.1.default_span(tcx)
     }
@@ -305,8 +265,6 @@ impl AsLocalKey for (CrateNum, DefId) {
 }
 
 impl Key for (CrateNum, SimplifiedType) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
@@ -322,120 +280,90 @@ impl AsLocalKey for (CrateNum, SimplifiedType) {
 }
 
 impl Key for (DefId, SimplifiedType) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }
 }
 
 impl Key for (DefId, ty::SizedTraitKind) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }
 }
 
 impl<'tcx> Key for GenericArgsRef<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for (DefId, GenericArgsRef<'tcx>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }
 }
 
 impl<'tcx> Key for (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         (self.0).def.default_span(tcx)
     }
 }
 
 impl<'tcx> Key for (LocalDefId, DefId, GenericArgsRef<'tcx>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }
 }
 
 impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.1.def_id)
     }
 }
 
 impl<'tcx> Key for ty::ParamEnvAnd<'tcx, Ty<'tcx>> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for ty::TraitRef<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.def_id)
     }
 }
 
 impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.def_id())
     }
 }
 
 impl<'tcx> Key for ty::PolyExistentialTraitRef<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.def_id())
     }
 }
 
 impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.0.def_id())
     }
 }
 
 impl<'tcx> Key for GenericArg<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for ty::Const<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for Ty<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
@@ -450,40 +378,30 @@ impl<'tcx> Key for Ty<'tcx> {
 }
 
 impl<'tcx> Key for TyAndLayout<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for ty::Clauses<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for ty::ParamEnv<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.value.default_span(tcx)
     }
@@ -494,24 +412,18 @@ impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> {
 }
 
 impl Key for Symbol {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl Key for Option {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for &'tcx OsStr {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
@@ -520,80 +432,60 @@ impl<'tcx> Key for &'tcx OsStr {
 /// Canonical query goals correspond to abstract trait operations that
 /// are not tied to any crate in particular.
 impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx, T: Clone> Key for (CanonicalQueryInput<'tcx, T>, bool) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl Key for (Symbol, u32, u32) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for (DefId, Ty<'tcx>, GenericArgsRef<'tcx>, ty::ParamEnv<'tcx>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for (Ty<'tcx>, rustc_abi::VariantIdx) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for (ty::PolyFnSig<'tcx>, &'tcx ty::List>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List>) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }
 }
 
 impl<'tcx> Key for ty::Value<'tcx> {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
 impl Key for HirId {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.hir_span(*self)
     }
@@ -605,8 +497,6 @@ impl Key for HirId {
 }
 
 impl Key for (LocalDefId, HirId) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.hir_span(self.1)
     }
@@ -631,8 +521,6 @@ impl<'tcx> Key for (LocalExpnId, &'tcx TokenStream) {
 }
 
 impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) {
-    type Cache = DefaultCache;
-
     // Just forward to `Ty<'tcx>`
 
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
@@ -648,8 +536,6 @@ impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>
 }
 
 impl<'tcx> Key for (ty::Instance<'tcx>, CollectionMode) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
     }

From aebcf78527c38199c48aea2939d6e5062ea85e9e Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Wed, 14 Jan 2026 14:12:59 +1100
Subject: [PATCH 256/583] Remove unused `Key`/`AsLocalKey` impls.

---
 compiler/rustc_middle/src/query/keys.rs | 166 +-----------------------
 1 file changed, 3 insertions(+), 163 deletions(-)

diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index 4fa8ca7d85bd..cccb7d51bd3e 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -3,8 +3,8 @@
 use std::ffi::OsStr;
 
 use rustc_ast::tokenstream::TokenStream;
-use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId};
-use rustc_hir::hir_id::{HirId, OwnerId};
+use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
+use rustc_hir::hir_id::OwnerId;
 use rustc_query_system::dep_graph::DepNodeIndex;
 use rustc_query_system::query::{DefIdCache, DefaultCache, SingleCache, VecCache};
 use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol};
@@ -12,7 +12,7 @@ use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol};
 use crate::infer::canonical::CanonicalQueryInput;
 use crate::mir::mono::CollectionMode;
 use crate::ty::fast_reject::SimplifiedType;
-use crate::ty::layout::{TyAndLayout, ValidityRequirement};
+use crate::ty::layout::ValidityRequirement;
 use crate::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt};
 use crate::{mir, traits};
 
@@ -69,15 +69,6 @@ impl<'tcx> Key for ty::InstanceKind<'tcx> {
     }
 }
 
-impl<'tcx> AsLocalKey for ty::InstanceKind<'tcx> {
-    type LocalKey = Self;
-
-    #[inline(always)]
-    fn as_local_key(&self) -> Option {
-        self.def_id().is_local().then(|| *self)
-    }
-}
-
 impl<'tcx> Key for ty::Instance<'tcx> {
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.def_id())
@@ -176,26 +167,6 @@ impl Key for LocalModDefId {
     }
 }
 
-impl Key for ModDefId {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.def_span(*self)
-    }
-
-    #[inline(always)]
-    fn key_as_def_id(&self) -> Option {
-        Some(self.to_def_id())
-    }
-}
-
-impl AsLocalKey for ModDefId {
-    type LocalKey = LocalModDefId;
-
-    #[inline(always)]
-    fn as_local_key(&self) -> Option {
-        self.as_local()
-    }
-}
-
 impl Key for SimplifiedType {
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
@@ -208,30 +179,6 @@ impl Key for (DefId, DefId) {
     }
 }
 
-impl<'tcx> Key for (ty::Instance<'tcx>, LocalDefId) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        self.0.default_span(tcx)
-    }
-}
-
-impl Key for (DefId, LocalDefId) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        self.1.default_span(tcx)
-    }
-}
-
-impl Key for (LocalDefId, DefId) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        self.0.default_span(tcx)
-    }
-}
-
-impl Key for (LocalDefId, LocalDefId) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        self.0.default_span(tcx)
-    }
-}
-
 impl Key for (DefId, Ident) {
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.0)
@@ -279,12 +226,6 @@ impl AsLocalKey for (CrateNum, SimplifiedType) {
     }
 }
 
-impl Key for (DefId, SimplifiedType) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        self.0.default_span(tcx)
-    }
-}
-
 impl Key for (DefId, ty::SizedTraitKind) {
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.0.default_span(tcx)
@@ -303,66 +244,18 @@ impl<'tcx> Key for (DefId, GenericArgsRef<'tcx>) {
     }
 }
 
-impl<'tcx> Key for (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        (self.0).def.default_span(tcx)
-    }
-}
-
-impl<'tcx> Key for (LocalDefId, DefId, GenericArgsRef<'tcx>) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        self.0.default_span(tcx)
-    }
-}
-
-impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.def_span(self.1.def_id)
-    }
-}
-
-impl<'tcx> Key for ty::ParamEnvAnd<'tcx, Ty<'tcx>> {
-    fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
-        DUMMY_SP
-    }
-}
-
 impl<'tcx> Key for ty::TraitRef<'tcx> {
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         tcx.def_span(self.def_id)
     }
 }
 
-impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.def_span(self.def_id())
-    }
-}
-
-impl<'tcx> Key for ty::PolyExistentialTraitRef<'tcx> {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.def_span(self.def_id())
-    }
-}
-
-impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.def_span(self.0.def_id())
-    }
-}
-
 impl<'tcx> Key for GenericArg<'tcx> {
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
     }
 }
 
-impl<'tcx> Key for ty::Const<'tcx> {
-    fn default_span(&self, _: TyCtxt<'_>) -> Span {
-        DUMMY_SP
-    }
-}
-
 impl<'tcx> Key for Ty<'tcx> {
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
@@ -377,12 +270,6 @@ impl<'tcx> Key for Ty<'tcx> {
     }
 }
 
-impl<'tcx> Key for TyAndLayout<'tcx> {
-    fn default_span(&self, _: TyCtxt<'_>) -> Span {
-        DUMMY_SP
-    }
-}
-
 impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) {
     fn default_span(&self, _: TyCtxt<'_>) -> Span {
         DUMMY_SP
@@ -395,12 +282,6 @@ impl<'tcx> Key for ty::Clauses<'tcx> {
     }
 }
 
-impl<'tcx> Key for ty::ParamEnv<'tcx> {
-    fn default_span(&self, _: TyCtxt<'_>) -> Span {
-        DUMMY_SP
-    }
-}
-
 impl<'tcx, T: Key> Key for ty::PseudoCanonicalInput<'tcx, T> {
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.value.default_span(tcx)
@@ -443,18 +324,6 @@ impl<'tcx, T: Clone> Key for (CanonicalQueryInput<'tcx, T>, bool) {
     }
 }
 
-impl Key for (Symbol, u32, u32) {
-    fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
-        DUMMY_SP
-    }
-}
-
-impl<'tcx> Key for (DefId, Ty<'tcx>, GenericArgsRef<'tcx>, ty::ParamEnv<'tcx>) {
-    fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
-        DUMMY_SP
-    }
-}
-
 impl<'tcx> Key for (Ty<'tcx>, rustc_abi::VariantIdx) {
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         DUMMY_SP
@@ -485,39 +354,10 @@ impl<'tcx> Key for ty::Value<'tcx> {
     }
 }
 
-impl Key for HirId {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir_span(*self)
-    }
-
-    #[inline(always)]
-    fn key_as_def_id(&self) -> Option {
-        None
-    }
-}
-
-impl Key for (LocalDefId, HirId) {
-    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir_span(self.1)
-    }
-
-    #[inline(always)]
-    fn key_as_def_id(&self) -> Option {
-        Some(self.0.into())
-    }
-}
-
 impl<'tcx> Key for (LocalExpnId, &'tcx TokenStream) {
-    type Cache = DefaultCache;
-
     fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
         self.0.expn_data().call_site
     }
-
-    #[inline(always)]
-    fn key_as_def_id(&self) -> Option {
-        None
-    }
 }
 
 impl<'tcx> Key for (ValidityRequirement, ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) {

From 5e629be64827e048388b41bfd236572fd3081f84 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Mon, 26 Jan 2026 22:21:21 +1100
Subject: [PATCH 257/583] Add `CompilerKind` to distinguish between rustc and
 rustdoc

---
 src/tools/compiletest/src/runtest.rs          | 64 +++++++++++++++----
 src/tools/compiletest/src/runtest/assembly.rs |  3 +-
 src/tools/compiletest/src/runtest/ui.rs       | 10 +--
 3 files changed, 59 insertions(+), 18 deletions(-)

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 2bfb73f05d16..5686bfa8f614 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -255,6 +255,13 @@ enum Emit {
     LinkArgsAsm,
 }
 
+/// Indicates whether we are using `rustc` or `rustdoc` to compile an input file.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum CompilerKind {
+    Rustc,
+    Rustdoc,
+}
+
 impl<'test> TestCx<'test> {
     /// Code executed for each revision in turn (or, if there are no
     /// revisions, exactly once, with revision == None).
@@ -958,6 +965,8 @@ impl<'test> TestCx<'test> {
         local_pm: Option,
         passes: Vec,
     ) -> ProcRes {
+        let compiler_kind = self.compiler_kind_for_non_aux();
+
         // Only use `make_exe_name` when the test ends up being executed.
         let output_file = match will_execute {
             WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()),
@@ -973,7 +982,7 @@ impl<'test> TestCx<'test> {
                 // want to actually assert warnings about all this code. Instead
                 // let's just ignore unused code warnings by defaults and tests
                 // can turn it back on if needed.
-                if !self.is_rustdoc()
+                if compiler_kind == CompilerKind::Rustc
                     // Note that we use the local pass mode here as we don't want
                     // to set unused to allow if we've overridden the pass mode
                     // via command line flags.
@@ -988,6 +997,7 @@ impl<'test> TestCx<'test> {
         };
 
         let rustc = self.make_compile_args(
+            compiler_kind,
             &self.testpaths.file,
             output_file,
             emit,
@@ -1347,6 +1357,7 @@ impl<'test> TestCx<'test> {
     fn build_minicore(&self) -> Utf8PathBuf {
         let output_file_path = self.output_base_dir().join("libminicore.rlib");
         let mut rustc = self.make_compile_args(
+            CompilerKind::Rustc,
             &self.config.minicore_path,
             TargetLocation::ThisFile(output_file_path.clone()),
             Emit::None,
@@ -1404,6 +1415,8 @@ impl<'test> TestCx<'test> {
         // Create the directory for the stdout/stderr files.
         create_dir_all(aux_cx.output_base_dir()).unwrap();
         let mut aux_rustc = aux_cx.make_compile_args(
+            // Always use `rustc` for aux crates, even in rustdoc tests.
+            CompilerKind::Rustc,
             &aux_path,
             aux_output,
             Emit::None,
@@ -1554,15 +1567,41 @@ impl<'test> TestCx<'test> {
         result
     }
 
-    fn is_rustdoc(&self) -> bool {
-        matches!(
-            self.config.suite,
-            TestSuite::RustdocUi | TestSuite::RustdocJs | TestSuite::RustdocJson
-        )
+    /// Choose a compiler kind (rustc or rustdoc) for compiling test files,
+    /// based on the test suite being tested.
+    fn compiler_kind_for_non_aux(&self) -> CompilerKind {
+        match self.config.suite {
+            TestSuite::RustdocJs | TestSuite::RustdocJson | TestSuite::RustdocUi => {
+                CompilerKind::Rustdoc
+            }
+
+            // Exhaustively match all other suites.
+            // Note that some suites never actually use this method, so the
+            // return value for those suites is not necessarily meaningful.
+            TestSuite::AssemblyLlvm
+            | TestSuite::BuildStd
+            | TestSuite::CodegenLlvm
+            | TestSuite::CodegenUnits
+            | TestSuite::Coverage
+            | TestSuite::CoverageRunRustdoc
+            | TestSuite::Crashes
+            | TestSuite::Debuginfo
+            | TestSuite::Incremental
+            | TestSuite::MirOpt
+            | TestSuite::Pretty
+            | TestSuite::RunMake
+            | TestSuite::RunMakeCargo
+            | TestSuite::RustdocGui
+            | TestSuite::RustdocHtml
+            | TestSuite::RustdocJsStd
+            | TestSuite::Ui
+            | TestSuite::UiFullDeps => CompilerKind::Rustc,
+        }
     }
 
     fn make_compile_args(
         &self,
+        compiler_kind: CompilerKind,
         input_file: &Utf8Path,
         output_file: TargetLocation,
         emit: Emit,
@@ -1570,12 +1609,12 @@ impl<'test> TestCx<'test> {
         link_to_aux: LinkToAux,
         passes: Vec, // Vec of passes under mir-opt test to be dumped
     ) -> Command {
-        let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary");
-        let is_rustdoc = self.is_rustdoc() && !is_aux;
-        let mut rustc = if !is_rustdoc {
-            Command::new(&self.config.rustc_path)
-        } else {
-            Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
+        let is_rustdoc = compiler_kind == CompilerKind::Rustdoc;
+        let mut rustc = match compiler_kind {
+            CompilerKind::Rustc => Command::new(&self.config.rustc_path),
+            CompilerKind::Rustdoc => {
+                Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
+            }
         };
         rustc.arg(input_file);
 
@@ -2127,6 +2166,7 @@ impl<'test> TestCx<'test> {
         let output_path = self.output_base_name().with_extension("ll");
         let input_file = &self.testpaths.file;
         let rustc = self.make_compile_args(
+            CompilerKind::Rustc,
             input_file,
             TargetLocation::ThisFile(output_path.clone()),
             Emit::LlvmIr,
diff --git a/src/tools/compiletest/src/runtest/assembly.rs b/src/tools/compiletest/src/runtest/assembly.rs
index c805b4c7a59e..18027328abfe 100644
--- a/src/tools/compiletest/src/runtest/assembly.rs
+++ b/src/tools/compiletest/src/runtest/assembly.rs
@@ -1,6 +1,6 @@
 use camino::Utf8PathBuf;
 
-use super::{AllowUnused, Emit, LinkToAux, ProcRes, TargetLocation, TestCx};
+use crate::runtest::{AllowUnused, CompilerKind, Emit, LinkToAux, ProcRes, TargetLocation, TestCx};
 
 impl TestCx<'_> {
     pub(super) fn run_assembly_test(&self) {
@@ -35,6 +35,7 @@ impl TestCx<'_> {
         };
 
         let rustc = self.make_compile_args(
+            CompilerKind::Rustc,
             input_file,
             TargetLocation::ThisFile(output_path.clone()),
             emit,
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index 9147471c5bbc..31b80d0924da 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -5,12 +5,11 @@ use std::io::Write;
 use rustfix::{Filter, apply_suggestions, get_suggestions_from_json};
 use tracing::debug;
 
-use super::{
-    AllowUnused, Emit, FailMode, LinkToAux, PassMode, RunFailMode, RunResult, TargetLocation,
-    TestCx, TestOutput, Truncated, UI_FIXED, WillExecute,
-};
 use crate::json;
-use crate::runtest::ProcRes;
+use crate::runtest::{
+    AllowUnused, Emit, FailMode, LinkToAux, PassMode, ProcRes, RunFailMode, RunResult,
+    TargetLocation, TestCx, TestOutput, Truncated, UI_FIXED, WillExecute,
+};
 
 impl TestCx<'_> {
     pub(super) fn run_ui_test(&self) {
@@ -228,6 +227,7 @@ impl TestCx<'_> {
             // And finally, compile the fixed code and make sure it both
             // succeeds and has no diagnostics.
             let mut rustc = self.make_compile_args(
+                self.compiler_kind_for_non_aux(),
                 &self.expected_output_path(UI_FIXED),
                 TargetLocation::ThisFile(self.make_exe_name()),
                 emit_metadata,

From 0b42f38dc72e6479ea59d64d997475fa09555d74 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Mon, 26 Jan 2026 22:41:57 +1100
Subject: [PATCH 258/583] Replace `is_rustdoc` with clearer compiler-kind
 checks

---
 src/tools/compiletest/src/runtest.rs | 69 +++++++++++++++-------------
 1 file changed, 36 insertions(+), 33 deletions(-)

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 5686bfa8f614..5fcc97fbf537 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1609,7 +1609,8 @@ impl<'test> TestCx<'test> {
         link_to_aux: LinkToAux,
         passes: Vec, // Vec of passes under mir-opt test to be dumped
     ) -> Command {
-        let is_rustdoc = compiler_kind == CompilerKind::Rustdoc;
+        // FIXME(Zalathar): We should have a cleaner distinction between
+        // `rustc` flags, `rustdoc` flags, and flags shared by both.
         let mut rustc = match compiler_kind {
             CompilerKind::Rustc => Command::new(&self.config.rustc_path),
             CompilerKind::Rustdoc => {
@@ -1672,7 +1673,7 @@ impl<'test> TestCx<'test> {
         }
         self.set_revision_flags(&mut rustc);
 
-        if !is_rustdoc {
+        if compiler_kind == CompilerKind::Rustc {
             if let Some(ref incremental_dir) = self.props.incremental_dir {
                 rustc.args(&["-C", &format!("incremental={}", incremental_dir)]);
                 rustc.args(&["-Z", "incremental-verify-ich"]);
@@ -1683,7 +1684,7 @@ impl<'test> TestCx<'test> {
             }
         }
 
-        if self.config.optimize_tests && !is_rustdoc {
+        if self.config.optimize_tests && compiler_kind == CompilerKind::Rustc {
             match self.config.mode {
                 TestMode::Ui => {
                     // If optimize-tests is true we still only want to optimize tests that actually get
@@ -1825,27 +1826,28 @@ impl<'test> TestCx<'test> {
             ));
         }
 
-        match emit {
-            Emit::None => {}
-            Emit::Metadata if is_rustdoc => {}
-            Emit::Metadata => {
-                rustc.args(&["--emit", "metadata"]);
-            }
-            Emit::LlvmIr => {
-                rustc.args(&["--emit", "llvm-ir"]);
-            }
-            Emit::Mir => {
-                rustc.args(&["--emit", "mir"]);
-            }
-            Emit::Asm => {
-                rustc.args(&["--emit", "asm"]);
-            }
-            Emit::LinkArgsAsm => {
-                rustc.args(&["-Clink-args=--emit=asm"]);
+        if compiler_kind == CompilerKind::Rustc {
+            match emit {
+                Emit::None => {}
+                Emit::Metadata => {
+                    rustc.args(&["--emit", "metadata"]);
+                }
+                Emit::LlvmIr => {
+                    rustc.args(&["--emit", "llvm-ir"]);
+                }
+                Emit::Mir => {
+                    rustc.args(&["--emit", "mir"]);
+                }
+                Emit::Asm => {
+                    rustc.args(&["--emit", "asm"]);
+                }
+                Emit::LinkArgsAsm => {
+                    rustc.args(&["-Clink-args=--emit=asm"]);
+                }
             }
         }
 
-        if !is_rustdoc {
+        if compiler_kind == CompilerKind::Rustc {
             if self.config.target == "wasm32-unknown-unknown" || self.is_vxworks_pure_static() {
                 // rustc.arg("-g"); // get any backtrace at all on errors
             } else if !self.props.no_prefer_dynamic {
@@ -1860,14 +1862,15 @@ impl<'test> TestCx<'test> {
             TargetLocation::ThisFile(path) => {
                 rustc.arg("-o").arg(path);
             }
-            TargetLocation::ThisDirectory(path) => {
-                if is_rustdoc {
+            TargetLocation::ThisDirectory(path) => match compiler_kind {
+                CompilerKind::Rustdoc => {
                     // `rustdoc` uses `-o` for the output directory.
                     rustc.arg("-o").arg(path);
-                } else {
+                }
+                CompilerKind::Rustc => {
                     rustc.arg("--out-dir").arg(path);
                 }
-            }
+            },
         }
 
         match self.config.compare_mode {
@@ -1910,17 +1913,17 @@ impl<'test> TestCx<'test> {
 
         if self.props.force_host {
             self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags);
-            if !is_rustdoc {
-                if let Some(ref linker) = self.config.host_linker {
-                    rustc.arg(format!("-Clinker={}", linker));
-                }
+            if compiler_kind == CompilerKind::Rustc
+                && let Some(ref linker) = self.config.host_linker
+            {
+                rustc.arg(format!("-Clinker={linker}"));
             }
         } else {
             self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
-            if !is_rustdoc {
-                if let Some(ref linker) = self.config.target_linker {
-                    rustc.arg(format!("-Clinker={}", linker));
-                }
+            if compiler_kind == CompilerKind::Rustc
+                && let Some(ref linker) = self.config.target_linker
+            {
+                rustc.arg(format!("-Clinker={linker}"));
             }
         }
 

From 40d4d322b80c59acc60aa647a27334d1231a5646 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Tue, 27 Jan 2026 16:23:50 +1100
Subject: [PATCH 259/583] Rename `rustc` command variable to `compiler`

---
 src/tools/compiletest/src/runtest.rs | 141 ++++++++++++++-------------
 1 file changed, 71 insertions(+), 70 deletions(-)

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 5fcc97fbf537..e6eb1f3bd957 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1611,16 +1611,16 @@ impl<'test> TestCx<'test> {
     ) -> Command {
         // FIXME(Zalathar): We should have a cleaner distinction between
         // `rustc` flags, `rustdoc` flags, and flags shared by both.
-        let mut rustc = match compiler_kind {
+        let mut compiler = match compiler_kind {
             CompilerKind::Rustc => Command::new(&self.config.rustc_path),
             CompilerKind::Rustdoc => {
                 Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
             }
         };
-        rustc.arg(input_file);
+        compiler.arg(input_file);
 
         // Use a single thread for efficiency and a deterministic error message order
-        rustc.arg("-Zthreads=1");
+        compiler.arg("-Zthreads=1");
 
         // Hide libstd sources from ui tests to make sure we generate the stderr
         // output that users will see.
@@ -1630,19 +1630,19 @@ impl<'test> TestCx<'test> {
         // This also has the benefit of more effectively normalizing output between different
         // compilers, so that we don't have to know the `/rustc/$sha` output to normalize after the
         // fact.
-        rustc.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
-        rustc.arg("-Ztranslate-remapped-path-to-local-path=no");
+        compiler.arg("-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX");
+        compiler.arg("-Ztranslate-remapped-path-to-local-path=no");
 
         // Hide Cargo dependency sources from ui tests to make sure the error message doesn't
         // change depending on whether $CARGO_HOME is remapped or not. If this is not present,
         // when $CARGO_HOME is remapped the source won't be shown, and when it's not remapped the
         // source will be shown, causing a blessing hell.
-        rustc.arg("-Z").arg(format!(
+        compiler.arg("-Z").arg(format!(
             "ignore-directory-in-diagnostics-source-blocks={}",
             home::cargo_home().expect("failed to find cargo home").to_str().unwrap()
         ));
         // Similarly, vendored sources shouldn't be shown when running from a dist tarball.
-        rustc.arg("-Z").arg(format!(
+        compiler.arg("-Z").arg(format!(
             "ignore-directory-in-diagnostics-source-blocks={}",
             self.config.src_root.join("vendor"),
         ));
@@ -1654,12 +1654,12 @@ impl<'test> TestCx<'test> {
             && !self.config.host_rustcflags.iter().any(|flag| flag == "--sysroot")
         {
             // In stage 0, make sure we use `stage0-sysroot` instead of the bootstrap sysroot.
-            rustc.arg("--sysroot").arg(&self.config.sysroot_base);
+            compiler.arg("--sysroot").arg(&self.config.sysroot_base);
         }
 
         // If the provided codegen backend is not LLVM, we need to pass it.
         if let Some(ref backend) = self.config.override_codegen_backend {
-            rustc.arg(format!("-Zcodegen-backend={}", backend));
+            compiler.arg(format!("-Zcodegen-backend={}", backend));
         }
 
         // Optionally prevent default --target if specified in test compile-flags.
@@ -1669,18 +1669,18 @@ impl<'test> TestCx<'test> {
             let target =
                 if self.props.force_host { &*self.config.host } else { &*self.config.target };
 
-            rustc.arg(&format!("--target={}", target));
+            compiler.arg(&format!("--target={}", target));
         }
-        self.set_revision_flags(&mut rustc);
+        self.set_revision_flags(&mut compiler);
 
         if compiler_kind == CompilerKind::Rustc {
             if let Some(ref incremental_dir) = self.props.incremental_dir {
-                rustc.args(&["-C", &format!("incremental={}", incremental_dir)]);
-                rustc.args(&["-Z", "incremental-verify-ich"]);
+                compiler.args(&["-C", &format!("incremental={}", incremental_dir)]);
+                compiler.args(&["-Z", "incremental-verify-ich"]);
             }
 
             if self.config.mode == TestMode::CodegenUnits {
-                rustc.args(&["-Z", "human_readable_cgu_names"]);
+                compiler.args(&["-Z", "human_readable_cgu_names"]);
             }
         }
 
@@ -1698,7 +1698,7 @@ impl<'test> TestCx<'test> {
                             .iter()
                             .any(|arg| arg == "-O" || arg.contains("opt-level"))
                     {
-                        rustc.arg("-O");
+                        compiler.arg("-O");
                     }
                 }
                 TestMode::DebugInfo => { /* debuginfo tests must be unoptimized */ }
@@ -1709,7 +1709,7 @@ impl<'test> TestCx<'test> {
                     // compile flags (below) or in per-test `compile-flags`.
                 }
                 _ => {
-                    rustc.arg("-O");
+                    compiler.arg("-O");
                 }
             }
         }
@@ -1730,24 +1730,24 @@ impl<'test> TestCx<'test> {
                 if self.props.error_patterns.is_empty()
                     && self.props.regex_error_patterns.is_empty()
                 {
-                    rustc.args(&["--error-format", "json"]);
-                    rustc.args(&["--json", "future-incompat"]);
+                    compiler.args(&["--error-format", "json"]);
+                    compiler.args(&["--json", "future-incompat"]);
                 }
-                rustc.arg("-Zui-testing");
-                rustc.arg("-Zdeduplicate-diagnostics=no");
+                compiler.arg("-Zui-testing");
+                compiler.arg("-Zdeduplicate-diagnostics=no");
             }
             TestMode::Ui => {
                 if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
-                    rustc.args(&["--error-format", "json"]);
-                    rustc.args(&["--json", "future-incompat"]);
+                    compiler.args(&["--error-format", "json"]);
+                    compiler.args(&["--json", "future-incompat"]);
                 }
-                rustc.arg("-Ccodegen-units=1");
+                compiler.arg("-Ccodegen-units=1");
                 // Hide line numbers to reduce churn
-                rustc.arg("-Zui-testing");
-                rustc.arg("-Zdeduplicate-diagnostics=no");
-                rustc.arg("-Zwrite-long-types-to-disk=no");
+                compiler.arg("-Zui-testing");
+                compiler.arg("-Zdeduplicate-diagnostics=no");
+                compiler.arg("-Zwrite-long-types-to-disk=no");
                 // FIXME: use this for other modes too, for perf?
-                rustc.arg("-Cstrip=debuginfo");
+                compiler.arg("-Cstrip=debuginfo");
             }
             TestMode::MirOpt => {
                 // We check passes under test to minimize the mir-opt test dump
@@ -1759,7 +1759,7 @@ impl<'test> TestCx<'test> {
                     "-Zdump-mir=all".to_string()
                 };
 
-                rustc.args(&[
+                compiler.args(&[
                     "-Copt-level=1",
                     &zdump_arg,
                     "-Zvalidate-mir",
@@ -1769,45 +1769,46 @@ impl<'test> TestCx<'test> {
                     "--crate-type=rlib",
                 ]);
                 if let Some(pass) = &self.props.mir_unit_test {
-                    rustc.args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
+                    compiler
+                        .args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
                 } else {
-                    rustc.args(&[
+                    compiler.args(&[
                         "-Zmir-opt-level=4",
                         "-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals",
                     ]);
                 }
 
-                set_mir_dump_dir(&mut rustc);
+                set_mir_dump_dir(&mut compiler);
             }
             TestMode::CoverageMap => {
-                rustc.arg("-Cinstrument-coverage");
+                compiler.arg("-Cinstrument-coverage");
                 // These tests only compile to LLVM IR, so they don't need the
                 // profiler runtime to be present.
-                rustc.arg("-Zno-profiler-runtime");
+                compiler.arg("-Zno-profiler-runtime");
                 // Coverage mappings are sensitive to MIR optimizations, and
                 // the current snapshots assume `opt-level=2` unless overridden
                 // by `compile-flags`.
-                rustc.arg("-Copt-level=2");
+                compiler.arg("-Copt-level=2");
             }
             TestMode::CoverageRun => {
-                rustc.arg("-Cinstrument-coverage");
+                compiler.arg("-Cinstrument-coverage");
                 // Coverage reports are sometimes sensitive to optimizations,
                 // and the current snapshots assume `opt-level=2` unless
                 // overridden by `compile-flags`.
-                rustc.arg("-Copt-level=2");
+                compiler.arg("-Copt-level=2");
             }
             TestMode::Assembly | TestMode::Codegen => {
-                rustc.arg("-Cdebug-assertions=no");
+                compiler.arg("-Cdebug-assertions=no");
                 // For assembly and codegen tests, we want to use the same order
                 // of the items of a codegen unit as the source order, so that
                 // we can compare the output with the source code through filecheck.
-                rustc.arg("-Zcodegen-source-order");
+                compiler.arg("-Zcodegen-source-order");
             }
             TestMode::Crashes => {
-                set_mir_dump_dir(&mut rustc);
+                set_mir_dump_dir(&mut compiler);
             }
             TestMode::CodegenUnits => {
-                rustc.arg("-Zprint-mono-items");
+                compiler.arg("-Zprint-mono-items");
             }
             TestMode::Pretty
             | TestMode::DebugInfo
@@ -1820,7 +1821,7 @@ impl<'test> TestCx<'test> {
         }
 
         if self.props.remap_src_base {
-            rustc.arg(format!(
+            compiler.arg(format!(
                 "--remap-path-prefix={}={}",
                 self.config.src_test_suite_root, FAKE_SRC_BASE,
             ));
@@ -1830,19 +1831,19 @@ impl<'test> TestCx<'test> {
             match emit {
                 Emit::None => {}
                 Emit::Metadata => {
-                    rustc.args(&["--emit", "metadata"]);
+                    compiler.args(&["--emit", "metadata"]);
                 }
                 Emit::LlvmIr => {
-                    rustc.args(&["--emit", "llvm-ir"]);
+                    compiler.args(&["--emit", "llvm-ir"]);
                 }
                 Emit::Mir => {
-                    rustc.args(&["--emit", "mir"]);
+                    compiler.args(&["--emit", "mir"]);
                 }
                 Emit::Asm => {
-                    rustc.args(&["--emit", "asm"]);
+                    compiler.args(&["--emit", "asm"]);
                 }
                 Emit::LinkArgsAsm => {
-                    rustc.args(&["-Clink-args=--emit=asm"]);
+                    compiler.args(&["-Clink-args=--emit=asm"]);
                 }
             }
         }
@@ -1851,7 +1852,7 @@ impl<'test> TestCx<'test> {
             if self.config.target == "wasm32-unknown-unknown" || self.is_vxworks_pure_static() {
                 // rustc.arg("-g"); // get any backtrace at all on errors
             } else if !self.props.no_prefer_dynamic {
-                rustc.args(&["-C", "prefer-dynamic"]);
+                compiler.args(&["-C", "prefer-dynamic"]);
             }
         }
 
@@ -1860,37 +1861,37 @@ impl<'test> TestCx<'test> {
             // avoid a compiler warning about `--out-dir` being ignored.
             _ if self.props.compile_flags.iter().any(|flag| flag == "-o") => {}
             TargetLocation::ThisFile(path) => {
-                rustc.arg("-o").arg(path);
+                compiler.arg("-o").arg(path);
             }
             TargetLocation::ThisDirectory(path) => match compiler_kind {
                 CompilerKind::Rustdoc => {
                     // `rustdoc` uses `-o` for the output directory.
-                    rustc.arg("-o").arg(path);
+                    compiler.arg("-o").arg(path);
                 }
                 CompilerKind::Rustc => {
-                    rustc.arg("--out-dir").arg(path);
+                    compiler.arg("--out-dir").arg(path);
                 }
             },
         }
 
         match self.config.compare_mode {
             Some(CompareMode::Polonius) => {
-                rustc.args(&["-Zpolonius=next"]);
+                compiler.args(&["-Zpolonius=next"]);
             }
             Some(CompareMode::NextSolver) => {
-                rustc.args(&["-Znext-solver"]);
+                compiler.args(&["-Znext-solver"]);
             }
             Some(CompareMode::NextSolverCoherence) => {
-                rustc.args(&["-Znext-solver=coherence"]);
+                compiler.args(&["-Znext-solver=coherence"]);
             }
             Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => {
-                rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
+                compiler.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
             }
             Some(CompareMode::SplitDwarf) => {
-                rustc.args(&["-Csplit-debuginfo=unpacked"]);
+                compiler.args(&["-Csplit-debuginfo=unpacked"]);
             }
             Some(CompareMode::SplitDwarfSingle) => {
-                rustc.args(&["-Csplit-debuginfo=packed"]);
+                compiler.args(&["-Csplit-debuginfo=packed"]);
             }
             None => {}
         }
@@ -1899,44 +1900,44 @@ impl<'test> TestCx<'test> {
         // overwrite this.
         // Don't allow `unused_attributes` since these are usually actual mistakes, rather than just unused code.
         if let AllowUnused::Yes = allow_unused {
-            rustc.args(&["-A", "unused", "-W", "unused_attributes"]);
+            compiler.args(&["-A", "unused", "-W", "unused_attributes"]);
         }
 
         // Allow tests to use internal features.
-        rustc.args(&["-A", "internal_features"]);
+        compiler.args(&["-A", "internal_features"]);
 
         // Allow tests to have unused parens and braces.
         // Add #![deny(unused_parens, unused_braces)] to the test file if you want to
         // test that these lints are working.
-        rustc.args(&["-A", "unused_parens"]);
-        rustc.args(&["-A", "unused_braces"]);
+        compiler.args(&["-A", "unused_parens"]);
+        compiler.args(&["-A", "unused_braces"]);
 
         if self.props.force_host {
-            self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags);
+            self.maybe_add_external_args(&mut compiler, &self.config.host_rustcflags);
             if compiler_kind == CompilerKind::Rustc
                 && let Some(ref linker) = self.config.host_linker
             {
-                rustc.arg(format!("-Clinker={linker}"));
+                compiler.arg(format!("-Clinker={linker}"));
             }
         } else {
-            self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
+            self.maybe_add_external_args(&mut compiler, &self.config.target_rustcflags);
             if compiler_kind == CompilerKind::Rustc
                 && let Some(ref linker) = self.config.target_linker
             {
-                rustc.arg(format!("-Clinker={linker}"));
+                compiler.arg(format!("-Clinker={linker}"));
             }
         }
 
         // Use dynamic musl for tests because static doesn't allow creating dylibs
         if self.config.host.contains("musl") || self.is_vxworks_pure_dynamic() {
-            rustc.arg("-Ctarget-feature=-crt-static");
+            compiler.arg("-Ctarget-feature=-crt-static");
         }
 
         if let LinkToAux::Yes = link_to_aux {
             // if we pass an `-L` argument to a directory that doesn't exist,
             // macOS ld emits warnings which disrupt the .stderr files
             if self.has_aux_dir() {
-                rustc.arg("-L").arg(self.aux_output_dir_name());
+                compiler.arg("-L").arg(self.aux_output_dir_name());
             }
         }
 
@@ -1950,13 +1951,13 @@ impl<'test> TestCx<'test> {
         //
         // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics.
         if self.props.add_minicore {
-            rustc.arg("-Cpanic=abort");
-            rustc.arg("-Cforce-unwind-tables=yes");
+            compiler.arg("-Cpanic=abort");
+            compiler.arg("-Cforce-unwind-tables=yes");
         }
 
-        rustc.args(&self.props.compile_flags);
+        compiler.args(&self.props.compile_flags);
 
-        rustc
+        compiler
     }
 
     fn make_exe_name(&self) -> Utf8PathBuf {

From bcb76f4ea6ee380972b1187423def5091c4ef25c Mon Sep 17 00:00:00 2001
From: zakie 
Date: Tue, 27 Jan 2026 14:57:42 +0900
Subject: [PATCH 260/583] docs: fix outdated Debian Ports ISO reference

---
 src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
index 1efea86df92b..5334a176027f 100644
--- a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
+++ b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
@@ -52,7 +52,7 @@ Atari systems or emulated environments such as QEMU version 4.2 or newer or ARAn
 ISO images for installation are provided by the Debian Ports team and can be obtained
 from the Debian CD image server available at:
 
-[https://cdimage.debian.org/cdimage/ports/current](https://cdimage.debian.org/cdimage/ports/current/)
+[https://cdimage.debian.org/cdimage/ports/](https://cdimage.debian.org/cdimage/ports/)
 
 Documentation for Debian/m68k is available on the Debian Wiki at:
 

From 50a9b17d7c7d291ba1e8ad181a4e90e87247fd2f Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Mon, 26 Jan 2026 22:07:44 -0800
Subject: [PATCH 261/583] Remove duplicated code in `slice/index.rs`

Looks like `const fn` is far enough along now that we can just not have these two copies any more, and have one call the other.
---
 library/core/src/slice/index.rs | 42 ++++-----------------------------
 1 file changed, 4 insertions(+), 38 deletions(-)

diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs
index d8ed521f4435..097310dc8fb0 100644
--- a/library/core/src/slice/index.rs
+++ b/library/core/src/slice/index.rs
@@ -910,29 +910,7 @@ where
     R: [const] ops::RangeBounds + [const] Destruct,
 {
     let len = bounds.end;
-
-    let end = match range.end_bound() {
-        ops::Bound::Included(&end) if end >= len => slice_index_fail(0, end, len),
-        // Cannot overflow because `end < len` implies `end < usize::MAX`.
-        ops::Bound::Included(&end) => end + 1,
-
-        ops::Bound::Excluded(&end) if end > len => slice_index_fail(0, end, len),
-        ops::Bound::Excluded(&end) => end,
-        ops::Bound::Unbounded => len,
-    };
-
-    let start = match range.start_bound() {
-        ops::Bound::Excluded(&start) if start >= end => slice_index_fail(start, end, len),
-        // Cannot overflow because `start < end` implies `start < usize::MAX`.
-        ops::Bound::Excluded(&start) => start + 1,
-
-        ops::Bound::Included(&start) if start > end => slice_index_fail(start, end, len),
-        ops::Bound::Included(&start) => start,
-
-        ops::Bound::Unbounded => 0,
-    };
-
-    ops::Range { start, end }
+    into_slice_range(len, (range.start_bound().cloned(), range.end_bound().cloned()))
 }
 
 /// Performs bounds checking of a range without panicking.
@@ -972,20 +950,8 @@ where
     R: ops::RangeBounds,
 {
     let len = bounds.end;
-
-    let start = match range.start_bound() {
-        ops::Bound::Included(&start) => start,
-        ops::Bound::Excluded(start) => start.checked_add(1)?,
-        ops::Bound::Unbounded => 0,
-    };
-
-    let end = match range.end_bound() {
-        ops::Bound::Included(end) => end.checked_add(1)?,
-        ops::Bound::Excluded(&end) => end,
-        ops::Bound::Unbounded => len,
-    };
-
-    if start > end || end > len { None } else { Some(ops::Range { start, end }) }
+    let r = into_range(len, (range.start_bound().cloned(), range.end_bound().cloned()))?;
+    if r.start > r.end || r.end > len { None } else { Some(r) }
 }
 
 /// Converts a pair of `ops::Bound`s into `ops::Range` without performing any
@@ -1036,7 +1002,7 @@ pub(crate) const fn into_range(
 
 /// Converts pair of `ops::Bound`s into `ops::Range`.
 /// Panics on overflowing indices.
-pub(crate) fn into_slice_range(
+pub(crate) const fn into_slice_range(
     len: usize,
     (start, end): (ops::Bound, ops::Bound),
 ) -> ops::Range {

From 6664fc61946f5bc3c2f8e372310ce83ac41e2ab0 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Tue, 27 Jan 2026 08:57:25 +0100
Subject: [PATCH 262/583] minor: Downgrade noisy log

---
 src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 62a3b3a17bdf..71c4b2accce9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -309,10 +309,10 @@ impl GlobalState {
 
         let event_dbg_msg = format!("{event:?}");
         tracing::debug!(?loop_start, ?event, "handle_event");
-        if tracing::enabled!(tracing::Level::INFO) {
+        if tracing::enabled!(tracing::Level::TRACE) {
             let task_queue_len = self.task_pool.handle.len();
             if task_queue_len > 0 {
-                tracing::info!("task queue len: {}", task_queue_len);
+                tracing::trace!("task queue len: {}", task_queue_len);
             }
         }
 

From 51de309db24a8fd25df589987bb6e37d3253c214 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Tue, 27 Jan 2026 00:10:12 -0800
Subject: [PATCH 263/583] Tweak `SlicePartialEq` to allow MIR-inlining the
 `compare_bytes` call

150265 disabled this because it was a net perf win, but let's see if we can tweak the structure of this to allow more inlining on this side while still not MIR-inlining the loop when it's not just `memcmp`.

This should also allow MIR-inlining the length check, which was previously blocked.
---
 library/core/src/slice/cmp.rs                 |  56 ++--
 ..._conditions.JumpThreading.panic-abort.diff | 290 +++++++++++++-----
 ...conditions.JumpThreading.panic-unwind.diff | 290 +++++++++++++-----
 3 files changed, 455 insertions(+), 181 deletions(-)

diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs
index c3ff928a3277..2390ca74a8e0 100644
--- a/library/core/src/slice/cmp.rs
+++ b/library/core/src/slice/cmp.rs
@@ -4,6 +4,7 @@ use super::{from_raw_parts, memchr};
 use crate::ascii;
 use crate::cmp::{self, BytewiseEq, Ordering};
 use crate::intrinsics::compare_bytes;
+use crate::mem::SizedTypeProperties;
 use crate::num::NonZero;
 use crate::ops::ControlFlow;
 
@@ -15,7 +16,14 @@ where
 {
     #[inline]
     fn eq(&self, other: &[U]) -> bool {
-        SlicePartialEq::equal(self, other)
+        let len = self.len();
+        if len == other.len() {
+            // SAFETY: Just checked that they're the same length, and the pointers
+            // come from references-to-slices so they're guaranteed readable.
+            unsafe { SlicePartialEq::equal_same_length(self.as_ptr(), other.as_ptr(), len) }
+        } else {
+            false
+        }
     }
 }
 
@@ -95,12 +103,14 @@ impl PartialOrd for [T] {
 // intermediate trait for specialization of slice's PartialEq
 #[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
 const trait SlicePartialEq {
-    fn equal(&self, other: &[B]) -> bool;
+    /// # Safety
+    /// `lhs` and `rhs` are both readable for `len` elements
+    unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool;
 }
 
 // Generic slice equality
 #[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
-impl const SlicePartialEq for [A]
+impl const SlicePartialEq for A
 where
     A: [const] PartialEq,
 {
@@ -109,19 +119,15 @@ where
     // such as in `::eq`.
     // The codegen backend can still inline it later if needed.
     #[rustc_no_mir_inline]
-    default fn equal(&self, other: &[B]) -> bool {
-        if self.len() != other.len() {
-            return false;
-        }
-
+    default unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool {
         // Implemented as explicit indexing rather
         // than zipped iterators for performance reasons.
         // See PR https://github.com/rust-lang/rust/pull/116846
-        // FIXME(const_hack): make this a `for idx in 0..self.len()` loop.
+        // FIXME(const_hack): make this a `for idx in 0..len` loop.
         let mut idx = 0;
-        while idx < self.len() {
-            // bound checks are optimized away
-            if self[idx] != other[idx] {
+        while idx < len {
+            // SAFETY: idx < len, so both are in-bounds and readable
+            if unsafe { *lhs.add(idx) != *rhs.add(idx) } {
                 return false;
             }
             idx += 1;
@@ -134,30 +140,18 @@ where
 // When each element can be compared byte-wise, we can compare all the bytes
 // from the whole size in one call to the intrinsics.
 #[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
-impl const SlicePartialEq for [A]
+impl const SlicePartialEq for A
 where
     A: [const] BytewiseEq,
 {
-    // This is usually a pretty good backend inlining candidate because the
-    // intrinsic tends to just be `memcmp`.  However, as of 2025-12 letting
-    // MIR inline this makes reuse worse because it means that, for example,
-    // `String::eq` doesn't inline, whereas by keeping this from inling all
-    // the wrappers until the call to this disappear.  If the heuristics have
-    // changed and this is no longer fruitful, though, please do remove it.
-    // In the mean time, it's fine to not inline it in MIR because the backend
-    // will still inline it if it things it's important to do so.
-    #[rustc_no_mir_inline]
     #[inline]
-    fn equal(&self, other: &[B]) -> bool {
-        if self.len() != other.len() {
-            return false;
-        }
-
-        // SAFETY: `self` and `other` are references and are thus guaranteed to be valid.
-        // The two slices have been checked to have the same size above.
+    unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool {
+        // SAFETY: by our precondition, `lhs` and `rhs` are guaranteed to be valid
+        // for reading `len` values, which also means the size is guaranteed
+        // not to overflow because it exists in memory;
         unsafe {
-            let size = size_of_val(self);
-            compare_bytes(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0
+            let size = crate::intrinsics::unchecked_mul(len, Self::SIZE);
+            compare_bytes(lhs as _, rhs as _, size) == 0
         }
     }
 }
diff --git a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff
index 6c9cf0cf623b..a0aeb0a30f41 100644
--- a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff
+++ b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff
@@ -93,64 +93,100 @@
                   }
                   scope 25 (inlined std::cmp::impls::::eq) {
                       scope 26 (inlined core::slice::cmp::::eq) {
+                          let _39: usize;
+                          let mut _40: bool;
+                          let mut _41: usize;
+                          let mut _42: *const u8;
+                          let mut _43: *const u8;
+                          scope 27 {
+                              scope 28 (inlined core::slice::::as_ptr) {
+                                  let mut _44: *const [u8];
+                              }
+                              scope 29 (inlined core::slice::::as_ptr) {
+                                  let mut _45: *const [u8];
+                              }
+                              scope 30 (inlined >::equal_same_length) {
+                                  let mut _46: i32;
+                                  scope 31 {
+                                  }
+                              }
+                          }
                       }
                   }
               }
           }
       }
-      scope 27 (inlined std::cmp::impls:: for &String>::eq) {
-          let mut _39: &std::string::String;
-          let mut _40: &str;
-          scope 28 (inlined >::eq) {
-              scope 29 (inlined #[track_caller] >::index) {
-                  let _41: &str;
-                  scope 30 (inlined String::as_str) {
-                      let _42: &[u8];
-                      scope 31 (inlined Vec::::as_slice) {
-                          let _43: *const [u8];
-                          let mut _44: *const u8;
-                          let mut _45: usize;
-                          scope 32 (inlined Vec::::as_ptr) {
-                              scope 33 (inlined alloc::raw_vec::RawVec::::ptr) {
-                                  scope 34 (inlined alloc::raw_vec::RawVecInner::ptr::) {
-                                      scope 35 (inlined alloc::raw_vec::RawVecInner::non_null::) {
-                                          let mut _46: std::ptr::NonNull;
-                                          scope 36 (inlined std::ptr::Unique::::cast::) {
-                                              scope 37 (inlined NonNull::::cast::) {
-                                                  scope 38 (inlined NonNull::::as_ptr) {
+      scope 32 (inlined std::cmp::impls:: for &String>::eq) {
+          let mut _47: &std::string::String;
+          let mut _48: &str;
+          scope 33 (inlined >::eq) {
+              scope 34 (inlined #[track_caller] >::index) {
+                  let _49: &str;
+                  scope 35 (inlined String::as_str) {
+                      let _50: &[u8];
+                      scope 36 (inlined Vec::::as_slice) {
+                          let _51: *const [u8];
+                          let mut _52: *const u8;
+                          let mut _53: usize;
+                          scope 37 (inlined Vec::::as_ptr) {
+                              scope 38 (inlined alloc::raw_vec::RawVec::::ptr) {
+                                  scope 39 (inlined alloc::raw_vec::RawVecInner::ptr::) {
+                                      scope 40 (inlined alloc::raw_vec::RawVecInner::non_null::) {
+                                          let mut _54: std::ptr::NonNull;
+                                          scope 41 (inlined std::ptr::Unique::::cast::) {
+                                              scope 42 (inlined NonNull::::cast::) {
+                                                  scope 43 (inlined NonNull::::as_ptr) {
                                                   }
                                               }
                                           }
-                                          scope 39 (inlined std::ptr::Unique::::as_non_null_ptr) {
+                                          scope 44 (inlined std::ptr::Unique::::as_non_null_ptr) {
                                           }
                                       }
-                                      scope 40 (inlined NonNull::::as_ptr) {
+                                      scope 45 (inlined NonNull::::as_ptr) {
                                       }
                                   }
                               }
                           }
                       }
-                      scope 41 (inlined from_utf8_unchecked) {
+                      scope 46 (inlined from_utf8_unchecked) {
                       }
                   }
-                  scope 42 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) {
+                  scope 47 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) {
                   }
               }
-              scope 43 (inlined #[track_caller] core::str::traits:: for str>::index) {
-                  scope 44 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) {
+              scope 48 (inlined #[track_caller] core::str::traits:: for str>::index) {
+                  scope 49 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) {
                   }
               }
-              scope 45 (inlined core::str::traits::::eq) {
-                  let mut _47: &&[u8];
-                  let _48: &[u8];
-                  let mut _49: &&[u8];
-                  let _50: &[u8];
-                  scope 46 (inlined core::str::::as_bytes) {
+              scope 50 (inlined core::str::traits::::eq) {
+                  let mut _55: &&[u8];
+                  let _56: &[u8];
+                  let mut _57: &&[u8];
+                  let _58: &[u8];
+                  scope 51 (inlined core::str::::as_bytes) {
                   }
-                  scope 47 (inlined core::str::::as_bytes) {
+                  scope 52 (inlined core::str::::as_bytes) {
                   }
-                  scope 48 (inlined std::cmp::impls::::eq) {
-                      scope 49 (inlined core::slice::cmp::::eq) {
+                  scope 53 (inlined std::cmp::impls::::eq) {
+                      scope 54 (inlined core::slice::cmp::::eq) {
+                          let _59: usize;
+                          let mut _60: bool;
+                          let mut _61: usize;
+                          let mut _62: *const u8;
+                          let mut _63: *const u8;
+                          scope 55 {
+                              scope 56 (inlined core::slice::::as_ptr) {
+                                  let mut _64: *const [u8];
+                              }
+                              scope 57 (inlined core::slice::::as_ptr) {
+                                  let mut _65: *const [u8];
+                              }
+                              scope 58 (inlined >::equal_same_length) {
+                                  let mut _66: i32;
+                                  scope 59 {
+                                  }
+                              }
+                          }
                       }
                   }
               }
@@ -179,7 +215,7 @@
       bb3: {
           _1 = chained_conditions::BacktraceStyle::Off;
 -         goto -> bb18;
-+         goto -> bb23;
++         goto -> bb29;
       }
   
       bb4: {
@@ -216,9 +252,17 @@
           StorageDead(_30);
           StorageLive(_36);
           StorageLive(_38);
+          StorageLive(_39);
+          StorageLive(_42);
+          StorageLive(_43);
           _36 = copy _29 as &[u8] (Transmute);
           _38 = copy _28 as &[u8] (Transmute);
-          _7 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _36, move _38) -> [return: bb19, unwind unreachable];
+          _39 = PtrMetadata(copy _36);
+          StorageLive(_40);
+          StorageLive(_41);
+          _41 = PtrMetadata(copy _38);
+          _40 = Eq(copy _39, move _41);
+          switchInt(move _40) -> [0: bb20, otherwise: bb19];
       }
   
       bb5: {
@@ -249,39 +293,47 @@
           StorageLive(_17);
           _20 = const chained_conditions::promoted[0];
           _17 = &(*_20);
-          StorageLive(_39);
-          StorageLive(_40);
-          _39 = copy (*_15);
-          _40 = copy (*_17);
-          StorageLive(_41);
-          StorageLive(_42);
-          StorageLive(_43);
-          StorageLive(_44);
-          StorageLive(_46);
-          _46 = copy ((((((*_39).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull);
-          _44 = copy _46 as *const u8 (Transmute);
-          StorageDead(_46);
-          StorageLive(_45);
-          _45 = copy (((*_39).0: std::vec::Vec).1: usize);
-          _43 = *const [u8] from (copy _44, move _45);
-          StorageDead(_45);
-          StorageDead(_44);
-          _42 = &(*_43);
-          StorageDead(_43);
-          _41 = copy _42 as &str (Transmute);
-          StorageDead(_42);
+          StorageLive(_47);
           StorageLive(_48);
+          _47 = copy (*_15);
+          _48 = copy (*_17);
+          StorageLive(_49);
           StorageLive(_50);
-          _48 = copy _41 as &[u8] (Transmute);
-          _50 = copy _40 as &[u8] (Transmute);
-          _14 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _48, move _50) -> [return: bb20, unwind unreachable];
+          StorageLive(_51);
+          StorageLive(_52);
+          StorageLive(_54);
+          _54 = copy ((((((*_47).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull);
+          _52 = copy _54 as *const u8 (Transmute);
+          StorageDead(_54);
+          StorageLive(_53);
+          _53 = copy (((*_47).0: std::vec::Vec).1: usize);
+          _51 = *const [u8] from (copy _52, move _53);
+          StorageDead(_53);
+          StorageDead(_52);
+          _50 = &(*_51);
+          StorageDead(_51);
+          _49 = copy _50 as &str (Transmute);
+          StorageDead(_50);
+          StorageLive(_56);
+          StorageLive(_58);
+          StorageLive(_59);
+          StorageLive(_62);
+          StorageLive(_63);
+          _56 = copy _49 as &[u8] (Transmute);
+          _58 = copy _48 as &[u8] (Transmute);
+          _59 = PtrMetadata(copy _56);
+          StorageLive(_60);
+          StorageLive(_61);
+          _61 = PtrMetadata(copy _58);
+          _60 = Eq(copy _59, move _61);
+          switchInt(move _60) -> [0: bb24, otherwise: bb23];
       }
   
       bb7: {
           StorageDead(_5);
           StorageDead(_6);
 -         goto -> bb18;
-+         goto -> bb21;
++         goto -> bb27;
       }
   
       bb8: {
@@ -304,14 +356,14 @@
           StorageDead(_13);
           _1 = chained_conditions::BacktraceStyle::Short;
 -         goto -> bb18;
-+         goto -> bb23;
++         goto -> bb29;
       }
   
       bb10: {
           StorageDead(_12);
           StorageDead(_13);
 -         goto -> bb18;
-+         goto -> bb21;
++         goto -> bb27;
       }
   
       bb11: {
@@ -356,6 +408,31 @@
       }
   
       bb19: {
+          StorageDead(_41);
+          StorageLive(_44);
+          _44 = &raw const (*_36);
+          _42 = copy _44 as *const u8 (PtrToPtr);
+          StorageDead(_44);
+          StorageLive(_45);
+          _45 = &raw const (*_38);
+          _43 = copy _45 as *const u8 (PtrToPtr);
+          StorageDead(_45);
+          StorageLive(_46);
+          _46 = compare_bytes(move _42, move _43, move _39) -> [return: bb22, unwind unreachable];
+      }
+  
+      bb20: {
+          StorageDead(_41);
+          _7 = const false;
+-         goto -> bb21;
++         goto -> bb32;
+      }
+  
+      bb21: {
+          StorageDead(_40);
+          StorageDead(_43);
+          StorageDead(_42);
+          StorageDead(_39);
           StorageDead(_38);
           StorageDead(_36);
           StorageDead(_29);
@@ -364,31 +441,94 @@
           switchInt(move _7) -> [0: bb6, otherwise: bb5];
       }
   
-      bb20: {
-          StorageDead(_50);
+      bb22: {
+          _7 = Eq(move _46, const 0_i32);
+          StorageDead(_46);
+          goto -> bb21;
+      }
+  
+      bb23: {
+          StorageDead(_61);
+          StorageLive(_64);
+          _64 = &raw const (*_56);
+          _62 = copy _64 as *const u8 (PtrToPtr);
+          StorageDead(_64);
+          StorageLive(_65);
+          _65 = &raw const (*_58);
+          _63 = copy _65 as *const u8 (PtrToPtr);
+          StorageDead(_65);
+          StorageLive(_66);
+          _66 = compare_bytes(move _62, move _63, move _59) -> [return: bb26, unwind unreachable];
+      }
+  
+      bb24: {
+          StorageDead(_61);
+          _14 = const false;
+-         goto -> bb25;
++         goto -> bb31;
+      }
+  
+      bb25: {
+          StorageDead(_60);
+          StorageDead(_63);
+          StorageDead(_62);
+          StorageDead(_59);
+          StorageDead(_58);
+          StorageDead(_56);
+          StorageDead(_49);
           StorageDead(_48);
-          StorageDead(_41);
-          StorageDead(_40);
-          StorageDead(_39);
+          StorageDead(_47);
           switchInt(move _14) -> [0: bb9, otherwise: bb8];
+      }
+  
+      bb26: {
+          _14 = Eq(move _66, const 0_i32);
+          StorageDead(_66);
+          goto -> bb25;
 +     }
 + 
-+     bb21: {
++     bb27: {
 +         _24 = discriminant(_2);
-+         switchInt(move _24) -> [1: bb22, otherwise: bb15];
++         switchInt(move _24) -> [1: bb28, otherwise: bb15];
 +     }
 + 
-+     bb22: {
++     bb28: {
 +         goto -> bb15;
 +     }
 + 
-+     bb23: {
++     bb29: {
 +         _24 = discriminant(_2);
-+         switchInt(move _24) -> [1: bb24, otherwise: bb15];
++         switchInt(move _24) -> [1: bb30, otherwise: bb15];
 +     }
 + 
-+     bb24: {
++     bb30: {
 +         goto -> bb17;
++     }
++ 
++     bb31: {
++         StorageDead(_60);
++         StorageDead(_63);
++         StorageDead(_62);
++         StorageDead(_59);
++         StorageDead(_58);
++         StorageDead(_56);
++         StorageDead(_49);
++         StorageDead(_48);
++         StorageDead(_47);
++         goto -> bb9;
++     }
++ 
++     bb32: {
++         StorageDead(_40);
++         StorageDead(_43);
++         StorageDead(_42);
++         StorageDead(_39);
++         StorageDead(_38);
++         StorageDead(_36);
++         StorageDead(_29);
++         StorageDead(_28);
++         StorageDead(_27);
++         goto -> bb6;
       }
   }
   
diff --git a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff
index 49cd68577a12..71d91fed6307 100644
--- a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff
+++ b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff
@@ -93,64 +93,100 @@
                   }
                   scope 25 (inlined std::cmp::impls::::eq) {
                       scope 26 (inlined core::slice::cmp::::eq) {
+                          let _39: usize;
+                          let mut _40: bool;
+                          let mut _41: usize;
+                          let mut _42: *const u8;
+                          let mut _43: *const u8;
+                          scope 27 {
+                              scope 28 (inlined core::slice::::as_ptr) {
+                                  let mut _44: *const [u8];
+                              }
+                              scope 29 (inlined core::slice::::as_ptr) {
+                                  let mut _45: *const [u8];
+                              }
+                              scope 30 (inlined >::equal_same_length) {
+                                  let mut _46: i32;
+                                  scope 31 {
+                                  }
+                              }
+                          }
                       }
                   }
               }
           }
       }
-      scope 27 (inlined std::cmp::impls:: for &String>::eq) {
-          let mut _39: &std::string::String;
-          let mut _40: &str;
-          scope 28 (inlined >::eq) {
-              scope 29 (inlined #[track_caller] >::index) {
-                  let _41: &str;
-                  scope 30 (inlined String::as_str) {
-                      let _42: &[u8];
-                      scope 31 (inlined Vec::::as_slice) {
-                          let _43: *const [u8];
-                          let mut _44: *const u8;
-                          let mut _45: usize;
-                          scope 32 (inlined Vec::::as_ptr) {
-                              scope 33 (inlined alloc::raw_vec::RawVec::::ptr) {
-                                  scope 34 (inlined alloc::raw_vec::RawVecInner::ptr::) {
-                                      scope 35 (inlined alloc::raw_vec::RawVecInner::non_null::) {
-                                          let mut _46: std::ptr::NonNull;
-                                          scope 36 (inlined std::ptr::Unique::::cast::) {
-                                              scope 37 (inlined NonNull::::cast::) {
-                                                  scope 38 (inlined NonNull::::as_ptr) {
+      scope 32 (inlined std::cmp::impls:: for &String>::eq) {
+          let mut _47: &std::string::String;
+          let mut _48: &str;
+          scope 33 (inlined >::eq) {
+              scope 34 (inlined #[track_caller] >::index) {
+                  let _49: &str;
+                  scope 35 (inlined String::as_str) {
+                      let _50: &[u8];
+                      scope 36 (inlined Vec::::as_slice) {
+                          let _51: *const [u8];
+                          let mut _52: *const u8;
+                          let mut _53: usize;
+                          scope 37 (inlined Vec::::as_ptr) {
+                              scope 38 (inlined alloc::raw_vec::RawVec::::ptr) {
+                                  scope 39 (inlined alloc::raw_vec::RawVecInner::ptr::) {
+                                      scope 40 (inlined alloc::raw_vec::RawVecInner::non_null::) {
+                                          let mut _54: std::ptr::NonNull;
+                                          scope 41 (inlined std::ptr::Unique::::cast::) {
+                                              scope 42 (inlined NonNull::::cast::) {
+                                                  scope 43 (inlined NonNull::::as_ptr) {
                                                   }
                                               }
                                           }
-                                          scope 39 (inlined std::ptr::Unique::::as_non_null_ptr) {
+                                          scope 44 (inlined std::ptr::Unique::::as_non_null_ptr) {
                                           }
                                       }
-                                      scope 40 (inlined NonNull::::as_ptr) {
+                                      scope 45 (inlined NonNull::::as_ptr) {
                                       }
                                   }
                               }
                           }
                       }
-                      scope 41 (inlined from_utf8_unchecked) {
+                      scope 46 (inlined from_utf8_unchecked) {
                       }
                   }
-                  scope 42 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) {
+                  scope 47 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) {
                   }
               }
-              scope 43 (inlined #[track_caller] core::str::traits:: for str>::index) {
-                  scope 44 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) {
+              scope 48 (inlined #[track_caller] core::str::traits:: for str>::index) {
+                  scope 49 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) {
                   }
               }
-              scope 45 (inlined core::str::traits::::eq) {
-                  let mut _47: &&[u8];
-                  let _48: &[u8];
-                  let mut _49: &&[u8];
-                  let _50: &[u8];
-                  scope 46 (inlined core::str::::as_bytes) {
+              scope 50 (inlined core::str::traits::::eq) {
+                  let mut _55: &&[u8];
+                  let _56: &[u8];
+                  let mut _57: &&[u8];
+                  let _58: &[u8];
+                  scope 51 (inlined core::str::::as_bytes) {
                   }
-                  scope 47 (inlined core::str::::as_bytes) {
+                  scope 52 (inlined core::str::::as_bytes) {
                   }
-                  scope 48 (inlined std::cmp::impls::::eq) {
-                      scope 49 (inlined core::slice::cmp::::eq) {
+                  scope 53 (inlined std::cmp::impls::::eq) {
+                      scope 54 (inlined core::slice::cmp::::eq) {
+                          let _59: usize;
+                          let mut _60: bool;
+                          let mut _61: usize;
+                          let mut _62: *const u8;
+                          let mut _63: *const u8;
+                          scope 55 {
+                              scope 56 (inlined core::slice::::as_ptr) {
+                                  let mut _64: *const [u8];
+                              }
+                              scope 57 (inlined core::slice::::as_ptr) {
+                                  let mut _65: *const [u8];
+                              }
+                              scope 58 (inlined >::equal_same_length) {
+                                  let mut _66: i32;
+                                  scope 59 {
+                                  }
+                              }
+                          }
                       }
                   }
               }
@@ -179,7 +215,7 @@
       bb3: {
           _1 = chained_conditions::BacktraceStyle::Off;
 -         goto -> bb19;
-+         goto -> bb27;
++         goto -> bb33;
       }
   
       bb4: {
@@ -216,9 +252,17 @@
           StorageDead(_30);
           StorageLive(_36);
           StorageLive(_38);
+          StorageLive(_39);
+          StorageLive(_42);
+          StorageLive(_43);
           _36 = copy _29 as &[u8] (Transmute);
           _38 = copy _28 as &[u8] (Transmute);
-          _7 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _36, move _38) -> [return: bb23, unwind: bb22];
+          _39 = PtrMetadata(copy _36);
+          StorageLive(_40);
+          StorageLive(_41);
+          _41 = PtrMetadata(copy _38);
+          _40 = Eq(copy _39, move _41);
+          switchInt(move _40) -> [0: bb24, otherwise: bb23];
       }
   
       bb5: {
@@ -249,39 +293,47 @@
           StorageLive(_17);
           _20 = const chained_conditions::promoted[0];
           _17 = &(*_20);
-          StorageLive(_39);
-          StorageLive(_40);
-          _39 = copy (*_15);
-          _40 = copy (*_17);
-          StorageLive(_41);
-          StorageLive(_42);
-          StorageLive(_43);
-          StorageLive(_44);
-          StorageLive(_46);
-          _46 = copy ((((((*_39).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull);
-          _44 = copy _46 as *const u8 (Transmute);
-          StorageDead(_46);
-          StorageLive(_45);
-          _45 = copy (((*_39).0: std::vec::Vec).1: usize);
-          _43 = *const [u8] from (copy _44, move _45);
-          StorageDead(_45);
-          StorageDead(_44);
-          _42 = &(*_43);
-          StorageDead(_43);
-          _41 = copy _42 as &str (Transmute);
-          StorageDead(_42);
+          StorageLive(_47);
           StorageLive(_48);
+          _47 = copy (*_15);
+          _48 = copy (*_17);
+          StorageLive(_49);
           StorageLive(_50);
-          _48 = copy _41 as &[u8] (Transmute);
-          _50 = copy _40 as &[u8] (Transmute);
-          _14 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _48, move _50) -> [return: bb24, unwind: bb22];
+          StorageLive(_51);
+          StorageLive(_52);
+          StorageLive(_54);
+          _54 = copy ((((((*_47).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull);
+          _52 = copy _54 as *const u8 (Transmute);
+          StorageDead(_54);
+          StorageLive(_53);
+          _53 = copy (((*_47).0: std::vec::Vec).1: usize);
+          _51 = *const [u8] from (copy _52, move _53);
+          StorageDead(_53);
+          StorageDead(_52);
+          _50 = &(*_51);
+          StorageDead(_51);
+          _49 = copy _50 as &str (Transmute);
+          StorageDead(_50);
+          StorageLive(_56);
+          StorageLive(_58);
+          StorageLive(_59);
+          StorageLive(_62);
+          StorageLive(_63);
+          _56 = copy _49 as &[u8] (Transmute);
+          _58 = copy _48 as &[u8] (Transmute);
+          _59 = PtrMetadata(copy _56);
+          StorageLive(_60);
+          StorageLive(_61);
+          _61 = PtrMetadata(copy _58);
+          _60 = Eq(copy _59, move _61);
+          switchInt(move _60) -> [0: bb28, otherwise: bb27];
       }
   
       bb7: {
           StorageDead(_5);
           StorageDead(_6);
 -         goto -> bb19;
-+         goto -> bb25;
++         goto -> bb31;
       }
   
       bb8: {
@@ -304,14 +356,14 @@
           StorageDead(_13);
           _1 = chained_conditions::BacktraceStyle::Short;
 -         goto -> bb19;
-+         goto -> bb27;
++         goto -> bb33;
       }
   
       bb10: {
           StorageDead(_12);
           StorageDead(_13);
 -         goto -> bb19;
-+         goto -> bb25;
++         goto -> bb31;
       }
   
       bb11: {
@@ -373,6 +425,31 @@
       }
   
       bb23: {
+          StorageDead(_41);
+          StorageLive(_44);
+          _44 = &raw const (*_36);
+          _42 = copy _44 as *const u8 (PtrToPtr);
+          StorageDead(_44);
+          StorageLive(_45);
+          _45 = &raw const (*_38);
+          _43 = copy _45 as *const u8 (PtrToPtr);
+          StorageDead(_45);
+          StorageLive(_46);
+          _46 = compare_bytes(move _42, move _43, move _39) -> [return: bb26, unwind unreachable];
+      }
+  
+      bb24: {
+          StorageDead(_41);
+          _7 = const false;
+-         goto -> bb25;
++         goto -> bb36;
+      }
+  
+      bb25: {
+          StorageDead(_40);
+          StorageDead(_43);
+          StorageDead(_42);
+          StorageDead(_39);
           StorageDead(_38);
           StorageDead(_36);
           StorageDead(_29);
@@ -381,31 +458,94 @@
           switchInt(move _7) -> [0: bb6, otherwise: bb5];
       }
   
-      bb24: {
-          StorageDead(_50);
+      bb26: {
+          _7 = Eq(move _46, const 0_i32);
+          StorageDead(_46);
+          goto -> bb25;
+      }
+  
+      bb27: {
+          StorageDead(_61);
+          StorageLive(_64);
+          _64 = &raw const (*_56);
+          _62 = copy _64 as *const u8 (PtrToPtr);
+          StorageDead(_64);
+          StorageLive(_65);
+          _65 = &raw const (*_58);
+          _63 = copy _65 as *const u8 (PtrToPtr);
+          StorageDead(_65);
+          StorageLive(_66);
+          _66 = compare_bytes(move _62, move _63, move _59) -> [return: bb30, unwind unreachable];
+      }
+  
+      bb28: {
+          StorageDead(_61);
+          _14 = const false;
+-         goto -> bb29;
++         goto -> bb35;
+      }
+  
+      bb29: {
+          StorageDead(_60);
+          StorageDead(_63);
+          StorageDead(_62);
+          StorageDead(_59);
+          StorageDead(_58);
+          StorageDead(_56);
+          StorageDead(_49);
           StorageDead(_48);
-          StorageDead(_41);
-          StorageDead(_40);
-          StorageDead(_39);
+          StorageDead(_47);
           switchInt(move _14) -> [0: bb9, otherwise: bb8];
+      }
+  
+      bb30: {
+          _14 = Eq(move _66, const 0_i32);
+          StorageDead(_66);
+          goto -> bb29;
 +     }
 + 
-+     bb25: {
++     bb31: {
 +         _24 = discriminant(_2);
-+         switchInt(move _24) -> [1: bb26, otherwise: bb16];
++         switchInt(move _24) -> [1: bb32, otherwise: bb16];
 +     }
 + 
-+     bb26: {
++     bb32: {
 +         goto -> bb16;
 +     }
 + 
-+     bb27: {
++     bb33: {
 +         _24 = discriminant(_2);
-+         switchInt(move _24) -> [1: bb28, otherwise: bb16];
++         switchInt(move _24) -> [1: bb34, otherwise: bb16];
 +     }
 + 
-+     bb28: {
++     bb34: {
 +         goto -> bb18;
++     }
++ 
++     bb35: {
++         StorageDead(_60);
++         StorageDead(_63);
++         StorageDead(_62);
++         StorageDead(_59);
++         StorageDead(_58);
++         StorageDead(_56);
++         StorageDead(_49);
++         StorageDead(_48);
++         StorageDead(_47);
++         goto -> bb9;
++     }
++ 
++     bb36: {
++         StorageDead(_40);
++         StorageDead(_43);
++         StorageDead(_42);
++         StorageDead(_39);
++         StorageDead(_38);
++         StorageDead(_36);
++         StorageDead(_29);
++         StorageDead(_28);
++         StorageDead(_27);
++         goto -> bb6;
       }
   }
   

From 814d902c503920334e3b992cb3d208b9742e24f3 Mon Sep 17 00:00:00 2001
From: joboet 
Date: Thu, 15 Jan 2026 14:27:10 +0100
Subject: [PATCH 264/583] std: move time implementations to `sys` (preparation)

---
 library/std/src/sys/mod.rs      | 1 +
 library/std/src/sys/time/mod.rs | 4 ++++
 2 files changed, 5 insertions(+)
 create mode 100644 library/std/src/sys/time/mod.rs

diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index c9035938cfdd..5436c144d333 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -26,6 +26,7 @@ pub mod stdio;
 pub mod sync;
 pub mod thread;
 pub mod thread_local;
+pub mod time;
 
 // FIXME(117276): remove this, move feature implementations into individual
 //                submodules.
diff --git a/library/std/src/sys/time/mod.rs b/library/std/src/sys/time/mod.rs
new file mode 100644
index 000000000000..015871e6f1ec
--- /dev/null
+++ b/library/std/src/sys/time/mod.rs
@@ -0,0 +1,4 @@
+cfg_select! {
+}
+
+pub use imp::{Instant, SystemTime, UNIX_EPOCH};

From 29b16c0a55e14504ec24eddc0a3ace7074687ce4 Mon Sep 17 00:00:00 2001
From: joboet 
Date: Thu, 15 Jan 2026 14:38:41 +0100
Subject: [PATCH 265/583] std: move time implementations to `sys` (small
 platforms)

Let's start with the easy ones:
* Motor just reexports its platform library
* The SGX code is just a trivial move
* Trusty, WASM and ZKVM are unsupported, this is very trivial. And we can get
  rid of some `#[path = ...]`s, yay!
---
 library/std/src/sys/pal/motor/mod.rs              |  1 -
 library/std/src/sys/pal/motor/time.rs             |  1 -
 library/std/src/sys/pal/sgx/mod.rs                |  1 -
 library/std/src/sys/pal/trusty/mod.rs             |  2 --
 library/std/src/sys/pal/unsupported/mod.rs        |  1 -
 library/std/src/sys/pal/wasm/mod.rs               |  2 --
 library/std/src/sys/pal/xous/mod.rs               |  1 -
 library/std/src/sys/pal/zkvm/mod.rs               |  2 --
 library/std/src/sys/time/mod.rs                   | 15 +++++++++++++++
 .../std/src/sys/{pal/sgx/time.rs => time/sgx.rs}  |  2 +-
 .../unsupported/time.rs => time/unsupported.rs}   |  0
 .../src/sys/{pal/xous/time.rs => time/xous.rs}    |  0
 12 files changed, 16 insertions(+), 12 deletions(-)
 delete mode 100644 library/std/src/sys/pal/motor/time.rs
 rename library/std/src/sys/{pal/sgx/time.rs => time/sgx.rs} (97%)
 rename library/std/src/sys/{pal/unsupported/time.rs => time/unsupported.rs} (100%)
 rename library/std/src/sys/{pal/xous/time.rs => time/xous.rs} (100%)

diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs
index e5b99cea01d5..a520375a4bbf 100644
--- a/library/std/src/sys/pal/motor/mod.rs
+++ b/library/std/src/sys/pal/motor/mod.rs
@@ -1,7 +1,6 @@
 #![allow(unsafe_op_in_unsafe_fn)]
 
 pub mod os;
-pub mod time;
 
 pub use moto_rt::futex;
 
diff --git a/library/std/src/sys/pal/motor/time.rs b/library/std/src/sys/pal/motor/time.rs
deleted file mode 100644
index e917fd466c2e..000000000000
--- a/library/std/src/sys/pal/motor/time.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub use moto_rt::time::{Instant, SystemTime, UNIX_EPOCH};
diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs
index 7f1c81a0ff7b..1de3ca4a5d79 100644
--- a/library/std/src/sys/pal/sgx/mod.rs
+++ b/library/std/src/sys/pal/sgx/mod.rs
@@ -12,7 +12,6 @@ pub mod abi;
 mod libunwind_integration;
 pub mod os;
 pub mod thread_parking;
-pub mod time;
 pub mod waitqueue;
 
 // SAFETY: must be called only once during runtime initialization.
diff --git a/library/std/src/sys/pal/trusty/mod.rs b/library/std/src/sys/pal/trusty/mod.rs
index 76a3a75b10c1..b785c2dbb789 100644
--- a/library/std/src/sys/pal/trusty/mod.rs
+++ b/library/std/src/sys/pal/trusty/mod.rs
@@ -5,7 +5,5 @@
 mod common;
 #[path = "../unsupported/os.rs"]
 pub mod os;
-#[path = "../unsupported/time.rs"]
-pub mod time;
 
 pub use common::*;
diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs
index c33d2e5fb02a..0f157819d5a6 100644
--- a/library/std/src/sys/pal/unsupported/mod.rs
+++ b/library/std/src/sys/pal/unsupported/mod.rs
@@ -1,7 +1,6 @@
 #![deny(unsafe_op_in_unsafe_fn)]
 
 pub mod os;
-pub mod time;
 
 mod common;
 pub use common::*;
diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs
index 80429a9aae18..5f56eddd6a81 100644
--- a/library/std/src/sys/pal/wasm/mod.rs
+++ b/library/std/src/sys/pal/wasm/mod.rs
@@ -18,8 +18,6 @@
 
 #[path = "../unsupported/os.rs"]
 pub mod os;
-#[path = "../unsupported/time.rs"]
-pub mod time;
 
 #[cfg(target_feature = "atomics")]
 #[path = "atomics/futex.rs"]
diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs
index 19575220b22e..87c99068929c 100644
--- a/library/std/src/sys/pal/xous/mod.rs
+++ b/library/std/src/sys/pal/xous/mod.rs
@@ -1,7 +1,6 @@
 #![forbid(unsafe_op_in_unsafe_fn)]
 
 pub mod os;
-pub mod time;
 
 #[path = "../unsupported/common.rs"]
 mod common;
diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs
index f09020820a03..1b18adb811d9 100644
--- a/library/std/src/sys/pal/zkvm/mod.rs
+++ b/library/std/src/sys/pal/zkvm/mod.rs
@@ -12,8 +12,6 @@ pub const WORD_SIZE: usize = size_of::();
 
 pub mod abi;
 pub mod os;
-#[path = "../unsupported/time.rs"]
-pub mod time;
 
 use crate::io as std_io;
 
diff --git a/library/std/src/sys/time/mod.rs b/library/std/src/sys/time/mod.rs
index 015871e6f1ec..81c568bf9320 100644
--- a/library/std/src/sys/time/mod.rs
+++ b/library/std/src/sys/time/mod.rs
@@ -1,4 +1,19 @@
 cfg_select! {
+    target_os = "motor" => {
+        use moto_rt::time as imp;
+    }
+    all(target_vendor = "fortanix", target_env = "sgx") => {
+        mod sgx;
+        use sgx as imp;
+    }
+    target_os = "xous" => {
+        mod xous;
+        use xous as imp;
+    }
+    _ => {
+        mod unsupported;
+        use unsupported as imp;
+    }
 }
 
 pub use imp::{Instant, SystemTime, UNIX_EPOCH};
diff --git a/library/std/src/sys/pal/sgx/time.rs b/library/std/src/sys/time/sgx.rs
similarity index 97%
rename from library/std/src/sys/pal/sgx/time.rs
rename to library/std/src/sys/time/sgx.rs
index a9a448226619..910e734c916e 100644
--- a/library/std/src/sys/pal/sgx/time.rs
+++ b/library/std/src/sys/time/sgx.rs
@@ -1,4 +1,4 @@
-use super::abi::usercalls;
+use crate::sys::pal::abi::usercalls;
 use crate::time::Duration;
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
diff --git a/library/std/src/sys/pal/unsupported/time.rs b/library/std/src/sys/time/unsupported.rs
similarity index 100%
rename from library/std/src/sys/pal/unsupported/time.rs
rename to library/std/src/sys/time/unsupported.rs
diff --git a/library/std/src/sys/pal/xous/time.rs b/library/std/src/sys/time/xous.rs
similarity index 100%
rename from library/std/src/sys/pal/xous/time.rs
rename to library/std/src/sys/time/xous.rs

From 5978e194564d628afe2e54cc1068d0791a7e2e8d Mon Sep 17 00:00:00 2001
From: joboet 
Date: Thu, 15 Jan 2026 14:57:03 +0100
Subject: [PATCH 266/583] std: move time implementations to `sys` (VEX)

Now that the `unsupported` module exists, we can use it for VEX. VEX actually
supports `Instant` though, so the implementation-select needs to combine that
with the `unsupported` module.
---
 library/std/src/sys/pal/vexos/mod.rs                   |  1 -
 library/std/src/sys/time/mod.rs                        | 10 ++++++++++
 .../std/src/sys/{pal/vexos/time.rs => time/vexos.rs}   |  5 -----
 3 files changed, 10 insertions(+), 6 deletions(-)
 rename library/std/src/sys/{pal/vexos/time.rs => time/vexos.rs} (83%)

diff --git a/library/std/src/sys/pal/vexos/mod.rs b/library/std/src/sys/pal/vexos/mod.rs
index 0abfc2fd7986..16aa3f088f04 100644
--- a/library/std/src/sys/pal/vexos/mod.rs
+++ b/library/std/src/sys/pal/vexos/mod.rs
@@ -1,5 +1,4 @@
 pub mod os;
-pub mod time;
 
 #[expect(dead_code)]
 #[path = "../unsupported/common.rs"]
diff --git a/library/std/src/sys/time/mod.rs b/library/std/src/sys/time/mod.rs
index 81c568bf9320..0e3376e2f910 100644
--- a/library/std/src/sys/time/mod.rs
+++ b/library/std/src/sys/time/mod.rs
@@ -6,6 +6,16 @@ cfg_select! {
         mod sgx;
         use sgx as imp;
     }
+    target_os = "vexos" => {
+        mod vexos;
+        #[expect(unused)]
+        mod unsupported;
+
+        mod imp {
+            pub use super::vexos::Instant;
+            pub use super::unsupported::{SystemTime, UNIX_EPOCH};
+        }
+    }
     target_os = "xous" => {
         mod xous;
         use xous as imp;
diff --git a/library/std/src/sys/pal/vexos/time.rs b/library/std/src/sys/time/vexos.rs
similarity index 83%
rename from library/std/src/sys/pal/vexos/time.rs
rename to library/std/src/sys/time/vexos.rs
index f95d96cd27ac..966c239699ce 100644
--- a/library/std/src/sys/pal/vexos/time.rs
+++ b/library/std/src/sys/time/vexos.rs
@@ -1,10 +1,5 @@
 use crate::time::Duration;
 
-#[expect(dead_code)]
-#[path = "../unsupported/time.rs"]
-mod unsupported_time;
-pub use unsupported_time::{SystemTime, UNIX_EPOCH};
-
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
 pub struct Instant(Duration);
 

From bd754c7119a73bfc5e9e0d23071ebffc2ebce245 Mon Sep 17 00:00:00 2001
From: joboet 
Date: Thu, 15 Jan 2026 14:47:05 +0100
Subject: [PATCH 267/583] std: move time implementations to `sys` (Solid)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

On SOLID, the conversion functions are also used to implement helpers for
timeout conversion, so these stay in the PAL. The `Instant` (µITRON) and
`SystemTime` (SOLID-specific) implementations are merged into one. While it was
nice to have the µITRON parts in a separate module, there really isn't a need
for this currently, as there is no other µITRON target. Let's not worry about
this until such a target gets added...

Note that I've extracted the `get_tim` call from `Instant` into a wrapper
function in the PAL to avoid the need to make the inner `Instant` field public
for use in the PAL.
---
 library/std/src/sys/pal/itron/time.rs         | 47 +++++--------------
 library/std/src/sys/pal/solid/mod.rs          |  1 -
 library/std/src/sys/time/mod.rs               |  4 ++
 .../sys/{pal/solid/time.rs => time/solid.rs}  | 35 ++++++++++++--
 4 files changed, 47 insertions(+), 40 deletions(-)
 rename library/std/src/sys/{pal/solid/time.rs => time/solid.rs} (65%)

diff --git a/library/std/src/sys/pal/itron/time.rs b/library/std/src/sys/pal/itron/time.rs
index 7976c27f4952..ff3cffd2069e 100644
--- a/library/std/src/sys/pal/itron/time.rs
+++ b/library/std/src/sys/pal/itron/time.rs
@@ -3,38 +3,16 @@ use super::error::expect_success;
 use crate::mem::MaybeUninit;
 use crate::time::Duration;
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub struct Instant(abi::SYSTIM);
+#[cfg(test)]
+mod tests;
 
-impl Instant {
-    pub fn now() -> Instant {
-        // Safety: The provided pointer is valid
-        unsafe {
-            let mut out = MaybeUninit::uninit();
-            expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
-            Instant(out.assume_init())
-        }
-    }
-
-    pub fn checked_sub_instant(&self, other: &Instant) -> Option {
-        self.0.checked_sub(other.0).map(|ticks| {
-            // `SYSTIM` is measured in microseconds
-            Duration::from_micros(ticks)
-        })
-    }
-
-    pub fn checked_add_duration(&self, other: &Duration) -> Option {
-        // `SYSTIM` is measured in microseconds
-        let ticks = other.as_micros();
-
-        Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
-    }
-
-    pub fn checked_sub_duration(&self, other: &Duration) -> Option {
-        // `SYSTIM` is measured in microseconds
-        let ticks = other.as_micros();
-
-        Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
+#[inline]
+pub fn get_tim() -> abi::SYSTIM {
+    // Safety: The provided pointer is valid
+    unsafe {
+        let mut out = MaybeUninit::uninit();
+        expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
+        out.assume_init()
     }
 }
 
@@ -98,7 +76,7 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -
     // a problem in practice. (`u64::MAX` μs ≈ 584942 years)
     let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM;
 
-    let start = Instant::now().0;
+    let start = get_tim();
     let mut elapsed = 0;
     let mut er = abi::E_TMOUT;
     while elapsed <= ticks {
@@ -106,11 +84,8 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -
         if er != abi::E_TMOUT {
             break;
         }
-        elapsed = Instant::now().0.wrapping_sub(start);
+        elapsed = get_tim().wrapping_sub(start);
     }
 
     er
 }
-
-#[cfg(test)]
-mod tests;
diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs
index 4eec12dacd7c..1376af8304cf 100644
--- a/library/std/src/sys/pal/solid/mod.rs
+++ b/library/std/src/sys/pal/solid/mod.rs
@@ -21,7 +21,6 @@ pub mod itron {
 pub(crate) mod error;
 pub mod os;
 pub use self::itron::thread_parking;
-pub mod time;
 
 // SAFETY: must be called only once during runtime initialization.
 // NOTE: this is not guaranteed to run, for example when Rust code is called externally.
diff --git a/library/std/src/sys/time/mod.rs b/library/std/src/sys/time/mod.rs
index 0e3376e2f910..e5b9a3a057f0 100644
--- a/library/std/src/sys/time/mod.rs
+++ b/library/std/src/sys/time/mod.rs
@@ -6,6 +6,10 @@ cfg_select! {
         mod sgx;
         use sgx as imp;
     }
+    target_os = "solid_asp3" => {
+        mod solid;
+        use solid as imp;
+    }
     target_os = "vexos" => {
         mod vexos;
         #[expect(unused)]
diff --git a/library/std/src/sys/pal/solid/time.rs b/library/std/src/sys/time/solid.rs
similarity index 65%
rename from library/std/src/sys/pal/solid/time.rs
rename to library/std/src/sys/time/solid.rs
index d5cf70f94c98..fa929c67241e 100644
--- a/library/std/src/sys/pal/solid/time.rs
+++ b/library/std/src/sys/time/solid.rs
@@ -1,9 +1,38 @@
-use super::abi;
-use super::error::expect_success;
-pub use super::itron::time::Instant;
 use crate::mem::MaybeUninit;
+use crate::sys::pal::error::expect_success;
+use crate::sys::pal::{abi, itron};
 use crate::time::Duration;
 
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(itron::abi::SYSTIM);
+
+impl Instant {
+    pub fn now() -> Instant {
+        Instant(itron::time::get_tim())
+    }
+
+    pub fn checked_sub_instant(&self, other: &Instant) -> Option {
+        self.0.checked_sub(other.0).map(|ticks| {
+            // `SYSTIM` is measured in microseconds
+            Duration::from_micros(ticks)
+        })
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option {
+        // `SYSTIM` is measured in microseconds
+        let ticks = other.as_micros();
+
+        Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option {
+        // `SYSTIM` is measured in microseconds
+        let ticks = other.as_micros();
+
+        Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
+    }
+}
+
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
 pub struct SystemTime(abi::time_t);
 

From 963f6029ce51ce1b0654620b80d7f8cb089b3142 Mon Sep 17 00:00:00 2001
From: joboet 
Date: Thu, 15 Jan 2026 14:53:52 +0100
Subject: [PATCH 268/583] std: move time implementations to `sys` (UEFI)

Next up: UEFI. Unfortunately the time conversion internals are also required by
the filesystem code, so I've left them in the PAL. The `Instant` internals
however are only used for the `Instant` implementation, so I've moved them to
`sys` (for now).
---
 library/std/src/sys/fs/uefi.rs                |   7 +-
 library/std/src/sys/pal/uefi/mod.rs           |   2 +-
 library/std/src/sys/pal/uefi/system_time.rs   | 151 +++++++++++++++
 library/std/src/sys/time/mod.rs               |   4 +
 .../sys/{pal/uefi/time.rs => time/uefi.rs}    | 172 +-----------------
 5 files changed, 168 insertions(+), 168 deletions(-)
 create mode 100644 library/std/src/sys/pal/uefi/system_time.rs
 rename library/std/src/sys/{pal/uefi/time.rs => time/uefi.rs} (52%)

diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs
index a1bb0c6e828b..8135519317a0 100644
--- a/library/std/src/sys/fs/uefi.rs
+++ b/library/std/src/sys/fs/uefi.rs
@@ -580,7 +580,8 @@ mod uefi_fs {
     use crate::path::Path;
     use crate::ptr::NonNull;
     use crate::sys::pal::helpers::{self, UefiBox};
-    use crate::sys::time::{self, SystemTime};
+    use crate::sys::pal::system_time;
+    use crate::sys::time::SystemTime;
 
     pub(crate) struct File {
         protocol: NonNull,
@@ -879,7 +880,7 @@ mod uefi_fs {
     /// conversion to SystemTime, we use the current time to get the timezone in such cases.
     pub(crate) fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> Option {
         time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
-            time::system_time_internal::now().timezone
+            system_time::now().timezone
         } else {
             time.timezone
         };
@@ -888,7 +889,7 @@ mod uefi_fs {
 
     /// Convert to UEFI Time with the current timezone.
     pub(crate) fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time {
-        let now = time::system_time_internal::now();
+        let now = system_time::now();
         time.to_uefi_loose(now.timezone, now.daylight)
     }
 
diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs
index b181d78c2345..e4a8f50e4274 100644
--- a/library/std/src/sys/pal/uefi/mod.rs
+++ b/library/std/src/sys/pal/uefi/mod.rs
@@ -15,7 +15,7 @@
 
 pub mod helpers;
 pub mod os;
-pub mod time;
+pub mod system_time;
 
 #[cfg(test)]
 mod tests;
diff --git a/library/std/src/sys/pal/uefi/system_time.rs b/library/std/src/sys/pal/uefi/system_time.rs
new file mode 100644
index 000000000000..557a49b27c2d
--- /dev/null
+++ b/library/std/src/sys/pal/uefi/system_time.rs
@@ -0,0 +1,151 @@
+use r_efi::efi::{RuntimeServices, Time};
+
+use super::helpers;
+use crate::mem::MaybeUninit;
+use crate::ptr::NonNull;
+use crate::time::Duration;
+
+const SECS_IN_MINUTE: u64 = 60;
+const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
+const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
+const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64;
+
+pub(crate) fn now() -> Time {
+    let runtime_services: NonNull =
+        helpers::runtime_services().expect("Runtime services are not available");
+    let mut t: MaybeUninit {
+    fn inner_array(&mut self) -> [A; N];
+}
+
+fn main() {}

From 5ddb7f6dd2279d920b8277485afef4a1dee3270d Mon Sep 17 00:00:00 2001
From: dianne 
Date: Tue, 27 Jan 2026 17:12:11 -0800
Subject: [PATCH 309/583] clean up checks for integer div/rem promotion

---
 .../rustc_mir_transform/src/promote_consts.rs | 62 +++++++------------
 1 file changed, 24 insertions(+), 38 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 6e7b93a5e719..3d1537b95efa 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -485,47 +485,33 @@ impl<'tcx> Validator<'_, 'tcx> {
                         if lhs_ty.is_integral() {
                             let sz = lhs_ty.primitive_size(self.tcx);
                             // Integer division: the RHS must be a non-zero const.
-                            let rhs_val = match rhs {
-                                Operand::Constant(c)
-                                    if self.should_evaluate_for_promotion_checks(c.const_) =>
-                                {
-                                    c.const_.try_eval_scalar_int(self.tcx, self.typing_env)
-                                }
-                                _ => None,
-                            };
-                            match rhs_val.map(|x| x.to_uint(sz)) {
+                            let rhs_val = if let Operand::Constant(rhs_c) = rhs
+                                && self.should_evaluate_for_promotion_checks(rhs_c.const_)
+                                && let Some(rhs_val) =
+                                    rhs_c.const_.try_eval_scalar_int(self.tcx, self.typing_env)
                                 // for the zero test, int vs uint does not matter
-                                Some(x) if x != 0 => {}        // okay
-                                _ => return Err(Unpromotable), // value not known or 0 -- not okay
-                            }
+                                && rhs_val.to_uint(sz) != 0
+                            {
+                                rhs_val
+                            } else {
+                                // value not known or 0 -- not okay
+                                return Err(Unpromotable);
+                            };
                             // Furthermore, for signed division, we also have to exclude `int::MIN /
                             // -1`.
-                            if lhs_ty.is_signed() {
-                                match rhs_val.map(|x| x.to_int(sz)) {
-                                    Some(-1) | None => {
-                                        // The RHS is -1 or unknown, so we have to be careful.
-                                        // But is the LHS int::MIN?
-                                        let lhs_val = match lhs {
-                                            Operand::Constant(c)
-                                                if self.should_evaluate_for_promotion_checks(
-                                                    c.const_,
-                                                ) =>
-                                            {
-                                                c.const_
-                                                    .try_eval_scalar_int(self.tcx, self.typing_env)
-                                            }
-                                            _ => None,
-                                        };
-                                        let lhs_min = sz.signed_int_min();
-                                        match lhs_val.map(|x| x.to_int(sz)) {
-                                            // okay
-                                            Some(x) if x != lhs_min => {}
-
-                                            // value not known or int::MIN -- not okay
-                                            _ => return Err(Unpromotable),
-                                        }
-                                    }
-                                    _ => {}
+                            if lhs_ty.is_signed() && rhs_val.to_int(sz) == -1 {
+                                // The RHS is -1, so we have to be careful. But is the LHS int::MIN?
+                                if let Operand::Constant(lhs_c) = lhs
+                                    && self.should_evaluate_for_promotion_checks(lhs_c.const_)
+                                    && let Some(lhs_val) =
+                                        lhs_c.const_.try_eval_scalar_int(self.tcx, self.typing_env)
+                                    && let lhs_min = sz.signed_int_min()
+                                    && lhs_val.to_int(sz) != lhs_min
+                                {
+                                    // okay
+                                } else {
+                                    // value not known or int::MIN -- not okay
+                                    return Err(Unpromotable);
                                 }
                             }
                         }

From 10e053dbb5c7ac3679044f222112682c9fc0758a Mon Sep 17 00:00:00 2001
From: Hood Chatham 
Date: Mon, 26 Jan 2026 10:34:50 -0800
Subject: [PATCH 310/583] Implement `set_output_kind` for Emscripten linker

This makes cdylibs compile to working Emscripten dynamic libraries without passing extra
RUSTFLAGS. This was previously approved as PR 98358 but there were CI failures that I
never got around to fixing.
---
 compiler/rustc_codegen_ssa/src/back/linker.rs     | 15 ++++++++++++++-
 .../src/spec/targets/wasm32_unknown_emscripten.rs |  2 ++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 1b75db51140b..db49f92e39ac 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -1208,10 +1208,23 @@ impl<'a> Linker for EmLinker<'a> {
 
     fn set_output_kind(
         &mut self,
-        _output_kind: LinkOutputKind,
+        output_kind: LinkOutputKind,
         _crate_type: CrateType,
         _out_filename: &Path,
     ) {
+        match output_kind {
+            LinkOutputKind::DynamicNoPicExe | LinkOutputKind::DynamicPicExe => {
+                self.cmd.arg("-sMAIN_MODULE=2");
+            }
+            LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
+                self.cmd.arg("-sSIDE_MODULE=2");
+            }
+            // -fno-pie is the default on Emscripten.
+            LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {}
+            LinkOutputKind::WasiReactorExe => {
+                unreachable!();
+            }
+        }
     }
 
     fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {
diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs
index 47623c34dce3..fb735b54dd82 100644
--- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs
+++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs
@@ -19,6 +19,8 @@ pub(crate) fn target() -> Target {
         pre_link_args,
         post_link_args,
         relocation_model: RelocModel::Pic,
+        crt_static_respected: true,
+        crt_static_default: true,
         panic_strategy: PanicStrategy::Unwind,
         no_default_libraries: false,
         families: cvs!["unix", "wasm"],

From 11ae531ac82fd502a6de59e7508a4546960cbee4 Mon Sep 17 00:00:00 2001
From: Arseni Novikau 
Date: Tue, 6 Jan 2026 01:22:43 +0300
Subject: [PATCH 311/583] diagnostics: don't suggest `#[derive]` if impl
 already exists

---
 .../rustc_hir_typeck/src/method/suggest.rs    | 88 +++++++++++++------
 ...rive-clone-already-present-issue-146515.rs | 20 +++++
 ...-clone-already-present-issue-146515.stderr | 23 +++++
 3 files changed, 102 insertions(+), 29 deletions(-)
 create mode 100644 tests/ui/suggestions/derive-clone-already-present-issue-146515.rs
 create mode 100644 tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr

diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index b2803f4347e3..5673d044ad2c 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -3281,6 +3281,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Checks if we can suggest a derive macro for the unmet trait bound.
+    /// Returns Some(list_of_derives) if possible, or None if not.
+    fn consider_suggesting_derives_for_ty(
+        &self,
+        trait_pred: ty::TraitPredicate<'tcx>,
+        adt: ty::AdtDef<'tcx>,
+    ) -> Option> {
+        let diagnostic_name = self.tcx.get_diagnostic_name(trait_pred.def_id())?;
+
+        let can_derive = match diagnostic_name {
+            sym::Default
+            | sym::Eq
+            | sym::PartialEq
+            | sym::Ord
+            | sym::PartialOrd
+            | sym::Clone
+            | sym::Copy
+            | sym::Hash
+            | sym::Debug => true,
+            _ => false,
+        };
+
+        if !can_derive {
+            return None;
+        }
+
+        let trait_def_id = trait_pred.def_id();
+        let self_ty = trait_pred.self_ty();
+
+        // We need to check if there is already a manual implementation of the trait
+        // for this specific ADT to avoid suggesting `#[derive(..)]` that would conflict.
+        if self.tcx.non_blanket_impls_for_ty(trait_def_id, self_ty).any(|impl_def_id| {
+            self.tcx
+                .type_of(impl_def_id)
+                .instantiate_identity()
+                .ty_adt_def()
+                .is_some_and(|def| def.did() == adt.did())
+        }) {
+            return None;
+        }
+
+        let mut derives = Vec::new();
+        let self_name = self_ty.to_string();
+        let self_span = self.tcx.def_span(adt.did());
+
+        for super_trait in supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref)) {
+            if let Some(parent_diagnostic_name) = self.tcx.get_diagnostic_name(super_trait.def_id())
+            {
+                derives.push((self_name.clone(), self_span, parent_diagnostic_name));
+            }
+        }
+
+        derives.push((self_name, self_span, diagnostic_name));
+
+        Some(derives)
+    }
+
     fn note_predicate_source_and_get_derives(
         &self,
         err: &mut Diag<'_>,
@@ -3298,35 +3355,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Some(adt) if adt.did().is_local() => adt,
                 _ => continue,
             };
-            if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) {
-                let can_derive = match diagnostic_name {
-                    sym::Default
-                    | sym::Eq
-                    | sym::PartialEq
-                    | sym::Ord
-                    | sym::PartialOrd
-                    | sym::Clone
-                    | sym::Copy
-                    | sym::Hash
-                    | sym::Debug => true,
-                    _ => false,
-                };
-                if can_derive {
-                    let self_name = trait_pred.self_ty().to_string();
-                    let self_span = self.tcx.def_span(adt.did());
-                    for super_trait in
-                        supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref))
-                    {
-                        if let Some(parent_diagnostic_name) =
-                            self.tcx.get_diagnostic_name(super_trait.def_id())
-                        {
-                            derives.push((self_name.clone(), self_span, parent_diagnostic_name));
-                        }
-                    }
-                    derives.push((self_name, self_span, diagnostic_name));
-                } else {
-                    traits.push(trait_pred.def_id());
-                }
+            if let Some(new_derives) = self.consider_suggesting_derives_for_ty(trait_pred, adt) {
+                derives.extend(new_derives);
             } else {
                 traits.push(trait_pred.def_id());
             }
diff --git a/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs b/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs
new file mode 100644
index 000000000000..083d73711a51
--- /dev/null
+++ b/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs
@@ -0,0 +1,20 @@
+// issue: https://github.com/rust-lang/rust/issues/146515
+
+use std::rc::Rc;
+
+#[derive(Clone)]
+struct ContainsRc {
+    value: Rc,
+}
+
+fn clone_me(x: &ContainsRc) -> ContainsRc {
+    //~^ NOTE expected `ContainsRc` because of return type
+    x.clone()
+    //~^ ERROR mismatched types
+    //~| NOTE expected `ContainsRc`, found `&ContainsRc`
+    //~| NOTE expected struct `ContainsRc<_>`
+    //~| NOTE `ContainsRc` does not implement `Clone`, so `&ContainsRc` was cloned instead
+    //~| NOTE the trait `Clone` must be implemented
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr b/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr
new file mode 100644
index 000000000000..516ef38f668d
--- /dev/null
+++ b/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr
@@ -0,0 +1,23 @@
+error[E0308]: mismatched types
+  --> $DIR/derive-clone-already-present-issue-146515.rs:12:5
+   |
+LL | fn clone_me(x: &ContainsRc) -> ContainsRc {
+   |                                      ------------- expected `ContainsRc` because of return type
+LL |
+LL |     x.clone()
+   |     ^^^^^^^^^ expected `ContainsRc`, found `&ContainsRc`
+   |
+   = note: expected struct `ContainsRc<_>`
+           found reference `&ContainsRc<_>`
+note: `ContainsRc` does not implement `Clone`, so `&ContainsRc` was cloned instead
+  --> $DIR/derive-clone-already-present-issue-146515.rs:12:5
+   |
+LL |     x.clone()
+   |     ^
+   = help: `Clone` is not implemented because the trait bound `T: Clone` is not satisfied
+note: the trait `Clone` must be implemented
+  --> $SRC_DIR/core/src/clone.rs:LL:COL
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.

From 9ca8ed38eb3d1e138cd8b6a8f586baa7fe52e980 Mon Sep 17 00:00:00 2001
From: Usman Akinyemi 
Date: Wed, 28 Jan 2026 02:54:32 +0530
Subject: [PATCH 312/583] rustc_parse: improve the error diagnostic for
 "missing let in let chain"

Signed-off-by: Usman Akinyemi 
---
 compiler/rustc_parse/src/parser/expr.rs       | 55 ++++++++++++++-----
 tests/ui/expr/if/bad-if-let-suggestion.rs     |  4 +-
 tests/ui/expr/if/bad-if-let-suggestion.stderr | 30 ++--------
 tests/ui/missing/missing-let.rs               |  6 ++
 tests/ui/missing/missing-let.stderr           | 18 ++++++
 5 files changed, 73 insertions(+), 40 deletions(-)
 create mode 100644 tests/ui/missing/missing-let.rs
 create mode 100644 tests/ui/missing/missing-let.stderr

diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index c31a4798b471..44c6acec8866 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2760,9 +2760,13 @@ impl<'a> Parser<'a> {
         let (mut cond, _) =
             self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?;
 
-        CondChecker::new(self, let_chains_policy).visit_expr(&mut cond);
-
-        Ok(cond)
+        let mut checker = CondChecker::new(self, let_chains_policy);
+        checker.visit_expr(&mut cond);
+        Ok(if let Some(guar) = checker.found_incorrect_let_chain {
+            self.mk_expr_err(cond.span, guar)
+        } else {
+            cond
+        })
     }
 
     /// Parses a `let $pat = $expr` pseudo-expression.
@@ -3484,13 +3488,19 @@ impl<'a> Parser<'a> {
         let if_span = self.prev_token.span;
         let mut cond = self.parse_match_guard_condition()?;
 
-        CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
+        let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed);
+        checker.visit_expr(&mut cond);
 
         if has_let_expr(&cond) {
             let span = if_span.to(cond.span);
             self.psess.gated_spans.gate(sym::if_let_guard, span);
         }
-        Ok(Some(cond))
+
+        Ok(Some(if let Some(guar) = checker.found_incorrect_let_chain {
+            self.mk_expr_err(cond.span, guar)
+        } else {
+            cond
+        }))
     }
 
     fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option>)> {
@@ -3511,13 +3521,23 @@ impl<'a> Parser<'a> {
                 let ast::PatKind::Paren(subpat) = pat.kind else { unreachable!() };
                 let ast::PatKind::Guard(_, mut cond) = subpat.kind else { unreachable!() };
                 self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span);
-                CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
+                let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed);
+                checker.visit_expr(&mut cond);
+
                 let right = self.prev_token.span;
                 self.dcx().emit_err(errors::ParenthesesInMatchPat {
                     span: vec![left, right],
                     sugg: errors::ParenthesesInMatchPatSugg { left, right },
                 });
-                Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond)))
+
+                Ok((
+                    self.mk_pat(span, ast::PatKind::Wild),
+                    (if let Some(guar) = checker.found_incorrect_let_chain {
+                        Some(self.mk_expr_err(cond.span, guar))
+                    } else {
+                        Some(cond)
+                    }),
+                ))
             } else {
                 Ok((pat, self.parse_match_arm_guard()?))
             }
@@ -4208,6 +4228,7 @@ struct CondChecker<'a> {
     forbid_let_reason: Option,
     missing_let: Option,
     comparison: Option,
+    found_incorrect_let_chain: Option,
 }
 
 impl<'a> CondChecker<'a> {
@@ -4218,6 +4239,7 @@ impl<'a> CondChecker<'a> {
             missing_let: None,
             comparison: None,
             let_chains_policy,
+            found_incorrect_let_chain: None,
             depth: 0,
         }
     }
@@ -4236,12 +4258,19 @@ impl MutVisitor for CondChecker<'_> {
                         NotSupportedOr(or_span) => {
                             self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span })
                         }
-                        _ => self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet {
-                            span,
-                            reason,
-                            missing_let: self.missing_let,
-                            comparison: self.comparison,
-                        }),
+                        _ => {
+                            let guar =
+                                self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet {
+                                    span,
+                                    reason,
+                                    missing_let: self.missing_let,
+                                    comparison: self.comparison,
+                                });
+                            if let Some(_) = self.missing_let {
+                                self.found_incorrect_let_chain = Some(guar);
+                            }
+                            guar
+                        }
                     };
                     *recovered = Recovered::Yes(error);
                 } else if self.depth > 1 {
diff --git a/tests/ui/expr/if/bad-if-let-suggestion.rs b/tests/ui/expr/if/bad-if-let-suggestion.rs
index b0d0676e1ea7..c462e32c9ef5 100644
--- a/tests/ui/expr/if/bad-if-let-suggestion.rs
+++ b/tests/ui/expr/if/bad-if-let-suggestion.rs
@@ -1,8 +1,6 @@
 fn a() {
     if let x = 1 && i = 2 {}
-    //~^ ERROR cannot find value `i` in this scope
-    //~| ERROR mismatched types
-    //~| ERROR expected expression, found `let` statement
+    //~^ ERROR expected expression, found `let` statement
 }
 
 fn b() {
diff --git a/tests/ui/expr/if/bad-if-let-suggestion.stderr b/tests/ui/expr/if/bad-if-let-suggestion.stderr
index 4244a3bb06ee..d0838fec67d6 100644
--- a/tests/ui/expr/if/bad-if-let-suggestion.stderr
+++ b/tests/ui/expr/if/bad-if-let-suggestion.stderr
@@ -15,13 +15,7 @@ LL |     if let x = 1 && i == 2 {}
    |                        +
 
 error[E0425]: cannot find value `i` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:2:21
-   |
-LL |     if let x = 1 && i = 2 {}
-   |                     ^ not found in this scope
-
-error[E0425]: cannot find value `i` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:9:9
+  --> $DIR/bad-if-let-suggestion.rs:7:9
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -36,7 +30,7 @@ LL +     if (a + j) = i {}
    |
 
 error[E0425]: cannot find value `j` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:9:13
+  --> $DIR/bad-if-let-suggestion.rs:7:13
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -51,7 +45,7 @@ LL +     if (i + a) = i {}
    |
 
 error[E0425]: cannot find value `i` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:9:18
+  --> $DIR/bad-if-let-suggestion.rs:7:18
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -66,7 +60,7 @@ LL +     if (i + j) = a {}
    |
 
 error[E0425]: cannot find value `x` in this scope
-  --> $DIR/bad-if-let-suggestion.rs:16:8
+  --> $DIR/bad-if-let-suggestion.rs:14:8
    |
 LL | fn a() {
    | ------ similarly named function `a` defined here
@@ -80,18 +74,6 @@ LL -     if x[0] = 1 {}
 LL +     if a[0] = 1 {}
    |
 
-error[E0308]: mismatched types
-  --> $DIR/bad-if-let-suggestion.rs:2:8
-   |
-LL |     if let x = 1 && i = 2 {}
-   |        ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
-   |
-help: you might have meant to compare for equality
-   |
-LL |     if let x = 1 && i == 2 {}
-   |                        +
+error: aborting due to 5 previous errors
 
-error: aborting due to 7 previous errors
-
-Some errors have detailed explanations: E0308, E0425.
-For more information about an error, try `rustc --explain E0308`.
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/missing/missing-let.rs b/tests/ui/missing/missing-let.rs
new file mode 100644
index 000000000000..36db7bc95826
--- /dev/null
+++ b/tests/ui/missing/missing-let.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let x = Some(42);
+    if let Some(_) = x
+        && Some(x) = x //~^ ERROR expected expression, found `let` statement
+    {}
+}
diff --git a/tests/ui/missing/missing-let.stderr b/tests/ui/missing/missing-let.stderr
new file mode 100644
index 000000000000..897ff6329d59
--- /dev/null
+++ b/tests/ui/missing/missing-let.stderr
@@ -0,0 +1,18 @@
+error: expected expression, found `let` statement
+  --> $DIR/missing-let.rs:3:8
+   |
+LL |     if let Some(_) = x
+   |        ^^^^^^^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if` and `while` expressions
+help: you might have meant to continue the let-chain
+   |
+LL |         && let Some(x) = x
+   |            +++
+help: you might have meant to compare for equality
+   |
+LL |         && Some(x) == x
+   |                     +
+
+error: aborting due to 1 previous error
+

From 7e6d3a29767a7fc184c51b3e8ebf123b19aa4b66 Mon Sep 17 00:00:00 2001
From: reddevilmidzy 
Date: Sun, 18 Jan 2026 21:33:10 +0900
Subject: [PATCH 313/583] Add section to tests/ui/README.md

---
 tests/ui/README.md | 131 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 125 insertions(+), 6 deletions(-)

diff --git a/tests/ui/README.md b/tests/ui/README.md
index 4c91f313a735..7788e7c2335c 100644
--- a/tests/ui/README.md
+++ b/tests/ui/README.md
@@ -22,6 +22,12 @@ These tests exercise alloc error handling.
 
 See .
 
+## `tests/ui/annotate-moves`
+
+These tests exercise the `annotate-moves` feature.
+
+See [`annotate-moves` | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/annotate-moves.html)
+
 ## `tests/ui/annotate-snippet`
 
 These tests exercise the [`annotate-snippets`]-based emitter implementation.
@@ -34,6 +40,12 @@ These tests exercise the [`annotate-snippets`]-based emitter implementation.
 
 These tests deal with anonymous parameters (no name, only type), a deprecated feature that becomes a hard error in Edition 2018.
 
+## `tests/ui/any`
+
+These tests exercise the `try_as_dyn` feature.
+
+See [`core::any::try_as_dyn`](https://doc.rust-lang.org/nightly/core/any/fn.try_as_dyn.html)
+
 ## `tests/ui/argfile`: External files providing command line arguments
 
 These tests exercise rustc reading command line arguments from an externally provided argfile (`@argsfile`).
@@ -244,6 +256,14 @@ See:
 
 This directory only contains one highly specific test. Other coinduction tests can be found down the deeply located `tests/ui/traits/next-solver/cycles/coinduction/` subdirectory.
 
+## `tests/ui/collections`
+
+These tests exercise the `collections` library.
+
+See [`std::collections`](https://doc.rust-lang.org/std/collections/index.html)
+
+**FIXME**: consider merge with `tests/ui/btreemap` and `tests/ui/hashmap`
+
 ## `tests/ui/command/`: `std::process::Command`
 
 This directory is actually for the standard library [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html) type, where some tests are too difficult or inconvenient to write as unit tests or integration tests within the standard library itself.
@@ -380,6 +400,18 @@ These tests use the unstable command line option `query-dep-graph` to examine th
 
 Tests for `#[deprecated]` attribute and `deprecated_in_future` internal lint.
 
+## `tests/ui/deref-patterns`: `#![feature(deref_patterns)]` and `#![feature(string_deref_patterns)]`
+
+Tests for `#![feature(deref_patterns)]` and `#![feature(string_deref_patterns)]`. See [Deref patterns | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/deref-patterns.html).
+
+**FIXME**: May have some overlap with `tests/ui/pattern/deref-patterns`.
+
+## `tests/ui/deref`
+
+Tests for `Deref` and `DerefMut` traits.
+
+See [`std::ops::Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) and [`std::ops::DerefMut`](https://doc.rust-lang.org/std/ops/trait.DerefMut.html)
+
 ## `tests/ui/derived-errors/`: Derived Error Messages
 
 Tests for quality of diagnostics involving suppression of cascading errors in some cases to avoid overwhelming the user.
@@ -430,6 +462,10 @@ Exercises diagnostics for when a code block attempts to gain ownership of a non-
 
 Exercises diagnostics for disallowed struct destructuring.
 
+## `tests/ui/dist`
+
+Tests that require distribution artifacts.
+
 ## `tests/ui/dollar-crate/`: `$crate` used with the `use` keyword
 
 There are a few rules - which are checked in this directory - to follow when using `$crate` - it must be used in the start of a `use` line and is a reserved identifier.
@@ -492,6 +528,10 @@ These tests run in specific Rust editions, such as Rust 2015 or Rust 2018, and c
 
 **FIXME**: Maybe regroup `rust-2018`, `rust-2021` and `rust-2024` under this umbrella?
 
+## `tests/ui/eii`: Externally Implementable Items
+
+Exercises `eii` keyword.
+
 ## `tests/ui/empty/`: Various tests related to the concept of "empty"
 
 **FIXME**: These tests need better homes, this is not very informative.
@@ -571,6 +611,10 @@ See:
 - [`ffi_const` | The Unstable book](https://doc.rust-lang.org/unstable-book/language-features/ffi-const.html)
 - [`ffi_pure` | The Unstable book](https://doc.rust-lang.org/beta/unstable-book/language-features/ffi-pure.html)
 
+## `tests/ui/float`
+
+See: [Tracking Issue for `f16` and `f128` float types #116909](https://github.com/rust-lang/rust/issues/116909)
+
 ## `tests/ui/fmt/`
 
 Exercises the `format!` macro.
@@ -579,6 +623,10 @@ Exercises the `format!` macro.
 
 A broad category of tests on functions.
 
+## `tests/ui/fn_traits`
+
+Tests for `#![feature(fn_traits)]`. See [`fn_traits` | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/library-features/fn-traits.html).
+
 ## `tests/ui/force-inlining/`: `#[rustc_force_inline]`
 
 Tests for `#[rustc_force_inline]`, which will force a function to always be labelled as inline by the compiler (it will be inserted at the point of its call instead of being used as a normal function call.) If the compiler is unable to inline the function, an error will be reported. See .
@@ -653,6 +701,10 @@ See:
 - [Higher-ranked trait bounds | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/traits/hrtb.html)
 - [Higher-ranked trait bounds | Nomicon](https://doc.rust-lang.org/nomicon/hrtb.html)
 
+## `tests/ui/higher-ranked-trait-bounds`
+
+**FIXME**: move to `tests/ui/higher-ranked/trait-bounds`
+
 ## `tests/ui/hygiene/`
 
 This seems to have been originally intended for "hygienic macros" - macros which work in all contexts, independent of what surrounds them. However, this category has grown into a mish-mash of many tests that may belong in the other directories.
@@ -835,6 +887,10 @@ Tests exercising analysis for unused variables, unreachable statements, function
 
 **FIXME**: This seems unrelated to "liveness" as defined in the rustc compiler guide. Is this misleadingly named? https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/lifetime_parameters.html#liveness-and-universal-regions
 
+## `tests/ui/loop-match`
+
+Tests for `loop` with `match` expressions.
+
 ## `tests/ui/loops/`
 
 Tests on the `loop` construct.
@@ -981,6 +1037,15 @@ Contains a single test. Check that we reject the ancient Rust syntax `x <- y` an
 
 **FIXME**: Definitely should be rehomed, maybe to `tests/ui/deprecation/`.
 
+## `tests/ui/offload`
+
+Exercises the offload feature.
+
+See:
+
+- [`std::offload` | rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/offload/internals.html)
+- [Tracking Issue for GPU-offload #131513](https://github.com/rust-lang/rust/issues/131513)
+
 ## `tests/ui/offset-of/`
 
 Exercises the [`std::mem::offset_of` macro](https://doc.rust-lang.org/beta/std/mem/macro.offset_of.html).
@@ -1039,6 +1104,16 @@ Broad category of tests surrounding patterns. See [Patterns | Reference](https:/
 
 **FIXME**: Some overlap with `tests/ui/match/`.
 
+## `tests/ui/pin`
+
+**FIXME**: Consider merging with `tests/ui/pin-macro`.
+
+## `tests/ui/pin-ergonomics`
+
+Exercises the `#![feature(pin_ergonomics)]` feature.
+
+See [Tracking issue for pin ergonomics #130494](https://github.com/rust-lang/rust/issues/130494)
+
 ## `tests/ui/pin-macro/`
 
 See [`std::pin`](https://doc.rust-lang.org/std/pin/).
@@ -1103,6 +1178,12 @@ Reachability tests, primarily unreachable code and coercions into the never type
 
 **FIXME**: Check for overlap with `ui/liveness`.
 
+## `tests/ui/reborrow`
+
+Exercises the `#![feature(reborrow)]` feature.
+
+See [Tracking Issue for Reborrow trait lang experiment #145612](https://github.com/rust-lang/rust/issues/145612)
+
 ## `tests/ui/recursion/`
 
 Broad category of tests exercising recursions (compile test and run time), in functions, macros, `type` definitions, and more.
@@ -1115,6 +1196,12 @@ Sets a recursion limit on recursive code.
 
 **FIXME**: Should be merged with `tests/ui/recursion/`.
 
+## `tests/ui/reflection/`
+
+Exercises the `#![feature(type_info)]` feature.
+
+See [Tracking Issue for type_info #146922](https://github.com/rust-lang/rust/issues/146922)
+
 ## `tests/ui/regions/`
 
 **FIXME**: Maybe merge with `ui/lifetimes`.
@@ -1157,9 +1244,17 @@ Exercises `.rmeta` crate metadata and the `--emit=metadata` cli flag.
 
 Tests for runtime environment on which Rust programs are executed. E.g. Unix `SIGPIPE`.
 
-## `tests/ui/rust-{2018,2021,2024}/`
+## `tests/ui/rust-2018`
 
-Tests that exercise behaviors and features that are specific to editions.
+Tests that exercise behaviors and features specific to the Rust 2018 edition.
+
+## `tests/ui/rust-2021`
+
+Tests that exercise behaviors and features specific to the Rust 2021 edition.
+
+## `tests/ui/rust-2024`
+
+Tests that exercise behaviors and features specific to the Rust 2024 edition.
 
 ## `tests/ui/rustc-env`
 
@@ -1169,10 +1264,20 @@ Tests on environmental variables that affect `rustc`.
 
 Hybrid tests that exercises `rustdoc`, and also some joint `rustdoc`/`rustc` interactions.
 
+## `tests/ui/sanitize-attr`
+
+Tests for the `#![feature(sanitize)]` attribute.
+
+See [Sanitize | The Unstable Book](https://doc.rust-lang.org/unstable-book/language-features/sanitize.html).
+
 ## `tests/ui/sanitizer/`
 
 Exercises sanitizer support. See [Sanitizer | The rustc book](https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html).
 
+## `tests/ui/scalable-vectors`
+
+See [Tracking Issue for Scalable Vectors #145052](https://github.com/rust-lang/rust/issues/145052)
+
 ## `tests/ui/self/`: `self` keyword
 
 Tests with erroneous ways of using `self`, such as using `this.x` syntax as seen in other languages, having it not be the first argument, or using it in a non-associated function (no `impl` or `trait`). It also contains correct uses of `self` which have previously been observed to cause ICEs.
@@ -1211,6 +1316,12 @@ This is a test directory for the specific error case where a lifetime never gets
 
 While many tests here involve the `Sized` trait directly, some instead test, for example the slight variations between returning a zero-sized `Vec` and a `Vec` with one item, where one has no known type and the other does.
 
+## `tests/ui/sized-hierarchy`
+
+Tests for `#![feature(sized_hierarchy)]` attribute.
+
+See [Tracking Issue for Sized Hierarchy #144404](https://github.com/rust-lang/rust/issues/144404)
+
 ## `tests/ui/span/`
 
 An assorted collection of tests that involves specific diagnostic spans.
@@ -1225,6 +1336,10 @@ See [Tracking issue for specialization (RFC 1210) #31844](https://github.com/rus
 
 Stability attributes used internally by the standard library: `#[stable()]` and `#[unstable()]`.
 
+## `tests/ui/stack-probes`
+
+**FIXME**: Contains a single test, should likely be rehomed to `tests/ui/abi`.
+
 ## `tests/ui/rustc_public-ir-print/`
 
 Some tests for pretty printing of rustc_public's IR.
@@ -1359,6 +1474,10 @@ Tests surrounding [`std::mem::transmute`](https://doc.rust-lang.org/std/mem/fn.t
 
 Exercises compiler development support flag `-Z treat-err-as-bug`.
 
+## `tests/ui/trimmed-paths/`
+
+Tests for the `#[doc(hidden)]` items.
+
 ## `tests/ui/trivial-bounds/`
 
 `#![feature(trivial_bounds)]`. See [RFC 2056 Allow trivial where clause constraints](https://github.com/rust-lang/rfcs/blob/master/text/2056-allow-trivial-where-clause-constraints.md).
@@ -1401,10 +1520,6 @@ General collection of type checking related tests.
 
 General collection of type inference related tests.
 
-## `tests/ui/typeof/`
-
-`typeof` keyword, reserved but unimplemented.
-
 ## `tests/ui/ufcs/`
 
 See [RFC 0132 Unified Function Call Syntax](https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md).
@@ -1487,6 +1602,10 @@ See:
 
 Exercises the `unused_crate_dependencies` lint.
 
+## `tests/ui/unstable-feature-bound`
+
+Tests for gating and diagnostics when unstable features are used.
+
 ## `tests/ui/unwind-abis/`
 
 **FIXME**: Contains a single test, should likely be rehomed to `tests/ui/abi/`.

From 500dc3c9c0416309be257d4c80457fa1165f0723 Mon Sep 17 00:00:00 2001
From: reddevilmidzy 
Date: Sun, 18 Jan 2026 23:44:09 +0900
Subject: [PATCH 314/583] Reorder tests/ui/README.md

---
 tests/ui/README.md | 152 ++++++++++++++++++++++-----------------------
 1 file changed, 76 insertions(+), 76 deletions(-)

diff --git a/tests/ui/README.md b/tests/ui/README.md
index 7788e7c2335c..237cfb9c4f07 100644
--- a/tests/ui/README.md
+++ b/tests/ui/README.md
@@ -10,18 +10,18 @@ These tests deal with *Application Binary Interfaces* (ABI), mostly relating to
 
 Tests for unsupported ABIs can be made cross-platform by using the `extern "rust-invalid"` ABI, which is considered unsupported on every platform.
 
-## `tests/ui/allocator`
-
-These tests exercise `#![feature(allocator_api)]` and the `#[global_allocator]` attribute.
-
-See [Allocator traits and `std::heap` #32838](https://github.com/rust-lang/rust/issues/32838).
-
 ## `tests/ui/alloc-error`
 
 These tests exercise alloc error handling.
 
 See .
 
+## `tests/ui/allocator`
+
+These tests exercise `#![feature(allocator_api)]` and the `#[global_allocator]` attribute.
+
+See [Allocator traits and `std::heap` #32838](https://github.com/rust-lang/rust/issues/32838).
+
 ## `tests/ui/annotate-moves`
 
 These tests exercise the `annotate-moves` feature.
@@ -52,14 +52,14 @@ These tests exercise rustc reading command line arguments from an externally pro
 
 See [Implement `@argsfile` to read arguments from command line #63576](https://github.com/rust-lang/rust/issues/63576).
 
-## `tests/ui/array-slice-vec`: Arrays, slices and vectors
-
-Exercises various aspects surrounding basic collection types `[]`, `&[]` and `Vec`. E.g. type-checking, out-of-bounds indices, attempted instructions which are allowed in other programming languages, and more.
-
 ## `tests/ui/argument-suggestions`: Argument suggestions
 
 Calling a function with the wrong number of arguments causes a compilation failure, but the compiler is able to, in some cases, provide suggestions on how to fix the error, such as which arguments to add or delete. These tests exercise the quality of such diagnostics.
 
+## `tests/ui/array-slice-vec`: Arrays, slices and vectors
+
+Exercises various aspects surrounding basic collection types `[]`, `&[]` and `Vec`. E.g. type-checking, out-of-bounds indices, attempted instructions which are allowed in other programming languages, and more.
+
 ## `tests/ui/asm`: `asm!` macro
 
 These tests exercise the `asm!` macro, which is used for adding inline assembly.
@@ -184,6 +184,10 @@ See [RFC 3729: Hierarchy of Sized traits](https://github.com/rust-lang/rfcs/pull
 
 Defining custom auto traits with the `auto` keyword belongs to `tests/ui/auto-traits/` instead.
 
+## `tests/ui/c-variadic`: C Variadic Function
+
+Tests for FFI with C varargs (`va_list`).
+
 ## `tests/ui/cast/`: Type Casting
 
 Tests for type casting using the `as` operator. Includes tests for valid/invalid casts between primitive types, trait objects, and custom types. For example, check that trying to cast `i32` into `bool` results in a helpful error message.
@@ -202,16 +206,16 @@ Tests for the `--check-cfg` compiler mechanism  for checking cfg configurations,
 
 See [Checking conditional configurations | The rustc book](https://doc.rust-lang.org/rustc/check-cfg.html).
 
-## `tests/ui/closure_context/`: Closure type inference in context
-
-Tests for closure type inference with respect to surrounding scopes, mostly quality of diagnostics.
-
 ## `tests/ui/closure-expected-type/`: Closure type inference
 
 Tests targeted at how we deduce the types of closure arguments. This process is a result of some heuristics which take into account the *expected type* we have alongside the *actual types* that we get from inputs.
 
 **FIXME**: Appears to have significant overlap with `tests/ui/closure_context` and `tests/ui/functions-closures/closure-expected-type`. Needs further investigation.
 
+## `tests/ui/closure_context`: Closure type inference in context
+
+Tests for closure type inference with respect to surrounding scopes, mostly quality of diagnostics.
+
 ## `tests/ui/closures/`: General Closure Tests
 
 Any closure-focused tests that does not fit in the other more specific closure subdirectories belong here. E.g. syntax, `move`, lifetimes.
@@ -305,10 +309,6 @@ See:
 - [Tracking Issue for complex generic constants: `feature(generic_const_exprs)` #76560](https://github.com/rust-lang/rust/issues/76560)
 - [Const generics | Reference](https://doc.rust-lang.org/reference/items/generics.html#const-generics)
 
-## `tests/ui/const_prop/`: Constant Propagation
-
-Tests exercising `ConstProp` mir-opt pass (mostly regression tests). See .
-
 ## `tests/ui/const-ptr/`: Constant Pointers
 
 Tests exercise const raw pointers. E.g. pointer arithmetic, casting and dereferencing, always with a `const`.
@@ -319,6 +319,10 @@ See:
 - [`std::ptr`](https://doc.rust-lang.org/std/ptr/index.html)
 - [Pointer types | Reference](https://doc.rust-lang.org/reference/types/pointer.html)
 
+## `tests/ui/const_prop`: Constant Propagation
+
+Tests exercising `ConstProp` mir-opt pass (mostly regression tests). See .
+
 ## `tests/ui/consts/`: General Constant Evaluation
 
 Anything to do with constants, which does not fit in the previous two `const` categories, goes here. This does not always imply use of the `const` keyword - other values considered constant, such as defining an enum variant as `enum Foo { Variant = 5 }` also counts.
@@ -360,10 +364,6 @@ Tests for `#[bench]`, `#[test_case]` attributes and the `custom_test_frameworks`
 
 See [Tracking issue for eRFC 2318, Custom test frameworks #50297](https://github.com/rust-lang/rust/issues/50297).
 
-## `tests/ui/c-variadic/`: C Variadic Function
-
-Tests for FFI with C varargs (`va_list`).
-
 ## `tests/ui/cycle-trait/`: Trait Cycle Detection
 
 Tests for detection and handling of cyclic trait dependencies.
@@ -400,16 +400,16 @@ These tests use the unstable command line option `query-dep-graph` to examine th
 
 Tests for `#[deprecated]` attribute and `deprecated_in_future` internal lint.
 
+## `tests/ui/deref`
+
+Tests for `Deref` and `DerefMut` traits.
+
 ## `tests/ui/deref-patterns`: `#![feature(deref_patterns)]` and `#![feature(string_deref_patterns)]`
 
 Tests for `#![feature(deref_patterns)]` and `#![feature(string_deref_patterns)]`. See [Deref patterns | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/deref-patterns.html).
 
 **FIXME**: May have some overlap with `tests/ui/pattern/deref-patterns`.
 
-## `tests/ui/deref`
-
-Tests for `Deref` and `DerefMut` traits.
-
 See [`std::ops::Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) and [`std::ops::DerefMut`](https://doc.rust-lang.org/std/ops/trait.DerefMut.html)
 
 ## `tests/ui/derived-errors/`: Derived Error Messages
@@ -438,6 +438,10 @@ These tests revolve around command-line flags which change the way error/warning
 
 **FIXME**: Check redundancy with `annotate-snippet`, which is another emitter.
 
+## `tests/ui/diagnostic-width`: `--diagnostic-width`
+
+Everything to do with `--diagnostic-width`.
+
 ## `tests/ui/diagnostic_namespace/`
 
 Exercises `#[diagnostic::*]` namespaced attributes. See [RFC 3368 Diagnostic attribute namespace](https://github.com/rust-lang/rfcs/blob/master/text/3368-diagnostic-attribute-namespace.md).
@@ -446,10 +450,6 @@ Exercises `#[diagnostic::*]` namespaced attributes. See [RFC 3368 Diagnostic att
 
 This directory contains tests and infrastructure related to the diagnostics system, including support for translatable diagnostics
 
-## `tests/ui/diagnostic-width/`: `--diagnostic-width`
-
-Everything to do with `--diagnostic-width`.
-
 ## `tests/ui/did_you_mean/`
 
 Tests for miscellaneous suggestions.
@@ -497,10 +497,6 @@ Tests for dynamically-sized types (DSTs). See [Dynamically Sized Types | Referen
 
 Tests about duplicated symbol names and associated errors, such as using the `#[export_name]` attribute to rename a function with the same name as another function.
 
-## `tests/ui/dynamically-sized-types/`: Dynamically Sized Types
-
-**FIXME**: should be coalesced with `tests/ui/dst`.
-
 ## `tests/ui/dyn-compatibility/`: Dyn-compatibility
 
 Tests for dyn-compatibility of traits.
@@ -522,6 +518,10 @@ The `dyn` keyword is used to highlight that calls to methods on the associated T
 
 See [`dyn` keyword](https://doc.rust-lang.org/std/keyword.dyn.html).
 
+## `tests/ui/dynamically-sized-types`: Dynamically Sized Types
+
+**FIXME**: should be coalesced with `tests/ui/dst`.
+
 ## `tests/ui/editions/`: Rust edition-specific peculiarities
 
 These tests run in specific Rust editions, such as Rust 2015 or Rust 2018, and check errors and functionality related to specific now-deprecated idioms and features.
@@ -627,6 +627,12 @@ A broad category of tests on functions.
 
 Tests for `#![feature(fn_traits)]`. See [`fn_traits` | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/library-features/fn-traits.html).
 
+## `tests/ui/for-loop-while`
+
+Anything to do with loops and `for`, `loop` and `while` keywords to express them.
+
+**FIXME**: After `ui/for` is merged into this, also carry over its SUMMARY text.
+
 ## `tests/ui/force-inlining/`: `#[rustc_force_inline]`
 
 Tests for `#[rustc_force_inline]`, which will force a function to always be labelled as inline by the compiler (it will be inserted at the point of its call instead of being used as a normal function call.) If the compiler is unable to inline the function, an error will be reported. See .
@@ -637,12 +643,6 @@ Tests for `extern "C"` and `extern "Rust`.
 
 **FIXME**: Check for potential overlap/merge with `ui/c-variadic` and/or `ui/extern`.
 
-## `tests/ui/for-loop-while/`
-
-Anything to do with loops and `for`, `loop` and `while` keywords to express them.
-
-**FIXME**: After `ui/for` is merged into this, also carry over its SUMMARY text.
-
 ## `tests/ui/frontmatter/`
 
 Tests for `#![feature(frontmatter)]`. See [Tracking Issue for `frontmatter` #136889](https://github.com/rust-lang/rust/issues/136889).
@@ -651,12 +651,6 @@ Tests for `#![feature(frontmatter)]`. See [Tracking Issue for `frontmatter` #136
 
 Tests for diagnostics when there may be identically named types that need further qualifications to disambiguate.
 
-## `tests/ui/functional-struct-update/`
-
-Functional Struct Update is the name for the idiom by which one can write `..` at the end of a struct literal expression to fill in all remaining fields of the struct literal by using `` as the source for them.
-
-See [RFC 0736 Privacy-respecting Functional Struct Update](https://github.com/rust-lang/rfcs/blob/master/text/0736-privacy-respecting-fru.md).
-
 ## `tests/ui/function-pointer/`
 
 Tests on function pointers, such as testing their compatibility with higher-ranked trait bounds.
@@ -666,6 +660,12 @@ See:
 - [Function pointer types | Reference](https://doc.rust-lang.org/reference/types/function-pointer.html)
 - [Higher-ranked trait bounds | Nomicon](https://doc.rust-lang.org/nomicon/hrtb.html)
 
+## `tests/ui/functional-struct-update/`
+
+Functional Struct Update is the name for the idiom by which one can write `..` at the end of a struct literal expression to fill in all remaining fields of the struct literal by using `` as the source for them.
+
+See [RFC 0736 Privacy-respecting Functional Struct Update](https://github.com/rust-lang/rfcs/blob/master/text/0736-privacy-respecting-fru.md).
+
 ## `tests/ui/functions-closures/`
 
 Tests on closures. See [Closure expressions | Reference](https://doc.rust-lang.org/reference/expressions/closure-expr.html).
@@ -721,14 +721,14 @@ This test category revolves around trait objects with `Sized` having illegal ope
 
 Tests on lifetime elision in impl function signatures. See [Lifetime elision | Nomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html).
 
-## `tests/ui/implied-bounds/`
-
-See [Implied bounds | Reference](https://doc.rust-lang.org/reference/trait-bounds.html#implied-bounds).
-
 ## `tests/ui/impl-trait/`
 
 Tests for trait impls.
 
+## `tests/ui/implied-bounds/`
+
+See [Implied bounds | Reference](https://doc.rust-lang.org/reference/trait-bounds.html#implied-bounds).
+
 ## `tests/ui/imports/`
 
 Tests for module system and imports.
@@ -854,6 +854,12 @@ Broad directory on lifetimes, including proper specifiers, lifetimes not living
 
 These tests exercises numerical limits, such as `[[u8; 1518599999]; 1518600000]`.
 
+## `tests/ui/link-native-libs/`
+
+Tests for `#[link(name = "", kind = "")]` and `-l` command line flag.
+
+See [Tracking Issue for linking modifiers for native libraries #81490](https://github.com/rust-lang/rust/issues/81490).
+
 ## `tests/ui/linkage-attr/`
 
 Tests for the linkage attribute `#[linkage]` of `#![feature(linkage)]`.
@@ -866,12 +872,6 @@ Tests on code which fails during the linking stage, or which contain arguments a
 
 See [Linkage | Reference](https://doc.rust-lang.org/reference/linkage.html).
 
-## `tests/ui/link-native-libs/`
-
-Tests for `#[link(name = "", kind = "")]` and `-l` command line flag.
-
-See [Tracking Issue for linking modifiers for native libraries #81490](https://github.com/rust-lang/rust/issues/81490).
-
 ## `tests/ui/lint/`
 
 Tests for the lint infrastructure, lint levels, lint reasons, etc.
@@ -997,6 +997,10 @@ See [RFC 3550 New Range](https://github.com/rust-lang/rfcs/blob/master/text/3550
 
 Tests for Non-lexical lifetimes. See [RFC 2094 NLL](https://rust-lang.github.io/rfcs/2094-nll.html).
 
+## `tests/ui/no_std/`
+
+Tests for where the standard library is disabled through `#![no_std]`.
+
 ## `tests/ui/non_modrs_mods/`
 
 Despite the size of the directory, this is a single test, spawning a sprawling `mod` dependency tree and checking its successful build.
@@ -1009,10 +1013,6 @@ A very similar principle as `non_modrs_mods`, but with an added inline `mod` sta
 
 **FIXME**: Consider merge with `tests/ui/modules/`, keeping the directory structure.
 
-## `tests/ui/no_std/`
-
-Tests for where the standard library is disabled through `#![no_std]`.
-
 ## `tests/ui/not-panic/`
 
 Tests checking various types, such as `&RefCell`, and whether they are not `UnwindSafe` as expected.
@@ -1134,6 +1134,10 @@ Exercises the `-Z print-type-sizes` flag.
 
 Exercises on name privacy. E.g. the meaning of `pub`, `pub(crate)`, etc.
 
+## `tests/ui/proc-macro/`
+
+Broad category of tests on proc-macros. See [Procedural Macros | Reference](https://doc.rust-lang.org/reference/procedural-macros.html).
+
 ## `tests/ui/process/`
 
 Some standard library process tests which are hard to write within standard library crate tests.
@@ -1142,10 +1146,6 @@ Some standard library process tests which are hard to write within standard libr
 
 Some standard library process termination tests which are hard to write within standard library crate tests.
 
-## `tests/ui/proc-macro/`
-
-Broad category of tests on proc-macros. See [Procedural Macros | Reference](https://doc.rust-lang.org/reference/procedural-macros.html).
-
 ## `tests/ui/ptr_ops/`: Using operations on a pointer
 
 Contains only 2 tests, related to a single issue, which was about an error caused by using addition on a pointer to `i8`.
@@ -1260,6 +1260,10 @@ Tests that exercise behaviors and features specific to the Rust 2024 edition.
 
 Tests on environmental variables that affect `rustc`.
 
+## `tests/ui/rustc_public-ir-print`
+
+Some tests for pretty printing of rustc_public's IR.
+
 ## `tests/ui/rustdoc`
 
 Hybrid tests that exercises `rustdoc`, and also some joint `rustdoc`/`rustc` interactions.
@@ -1340,10 +1344,6 @@ Stability attributes used internally by the standard library: `#[stable()]` and
 
 **FIXME**: Contains a single test, should likely be rehomed to `tests/ui/abi`.
 
-## `tests/ui/rustc_public-ir-print/`
-
-Some tests for pretty printing of rustc_public's IR.
-
 ## `tests/ui/stack-protector/`: `-Z stack-protector` command line flag
 
 See [Tracking Issue for stabilizing stack smashing protection (i.e., `-Z stack-protector`) #114903](https://github.com/rust-lang/rust/issues/114903).
@@ -1512,14 +1512,14 @@ Tests for `type` aliases in the context of `enum` variants, such as that applied
 
 `#![feature(type_alias_impl_trait)]`. See [Type Alias Impl Trait | The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/language-features/type-alias-impl-trait.html).
 
-## `tests/ui/typeck/`
-
-General collection of type checking related tests.
-
 ## `tests/ui/type-inference/`
 
 General collection of type inference related tests.
 
+## `tests/ui/typeck`
+
+General collection of type checking related tests.
+
 ## `tests/ui/ufcs/`
 
 See [RFC 0132 Unified Function Call Syntax](https://github.com/rust-lang/rfcs/blob/master/text/0132-ufcs.md).
@@ -1598,14 +1598,14 @@ See:
 
 **FIXME**: Seems to also contain more generic tests that fit in `tests/ui/unsized/`.
 
-## `tests/ui/unused-crate-deps/`
-
-Exercises the `unused_crate_dependencies` lint.
-
 ## `tests/ui/unstable-feature-bound`
 
 Tests for gating and diagnostics when unstable features are used.
 
+## `tests/ui/unused-crate-deps`
+
+Exercises the `unused_crate_dependencies` lint.
+
 ## `tests/ui/unwind-abis/`
 
 **FIXME**: Contains a single test, should likely be rehomed to `tests/ui/abi/`.

From a08541085aa3390ddf88339a2adeb29e8a118315 Mon Sep 17 00:00:00 2001
From: reddevilmidzy 
Date: Thu, 1 Jan 2026 21:05:02 +0900
Subject: [PATCH 315/583] Add subdirectory change detection to tidy

---
 src/tools/tidy/src/ui_tests.rs | 69 ++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index bf51810810a6..b5643034d45c 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -73,6 +73,57 @@ pub fn check(root_path: &Path, tidy_ctx: TidyCtx) {
             ));
         }
     }
+
+    // The list of subdirectories in ui tests.
+    // Compare previous subdirectory with current subdirectory
+    // to sync with `tests/ui/README.md`.
+    // See 
+    let mut prev_line = String::new();
+    let mut is_sorted = true;
+    let documented_subdirs: BTreeSet<_> = include_str!("../../../../tests/ui/README.md")
+        .lines()
+        .filter_map(|line| {
+            static_regex!(r"^##.*?`(?[^`]+)`").captures(line).map(|cap| {
+                let dir = &cap["dir"];
+                // FIXME(reddevilmidzy) normalize subdirs title in tests/ui/README.md
+                if dir.ends_with('/') {
+                    dir.strip_suffix('/').unwrap().to_string()
+                } else {
+                    dir.to_string()
+                }
+            })
+        })
+        .inspect(|line| {
+            if prev_line.as_str() > line.as_str() {
+                is_sorted = false;
+            }
+
+            prev_line = line.clone();
+        })
+        .collect();
+    let filesystem_subdirs = collect_ui_tests_subdirs(&path);
+    let is_modified = !filesystem_subdirs.eq(&documented_subdirs);
+
+    if !is_sorted {
+        check.error("`tests/ui/README.md` is not in order");
+    }
+    if is_modified {
+        for directory in documented_subdirs.symmetric_difference(&filesystem_subdirs) {
+            if documented_subdirs.contains(directory) {
+                check.error(format!(
+                               "ui subdirectory `{directory}` is listed in `tests/ui/README.md` but does not exist in the filesystem"
+                           ));
+            } else {
+                check.error(format!(
+                               "ui subdirectory `{directory}` exists in the filesystem but is not documented in `tests/ui/README.md`"
+                           ));
+            }
+        }
+        check.error(
+                   "`tests/ui/README.md` subdirectory listing is out of sync with the filesystem. \
+                    Please add or remove subdirectory entries (## headers with backtick-wrapped names) to match the actual directories in `tests/ui/`"
+               );
+    }
 }
 
 fn deny_new_top_level_ui_tests(check: &mut RunningCheck, tests_path: &Path) {
@@ -137,6 +188,24 @@ fn recursively_check_ui_tests<'issues>(
     remaining_issue_names
 }
 
+fn collect_ui_tests_subdirs(path: &Path) -> BTreeSet {
+    let ui = path.join("ui");
+    let entries = std::fs::read_dir(ui.as_path()).unwrap();
+
+    entries
+        .filter_map(|entry| entry.ok())
+        .map(|entry| entry.path())
+        .filter(|path| path.is_dir())
+        .map(|dir_path| {
+            let dir_path = dir_path.strip_prefix(path).unwrap();
+            format!(
+                "tests/{}",
+                dir_path.to_string_lossy().replace(std::path::MAIN_SEPARATOR_STR, "/")
+            )
+        })
+        .collect()
+}
+
 fn check_unexpected_extension(check: &mut RunningCheck, file_path: &Path, ext: &str) {
     const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
         "rs",     // test source files

From ce03e7b33ab1e26bb9a553dd35f0af06b372b7a9 Mon Sep 17 00:00:00 2001
From: Lukas Bergdoll 
Date: Tue, 27 Jan 2026 18:29:25 +0100
Subject: [PATCH 316/583] Avoid miri error in `slice::sort` under Stacked
 Borrows

See comment in code.

Fixes: https://github.com/rust-lang/rust/pull/131065
---
 library/alloctests/tests/sort/tests.rs          | 8 ++++++++
 library/core/src/slice/sort/stable/quicksort.rs | 9 ++++++---
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/library/alloctests/tests/sort/tests.rs b/library/alloctests/tests/sort/tests.rs
index d321f8df5189..09b76773d6b2 100644
--- a/library/alloctests/tests/sort/tests.rs
+++ b/library/alloctests/tests/sort/tests.rs
@@ -362,6 +362,13 @@ fn sort_vs_sort_by_impl() {
     assert_eq!(input_sort_by, expected);
 }
 
+pub fn box_value_impl() {
+    for len in [3, 9, 35, 56, 132] {
+        test_is_sorted::, S>(len, Box::new, patterns::random);
+        test_is_sorted::, S>(len, Box::new, |len| patterns::random_sorted(len, 80.0));
+    }
+}
+
 gen_sort_test_fns_with_default_patterns!(
     correct_i32,
     |len, pattern_fn| test_is_sorted::(len, |val| val, pattern_fn),
@@ -967,6 +974,7 @@ define_instantiate_sort_tests!(
     [miri_yes, fixed_seed_rand_vec_prefix],
     [miri_yes, int_edge],
     [miri_yes, sort_vs_sort_by],
+    [miri_yes, box_value],
     [miri_yes, correct_i32_random],
     [miri_yes, correct_i32_random_z1],
     [miri_yes, correct_i32_random_d2],
diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs
index 734a495ce225..acc8a5e838e1 100644
--- a/library/core/src/slice/sort/stable/quicksort.rs
+++ b/library/core/src/slice/sort/stable/quicksort.rs
@@ -1,6 +1,6 @@
 //! This module contains a stable quicksort and partition implementation.
 
-use crate::mem::{ManuallyDrop, MaybeUninit};
+use crate::mem::MaybeUninit;
 use crate::slice::sort::shared::FreezeMarker;
 use crate::slice::sort::shared::pivot::choose_pivot;
 use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl;
@@ -41,8 +41,11 @@ pub fn quicksort bool>(
         // SAFETY: We only access the temporary copy for Freeze types, otherwise
         // self-modifications via `is_less` would not be observed and this would
         // be unsound. Our temporary copy does not escape this scope.
-        let pivot_copy = unsafe { ManuallyDrop::new(ptr::read(&v[pivot_pos])) };
-        let pivot_ref = (!has_direct_interior_mutability::()).then_some(&*pivot_copy);
+        // We use `MaybeUninit` to avoid re-tag issues. FIXME: use `MaybeDangling`.
+        let pivot_copy = unsafe { ptr::read((&raw const v[pivot_pos]).cast::>()) };
+        let pivot_ref =
+            // SAFETY: We created the value in an init state.
+            (!has_direct_interior_mutability::()).then_some(unsafe { &*pivot_copy.as_ptr() });
 
         // We choose a pivot, and check if this pivot is equal to our left
         // ancestor. If true, we do a partition putting equal elements on the

From 890e50de69b40c9a9f3b7a636286c066df2c9c52 Mon Sep 17 00:00:00 2001
From: Zachary S 
Date: Wed, 28 Jan 2026 09:57:16 -0600
Subject: [PATCH 317/583] Stabilize feature(push_mut)

---
 library/alloc/src/collections/linked_list.rs   | 6 ++----
 library/alloc/src/collections/vec_deque/mod.rs | 9 +++------
 library/alloc/src/vec/mod.rs                   | 9 ++-------
 3 files changed, 7 insertions(+), 17 deletions(-)

diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs
index 6cab5728a281..3889fca30bc8 100644
--- a/library/alloc/src/collections/linked_list.rs
+++ b/library/alloc/src/collections/linked_list.rs
@@ -854,7 +854,6 @@ impl LinkedList {
     /// # Examples
     ///
     /// ```
-    /// #![feature(push_mut)]
     /// use std::collections::LinkedList;
     ///
     /// let mut dl = LinkedList::from([1, 2, 3]);
@@ -863,7 +862,7 @@ impl LinkedList {
     /// *ptr += 4;
     /// assert_eq!(dl.front().unwrap(), &6);
     /// ```
-    #[unstable(feature = "push_mut", issue = "135974")]
+    #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")]
     #[must_use = "if you don't need a reference to the value, use `LinkedList::push_front` instead"]
     pub fn push_front_mut(&mut self, elt: T) -> &mut T {
         let mut node =
@@ -926,7 +925,6 @@ impl LinkedList {
     /// # Examples
     ///
     /// ```
-    /// #![feature(push_mut)]
     /// use std::collections::LinkedList;
     ///
     /// let mut dl = LinkedList::from([1, 2, 3]);
@@ -935,7 +933,7 @@ impl LinkedList {
     /// *ptr += 4;
     /// assert_eq!(dl.back().unwrap(), &6);
     /// ```
-    #[unstable(feature = "push_mut", issue = "135974")]
+    #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")]
     #[must_use = "if you don't need a reference to the value, use `LinkedList::push_back` instead"]
     pub fn push_back_mut(&mut self, elt: T) -> &mut T {
         let mut node =
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index c51317a1a68f..eda29db44257 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -2168,7 +2168,6 @@ impl VecDeque {
     /// # Examples
     ///
     /// ```
-    /// #![feature(push_mut)]
     /// use std::collections::VecDeque;
     ///
     /// let mut d = VecDeque::from([1, 2, 3]);
@@ -2176,7 +2175,7 @@ impl VecDeque {
     /// *x -= 1;
     /// assert_eq!(d.front(), Some(&7));
     /// ```
-    #[unstable(feature = "push_mut", issue = "135974")]
+    #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")]
     #[must_use = "if you don't need a reference to the value, use `VecDeque::push_front` instead"]
     pub fn push_front_mut(&mut self, value: T) -> &mut T {
         if self.is_full() {
@@ -2212,7 +2211,6 @@ impl VecDeque {
     /// # Examples
     ///
     /// ```
-    /// #![feature(push_mut)]
     /// use std::collections::VecDeque;
     ///
     /// let mut d = VecDeque::from([1, 2, 3]);
@@ -2220,7 +2218,7 @@ impl VecDeque {
     /// *x += 1;
     /// assert_eq!(d.back(), Some(&10));
     /// ```
-    #[unstable(feature = "push_mut", issue = "135974")]
+    #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")]
     #[must_use = "if you don't need a reference to the value, use `VecDeque::push_back` instead"]
     pub fn push_back_mut(&mut self, value: T) -> &mut T {
         if self.is_full() {
@@ -2419,7 +2417,6 @@ impl VecDeque {
     /// # Examples
     ///
     /// ```
-    /// #![feature(push_mut)]
     /// use std::collections::VecDeque;
     ///
     /// let mut vec_deque = VecDeque::from([1, 2, 3]);
@@ -2428,7 +2425,7 @@ impl VecDeque {
     /// *x += 7;
     /// assert_eq!(vec_deque, &[1, 12, 2, 3]);
     /// ```
-    #[unstable(feature = "push_mut", issue = "135974")]
+    #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")]
     #[must_use = "if you don't need a reference to the value, use `VecDeque::insert` instead"]
     pub fn insert_mut(&mut self, index: usize, value: T) -> &mut T {
         assert!(index <= self.len(), "index out of bounds");
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index aaf22e80ec60..6cbe89d9da4f 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1003,9 +1003,6 @@ const impl Vec {
     /// # Examples
     ///
     /// ```
-    /// #![feature(push_mut)]
-    ///
-    ///
     /// let mut vec = vec![1, 2];
     /// let last = vec.push_mut(3);
     /// assert_eq!(*last, 3);
@@ -1023,7 +1020,7 @@ const impl Vec {
     /// vector's elements to a larger allocation. This expensive operation is
     /// offset by the *capacity* *O*(1) insertions it allows.
     #[inline]
-    #[unstable(feature = "push_mut", issue = "135974")]
+    #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")]
     #[must_use = "if you don't need a reference to the value, use `Vec::push` instead"]
     pub fn push_mut(&mut self, value: T) -> &mut T {
         // Inform codegen that the length does not change across grow_one().
@@ -2196,7 +2193,6 @@ impl Vec {
     /// # Examples
     ///
     /// ```
-    /// #![feature(push_mut)]
     /// let mut vec = vec![1, 3, 5, 9];
     /// let x = vec.insert_mut(3, 6);
     /// *x += 1;
@@ -2210,7 +2206,7 @@ impl Vec {
     /// the insertion index is 0.
     #[cfg(not(no_global_oom_handling))]
     #[inline]
-    #[unstable(feature = "push_mut", issue = "135974")]
+    #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")]
     #[track_caller]
     #[must_use = "if you don't need a reference to the value, use `Vec::insert` instead"]
     pub fn insert_mut(&mut self, index: usize, element: T) -> &mut T {
@@ -2689,7 +2685,6 @@ impl Vec {
     /// Takes *O*(1) time.
     #[inline]
     #[unstable(feature = "vec_push_within_capacity", issue = "100486")]
-    // #[unstable(feature = "push_mut", issue = "135974")]
     pub fn push_within_capacity(&mut self, value: T) -> Result<&mut T, T> {
         if self.len == self.buf.capacity() {
             return Err(value);

From f5f2ca0dc62b4a6d38ba221ef64090172da7f569 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Heath=20Dutton=F0=9F=95=B4=EF=B8=8F?=
 
Date: Thu, 1 Jan 2026 17:25:25 -0500
Subject: [PATCH 318/583] Improve move error diagnostic for `AsyncFn` closures

When an async closure captures a variable by move but is constrained to
`AsyncFn` or `AsyncFnMut`, the error message now explains that the
closure kind is the issue and points to the trait bound, similar to the
existing diagnostic for `Fn`/`FnMut` closures.
---
 .../src/diagnostics/move_errors.rs            | 199 ++++++++++++------
 .../move-from-async-fn-bound.rs               |  13 ++
 .../move-from-async-fn-bound.stderr           |  30 +++
 3 files changed, 178 insertions(+), 64 deletions(-)
 create mode 100644 tests/ui/async-await/async-closures/move-from-async-fn-bound.rs
 create mode 100644 tests/ui/async-await/async-closures/move-from-async-fn-bound.stderr

diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index f83931d37599..986ade57fb31 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -1,3 +1,4 @@
+use rustc_abi::FieldIdx;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::intravisit::Visitor;
@@ -7,7 +8,7 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
 use rustc_span::def_id::DefId;
-use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
+use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
 use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
 use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::debug;
@@ -472,49 +473,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 if def_id.as_local() == Some(self.mir_def_id())
                     && let Some(upvar_field) = upvar_field =>
             {
-                let closure_kind_ty = closure_args.as_closure().kind_ty();
-                let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
-                    Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
-                    Some(ty::ClosureKind::FnOnce) => {
-                        bug!("closure kind does not match first argument type")
-                    }
-                    None => bug!("closure kind not inferred by borrowck"),
-                };
-                let capture_description =
-                    format!("captured variable in an `{closure_kind}` closure");
-
-                let upvar = &self.upvars[upvar_field.index()];
-                let upvar_hir_id = upvar.get_root_variable();
-                let upvar_name = upvar.to_string(tcx);
-                let upvar_span = tcx.hir_span(upvar_hir_id);
-
-                let place_name = self.describe_any_place(move_place.as_ref());
-
-                let place_description =
-                    if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
-                        format!("{place_name}, a {capture_description}")
-                    } else {
-                        format!("{place_name}, as `{upvar_name}` is a {capture_description}")
-                    };
-
-                debug!(
-                    "report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
-                    closure_kind_ty, closure_kind, place_description,
-                );
-
-                let closure_span = tcx.def_span(def_id);
-
-                self.cannot_move_out_of(span, &place_description)
-                    .with_span_label(upvar_span, "captured outer variable")
-                    .with_span_label(
-                        closure_span,
-                        format!("captured by this `{closure_kind}` closure"),
-                    )
-                    .with_span_help(
-                        self.get_closure_bound_clause_span(*def_id),
-                        "`Fn` and `FnMut` closures require captured values to be able to be \
-                         consumed multiple times, but `FnOnce` closures may consume them only once",
-                    )
+                self.report_closure_move_error(
+                    span,
+                    move_place,
+                    *def_id,
+                    closure_args.as_closure().kind_ty(),
+                    upvar_field,
+                    ty::Asyncness::No,
+                )
+            }
+            ty::CoroutineClosure(def_id, closure_args)
+                if def_id.as_local() == Some(self.mir_def_id())
+                    && let Some(upvar_field) = upvar_field
+                    && self
+                        .get_closure_bound_clause_span(*def_id, ty::Asyncness::Yes)
+                        .is_some() =>
+            {
+                self.report_closure_move_error(
+                    span,
+                    move_place,
+                    *def_id,
+                    closure_args.as_coroutine_closure().kind_ty(),
+                    upvar_field,
+                    ty::Asyncness::Yes,
+                )
             }
             _ => {
                 let source = self.borrowed_content_source(deref_base);
@@ -563,45 +545,134 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         err
     }
 
-    fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
+    fn report_closure_move_error(
+        &self,
+        span: Span,
+        move_place: Place<'tcx>,
+        def_id: DefId,
+        closure_kind_ty: Ty<'tcx>,
+        upvar_field: FieldIdx,
+        asyncness: ty::Asyncness,
+    ) -> Diag<'infcx> {
+        let tcx = self.infcx.tcx;
+
+        let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
+            Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
+            Some(ty::ClosureKind::FnOnce) => {
+                bug!("closure kind does not match first argument type")
+            }
+            None => bug!("closure kind not inferred by borrowck"),
+        };
+
+        let async_prefix = if asyncness.is_async() { "Async" } else { "" };
+        let capture_description =
+            format!("captured variable in an `{async_prefix}{closure_kind}` closure");
+
+        let upvar = &self.upvars[upvar_field.index()];
+        let upvar_hir_id = upvar.get_root_variable();
+        let upvar_name = upvar.to_string(tcx);
+        let upvar_span = tcx.hir_span(upvar_hir_id);
+
+        let place_name = self.describe_any_place(move_place.as_ref());
+
+        let place_description = if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
+            format!("{place_name}, a {capture_description}")
+        } else {
+            format!("{place_name}, as `{upvar_name}` is a {capture_description}")
+        };
+
+        debug!(?closure_kind_ty, ?closure_kind, ?place_description);
+
+        let closure_span = tcx.def_span(def_id);
+
+        let help_msg = format!(
+            "`{async_prefix}Fn` and `{async_prefix}FnMut` closures require captured values to \
+             be able to be consumed multiple times, but `{async_prefix}FnOnce` closures may \
+             consume them only once"
+        );
+
+        let mut err = self
+            .cannot_move_out_of(span, &place_description)
+            .with_span_label(upvar_span, "captured outer variable")
+            .with_span_label(
+                closure_span,
+                format!("captured by this `{async_prefix}{closure_kind}` closure"),
+            );
+
+        if let Some(bound_span) = self.get_closure_bound_clause_span(def_id, asyncness) {
+            err.span_help(bound_span, help_msg);
+        } else if !asyncness.is_async() {
+            // For sync closures, always emit the help message even without a span.
+            // For async closures, we only enter this branch if we found a valid span
+            // (due to the match guard), so no fallback is needed.
+            err.help(help_msg);
+        }
+
+        err
+    }
+
+    fn get_closure_bound_clause_span(
+        &self,
+        def_id: DefId,
+        asyncness: ty::Asyncness,
+    ) -> Option {
         let tcx = self.infcx.tcx;
         let typeck_result = tcx.typeck(self.mir_def_id());
         // Check whether the closure is an argument to a call, if so,
         // get the instantiated where-bounds of that call.
         let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
-        let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP };
+        let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return None };
 
         let predicates = match parent.kind {
             hir::ExprKind::Call(callee, _) => {
-                let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
-                let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
+                let ty = typeck_result.node_type_opt(callee.hir_id)?;
+                let ty::FnDef(fn_def_id, args) = ty.kind() else { return None };
                 tcx.predicates_of(fn_def_id).instantiate(tcx, args)
             }
             hir::ExprKind::MethodCall(..) => {
-                let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
-                    return DUMMY_SP;
-                };
+                let (_, method) = typeck_result.type_dependent_def(parent.hir_id)?;
                 let args = typeck_result.node_args(parent.hir_id);
                 tcx.predicates_of(method).instantiate(tcx, args)
             }
-            _ => return DUMMY_SP,
+            _ => return None,
         };
 
-        // Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`.
+        // Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`
+        // or `AsyncFn[Mut]`.
         for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
-            if let Some(clause) = pred.as_trait_clause()
-                && let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
-                && *clause_closure_def_id == def_id
-                && (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
-                    || tcx.lang_items().fn_trait() == Some(clause.def_id()))
-            {
-                // Found ``
-                // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
-                // could be changed to `FnOnce()` to avoid the move error.
-                return *span;
+            let dominated_by_fn_trait = self
+                .closure_clause_kind(*pred, def_id, asyncness)
+                .is_some_and(|kind| matches!(kind, ty::ClosureKind::Fn | ty::ClosureKind::FnMut));
+            if dominated_by_fn_trait {
+                // Found `` or
+                // ``.
+                // We point at the bound that coerced the closure, which could be changed
+                // to `FnOnce()` or `AsyncFnOnce()` to avoid the move error.
+                return Some(*span);
             }
         }
-        DUMMY_SP
+        None
+    }
+
+    /// If `pred` is a trait clause binding the closure `def_id` to `Fn`/`FnMut`/`FnOnce`
+    /// (or their async equivalents based on `asyncness`), returns the corresponding
+    /// `ClosureKind`. Otherwise returns `None`.
+    fn closure_clause_kind(
+        &self,
+        pred: ty::Clause<'tcx>,
+        def_id: DefId,
+        asyncness: ty::Asyncness,
+    ) -> Option {
+        let tcx = self.infcx.tcx;
+        let clause = pred.as_trait_clause()?;
+        let kind = match asyncness {
+            ty::Asyncness::Yes => tcx.async_fn_trait_kind_from_def_id(clause.def_id()),
+            ty::Asyncness::No => tcx.fn_trait_kind_from_def_id(clause.def_id()),
+        }?;
+        match clause.self_ty().skip_binder().kind() {
+            ty::Closure(id, _) | ty::CoroutineClosure(id, _) if *id == def_id => Some(kind),
+            _ => None,
+        }
     }
 
     fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {
diff --git a/tests/ui/async-await/async-closures/move-from-async-fn-bound.rs b/tests/ui/async-await/async-closures/move-from-async-fn-bound.rs
new file mode 100644
index 000000000000..fbd8aac2515b
--- /dev/null
+++ b/tests/ui/async-await/async-closures/move-from-async-fn-bound.rs
@@ -0,0 +1,13 @@
+//@ edition:2021
+// Test that a by-ref `AsyncFn` closure gets an error when it tries to
+// consume a value, with a helpful diagnostic pointing to the bound.
+
+fn call(_: F) where F: AsyncFn() {}
+
+fn main() {
+    let y = vec![format!("World")];
+    call(async || {
+        //~^ ERROR cannot move out of `y`, a captured variable in an `AsyncFn` closure
+        y.into_iter();
+    });
+}
diff --git a/tests/ui/async-await/async-closures/move-from-async-fn-bound.stderr b/tests/ui/async-await/async-closures/move-from-async-fn-bound.stderr
new file mode 100644
index 000000000000..1a881db2a37d
--- /dev/null
+++ b/tests/ui/async-await/async-closures/move-from-async-fn-bound.stderr
@@ -0,0 +1,30 @@
+error[E0507]: cannot move out of `y`, a captured variable in an `AsyncFn` closure
+  --> $DIR/move-from-async-fn-bound.rs:9:10
+   |
+LL |     let y = vec![format!("World")];
+   |         - captured outer variable
+LL |     call(async || {
+   |          ^^^^^^^^
+   |          |
+   |          captured by this `AsyncFn` closure
+   |          `y` is moved here
+LL |
+LL |         y.into_iter();
+   |         -
+   |         |
+   |         variable moved due to use in coroutine
+   |         move occurs because `y` has type `Vec`, which does not implement the `Copy` trait
+   |
+help: `AsyncFn` and `AsyncFnMut` closures require captured values to be able to be consumed multiple times, but `AsyncFnOnce` closures may consume them only once
+  --> $DIR/move-from-async-fn-bound.rs:5:27
+   |
+LL | fn call(_: F) where F: AsyncFn() {}
+   |                           ^^^^^^^^^
+help: consider cloning the value if the performance cost is acceptable
+   |
+LL |         y.clone().into_iter();
+   |          ++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0507`.

From 457148406f7fbeaee433d9b966d458a6033b4758 Mon Sep 17 00:00:00 2001
From: lolbinarycat 
Date: Wed, 28 Jan 2026 12:38:09 -0600
Subject: [PATCH 319/583] Refer to debug-logging bootstrap config by its full
 path.

---
 src/doc/rustc-dev-guide/src/tracing.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md
index 4d52f9c86508..1005ad2c2346 100644
--- a/src/doc/rustc-dev-guide/src/tracing.md
+++ b/src/doc/rustc-dev-guide/src/tracing.md
@@ -183,7 +183,7 @@ rustc.
 
 While calls to `error!`, `warn!` and `info!` are included in every build of the compiler,
 calls to `debug!` and `trace!` are only included in the program if
-`debug-logging=true` is turned on in bootstrap.toml (it is
+`rust.debug-logging=true` is turned on in bootstrap.toml (it is
 turned off by default), so if you don't see `DEBUG` logs, especially
 if you run the compiler with `RUSTC_LOG=rustc rustc some.rs` and only see
 `INFO` logs, make sure that `debug-logging=true` is turned on in your

From fb995ef5b3731fb627653a93cf54cea5ad1783ed Mon Sep 17 00:00:00 2001
From: Ed Page 
Date: Tue, 9 Dec 2025 14:56:56 -0600
Subject: [PATCH 320/583] 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 000000000000..768282fc2b93
--- /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 321/583] 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 7c969dd7f9f4..6c9ecd75cdbc 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 322/583] 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 6c9ecd75cdbc..4fbc8f3938ad 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 323/583] 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 4fbc8f3938ad..4d0139e179f4 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 324/583] 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 449d0b964fd4..3f3300117460 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 42327c7e343d..4e789a321649 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 4d0139e179f4..f9bf50de091a 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 768282fc2b93..71bede928f37 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 000000000000..d59b899cc4c3
--- /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 e1417f408e0c1f35dd695da6c68e176faacfb4d9 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Wed, 28 Jan 2026 22:06:18 +0100
Subject: [PATCH 325/583] Update `askama` to `0.15.3`

---
 Cargo.lock                              | 16 ++++++++--------
 src/ci/citool/Cargo.toml                |  2 +-
 src/librustdoc/Cargo.toml               |  2 +-
 src/tools/generate-copyright/Cargo.toml |  2 +-
 4 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 63c7d97bce43..35ad4472e6a3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -184,9 +184,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
 [[package]]
 name = "askama"
-version = "0.15.2"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03341eae1125472b0672fbf35cc9aa7b74cd8e0c3d02f02c28a04678f12aaa7a"
+checksum = "10a800c6f7c005e5bcb76ff0b9e61c9e54ad379ce4e83a88ed14ff487a73776d"
 dependencies = [
  "askama_macros",
  "itoa",
@@ -197,9 +197,9 @@ dependencies = [
 
 [[package]]
 name = "askama_derive"
-version = "0.15.2"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "461bd78f3da90b5e44eee4272cfb1c4832aa3dcdb6c370aedd3eb253d2b9e3ca"
+checksum = "0cb7657165bac49b5c533850e7cd67c1c60059aefc31088f89aa431c8a90d5d9"
 dependencies = [
  "askama_parser",
  "basic-toml",
@@ -214,18 +214,18 @@ dependencies = [
 
 [[package]]
 name = "askama_macros"
-version = "0.15.2"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba49fb22ee3074574b8510abd9495d4f0bb9b8f87e8e45ee31e2cee508f7a8e5"
+checksum = "e55eacd3e54d32483cd10d0a881a0f28a40f3a763704ac9b8693edc39d7321c7"
 dependencies = [
  "askama_derive",
 ]
 
 [[package]]
 name = "askama_parser"
-version = "0.15.2"
+version = "0.15.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e33eb7484958aaa1f27e9adb556f5d557331cd891bdbb33781bc1f9550b6f6e"
+checksum = "20c3df8886ab5acdcd76eee93b3e2df1ef734251438b5b942b5fea22c50d2a0f"
 dependencies = [
  "rustc-hash 2.1.1",
  "serde",
diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml
index b394c6fbefff..c2a926b3eaef 100644
--- a/src/ci/citool/Cargo.toml
+++ b/src/ci/citool/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 anyhow = "1"
-askama = "0.15.2"
+askama = "0.15.3"
 clap = { version = "4.5", features = ["derive"] }
 csv = "1"
 diff = "0.1"
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 43006435fcde..efc44a8a2d51 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -10,7 +10,7 @@ path = "lib.rs"
 [dependencies]
 # tidy-alphabetical-start
 arrayvec = { version = "0.7", default-features = false }
-askama = { version = "0.15.2", default-features = false, features = ["alloc", "config", "derive"] }
+askama = { version = "0.15.3", default-features = false, features = ["alloc", "config", "derive"] }
 base64 = "0.21.7"
 indexmap = { version = "2", features = ["serde"] }
 itertools = "0.12"
diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml
index e1a5ca31a1db..965f7f992e75 100644
--- a/src/tools/generate-copyright/Cargo.toml
+++ b/src/tools/generate-copyright/Cargo.toml
@@ -8,7 +8,7 @@ description = "Produces a manifest of all the copyrighted materials in the Rust
 
 [dependencies]
 anyhow = "1.0.65"
-askama = "0.15.2"
+askama = "0.15.3"
 cargo_metadata = "0.21"
 serde = { version = "1.0.147", features = ["derive"] }
 serde_json = "1.0.85"

From 9e61014a8a0bb1f1d7911511c303a7ae2a9c2a7d Mon Sep 17 00:00:00 2001
From: Jonathan Brouwer 
Date: Wed, 28 Jan 2026 08:59:16 +0100
Subject: [PATCH 326/583] 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 cbc70b55d7ee..e71c84c805a7 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 fcae379d982f..506ec7f88a82 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 cf5c0c2e6491..29132b8325f3 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 327/583] Convert `parse_nested_meta` to `parse_args_with` for
 `#[subdiagnostic]`

---
 .../src/diagnostics/subdiagnostic.rs          |  51 ++--
 .../rustc_macros/src/diagnostics/utils.rs     | 242 ++++++++----------
 .../session-diagnostic/diagnostic-derive.rs   |   2 +-
 .../diagnostic-derive.stderr                  |  14 +-
 .../subdiagnostic-derive.rs                   |   7 +-
 .../subdiagnostic-derive.stderr               |  46 ++--
 6 files changed, 165 insertions(+), 197 deletions(-)

diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index dcd0116d804d..189d83c42160 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 c310b99d5351..6ca2c90abb90 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::()?]);
+    }
 
-        if let AllowMultipleAlternatives::No = allow_multiple {
+    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![]);
+    }
+
+    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;
 
-                first = false;
-                return Ok(());
-            }
+                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::()?;
 
-            first = false;
+                        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
+                        });
 
-            let nested_name = nested.path.segments.last().unwrap().ident.to_string();
-            let nested_name = nested_name.as_str();
+                        suggestion_kind.set_once(value, span);
+                    }
 
-            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);
+                        // 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::();
+                    }
                 }
 
-                // 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;
-                }
+                if input.is_empty() { break }
+                input.parse::()?;
             }
-
-            if has_errors {
-                // Consume the rest of the input to avoid spamming errors
-                let _ = input.parse::();
-            }
-
             Ok(())
         })?;
 
diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
index 506ec7f88a82..72b414362c72 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 29132b8325f3..3ccf89cedec7 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 941668ad602e..578fc728de53 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 c31da4421d25..568d75d838fe 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 328/583] 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 e71c84c805a7..748d1d3a60f3 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 189d83c42160..e00d175fc295 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 6ca2c90abb90..4c73e6d453e9 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 72b414362c72..798eca68b1c9 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 3ccf89cedec7..9ecb6c15767e 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 578fc728de53..b2e7b4c61daa 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 568d75d838fe..63634741e934 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 329/583] 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     | 190 +++++++++---------
 3 files changed, 101 insertions(+), 117 deletions(-)

diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 748d1d3a60f3..25110fd4f908 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 e00d175fc295..db2a19ab85ba 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 4c73e6d453e9..f084ba60ae3f 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!`.
+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();
 
-    /// 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();
 
-        // 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;
-                    }
-                }
-                // Eat until (and including) the matching '}'
-                while it.next()? != '}' {
-                    continue;
-                }
-                Some(result)
-            };
-
-            if let Some(referenced_field) = eat_argument() {
-                referenced_fields.insert(referenced_field);
-            }
+    // 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;
+                }
+            }
+            // Eat until (and including) the matching '}'
+            while it.next()? != '}' {
+                continue;
+            }
+            Some(result)
+        };
 
-        // 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"),
-                    )
+        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 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 6eb9c02a96b6086481a80b64019d950d6c385d7a Mon Sep 17 00:00:00 2001
From: Jonathan Brouwer 
Date: Wed, 28 Jan 2026 22:40:28 +0100
Subject: [PATCH 330/583] Subscribe myself to translation diagnostics

---
 triagebot.toml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/triagebot.toml b/triagebot.toml
index 1572c4c8d045..eb25d6e1b201 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1182,11 +1182,11 @@ cc = ["@Muscraft"]
 
 [mentions."compiler/rustc_errors/src/translation.rs"]
 message = "`rustc_errors::translation` was changed"
-cc = ["@davidtwco", "@TaKO8Ki"]
+cc = ["@davidtwco", "@TaKO8Ki", "@JonathanBrouwer"]
 
 [mentions."compiler/rustc_macros/src/diagnostics"]
 message = "`rustc_macros::diagnostics` was changed"
-cc = ["@davidtwco", "@TaKO8Ki"]
+cc = ["@davidtwco", "@TaKO8Ki", "@JonathanBrouwer"]
 
 [mentions."compiler/rustc_public"]
 message = "This PR changes rustc_public"

From b71ff51277dbd5907a7a8089b5d926a02e53d44d Mon Sep 17 00:00:00 2001
From: Caleb Zulawski 
Date: Wed, 28 Jan 2026 00:58:13 -0500
Subject: [PATCH 331/583] 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 139d2a4b4272..429f90acec34 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 56c000633e58..6c5e06518f56 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 3d8376207cd0..1e2d097198d0 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 2a96ea0beefef3c9bfbbca803565e7afb6732b17 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Thu, 29 Jan 2026 10:35:05 +1100
Subject: [PATCH 332/583] Make `QueryDispatcher::Qcx` an associated type

---
 compiler/rustc_query_impl/src/lib.rs          |  15 +--
 compiler/rustc_query_impl/src/plumbing.rs     |  11 +-
 .../src/query/dispatcher.rs                   |  44 ++++---
 .../rustc_query_system/src/query/plumbing.rs  | 108 ++++++++----------
 4 files changed, 86 insertions(+), 92 deletions(-)

diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index b628224db536..d6310b62b275 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -67,11 +67,11 @@ impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDA
 
 // This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`.
 impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool>
-    QueryDispatcher>
-    for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
+    QueryDispatcher for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
 where
     for<'a> C::Key: HashStable>,
 {
+    type Qcx = QueryCtxt<'tcx>;
     type Key = C::Key;
     type Value = C::Value;
     type Cache = C;
@@ -104,10 +104,7 @@ where
     }
 
     #[inline(always)]
-    fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache
-    where
-        'tcx: 'a,
-    {
+    fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache {
         // Safety:
         // This is just manually doing the subfield referencing through pointer math.
         unsafe {
@@ -215,15 +212,13 @@ where
 /// on the type `rustc_query_impl::query_impl::$name::QueryType`.
 trait QueryDispatcherUnerased<'tcx> {
     type UnerasedValue;
-    type Dispatcher: QueryDispatcher>;
+    type Dispatcher: QueryDispatcher>;
 
     const NAME: &'static &'static str;
 
     fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher;
 
-    fn restore_val(
-        value: >>::Value,
-    ) -> Self::UnerasedValue;
+    fn restore_val(value: ::Value) -> Self::UnerasedValue;
 }
 
 pub fn query_system<'a>(
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 6ead03a527a7..0223981fd55d 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -414,7 +414,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>(
 }
 
 pub(crate) fn query_key_hash_verify<'tcx>(
-    query: impl QueryDispatcher>,
+    query: impl QueryDispatcher>,
     qcx: QueryCtxt<'tcx>,
 ) {
     let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name());
@@ -442,7 +442,7 @@ pub(crate) fn query_key_hash_verify<'tcx>(
 
 fn try_load_from_on_disk_cache<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode)
 where
-    Q: QueryDispatcher>,
+    Q: QueryDispatcher>,
 {
     debug_assert!(tcx.dep_graph.is_green(&dep_node));
 
@@ -488,7 +488,7 @@ where
 
 fn force_from_dep_node<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) -> bool
 where
-    Q: QueryDispatcher>,
+    Q: QueryDispatcher>,
 {
     // We must avoid ever having to call `force_from_dep_node()` for a
     // `DepNode::codegen_unit`:
@@ -523,8 +523,7 @@ pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>(
 where
     Q: QueryDispatcherUnerased<'tcx>,
 {
-    let fingerprint_style =
-        >>::Key::fingerprint_style();
+    let fingerprint_style = ::Key::fingerprint_style();
 
     if is_anon || !fingerprint_style.reconstructible() {
         return DepKindVTable {
@@ -731,7 +730,7 @@ macro_rules! define_queries {
                 }
 
                 #[inline(always)]
-                fn restore_val(value: >>::Value) -> Self::UnerasedValue {
+                fn restore_val(value: ::Value) -> Self::UnerasedValue {
                     restore::>(value)
                 }
             }
diff --git a/compiler/rustc_query_system/src/query/dispatcher.rs b/compiler/rustc_query_system/src/query/dispatcher.rs
index bba1703dfbb6..1ca76a70364c 100644
--- a/compiler/rustc_query_system/src/query/dispatcher.rs
+++ b/compiler/rustc_query_system/src/query/dispatcher.rs
@@ -5,13 +5,18 @@ use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_span::ErrorGuaranteed;
 
 use super::QueryStackFrameExtra;
-use crate::dep_graph::{DepKind, DepNode, DepNodeParams, SerializedDepNodeIndex};
+use crate::dep_graph::{DepKind, DepNode, DepNodeParams, HasDepContext, SerializedDepNodeIndex};
 use crate::ich::StableHashingContext;
 use crate::query::caches::QueryCache;
 use crate::query::{CycleError, CycleErrorHandling, DepNodeIndex, QueryContext, QueryState};
 
 pub type HashResult = Option, &V) -> Fingerprint>;
 
+/// Unambiguous shorthand for `::DepContext`.
+#[expect(type_alias_bounds)]
+type DepContextOf =
+    <::Qcx as HasDepContext>::DepContext;
+
 /// Trait that can be used as a vtable for a single query, providing operations
 /// and metadata for that query.
 ///
@@ -20,12 +25,15 @@ pub type HashResult = Option, &V) -> Fingerp
 /// Those types are not visible from this `rustc_query_system` crate.
 ///
 /// "Dispatcher" should be understood as a near-synonym of "vtable".
-pub trait QueryDispatcher: Copy {
+pub trait QueryDispatcher: Copy {
     fn name(self) -> &'static str;
 
+    /// Query context used by this dispatcher, i.e. `rustc_query_impl::QueryCtxt`.
+    type Qcx: QueryContext;
+
     // `Key` and `Value` are `Copy` instead of `Clone` to ensure copying them stays cheap,
     // but it isn't necessary.
-    type Key: DepNodeParams + Eq + Hash + Copy + Debug;
+    type Key: DepNodeParams> + Eq + Hash + Copy + Debug;
     type Value: Copy;
 
     type Cache: QueryCache;
@@ -33,36 +41,40 @@ pub trait QueryDispatcher: Copy {
     fn format_value(self) -> fn(&Self::Value) -> String;
 
     // Don't use this method to access query results, instead use the methods on TyCtxt
-    fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState
-    where
-        Qcx: 'a;
+    fn query_state<'a>(
+        self,
+        tcx: Self::Qcx,
+    ) -> &'a QueryState::QueryInfo>;
 
     // Don't use this method to access query results, instead use the methods on TyCtxt
-    fn query_cache<'a>(self, tcx: Qcx) -> &'a Self::Cache
-    where
-        Qcx: 'a;
+    fn query_cache<'a>(self, tcx: Self::Qcx) -> &'a Self::Cache;
 
-    fn cache_on_disk(self, tcx: Qcx::DepContext, key: &Self::Key) -> bool;
+    fn cache_on_disk(self, tcx: DepContextOf, key: &Self::Key) -> bool;
 
     // Don't use this method to compute query results, instead use the methods on TyCtxt
-    fn execute_query(self, tcx: Qcx::DepContext, k: Self::Key) -> Self::Value;
+    fn execute_query(self, tcx: DepContextOf, k: Self::Key) -> Self::Value;
 
-    fn compute(self, tcx: Qcx, key: Self::Key) -> Self::Value;
+    fn compute(self, tcx: Self::Qcx, key: Self::Key) -> Self::Value;
 
     fn try_load_from_disk(
         self,
-        tcx: Qcx,
+        tcx: Self::Qcx,
         key: &Self::Key,
         prev_index: SerializedDepNodeIndex,
         index: DepNodeIndex,
     ) -> Option;
 
-    fn loadable_from_disk(self, qcx: Qcx, key: &Self::Key, idx: SerializedDepNodeIndex) -> bool;
+    fn loadable_from_disk(
+        self,
+        qcx: Self::Qcx,
+        key: &Self::Key,
+        idx: SerializedDepNodeIndex,
+    ) -> bool;
 
     /// Synthesize an error value to let compilation continue after a cycle.
     fn value_from_cycle_error(
         self,
-        tcx: Qcx::DepContext,
+        tcx: DepContextOf,
         cycle_error: &CycleError,
         guar: ErrorGuaranteed,
     ) -> Self::Value;
@@ -77,7 +89,7 @@ pub trait QueryDispatcher: Copy {
     fn hash_result(self) -> HashResult;
 
     // Just here for convenience and checking that the key matches the kind, don't override this.
-    fn construct_dep_node(self, tcx: Qcx::DepContext, key: &Self::Key) -> DepNode {
+    fn construct_dep_node(self, tcx: DepContextOf, key: &Self::Key) -> DepNode {
         DepNode::construct(tcx, self.dep_kind(), key)
     }
 }
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 9afad1546e9e..7e9f83e8fe82 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -19,7 +19,9 @@ use rustc_span::{DUMMY_SP, Span};
 use tracing::instrument;
 
 use super::{QueryDispatcher, QueryStackFrameExtra};
-use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams};
+use crate::dep_graph::{
+    DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams, HasDepContext,
+};
 use crate::ich::StableHashingContext;
 use crate::query::caches::QueryCache;
 use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle};
@@ -124,24 +126,22 @@ where
 
 #[cold]
 #[inline(never)]
-fn mk_cycle(query: Q, qcx: Qcx, cycle_error: CycleError) -> Q::Value
+fn mk_cycle(query: Q, qcx: Q::Qcx, cycle_error: CycleError) -> Q::Value
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
     handle_cycle_error(query, qcx, &cycle_error, error)
 }
 
-fn handle_cycle_error(
+fn handle_cycle_error(
     query: Q,
-    qcx: Qcx,
+    qcx: Q::Qcx,
     cycle_error: &CycleError,
     error: Diag<'_>,
 ) -> Q::Value
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     match query.cycle_error_handling() {
         CycleErrorHandling::Error => {
@@ -272,15 +272,14 @@ where
 
 #[cold]
 #[inline(never)]
-fn cycle_error(
+fn cycle_error(
     query: Q,
-    qcx: Qcx,
+    qcx: Q::Qcx,
     try_execute: QueryJobId,
     span: Span,
 ) -> (Q::Value, Option)
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     // Ensure there was no errors collecting all active jobs.
     // We need the complete map to ensure we find a cycle to break.
@@ -291,17 +290,16 @@ where
 }
 
 #[inline(always)]
-fn wait_for_query(
+fn wait_for_query(
     query: Q,
-    qcx: Qcx,
+    qcx: Q::Qcx,
     span: Span,
     key: Q::Key,
-    latch: QueryLatch,
+    latch: QueryLatch<::QueryInfo>,
     current: Option,
 ) -> (Q::Value, Option)
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     // For parallel queries, we'll block and wait until the query running
     // in another thread has completed. Record how long we wait in the
@@ -341,16 +339,15 @@ where
 }
 
 #[inline(never)]
-fn try_execute_query(
+fn try_execute_query(
     query: Q,
-    qcx: Qcx,
+    qcx: Q::Qcx,
     span: Span,
     key: Q::Key,
     dep_node: Option,
 ) -> (Q::Value, Option)
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     let state = query.query_state(qcx);
     let key_hash = sharded::make_hash(&key);
@@ -382,7 +379,7 @@ where
             // Drop the lock before we start executing the query
             drop(state_lock);
 
-            execute_job::<_, _, INCR>(query, qcx, state, key, key_hash, id, dep_node)
+            execute_job::(query, qcx, state, key, key_hash, id, dep_node)
         }
         Entry::Occupied(mut entry) => {
             match &mut entry.get_mut().1 {
@@ -411,18 +408,17 @@ where
 }
 
 #[inline(always)]
-fn execute_job(
+fn execute_job(
     query: Q,
-    qcx: Qcx,
-    state: &QueryState,
+    qcx: Q::Qcx,
+    state: &QueryState::QueryInfo>,
     key: Q::Key,
     key_hash: u64,
     id: QueryJobId,
     dep_node: Option,
 ) -> (Q::Value, Option)
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     // Use `JobOwner` so the query will be poisoned if executing it panics.
     let job_owner = JobOwner { state, key };
@@ -484,15 +480,14 @@ where
 
 // Fast path for when incr. comp. is off.
 #[inline(always)]
-fn execute_job_non_incr(
+fn execute_job_non_incr(
     query: Q,
-    qcx: Qcx,
+    qcx: Q::Qcx,
     key: Q::Key,
     job_id: QueryJobId,
 ) -> (Q::Value, DepNodeIndex)
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
 
@@ -521,17 +516,16 @@ where
 }
 
 #[inline(always)]
-fn execute_job_incr(
+fn execute_job_incr(
     query: Q,
-    qcx: Qcx,
-    dep_graph_data: &DepGraphData,
+    qcx: Q::Qcx,
+    dep_graph_data: &DepGraphData<::Deps>,
     key: Q::Key,
     mut dep_node_opt: Option,
     job_id: QueryJobId,
 ) -> (Q::Value, DepNodeIndex)
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     if !query.anon() && !query.eval_always() {
         // `to_dep_node` is expensive for some `DepKind`s.
@@ -577,16 +571,15 @@ where
 }
 
 #[inline(always)]
-fn try_load_from_disk_and_cache_in_memory(
+fn try_load_from_disk_and_cache_in_memory(
     query: Q,
-    dep_graph_data: &DepGraphData,
-    qcx: Qcx,
+    dep_graph_data: &DepGraphData<::Deps>,
+    qcx: Q::Qcx,
     key: &Q::Key,
     dep_node: &DepNode,
 ) -> Option<(Q::Value, DepNodeIndex)>
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     // Note this function can be called concurrently from the same query
     // We must ensure that this is handled correctly.
@@ -764,15 +757,14 @@ fn incremental_verify_ich_failed(
 ///
 /// Note: The optimization is only available during incr. comp.
 #[inline(never)]
-fn ensure_must_run(
+fn ensure_must_run(
     query: Q,
-    qcx: Qcx,
+    qcx: Q::Qcx,
     key: &Q::Key,
     check_cache: bool,
 ) -> (bool, Option)
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     if query.eval_always() {
         return (true, None);
@@ -817,27 +809,25 @@ pub enum QueryMode {
 }
 
 #[inline(always)]
-pub fn get_query_non_incr(query: Q, qcx: Qcx, span: Span, key: Q::Key) -> Q::Value
+pub fn get_query_non_incr(query: Q, qcx: Q::Qcx, span: Span, key: Q::Key) -> Q::Value
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
 
-    ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, None).0)
+    ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, None).0)
 }
 
 #[inline(always)]
-pub fn get_query_incr(
+pub fn get_query_incr(
     query: Q,
-    qcx: Qcx,
+    qcx: Q::Qcx,
     span: Span,
     key: Q::Key,
     mode: QueryMode,
 ) -> Option
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     debug_assert!(qcx.dep_context().dep_graph().is_fully_enabled());
 
@@ -851,19 +841,17 @@ where
         None
     };
 
-    let (result, dep_node_index) = ensure_sufficient_stack(|| {
-        try_execute_query::<_, _, true>(query, qcx, span, key, dep_node)
-    });
+    let (result, dep_node_index) =
+        ensure_sufficient_stack(|| try_execute_query::(query, qcx, span, key, dep_node));
     if let Some(dep_node_index) = dep_node_index {
         qcx.dep_context().dep_graph().read_index(dep_node_index)
     }
     Some(result)
 }
 
-pub fn force_query(query: Q, qcx: Qcx, key: Q::Key, dep_node: DepNode)
+pub fn force_query(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode)
 where
-    Q: QueryDispatcher,
-    Qcx: QueryContext,
+    Q: QueryDispatcher,
 {
     // We may be concurrently trying both execute and force a query.
     // Ensure that only one of them runs the query.
@@ -875,6 +863,6 @@ where
     debug_assert!(!query.anon());
 
     ensure_sufficient_stack(|| {
-        try_execute_query::<_, _, true>(query, qcx, DUMMY_SP, key, Some(dep_node))
+        try_execute_query::(query, qcx, DUMMY_SP, key, Some(dep_node))
     });
 }

From 99591e6d427568ddfee8212ebe12d4300c111f16 Mon Sep 17 00:00:00 2001
From: sgasho 
Date: Sat, 24 Jan 2026 23:44:31 +0900
Subject: [PATCH 333/583] Fix -Zmir-enable-passes to detect unregistered enum
 names

---
 compiler/rustc_mir_transform/src/lib.rs       | 30 +++++++++++++------
 tests/ui/lint/invalid_value-polymorphic.rs    |  2 +-
 ...s_validation.enum_not_in_pass_names.stderr |  8 +++++
 tests/ui/mir/enable_passes_validation.rs      |  9 ++++++
 4 files changed, 39 insertions(+), 10 deletions(-)
 create mode 100644 tests/ui/mir/enable_passes_validation.enum_not_in_pass_names.stderr

diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 2f8c6b876398..0e6a1a414e45 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -91,20 +91,32 @@ macro_rules! declare_passes {
             )+
         )*
 
-        static PASS_NAMES: LazyLock> = LazyLock::new(|| [
+        static PASS_NAMES: LazyLock> = LazyLock::new(|| {
+            let mut set = FxIndexSet::default();
             // Fake marker pass
-            "PreCodegen",
+            set.insert("PreCodegen");
             $(
                 $(
-                    stringify!($pass_name),
-                    $(
-                        $(
-                            $mod_name::$pass_name::$ident.name(),
-                        )*
-                    )?
+                    set.extend(pass_names!($mod_name : $pass_name $( { $($ident),* } )? ));
                 )+
             )*
-        ].into_iter().collect());
+            set
+        });
+    };
+}
+
+macro_rules! pass_names {
+    // pass groups: only pass names inside are considered pass_names
+    ($mod_name:ident : $pass_group:ident { $($pass_name:ident),* $(,)? }) => {
+        [
+            $(
+                $mod_name::$pass_group::$pass_name.name(),
+            )*
+        ]
+    };
+    // lone pass names: stringify the struct or enum name
+    ($mod_name:ident : $pass_name:ident) => {
+        [stringify!($pass_name)]
     };
 }
 
diff --git a/tests/ui/lint/invalid_value-polymorphic.rs b/tests/ui/lint/invalid_value-polymorphic.rs
index 6a31ac17d96f..4ed8950d20fa 100644
--- a/tests/ui/lint/invalid_value-polymorphic.rs
+++ b/tests/ui/lint/invalid_value-polymorphic.rs
@@ -1,4 +1,4 @@
-//@ compile-flags: --crate-type=lib -Zmir-enable-passes=+InstSimplify
+//@ compile-flags: --crate-type=lib -Zmir-enable-passes=+InstSimplify-before-inline
 //@ build-pass
 
 #![feature(core_intrinsics)]
diff --git a/tests/ui/mir/enable_passes_validation.enum_not_in_pass_names.stderr b/tests/ui/mir/enable_passes_validation.enum_not_in_pass_names.stderr
new file mode 100644
index 000000000000..89229ebabe84
--- /dev/null
+++ b/tests/ui/mir/enable_passes_validation.enum_not_in_pass_names.stderr
@@ -0,0 +1,8 @@
+warning: MIR pass `SimplifyCfg` is unknown and will be ignored
+
+warning: MIR pass `SimplifyCfg` is unknown and will be ignored
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/mir/enable_passes_validation.rs b/tests/ui/mir/enable_passes_validation.rs
index 99b1ba528b0c..d3d22b49ac7f 100644
--- a/tests/ui/mir/enable_passes_validation.rs
+++ b/tests/ui/mir/enable_passes_validation.rs
@@ -1,4 +1,5 @@
 //@ revisions: empty unprefixed all_unknown all_known mixed
+//@ revisions: enum_not_in_pass_names enum_in_pass_names
 
 //@[empty] compile-flags: -Zmir-enable-passes=
 
@@ -13,6 +14,12 @@
 //@[mixed] check-pass
 //@[mixed] compile-flags: -Zmir-enable-passes=+ThisPassDoesNotExist,+CheckAlignment
 
+//@[enum_not_in_pass_names] check-pass
+//@[enum_not_in_pass_names] compile-flags: -Zmir-enable-passes=+SimplifyCfg
+
+//@[enum_in_pass_names] check-pass
+//@[enum_in_pass_names] compile-flags: -Zmir-enable-passes=+AddCallGuards
+
 fn main() {}
 
 //[empty]~? ERROR incorrect value `` for unstable option `mir-enable-passes`
@@ -23,3 +30,5 @@ fn main() {}
 //[all_unknown]~? WARN MIR pass `DoesNotExist` is unknown and will be ignored
 //[all_unknown]~? WARN MIR pass `ThisPass` is unknown and will be ignored
 //[all_unknown]~? WARN MIR pass `DoesNotExist` is unknown and will be ignored
+//[enum_not_in_pass_names]~? WARN MIR pass `SimplifyCfg` is unknown and will be ignored
+//[enum_not_in_pass_names]~? WARN MIR pass `SimplifyCfg` is unknown and will be ignored

From 8238fa692a4a7a99ad8aca5b147b63b9ff17e80d Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Thu, 29 Jan 2026 13:08:13 +1100
Subject: [PATCH 334/583] Remove or narrow some `typos.toml` allowlist entries

---
 typos.toml | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/typos.toml b/typos.toml
index 920234a9381b..7fbf129f9cc7 100644
--- a/typos.toml
+++ b/typos.toml
@@ -19,7 +19,6 @@ extend-exclude = [
 arange = "arange"
 childs = "childs"
 clonable = "clonable"
-Datas = "Datas"
 filetimes = "filetimes"
 leafs = "leafs"
 makro = "makro"
@@ -28,7 +27,6 @@ moreso = "moreso"
 optin = "optin"
 publically = "publically"
 rplace = "rplace"
-smove = "smove"
 splitted = "splitted"
 taits = "taits"
 targetting = "targetting"
@@ -37,9 +35,6 @@ unstability = "unstability"
 unstalled = "unstalled"
 numer = "numer"
 
-# this can be valid word, depends on dictionary edition
-#matcheable = "matcheable"
-
 [default.extend-identifiers]
 # An entry goes here if the typo is part of some existing ident
 # where you want to keep it, but don't want to allow
@@ -63,6 +58,7 @@ Oppen = "Oppen"
 # typos treats this as two different camelcase words (`SETTIN` and `Gs`)
 # Tracked in: https://github.com/crate-ci/typos/issues/745
 SETTINGs = "SETTINGs"
+key_smove = "key_smove" # shifted move key, used by terminfo
 tolen = "tolen"
 
 [default]

From e8bdcb084d74b1a3db48ba2b3d5a23999d57773d Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Thu, 29 Jan 2026 12:55:26 +1100
Subject: [PATCH 335/583] Enforce alphabetical sorting in `typos.toml` and
 tweak comments

---
 src/tools/tidy/src/main.rs |  2 ++
 typos.toml                 | 40 +++++++++++++++++++++++++-------------
 2 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 94c24f11ed12..0d9a22556dfa 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -32,6 +32,7 @@ fn main() {
     let npm: PathBuf = env::args_os().nth(5).expect("need name/path of npm command").into();
 
     let root_manifest = root_path.join("Cargo.toml");
+    let typos_toml = root_path.join("typos.toml");
     let src_path = root_path.join("src");
     let tests_path = root_path.join("tests");
     let library_path = root_path.join("library");
@@ -143,6 +144,7 @@ fn main() {
         check!(edition, &library_path);
 
         check!(alphabetical, &root_manifest);
+        check!(alphabetical, &typos_toml);
         check!(alphabetical, &src_path);
         check!(alphabetical, &tests_path);
         check!(alphabetical, &compiler_path);
diff --git a/typos.toml b/typos.toml
index 7fbf129f9cc7..a735187d1669 100644
--- a/typos.toml
+++ b/typos.toml
@@ -1,3 +1,6 @@
+# Config for the `typos` crate, used by `./x test tidy --extra-checks=spellcheck`.
+# See also: https://github.com/crate-ci/typos/blob/v1.28.2/docs/reference.md
+
 [files]
 extend-exclude = [
     # exclude git (sub)modules and generated content
@@ -13,9 +16,10 @@ extend-exclude = [
 ]
 
 [default.extend-words]
-# Add exclusions here, lines should be like `x = "x"`, where `x` is excluded word.
+# Allowlist for words that look like typos but are not, or aren't worth fixing
+# right now. Entries should look like `mipsel = "mipsel"`.
 #
-# Also see docs: https://github.com/crate-ci/typos/blob/v1.28.2/docs/reference.md
+# tidy-alphabetical-start
 arange = "arange"
 childs = "childs"
 clonable = "clonable"
@@ -24,6 +28,7 @@ leafs = "leafs"
 makro = "makro"
 misformed = "misformed"
 moreso = "moreso"
+numer = "numer"
 optin = "optin"
 publically = "publically"
 rplace = "rplace"
@@ -33,33 +38,42 @@ targetting = "targetting"
 unparseable = "unparseable"
 unstability = "unstability"
 unstalled = "unstalled"
-numer = "numer"
+# tidy-alphabetical-end
+
+# Denylist to forbid misspelled words that aren't detected by the built-in
+# dictionary. Entries should look like `mipsel = ""` or `mipsel = "misspell"`;
+# the non-empty form can be automatically fixed by `--bless`.
+#
+# tidy-alphabetical-start
+# tidy-alphabetical-end
 
 [default.extend-identifiers]
-# An entry goes here if the typo is part of some existing ident
-# where you want to keep it, but don't want to allow
-# such typos everywhere.
+# Allowlist for specific identifiers that should be permitted even though they
+# appear to contain typos that would be forbidden in other identifiers.
 #
-# I.e. you don't want (or can't) fix some constant name, like
-# `DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME` but actually
-# want to see `INVAILD` typo fixed in other places.
-debug_aranges = "debug_aranges"
+# For example, you might want to allow a specific constant like
+# `DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME`, but still want to forbid
+# the typo `INVAILD` in other places.
+#
+# tidy-alphabetical-start
 DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME = "DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME"
-EnzymeTypeTreeShiftIndiciesEq = "EnzymeTypeTreeShiftIndiciesEq"
-EnzymeTypeTreeShiftIndiciesEqFn = "EnzymeTypeTreeShiftIndiciesEqFn"
-shift_indicies_eq = "shift_indicies_eq"
 ERRNO_ACCES = "ERRNO_ACCES"
 ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS = "ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS"
 ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC = "ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC"
 ERROR_FILENAME_EXCED_RANGE = "ERROR_FILENAME_EXCED_RANGE"
 ERROR_MCA_OCCURED = "ERROR_MCA_OCCURED"
 ERROR_REQ_NOT_ACCEP = "ERROR_REQ_NOT_ACCEP"
+EnzymeTypeTreeShiftIndiciesEq = "EnzymeTypeTreeShiftIndiciesEq"
+EnzymeTypeTreeShiftIndiciesEqFn = "EnzymeTypeTreeShiftIndiciesEqFn"
 Oppen = "Oppen"
 # typos treats this as two different camelcase words (`SETTIN` and `Gs`)
 # Tracked in: https://github.com/crate-ci/typos/issues/745
 SETTINGs = "SETTINGs"
+debug_aranges = "debug_aranges"
 key_smove = "key_smove" # shifted move key, used by terminfo
+shift_indicies_eq = "shift_indicies_eq"
 tolen = "tolen"
+# tidy-alphabetical-end
 
 [default]
 extend-ignore-words-re = [

From 38a60a63079923034699045ff27d1ec14c9d4c75 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Thu, 29 Jan 2026 13:28:10 +1100
Subject: [PATCH 336/583] Explain some allowlist entries in `typos.toml`

---
 typos.toml | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/typos.toml b/typos.toml
index a735187d1669..c65e2ba293e5 100644
--- a/typos.toml
+++ b/typos.toml
@@ -20,24 +20,24 @@ extend-exclude = [
 # right now. Entries should look like `mipsel = "mipsel"`.
 #
 # tidy-alphabetical-start
-arange = "arange"
+arange = "arange" # short for A-range
 childs = "childs"
 clonable = "clonable"
-filetimes = "filetimes"
+filetimes = "filetimes" # short for "file times", not a typo for "lifetimes"
 leafs = "leafs"
-makro = "makro"
+makro = "makro" # deliberate misspelling to avoid `macro` keyword
 misformed = "misformed"
 moreso = "moreso"
-numer = "numer"
-optin = "optin"
+numer = "numer" # short for numerator, not a typo for "number"
+optin = "optin" # short for opt-in
 publically = "publically"
-rplace = "rplace"
+rplace = "rplace" # short for R-place
 splitted = "splitted"
-taits = "taits"
+taits = "taits" # lowercase for TAITs (type alias impl trait)
 targetting = "targetting"
 unparseable = "unparseable"
 unstability = "unstability"
-unstalled = "unstalled"
+unstalled = "unstalled" # short for un-stalled
 # tidy-alphabetical-end
 
 # Denylist to forbid misspelled words that aren't detected by the built-in
@@ -65,14 +65,14 @@ ERROR_MCA_OCCURED = "ERROR_MCA_OCCURED"
 ERROR_REQ_NOT_ACCEP = "ERROR_REQ_NOT_ACCEP"
 EnzymeTypeTreeShiftIndiciesEq = "EnzymeTypeTreeShiftIndiciesEq"
 EnzymeTypeTreeShiftIndiciesEqFn = "EnzymeTypeTreeShiftIndiciesEqFn"
-Oppen = "Oppen"
+Oppen = "Oppen" # Derek C. Oppen, author of "Pretty Printing" (1979)
 # typos treats this as two different camelcase words (`SETTIN` and `Gs`)
 # Tracked in: https://github.com/crate-ci/typos/issues/745
 SETTINGs = "SETTINGs"
-debug_aranges = "debug_aranges"
+debug_aranges = "debug_aranges" # debug A-ranges
 key_smove = "key_smove" # shifted move key, used by terminfo
 shift_indicies_eq = "shift_indicies_eq"
-tolen = "tolen"
+tolen = "tolen" # length of "to" buffer, used by `sendto` in Windows sockets
 # tidy-alphabetical-end
 
 [default]

From a42bbe697f24adbb3683ea0479c9b78a04a50e2f Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Thu, 29 Jan 2026 13:10:58 +1100
Subject: [PATCH 337/583] Fix some typos of "definition"

---
 compiler/rustc_resolve/src/build_reduced_graph.rs | 4 ++--
 typos.toml                                        | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index e06ca59771e6..f0dffd8829da 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -60,7 +60,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
     }
 
-    /// Create a name definitinon from the given components, and put it into the local module.
+    /// Create a name definition from the given components, and put it into the local module.
     fn define_local(
         &mut self,
         parent: Module<'ra>,
@@ -76,7 +76,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         self.plant_decl_into_local_module(ident, orig_ident.span, ns, decl);
     }
 
-    /// Create a name definitinon from the given components, and put it into the extern module.
+    /// Create a name definition from the given components, and put it into the extern module.
     fn define_extern(
         &self,
         parent: Module<'ra>,
diff --git a/typos.toml b/typos.toml
index c65e2ba293e5..25083174cb8f 100644
--- a/typos.toml
+++ b/typos.toml
@@ -45,6 +45,7 @@ unstalled = "unstalled" # short for un-stalled
 # the non-empty form can be automatically fixed by `--bless`.
 #
 # tidy-alphabetical-start
+definitinon = "definition"
 # tidy-alphabetical-end
 
 [default.extend-identifiers]

From b235cc22acdde9be7a25c3b5828e9c5863a0b54f Mon Sep 17 00:00:00 2001
From: Adwin White 
Date: Thu, 7 Aug 2025 19:41:57 +0800
Subject: [PATCH 338/583] fix accidental type variable resolution in array
 coercion

---
 compiler/rustc_hir_typeck/src/coercion.rs    | 23 +++++++++++++
 compiler/rustc_hir_typeck/src/expr.rs        | 13 ++++++-
 tests/ui/coercion/closure-in-array.rs        |  7 ++++
 tests/ui/coercion/closure-in-array.stderr    | 14 ++++++++
 tests/ui/coercion/coerce-many-with-ty-var.rs | 36 ++++++++++++++++++++
 5 files changed, 92 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/coercion/closure-in-array.rs
 create mode 100644 tests/ui/coercion/closure-in-array.stderr
 create mode 100644 tests/ui/coercion/coerce-many-with-ty-var.rs

diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 52ea6cdeaa0e..0e87b90dc6c1 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1365,6 +1365,7 @@ pub fn can_coerce<'tcx>(
 ///   - WARNING: I don't believe this final type is guaranteed to be
 ///     related to your initial `expected_ty` in any particular way,
 ///     although it will typically be a subtype, so you should check it.
+///     Check the note below for more details.
 ///   - Invoking `complete()` may cause us to go and adjust the "adjustments" on
 ///     previously coerced expressions.
 ///
@@ -1378,6 +1379,28 @@ pub fn can_coerce<'tcx>(
 /// }
 /// let final_ty = coerce.complete(fcx);
 /// ```
+///
+/// NOTE: Why does the `expected_ty` participate in the LUB?
+/// When coercing, each branch should use the following expectations for type inference:
+/// - The branch can be coerced to the expected type of the match/if/whatever.
+/// - The branch can be coercion lub'd with the types of the previous branches.
+/// Ideally we'd have some sort of `Expectation::ParticipatesInCoerceLub(ongoing_lub_ty, final_ty)`,
+/// but adding and using this feels very challenging.
+/// What we instead do is to use the expected type of the match/if/whatever as
+/// the initial coercion lub. This allows us to use the lub of "expected type of match" with
+/// "types from previous branches" as the coercion target, which can contains both expectations.
+///
+/// Two concerns with this approach:
+/// - We may have incompatible `final_ty` if that lub is different from the expected
+///   type of the match. However, in this case coercing the final type of the
+///   `CoerceMany` to its expected type would have error'd anyways, so we don't care.
+/// - We may constrain the `expected_ty` too early. For some branches with
+///   type `a` and `b`, we end up with `(a lub expected_ty) lub b` instead of
+///   `(a lub b) lub expected_ty`. They should be the same type. However,
+///   `a lub expected_ty` may constrain inference variables in `expected_ty`.
+///   In this case the difference does matter and we get actually incorrect results.
+/// FIXME: Ideally we'd compute the final type without unnecessarily constraining
+/// the expected type of the match when computing the types of its branches.
 pub(crate) struct CoerceMany<'tcx> {
     expected_ty: Ty<'tcx>,
     final_ty: Option>,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 885af3b909b8..5b40531f9462 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1670,11 +1670,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let coerce_to = expected
                 .to_option(self)
-                .and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index())
+                .and_then(|uty| {
+                    self.try_structurally_resolve_type(expr.span, uty)
+                        .builtin_index()
+                        // Avoid using the original type variable as the coerce_to type, as it may resolve
+                        // during the first coercion instead of being the LUB type.
+                        .filter(|t| !self.try_structurally_resolve_type(expr.span, *t).is_ty_var())
+                })
                 .unwrap_or_else(|| self.next_ty_var(expr.span));
             let mut coerce = CoerceMany::with_capacity(coerce_to, args.len());
 
             for e in args {
+                // FIXME: the element expectation should use
+                // `try_structurally_resolve_and_adjust_for_branches` just like in `if` and `match`.
+                // While that fixes nested coercion, it will break [some
+                // code like this](https://github.com/rust-lang/rust/pull/140283#issuecomment-2958776528).
+                // If we find a way to support recursive tuple coercion, this break can be avoided.
                 let e_ty = self.check_expr_with_hint(e, coerce_to);
                 let cause = self.misc(e.span);
                 coerce.coerce(self, &cause, e, e_ty);
diff --git a/tests/ui/coercion/closure-in-array.rs b/tests/ui/coercion/closure-in-array.rs
new file mode 100644
index 000000000000..ca5c021c77a7
--- /dev/null
+++ b/tests/ui/coercion/closure-in-array.rs
@@ -0,0 +1,7 @@
+// Weakened closure sig inference by #140283.
+fn foo usize, const N: usize>(x: [F; N]) {}
+
+fn main() {
+    foo([|s| s.len()])
+    //~^ ERROR: type annotations needed
+}
diff --git a/tests/ui/coercion/closure-in-array.stderr b/tests/ui/coercion/closure-in-array.stderr
new file mode 100644
index 000000000000..90cf590c09e7
--- /dev/null
+++ b/tests/ui/coercion/closure-in-array.stderr
@@ -0,0 +1,14 @@
+error[E0282]: type annotations needed
+  --> $DIR/closure-in-array.rs:5:11
+   |
+LL |     foo([|s| s.len()])
+   |           ^  - type must be known at this point
+   |
+help: consider giving this closure parameter an explicit type
+   |
+LL |     foo([|s: /* Type */| s.len()])
+   |            ++++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/coercion/coerce-many-with-ty-var.rs b/tests/ui/coercion/coerce-many-with-ty-var.rs
new file mode 100644
index 000000000000..cbd16f58ea5b
--- /dev/null
+++ b/tests/ui/coercion/coerce-many-with-ty-var.rs
@@ -0,0 +1,36 @@
+//@ run-pass
+// Check that least upper bound coercions don't resolve type variable merely based on the first
+// coercion. Check issue #136420.
+
+fn foo() {}
+fn bar() {}
+
+fn infer(_: T) {}
+
+fn infer_array_element(_: [T; 2]) {}
+
+fn main() {
+    // Previously the element type's ty var will be unified with `foo`.
+    let _: [_; 2] = [foo, bar];
+    infer_array_element([foo, bar]);
+
+    let _ = if false {
+        foo
+    } else {
+        bar
+    };
+    infer(if false {
+        foo
+    } else {
+        bar
+    });
+
+    let _ = match false {
+        true => foo,
+        false => bar,
+    };
+    infer(match false {
+        true => foo,
+        false => bar,
+    });
+}

From f5b53682a1b14b97b13f335110d8ad55f6c7fb9f Mon Sep 17 00:00:00 2001
From: Zen <250477360+ZenPZero@users.noreply.github.com>
Date: Wed, 28 Jan 2026 23:00:39 -0500
Subject: [PATCH 339/583] Fix grammar

---
 library/std/src/env.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 5c068ad2471a..615b767a4ea5 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -728,7 +728,7 @@ pub fn temp_dir() -> PathBuf {
 ///
 /// You expected to safely execute the current executable, but you're
 /// instead executing something completely different. The code you
-/// just executed run with your privileges.
+/// just executed runs with your privileges.
 ///
 /// This sort of behavior has been known to [lead to privilege escalation] when
 /// used incorrectly.

From b801df5641f4ea73abaac3aec90a26efe267cb95 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Thu, 29 Jan 2026 04:39:36 +0000
Subject: [PATCH 340/583] Prepare for merging from rust-lang/rust

This updates the rust-version file to ba284f468cd2cda48420251efc991758ec13d450.
---
 src/tools/rust-analyzer/rust-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 17b678eed936..a1011c4a0acf 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-94a0cd15f5976fa35e5e6784e621c04e9f958e57
+ba284f468cd2cda48420251efc991758ec13d450

From 3e6b27514360856d4b4b616983766809a885258d Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Thu, 29 Jan 2026 13:48:07 +0800
Subject: [PATCH 341/583] fix: complete inferred type in static

Example
---
```rust
struct Foo(T);
static FOO: $0 = Foo(2);
```

**Before this PR**

```text
...
bt u32                      u32
...
```

**After this PR**

```text
...
bt u32                      u32
it Foo
...
```
---
 .../ide-completion/src/context/analysis.rs    |  5 ++++
 .../ide-completion/src/tests/type_pos.rs      | 29 +++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 8842d29c8d90..1c8bc656ca25 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -1250,6 +1250,11 @@ fn classify_name_ref<'db>(
                     let original = ast::Const::cast(name.syntax().parent()?)?;
                     TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
                 },
+                ast::Static(it) => {
+                    let name = find_opt_node_in_file(original_file, it.name())?;
+                    let original = ast::Static::cast(name.syntax().parent()?)?;
+                    TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
+                },
                 ast::RetType(it) => {
                     it.thin_arrow_token()?;
                     let parent = match ast::Fn::cast(parent.parent()?) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index 3bbba18c2b9f..7c6b7370aafd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -183,6 +183,35 @@ const FOO: $0 = Foo(2);
     );
 }
 
+#[test]
+fn inferred_type_static() {
+    check_with_base_items(
+        r#"
+struct Foo(T);
+static FOO: $0 = Foo(2);
+"#,
+        expect![[r#"
+            en Enum                    Enum
+            ma makro!(…) macro_rules! makro
+            md module
+            st Foo<…>        Foo<{unknown}>
+            st Record                Record
+            st Tuple                  Tuple
+            st Unit                    Unit
+            tt Trait
+            un Union                  Union
+            bt u32                      u32
+            it Foo
+            kw crate::
+            kw dyn
+            kw fn
+            kw for
+            kw impl
+            kw self::
+        "#]],
+    );
+}
+
 #[test]
 fn inferred_type_closure_param() {
     check_with_base_items(

From a5052a093fa805edb5efdc512761e1cc8086f134 Mon Sep 17 00:00:00 2001
From: Andrew Zhogin 
Date: Mon, 6 Oct 2025 04:39:10 +0700
Subject: [PATCH 342/583] hir_owner_parent optimized to inlined call for
 non-incremental build

---
 compiler/rustc_middle/src/hir/mod.rs   | 42 +++++++++++++++++---------
 compiler/rustc_middle/src/query/mod.rs |  2 +-
 2 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs
index ba2d8febad7c..82f8eb4bbc4a 100644
--- a/compiler/rustc_middle/src/hir/mod.rs
+++ b/compiler/rustc_middle/src/hir/mod.rs
@@ -365,6 +365,33 @@ impl<'tcx> TyCtxt<'tcx> {
             }
         }
     }
+
+    #[inline]
+    fn hir_owner_parent_impl(self, owner_id: OwnerId) -> HirId {
+        self.opt_local_parent(owner_id.def_id).map_or(CRATE_HIR_ID, |parent_def_id| {
+            let parent_owner_id = self.local_def_id_to_hir_id(parent_def_id).owner;
+            HirId {
+                owner: parent_owner_id,
+                local_id: self.hir_crate(()).owners[parent_owner_id.def_id]
+                    .unwrap()
+                    .parenting
+                    .get(&owner_id.def_id)
+                    .copied()
+                    .unwrap_or(ItemLocalId::ZERO),
+            }
+        })
+    }
+
+    /// Optimization of `hir_owner_parent` query as an inlined function
+    /// in case of non-incremental build. The query itself renamed to `hir_owner_parent_q`.
+    #[inline]
+    pub fn hir_owner_parent(self, owner_id: OwnerId) -> HirId {
+        if self.dep_graph.is_fully_enabled() {
+            self.hir_owner_parent_q(owner_id)
+        } else {
+            self.hir_owner_parent_impl(owner_id)
+        }
+    }
 }
 
 /// Hashes computed by [`TyCtxt::hash_owner_nodes`] if necessary.
@@ -386,20 +413,7 @@ pub fn provide(providers: &mut Providers) {
     };
     providers.opt_hir_owner_nodes =
         |tcx, id| tcx.hir_crate(()).owners.get(id)?.as_owner().map(|i| &i.nodes);
-    providers.hir_owner_parent = |tcx, owner_id| {
-        tcx.opt_local_parent(owner_id.def_id).map_or(CRATE_HIR_ID, |parent_def_id| {
-            let parent_owner_id = tcx.local_def_id_to_hir_id(parent_def_id).owner;
-            HirId {
-                owner: parent_owner_id,
-                local_id: tcx.hir_crate(()).owners[parent_owner_id.def_id]
-                    .unwrap()
-                    .parenting
-                    .get(&owner_id.def_id)
-                    .copied()
-                    .unwrap_or(ItemLocalId::ZERO),
-            }
-        })
-    };
+    providers.hir_owner_parent_q = |tcx, owner_id| tcx.hir_owner_parent_impl(owner_id);
     providers.hir_attr_map = |tcx, id| {
         tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs)
     };
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 9d17c998a8f2..0a4faa1b6247 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -266,7 +266,7 @@ rustc_queries! {
     ///
     /// This can be conveniently accessed by `tcx.hir_*` methods.
     /// Avoid calling this query directly.
-    query hir_owner_parent(key: hir::OwnerId) -> hir::HirId {
+    query hir_owner_parent_q(key: hir::OwnerId) -> hir::HirId {
         desc { |tcx| "getting HIR parent of `{}`", tcx.def_path_str(key) }
     }
 

From 59c77c43d941dd621701d8e452928846af934497 Mon Sep 17 00:00:00 2001
From: Shunpoco 
Date: Sat, 17 Jan 2026 10:14:47 +0000
Subject: [PATCH 343/583] use clap on tidy

Current tidy parses paths and options manually, and parsing is spreading multple files. This commit introduces a parser using clap to clean and centralize it.
---
 Cargo.lock                               |   9 +-
 src/tools/tidy/Cargo.toml                |   1 +
 src/tools/tidy/src/alphabetical/tests.rs |   2 +-
 src/tools/tidy/src/arg_parser.rs         |  92 +++++++++++++
 src/tools/tidy/src/arg_parser/tests.rs   | 162 +++++++++++++++++++++++
 src/tools/tidy/src/diagnostics.rs        |  12 +-
 src/tools/tidy/src/extra_checks/mod.rs   |  12 +-
 src/tools/tidy/src/lib.rs                |   1 +
 src/tools/tidy/src/main.rs               |  39 +++---
 9 files changed, 284 insertions(+), 46 deletions(-)
 create mode 100644 src/tools/tidy/src/arg_parser.rs
 create mode 100644 src/tools/tidy/src/arg_parser/tests.rs

diff --git a/Cargo.lock b/Cargo.lock
index 01300d56cff9..f279ad6cae9d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -580,9 +580,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.51"
+version = "4.5.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
+checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -600,9 +600,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.51"
+version = "4.5.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
+checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
 dependencies = [
  "anstream",
  "anstyle",
@@ -5622,6 +5622,7 @@ version = "0.1.0"
 dependencies = [
  "build_helper",
  "cargo_metadata 0.21.0",
+ "clap",
  "fluent-syntax",
  "globset",
  "ignore",
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index cbf27ea87a07..d5433080efc0 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -20,6 +20,7 @@ fluent-syntax = "0.12"
 similar = "2.5.0"
 toml = "0.7.8"
 tempfile = "3.15.0"
+clap = "4.5.54"
 
 [features]
 build-metrics = ["dep:serde"]
diff --git a/src/tools/tidy/src/alphabetical/tests.rs b/src/tools/tidy/src/alphabetical/tests.rs
index 4a1d263f1a4f..5fa0dd751b64 100644
--- a/src/tools/tidy/src/alphabetical/tests.rs
+++ b/src/tools/tidy/src/alphabetical/tests.rs
@@ -37,7 +37,7 @@ fn bless_test(before: &str, after: &str) {
     let temp_path = tempfile::Builder::new().tempfile().unwrap().into_temp_path();
     std::fs::write(&temp_path, before).unwrap();
 
-    let tidy_ctx = TidyCtx::new(Path::new("/"), false, TidyFlags::new(&["--bless".to_owned()]));
+    let tidy_ctx = TidyCtx::new(Path::new("/"), false, TidyFlags::new(true));
 
     let mut check = tidy_ctx.start_check("alphabetical-test");
     check_lines(&temp_path, before, &tidy_ctx, &mut check);
diff --git a/src/tools/tidy/src/arg_parser.rs b/src/tools/tidy/src/arg_parser.rs
new file mode 100644
index 000000000000..ca7ee04da8fb
--- /dev/null
+++ b/src/tools/tidy/src/arg_parser.rs
@@ -0,0 +1,92 @@
+use std::num::NonZeroUsize;
+use std::path::PathBuf;
+
+use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
+
+#[cfg(test)]
+mod tests;
+
+#[derive(Debug, Clone)]
+pub struct TidyArgParser {
+    pub root_path: PathBuf,
+    pub cargo: PathBuf,
+    pub output_directory: PathBuf,
+    pub concurrency: NonZeroUsize,
+    pub npm: PathBuf,
+    pub verbose: bool,
+    pub bless: bool,
+    pub extra_checks: Option>,
+    pub pos_args: Vec,
+}
+
+impl TidyArgParser {
+    fn command() -> Command {
+        Command::new("rust-tidy")
+            .arg(
+                Arg::new("root_path")
+                    .help("path of the root directory")
+                    .required(true)
+                    .value_parser(value_parser!(PathBuf)),
+            )
+            .arg(
+                Arg::new("cargo")
+                    .help("path of cargo")
+                    .required(true)
+                    .value_parser(value_parser!(PathBuf)),
+            )
+            .arg(
+                Arg::new("output_directory")
+                    .help("path of output directory")
+                    .required(true)
+                    .value_parser(value_parser!(PathBuf)),
+            )
+            .arg(Arg::new("concurrency").required(true).value_parser(value_parser!(NonZeroUsize)))
+            .arg(
+                Arg::new("npm")
+                    .help("path of npm")
+                    .required(true)
+                    .value_parser(value_parser!(PathBuf)),
+            )
+            .arg(Arg::new("verbose").help("verbose").long("verbose").action(ArgAction::SetTrue))
+            .arg(Arg::new("bless").help("bless").long("bless").action(ArgAction::SetTrue))
+            .arg(
+                Arg::new("extra_checks")
+                    .help("extra checks")
+                    .long("extra-checks")
+                    .value_delimiter(',')
+                    .action(ArgAction::Append),
+            )
+            .arg(Arg::new("pos_args").help("for extra checks. you can specify configs and target files for external check tools").action(ArgAction::Append).last(true))
+    }
+
+    fn build(matches: ArgMatches) -> Self {
+        let mut tidy_flags = Self {
+            root_path: matches.get_one::("root_path").unwrap().clone(),
+            cargo: matches.get_one::("cargo").unwrap().clone(),
+            output_directory: matches.get_one::("output_directory").unwrap().clone(),
+            concurrency: *matches.get_one::("concurrency").unwrap(),
+            npm: matches.get_one::("npm").unwrap().clone(),
+            verbose: *matches.get_one::("verbose").unwrap(),
+            bless: *matches.get_one::("bless").unwrap(),
+            extra_checks: None,
+            pos_args: vec![],
+        };
+
+        if let Some(extra_checks) = matches.get_many::("extra_checks") {
+            tidy_flags.extra_checks = Some(extra_checks.map(|s| s.to_string()).collect::>());
+        }
+
+        tidy_flags.pos_args = matches
+            .get_many::("pos_args")
+            .unwrap_or_default()
+            .map(|v| v.to_string())
+            .collect::>();
+
+        tidy_flags
+    }
+
+    pub fn parse() -> Self {
+        let matches = Self::command().get_matches();
+        Self::build(matches)
+    }
+}
diff --git a/src/tools/tidy/src/arg_parser/tests.rs b/src/tools/tidy/src/arg_parser/tests.rs
new file mode 100644
index 000000000000..856e80279e27
--- /dev/null
+++ b/src/tools/tidy/src/arg_parser/tests.rs
@@ -0,0 +1,162 @@
+use std::path::PathBuf;
+
+use crate::arg_parser::TidyArgParser;
+
+#[test]
+fn test_tidy_parser() {
+    let args = vec![
+        "rust-tidy",
+        "/home/user/rust", // Root dir
+        "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", // Cardo location
+        "/home/user/rust/build", // Build dir
+        "16",              // Number of concurrency
+        "/home/user/rust/build/misc-tools/bin/yarn", // Yarn location
+        "--verbose",
+        "--bless",
+        "--extra-checks",
+        "if-installed:auto:js,auto:if-installed:py,if-installed:auto:cpp,if-installed:auto:spellcheck",
+        "--", // pos_args
+        "some-file",
+        "some-file2",
+    ];
+    let cmd = TidyArgParser::command();
+    let parsed_args = TidyArgParser::build(cmd.get_matches_from(args));
+
+    assert_eq!(parsed_args.root_path, PathBuf::from("/home/user/rust"));
+    assert_eq!(
+        parsed_args.cargo,
+        PathBuf::from("/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo")
+    );
+    assert_eq!(parsed_args.output_directory, PathBuf::from("/home/user/rust/build"));
+    assert_eq!(parsed_args.concurrency.get(), 16);
+    assert_eq!(parsed_args.npm, PathBuf::from("/home/user/rust/build/misc-tools/bin/yarn"));
+    assert!(parsed_args.verbose);
+    assert!(parsed_args.bless);
+    assert_eq!(
+        parsed_args.extra_checks,
+        Some(vec![
+            "if-installed:auto:js".to_string(),
+            "auto:if-installed:py".to_string(),
+            "if-installed:auto:cpp".to_string(),
+            "if-installed:auto:spellcheck".to_string(),
+        ])
+    );
+    assert_eq!(parsed_args.pos_args, vec!["some-file".to_string(), "some-file2".to_string()]);
+}
+
+// The parser can take required args any order
+#[test]
+fn test_tidy_parser_any_order() {
+    let args = vec![
+        "rust-tidy",
+        "--npm-path",
+        "yarn",
+        "--concurrency",
+        "16",
+        "--output-dir",
+        "/home/user/rust/build",
+        "--cargo-path",
+        "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo",
+        "--root-path",
+        "/home/user/rust",
+    ];
+    let cmd = TidyArgParser::command();
+    let parsed_args = TidyArgParser::build(cmd.get_matches_from(args));
+
+    assert_eq!(parsed_args.root_path, PathBuf::from("/home/user/rust"));
+    assert_eq!(
+        parsed_args.cargo,
+        PathBuf::from("/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo")
+    );
+    assert_eq!(parsed_args.output_directory, PathBuf::from("/home/user/rust/build"));
+    assert_eq!(parsed_args.concurrency.get(), 16);
+    assert_eq!(parsed_args.npm, PathBuf::from("yarn"));
+}
+
+// --root-path is required
+#[test]
+fn test_tidy_parser_missing_root_path() {
+    let args = vec![
+        "rust-tidy",
+        "--npm-path",
+        "yarn",
+        "--concurrency",
+        "16",
+        "--output-dir",
+        "/home/user/rust/build",
+        "--cargo-path",
+        "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo",
+    ];
+    let cmd = TidyArgParser::command();
+    assert!(cmd.try_get_matches_from(args).is_err());
+}
+
+// --cargo-path is required
+#[test]
+fn test_tidy_parser_missing_cargo_path() {
+    let args = vec![
+        "rust-tidy",
+        "--npm-path",
+        "yarn",
+        "--concurrency",
+        "16",
+        "--output-dir",
+        "/home/user/rust/build",
+        "--root-path",
+        "/home/user/rust",
+    ];
+    let cmd = TidyArgParser::command();
+    assert!(cmd.try_get_matches_from(args).is_err());
+}
+
+// --output-dir is required
+#[test]
+fn test_tidy_parser_missing_output_dir() {
+    let args = vec![
+        "rust-tidy",
+        "--npm-path",
+        "yarn",
+        "--concurrency",
+        "16",
+        "--cargo-path",
+        "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo",
+        "--root-path",
+        "/home/user/rust",
+    ];
+    let cmd = TidyArgParser::command();
+    assert!(cmd.try_get_matches_from(args).is_err());
+}
+
+// --concurrency is required
+#[test]
+fn test_tidy_parser_missing_concurrency() {
+    let args = vec![
+        "rust-tidy",
+        "--npm-path",
+        "yarn",
+        "--output-dir",
+        "/home/user/rust/build",
+        "--cargo-path",
+        "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo",
+        "--root-path",
+        "/home/user/rust",
+    ];
+    let cmd = TidyArgParser::command();
+    assert!(cmd.try_get_matches_from(args).is_err());
+}
+
+// --npm-path is required
+#[test]
+fn test_tidy_parser_missing_npm_path() {
+    let args = vec![
+        "rust-tidy",
+        "--concurrency",
+        "16",
+        "--output-dir",
+        "/home/user/rust/build",
+        "--cargo-path",
+        "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo",
+    ];
+    let cmd = TidyArgParser::command();
+    assert!(cmd.try_get_matches_from(args).is_err());
+}
diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs
index 6f53f2fff1a4..4e6c316f5e18 100644
--- a/src/tools/tidy/src/diagnostics.rs
+++ b/src/tools/tidy/src/diagnostics.rs
@@ -14,16 +14,8 @@ pub struct TidyFlags {
 }
 
 impl TidyFlags {
-    pub fn new(cfg_args: &[String]) -> Self {
-        let mut flags = Self::default();
-
-        for arg in cfg_args {
-            match arg.as_str() {
-                "--bless" => flags.bless = true,
-                _ => continue,
-            }
-        }
-        flags
+    pub fn new(bless: bool) -> Self {
+        Self { bless }
     }
 }
 
diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs
index 1c81a485608a..6272e00591d7 100644
--- a/src/tools/tidy/src/extra_checks/mod.rs
+++ b/src/tools/tidy/src/extra_checks/mod.rs
@@ -58,8 +58,8 @@ pub fn check(
     tools_path: &Path,
     npm: &Path,
     cargo: &Path,
-    extra_checks: Option<&str>,
-    pos_args: &[String],
+    extra_checks: Option>,
+    pos_args: Vec,
     tidy_ctx: TidyCtx,
 ) {
     let mut check = tidy_ctx.start_check("extra_checks");
@@ -88,8 +88,8 @@ fn check_impl(
     tools_path: &Path,
     npm: &Path,
     cargo: &Path,
-    extra_checks: Option<&str>,
-    pos_args: &[String],
+    extra_checks: Option>,
+    pos_args: Vec,
     tidy_ctx: &TidyCtx,
 ) -> Result<(), Error> {
     let show_diff =
@@ -99,9 +99,7 @@ fn check_impl(
     // Split comma-separated args up
     let mut lint_args = match extra_checks {
         Some(s) => s
-            .strip_prefix("--extra-checks=")
-            .unwrap()
-            .split(',')
+            .iter()
             .map(|s| {
                 if s == "spellcheck:fix" {
                     eprintln!("warning: `spellcheck:fix` is no longer valid, use `--extra-checks=spellcheck --bless`");
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index 425f43e42b7f..19a9fa80d9f0 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -157,6 +157,7 @@ pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool {
 }
 
 pub mod alphabetical;
+pub mod arg_parser;
 pub mod bins;
 pub mod debug_artifacts;
 pub mod deps;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 94c24f11ed12..457fbe93e6d2 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -5,12 +5,10 @@
 //! builders. The tidy checks can be executed with `./x.py test tidy`.
 
 use std::collections::VecDeque;
-use std::num::NonZeroUsize;
-use std::path::PathBuf;
-use std::str::FromStr;
 use std::thread::{self, ScopedJoinHandle, scope};
 use std::{env, process};
 
+use tidy::arg_parser::TidyArgParser;
 use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, TidyCtx, TidyFlags, output_message};
 use tidy::*;
 
@@ -22,14 +20,13 @@ fn main() {
         env::set_var("RUSTC_BOOTSTRAP", "1");
     }
 
-    let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into();
-    let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into();
-    let output_directory: PathBuf =
-        env::args_os().nth(3).expect("need path to output directory").into();
-    let concurrency: NonZeroUsize =
-        FromStr::from_str(&env::args().nth(4).expect("need concurrency"))
-            .expect("concurrency must be a number");
-    let npm: PathBuf = env::args_os().nth(5).expect("need name/path of npm command").into();
+    let parsed_args = TidyArgParser::parse();
+
+    let root_path = parsed_args.root_path;
+    let cargo = parsed_args.cargo;
+    let output_directory = parsed_args.output_directory;
+    let concurrency = parsed_args.concurrency.get();
+    let npm = parsed_args.npm;
 
     let root_manifest = root_path.join("Cargo.toml");
     let src_path = root_path.join("src");
@@ -40,17 +37,12 @@ fn main() {
     let tools_path = src_path.join("tools");
     let crashes_path = tests_path.join("crashes");
 
-    let args: Vec = env::args().skip(1).collect();
-    let (cfg_args, pos_args) = match args.iter().position(|arg| arg == "--") {
-        Some(pos) => (&args[..pos], &args[pos + 1..]),
-        None => (&args[..], [].as_slice()),
-    };
-    let verbose = cfg_args.iter().any(|s| *s == "--verbose");
-    let extra_checks =
-        cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str);
+    let verbose = parsed_args.verbose;
+    let bless = parsed_args.bless;
+    let extra_checks = parsed_args.extra_checks;
+    let pos_args = parsed_args.pos_args;
 
-    let tidy_flags = TidyFlags::new(cfg_args);
-    let tidy_ctx = TidyCtx::new(&root_path, verbose, tidy_flags);
+    let tidy_ctx = TidyCtx::new(&root_path, verbose, TidyFlags::new(bless));
     let ci_info = CiInfo::new(tidy_ctx.clone());
 
     let drain_handles = |handles: &mut VecDeque>| {
@@ -61,14 +53,13 @@ fn main() {
             }
         }
 
-        while handles.len() >= concurrency.get() {
+        while handles.len() >= concurrency {
             handles.pop_front().unwrap().join().unwrap();
         }
     };
 
     scope(|s| {
-        let mut handles: VecDeque> =
-            VecDeque::with_capacity(concurrency.get());
+        let mut handles: VecDeque> = VecDeque::with_capacity(concurrency);
 
         macro_rules! check {
             ($p:ident) => {

From 14858732330380e488ffb406d4a537bf133d90f9 Mon Sep 17 00:00:00 2001
From: Shunpoco 
Date: Sun, 25 Jan 2026 10:13:48 +0000
Subject: [PATCH 344/583] set long variants for required args

Currently some required arguments (like path of the root dir) are ordered, but it causes an user (mainly bootstrap) needs to remember the order. This commit introduces long arguments (e.g., --root-path) for required args.
---
 src/bootstrap/src/core/build_steps/test.rs | 12 ++++++------
 src/tools/tidy/src/arg_parser.rs           | 14 ++++++++++++--
 src/tools/tidy/src/arg_parser/tests.rs     | 20 +++++++++++++-------
 3 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 52b38421eec2..ca70a7758d58 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1293,19 +1293,19 @@ impl Step for Tidy {
     /// for the `dev` or `nightly` channels.
     fn run(self, builder: &Builder<'_>) {
         let mut cmd = builder.tool_cmd(Tool::Tidy);
-        cmd.arg(&builder.src);
-        cmd.arg(&builder.initial_cargo);
-        cmd.arg(&builder.out);
+        cmd.arg(format!("--root-path={}", &builder.src.display()));
+        cmd.arg(format!("--cargo-path={}", &builder.initial_cargo.display()));
+        cmd.arg(format!("--output-dir={}", &builder.out.display()));
         // Tidy is heavily IO constrained. Still respect `-j`, but use a higher limit if `jobs` hasn't been configured.
         let jobs = builder.config.jobs.unwrap_or_else(|| {
             8 * std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32
         });
-        cmd.arg(jobs.to_string());
+        cmd.arg(format!("--concurrency={jobs}"));
         // pass the path to the yarn command used for installing js deps.
         if let Some(yarn) = &builder.config.yarn {
-            cmd.arg(yarn);
+            cmd.arg(format!("--npm-path={}", yarn.display()));
         } else {
-            cmd.arg("yarn");
+            cmd.arg("--npm-path=yarn");
         }
         if builder.is_verbose() {
             cmd.arg("--verbose");
diff --git a/src/tools/tidy/src/arg_parser.rs b/src/tools/tidy/src/arg_parser.rs
index ca7ee04da8fb..8041f739308d 100644
--- a/src/tools/tidy/src/arg_parser.rs
+++ b/src/tools/tidy/src/arg_parser.rs
@@ -25,30 +25,40 @@ impl TidyArgParser {
             .arg(
                 Arg::new("root_path")
                     .help("path of the root directory")
+                    .long("root-path")
                     .required(true)
                     .value_parser(value_parser!(PathBuf)),
             )
             .arg(
                 Arg::new("cargo")
                     .help("path of cargo")
+                    .long("cargo-path")
                     .required(true)
                     .value_parser(value_parser!(PathBuf)),
             )
             .arg(
                 Arg::new("output_directory")
                     .help("path of output directory")
+                    .long("output-dir")
                     .required(true)
                     .value_parser(value_parser!(PathBuf)),
             )
-            .arg(Arg::new("concurrency").required(true).value_parser(value_parser!(NonZeroUsize)))
+            .arg(
+                Arg::new("concurrency")
+                    .help("number of threads working concurrently")
+                    .long("concurrency")
+                    .required(true)
+                    .value_parser(value_parser!(NonZeroUsize)),
+            )
             .arg(
                 Arg::new("npm")
                     .help("path of npm")
+                    .long("npm-path")
                     .required(true)
                     .value_parser(value_parser!(PathBuf)),
             )
             .arg(Arg::new("verbose").help("verbose").long("verbose").action(ArgAction::SetTrue))
-            .arg(Arg::new("bless").help("bless").long("bless").action(ArgAction::SetTrue))
+            .arg(Arg::new("bless").help("target files are modified").long("bless").action(ArgAction::SetTrue))
             .arg(
                 Arg::new("extra_checks")
                     .help("extra checks")
diff --git a/src/tools/tidy/src/arg_parser/tests.rs b/src/tools/tidy/src/arg_parser/tests.rs
index 856e80279e27..c5e7aed21c1a 100644
--- a/src/tools/tidy/src/arg_parser/tests.rs
+++ b/src/tools/tidy/src/arg_parser/tests.rs
@@ -2,15 +2,21 @@ use std::path::PathBuf;
 
 use crate::arg_parser::TidyArgParser;
 
+// Test all arguments
 #[test]
-fn test_tidy_parser() {
+fn test_tidy_parser_full() {
     let args = vec![
         "rust-tidy",
-        "/home/user/rust", // Root dir
-        "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo", // Cardo location
-        "/home/user/rust/build", // Build dir
-        "16",              // Number of concurrency
-        "/home/user/rust/build/misc-tools/bin/yarn", // Yarn location
+        "--root-path",
+        "/home/user/rust",
+        "--cargo-path",
+        "/home/user/rust/build/x86_64-unknown-linux-gnu/stage0/bin/cargo",
+        "--output-dir",
+        "/home/user/rust/build",
+        "--concurrency",
+        "16",
+        "--npm-path",
+        "yarn",
         "--verbose",
         "--bless",
         "--extra-checks",
@@ -29,7 +35,7 @@ fn test_tidy_parser() {
     );
     assert_eq!(parsed_args.output_directory, PathBuf::from("/home/user/rust/build"));
     assert_eq!(parsed_args.concurrency.get(), 16);
-    assert_eq!(parsed_args.npm, PathBuf::from("/home/user/rust/build/misc-tools/bin/yarn"));
+    assert_eq!(parsed_args.npm, PathBuf::from("yarn"));
     assert!(parsed_args.verbose);
     assert!(parsed_args.bless);
     assert_eq!(

From 4b22ee9fc5a208658d1fbfd2492ea3105f822e5d Mon Sep 17 00:00:00 2001
From: Yuki Okushi 
Date: Thu, 22 Jan 2026 21:09:44 +0900
Subject: [PATCH 345/583] 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 5673d044ad2c..b318f5bb25d2 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 5943111efd58..6a867350a3b3 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 000000000000..4fb4cd61ac51
--- /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 000000000000..42969de715eb
--- /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`.

From 4803644df98f09d0da1022e85e4820403c785fe1 Mon Sep 17 00:00:00 2001
From: yukang 
Date: Thu, 29 Jan 2026 17:20:32 +0800
Subject: [PATCH 346/583] Fix false positive in unused_parens caused by break

---
 compiler/rustc_lint/src/unused.rs             |  5 +++-
 ...nused-parens-labeled-break-issue-143256.rs | 25 +++++++++++++++++++
 2 files changed, 29 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/lint/unused-parens-labeled-break-issue-143256.rs

diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 506a16355e22..27ddd4fb5f28 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -794,7 +794,10 @@ trait UnusedDelimLint {
 
                 ExprKind::Break(_label, None) => return false,
                 ExprKind::Break(_label, Some(break_expr)) => {
-                    return matches!(break_expr.kind, ExprKind::Block(..));
+                    // `if (break 'label i) { ... }` removing parens would make `i { ... }`
+                    // be parsed as a struct literal, so keep parentheses if the break value
+                    // ends with a path (which could be mistaken for a struct name).
+                    return matches!(break_expr.kind, ExprKind::Block(..) | ExprKind::Path(..));
                 }
 
                 ExprKind::Range(_lhs, Some(rhs), _limits) => {
diff --git a/tests/ui/lint/unused-parens-labeled-break-issue-143256.rs b/tests/ui/lint/unused-parens-labeled-break-issue-143256.rs
new file mode 100644
index 000000000000..8594e646f605
--- /dev/null
+++ b/tests/ui/lint/unused-parens-labeled-break-issue-143256.rs
@@ -0,0 +1,25 @@
+//@ check-pass
+// testcase for https://github.com/rust-lang/rust/issues/143256
+
+#![deny(unused_parens)]
+#![allow(unreachable_code, unused_variables, dead_code)]
+
+fn foo() {
+    let _x = || 'outer: loop {
+        let inner = 'inner: loop {
+            let i = Default::default();
+            // the parentheses here are necessary
+            if (break 'outer i) {
+                loop {
+                    break 'inner 5i8;
+                }
+            } else if true {
+                break 'inner 6;
+            }
+            break 7;
+        };
+        break inner < 8;
+    };
+}
+
+fn main() {}

From 25c13655072475476f6ff3f26dc5cfda39db44d8 Mon Sep 17 00:00:00 2001
From: James Barford-Evans 
Date: Mon, 22 Dec 2025 14:45:08 +0000
Subject: [PATCH 347/583] Part 2 refactoring of moving placeholder types to
 `rustc_type_ir`

---
 .../src/diagnostics/bound_region_errors.rs    |   4 +-
 .../src/region_infer/graphviz.rs              |   2 +-
 .../rustc_borrowck/src/region_infer/values.rs |  10 +-
 compiler/rustc_borrowck/src/type_check/mod.rs |   2 +-
 .../src/type_check/relate_tys.rs              |  14 +-
 .../src/check/compare_impl_item.rs            |   6 +-
 .../src/collect/item_bounds.rs                |   5 +-
 .../src/collect/resolve_bound_vars.rs         |  48 +--
 .../src/hir_ty_lowering/bounds.rs             |   6 +-
 .../src/hir_ty_lowering/mod.rs                |   6 +-
 .../src/infer/canonical/canonicalizer.rs      |   6 +-
 compiler/rustc_infer/src/infer/mod.rs         |   8 +-
 .../src/infer/outlives/test_type_match.rs     |   4 +-
 .../src/infer/relate/higher_ranked.rs         |   6 +-
 .../rustc_lint/src/impl_trait_overcaptures.rs |   2 +-
 .../src/middle/resolve_bound_vars.rs          |   4 +-
 compiler/rustc_middle/src/query/erase.rs      |   2 +-
 compiler/rustc_middle/src/query/mod.rs        |   4 +-
 compiler/rustc_middle/src/ty/codec.rs         |   6 +-
 compiler/rustc_middle/src/ty/consts.rs        |   8 +-
 compiler/rustc_middle/src/ty/context.rs       |  26 +-
 compiler/rustc_middle/src/ty/fold.rs          |  49 ++-
 compiler/rustc_middle/src/ty/mod.rs           | 104 +-----
 compiler/rustc_middle/src/ty/print/pretty.rs  |  14 +-
 compiler/rustc_middle/src/ty/region.rs        |  95 +----
 .../rustc_middle/src/ty/structural_impls.rs   |  28 +-
 compiler/rustc_middle/src/ty/sty.rs           |  84 ++---
 compiler/rustc_middle/src/ty/visit.rs         |  14 +-
 .../src/canonical/canonicalizer.rs            |  25 +-
 .../src/canonical/mod.rs                      |   4 +-
 .../src/placeholder.rs                        |  22 +-
 .../src/unstable/convert/internal.rs          |   2 +-
 .../src/unstable/convert/stable/ty.rs         |  10 +-
 .../src/unstable/internal_cx/mod.rs           |   5 +-
 compiler/rustc_public/src/unstable/mod.rs     |   5 +-
 .../nice_region_error/placeholder_relation.rs |   8 +-
 .../src/error_reporting/infer/region.rs       |   2 +-
 .../src/traits/coherence.rs                   |   8 +-
 .../rustc_trait_selection/src/traits/mod.rs   |   2 +-
 .../rustc_trait_selection/src/traits/util.rs  |  12 +-
 compiler/rustc_ty_utils/src/ty.rs             |   2 +-
 compiler/rustc_type_ir/src/binder.rs          | 352 +++++++++++++++++-
 compiler/rustc_type_ir/src/canonical.rs       |   6 +-
 compiler/rustc_type_ir/src/const_kind.rs      |   4 +-
 compiler/rustc_type_ir/src/error.rs           |   2 +-
 compiler/rustc_type_ir/src/inherent.rs        |  74 +---
 compiler/rustc_type_ir/src/interner.rs        |  18 +-
 compiler/rustc_type_ir/src/outlives.rs        |   2 +-
 compiler/rustc_type_ir/src/region_kind.rs     |  10 +-
 compiler/rustc_type_ir/src/ty_kind.rs         |   4 +-
 src/librustdoc/clean/mod.rs                   |   2 +-
 src/tools/clippy/clippy_utils/src/ty/mod.rs   |   8 +-
 52 files changed, 610 insertions(+), 546 deletions(-)

diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index 254d28d243ff..6ed07cf9b1c8 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -169,7 +169,7 @@ pub(crate) trait TypeOpInfo<'tcx> {
 
         let placeholder_region = ty::Region::new_placeholder(
             tcx,
-            ty::Placeholder::new(adjusted_universe.into(), placeholder.bound),
+            ty::PlaceholderRegion::new(adjusted_universe.into(), placeholder.bound),
         );
 
         let error_region =
@@ -179,7 +179,7 @@ pub(crate) trait TypeOpInfo<'tcx> {
                 adjusted_universe.map(|adjusted| {
                     ty::Region::new_placeholder(
                         tcx,
-                        ty::Placeholder::new(adjusted.into(), error_placeholder.bound),
+                        ty::PlaceholderRegion::new(adjusted.into(), error_placeholder.bound),
                     )
                 })
             } else {
diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs
index 526e1850c2ef..ceb33d82deba 100644
--- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs
+++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs
@@ -52,7 +52,7 @@ fn render_region_vid<'tcx>(
                 format!(" (for<{}>)", tcx.item_name(def_id))
             }
             ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => " (for<'_>)".to_string(),
-            ty::BoundRegionKind::NamedAnon(_) => {
+            ty::BoundRegionKind::NamedForPrinting(_) => {
                 bug!("only used for pretty printing")
             }
         },
diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs
index 0063af25d781..1dd3bc831f45 100644
--- a/compiler/rustc_borrowck/src/region_infer/values.rs
+++ b/compiler/rustc_borrowck/src/region_infer/values.rs
@@ -10,8 +10,8 @@ use rustc_middle::ty::{self, RegionVid};
 use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
 use tracing::debug;
 
+use crate::BorrowIndex;
 use crate::polonius::LiveLoans;
-use crate::{BorrowIndex, TyCtxt};
 
 rustc_index::newtype_index! {
     /// A single integer representing a `ty::Placeholder`.
@@ -420,18 +420,18 @@ impl ToElementIndex<'_> for RegionVid {
 impl<'tcx> ToElementIndex<'tcx> for ty::PlaceholderRegion<'tcx> {
     fn add_to_row(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool
     where
-        Self: Into, ty::BoundRegion>>,
+        Self: Into>,
     {
-        let placeholder: ty::Placeholder, ty::BoundRegion> = self.into();
+        let placeholder: ty::PlaceholderRegion<'tcx> = self.into();
         let index = values.placeholder_indices.lookup_index(placeholder);
         values.placeholders.insert(row, index)
     }
 
     fn contained_in_row(self, values: &RegionValues<'tcx, N>, row: N) -> bool
     where
-        Self: Into, ty::BoundRegion>>,
+        Self: Into>,
     {
-        let placeholder: ty::Placeholder, ty::BoundRegion> = self.into();
+        let placeholder: ty::PlaceholderRegion<'tcx> = self.into();
         let index = values.placeholder_indices.lookup_index(placeholder);
         values.placeholders.contains(row, index)
     }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index d2464c7e99ee..e9dfea6043a3 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -772,7 +772,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                             ty::BoundRegionKind::Anon => sym::anon,
                             ty::BoundRegionKind::Named(def_id) => tcx.item_name(def_id),
                             ty::BoundRegionKind::ClosureEnv => sym::env,
-                            ty::BoundRegionKind::NamedAnon(_) => {
+                            ty::BoundRegionKind::NamedForPrinting(_) => {
                                 bug!("only used for pretty printing")
                             }
                         };
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index 045507ceb4b4..e2d684e12a81 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -174,7 +174,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
             let infcx = self.type_checker.infcx;
             let mut lazy_universe = None;
             let delegate = FnMutDelegate {
-                regions: &mut |br: ty::BoundRegion| {
+                regions: &mut |br: ty::BoundRegion<'tcx>| {
                     // The first time this closure is called, create a
                     // new universe for the placeholders we will make
                     // from here out.
@@ -191,10 +191,10 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
 
                     placeholder_reg
                 },
-                types: &mut |_bound_ty: ty::BoundTy| {
+                types: &mut |_bound_ty: ty::BoundTy<'tcx>| {
                     unreachable!("we only replace regions in nll_relate, not types")
                 },
-                consts: &mut |_bound_const: ty::BoundConst| {
+                consts: &mut |_bound_const: ty::BoundConst<'tcx>| {
                     unreachable!("we only replace regions in nll_relate, not consts")
                 },
             };
@@ -218,7 +218,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
         let infcx = self.type_checker.infcx;
         let mut reg_map = FxHashMap::default();
         let delegate = FnMutDelegate {
-            regions: &mut |br: ty::BoundRegion| {
+            regions: &mut |br: ty::BoundRegion<'tcx>| {
                 if let Some(ex_reg_var) = reg_map.get(&br) {
                     *ex_reg_var
                 } else {
@@ -230,10 +230,10 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
                     ex_reg_var
                 }
             },
-            types: &mut |_bound_ty: ty::BoundTy| {
+            types: &mut |_bound_ty: ty::BoundTy<'tcx>| {
                 unreachable!("we only replace regions in nll_relate, not types")
             },
-            consts: &mut |_bound_const: ty::BoundConst| {
+            consts: &mut |_bound_const: ty::BoundConst<'tcx>| {
                 unreachable!("we only replace regions in nll_relate, not consts")
             },
         };
@@ -268,7 +268,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
             ty::BoundRegionKind::Anon => sym::anon,
             ty::BoundRegionKind::Named(def_id) => self.type_checker.tcx().item_name(def_id),
             ty::BoundRegionKind::ClosureEnv => sym::env,
-            ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
+            ty::BoundRegionKind::NamedForPrinting(_) => bug!("only used for pretty printing"),
         };
 
         if cfg!(debug_assertions) {
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 4534cfcf962e..dd7b03c9dac3 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -592,7 +592,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
                 ty,
                 Ty::new_placeholder(
                     tcx,
-                    ty::Placeholder::new(
+                    ty::PlaceholderType::new(
                         universe,
                         ty::BoundTy { var: idx, kind: ty::BoundTyKind::Anon },
                     ),
@@ -2551,7 +2551,7 @@ fn param_env_with_gat_bounds<'tcx>(
             }
         };
 
-        let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
+        let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind<'tcx>; 8]> =
             smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).own_params.len());
         // Extend the impl's identity args with late-bound GAT vars
         let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id)
@@ -2587,7 +2587,7 @@ fn param_env_with_gat_bounds<'tcx>(
                     ty::Const::new_bound(
                         tcx,
                         ty::INNERMOST,
-                        ty::BoundConst { var: ty::BoundVar::from_usize(bound_vars.len() - 1) },
+                        ty::BoundConst::new(ty::BoundVar::from_usize(bound_vars.len() - 1)),
                     )
                     .into()
                 }
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 7025f7ac84b0..a01ee2d31a3d 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -241,7 +241,7 @@ struct MapAndCompressBoundVars<'tcx> {
     binder: ty::DebruijnIndex,
     /// List of bound vars that remain unsubstituted because they were not
     /// mentioned in the GAT's args.
-    still_bound_vars: Vec,
+    still_bound_vars: Vec>,
     /// Subtle invariant: If the `GenericArg` is bound, then it should be
     /// stored with the debruijn index of `INNERMOST` so it can be shifted
     /// correctly during substitution.
@@ -330,7 +330,8 @@ impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> {
             } else {
                 let var = ty::BoundVar::from_usize(self.still_bound_vars.len());
                 self.still_bound_vars.push(ty::BoundVariableKind::Const);
-                let mapped = ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst { var });
+                let mapped =
+                    ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var));
                 self.mapping.insert(old_bound.var, mapped.into());
                 mapped
             };
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 02443b577d38..26f79d374075 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -63,9 +63,9 @@ impl ResolvedArg {
 
 struct BoundVarContext<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    rbv: &'a mut ResolveBoundVars,
+    rbv: &'a mut ResolveBoundVars<'tcx>,
     disambiguator: &'a mut DisambiguatorState,
-    scope: ScopeRef<'a>,
+    scope: ScopeRef<'a, 'tcx>,
     opaque_capture_errors: RefCell>,
 }
 
@@ -76,7 +76,7 @@ struct OpaqueHigherRankedLifetimeCaptureErrors {
 }
 
 #[derive(Debug)]
-enum Scope<'a> {
+enum Scope<'a, 'tcx> {
     /// Declares lifetimes, and each can be early-bound or late-bound.
     /// The `DebruijnIndex` of late-bound lifetimes starts at `1` and
     /// it should be shifted by the number of `Binder`s in between the
@@ -94,7 +94,7 @@ enum Scope<'a> {
         /// to append to.
         hir_id: HirId,
 
-        s: ScopeRef<'a>,
+        s: ScopeRef<'a, 'tcx>,
 
         /// If this binder comes from a where clause, specify how it was created.
         /// This is used to diagnose inaccessible lifetimes in APIT:
@@ -110,7 +110,7 @@ enum Scope<'a> {
     /// e.g., `(&T, fn(&T) -> &T);` becomes `(&'_ T, for<'a> fn(&'a T) -> &'a T)`.
     Body {
         id: hir::BodyId,
-        s: ScopeRef<'a>,
+        s: ScopeRef<'a, 'tcx>,
     },
 
     /// Use a specific lifetime (if `Some`) or leave it unset (to be
@@ -118,7 +118,7 @@ enum Scope<'a> {
     /// for the default choice of lifetime in a trait object type.
     ObjectLifetimeDefault {
         lifetime: Option,
-        s: ScopeRef<'a>,
+        s: ScopeRef<'a, 'tcx>,
     },
 
     /// When we have nested trait refs, we concatenate late bound vars for inner
@@ -126,12 +126,12 @@ enum Scope<'a> {
     /// lifetimes encountered when identifying the trait that an associated type
     /// is declared on.
     Supertrait {
-        bound_vars: Vec,
-        s: ScopeRef<'a>,
+        bound_vars: Vec>,
+        s: ScopeRef<'a, 'tcx>,
     },
 
     TraitRefBoundary {
-        s: ScopeRef<'a>,
+        s: ScopeRef<'a, 'tcx>,
     },
 
     /// Remap lifetimes that appear in opaque types to fresh lifetime parameters. Given:
@@ -148,7 +148,7 @@ enum Scope<'a> {
         /// Mapping from each captured lifetime `'a` to the duplicate generic parameter `'b`.
         captures: &'a RefCell>,
 
-        s: ScopeRef<'a>,
+        s: ScopeRef<'a, 'tcx>,
     },
 
     /// Disallows capturing late-bound vars from parent scopes.
@@ -157,7 +157,7 @@ enum Scope<'a> {
     /// since we don't do something more correct like replacing any captured
     /// late-bound vars with early-bound params in the const's own generics.
     LateBoundary {
-        s: ScopeRef<'a>,
+        s: ScopeRef<'a, 'tcx>,
         what: &'static str,
         deny_late_regions: bool,
     },
@@ -167,7 +167,7 @@ enum Scope<'a> {
     },
 }
 
-impl<'a> Scope<'a> {
+impl<'a, 'tcx> Scope<'a, 'tcx> {
     // A helper for debugging scopes without printing parent scopes
     fn debug_truncated(&self) -> impl fmt::Debug {
         fmt::from_fn(move |f| match self {
@@ -227,7 +227,7 @@ enum BinderScopeType {
     Concatenating,
 }
 
-type ScopeRef<'a> = &'a Scope<'a>;
+type ScopeRef<'a, 'tcx> = &'a Scope<'a, 'tcx>;
 
 /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]
 pub(crate) fn provide(providers: &mut Providers) {
@@ -253,7 +253,7 @@ pub(crate) fn provide(providers: &mut Providers) {
 /// You should not read the result of this query directly, but rather use
 /// `named_variable_map`, `late_bound_vars_map`, etc.
 #[instrument(level = "debug", skip(tcx))]
-fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars {
+fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars<'_> {
     let mut rbv = ResolveBoundVars::default();
     let mut visitor = BoundVarContext {
         tcx,
@@ -287,7 +287,7 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou
     rbv
 }
 
-fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableKind {
+fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableKind<'tcx> {
     let def_id = param.def_id.to_def_id();
     match param.kind {
         GenericParamKind::Lifetime { .. } => {
@@ -301,7 +301,9 @@ fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableK
 /// Turn a [`ty::GenericParamDef`] into a bound arg. Generally, this should only
 /// be used when turning early-bound vars into late-bound vars when lowering
 /// return type notation.
-fn generic_param_def_as_bound_arg(param: &ty::GenericParamDef) -> ty::BoundVariableKind {
+fn generic_param_def_as_bound_arg<'tcx>(
+    param: &ty::GenericParamDef,
+) -> ty::BoundVariableKind<'tcx> {
     match param.kind {
         ty::GenericParamDefKind::Lifetime => {
             ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(param.def_id))
@@ -329,7 +331,9 @@ fn opaque_captures_all_in_scope_lifetimes<'tcx>(opaque: &'tcx hir::OpaqueTy<'tcx
 
 impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
     /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref.
-    fn poly_trait_ref_binder_info(&mut self) -> (Vec, BinderScopeType) {
+    fn poly_trait_ref_binder_info(
+        &mut self,
+    ) -> (Vec>, BinderScopeType) {
         let mut scope = self.scope;
         let mut supertrait_bound_vars = vec![];
         loop {
@@ -364,7 +368,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
 
                 Scope::Binder { hir_id, .. } => {
                     // Nested poly trait refs have the binders concatenated
-                    let mut full_binders =
+                    let mut full_binders: Vec> =
                         self.rbv.late_bound_vars.get_mut_or_insert_default(hir_id.local_id).clone();
                     full_binders.extend(supertrait_bound_vars);
                     break (full_binders, BinderScopeType::Concatenating);
@@ -1094,7 +1098,7 @@ fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectL
 }
 
 impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
-    fn with(&mut self, wrap_scope: Scope<'_>, f: F)
+    fn with(&mut self, wrap_scope: Scope<'_, 'tcx>, f: F)
     where
         F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>),
     {
@@ -1115,7 +1119,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         *self.opaque_capture_errors.borrow_mut() = this.opaque_capture_errors.into_inner();
     }
 
-    fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec) {
+    fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec>) {
         if let Some(old) = self.rbv.late_bound_vars.insert(hir_id.local_id, binder) {
             bug!(
                 "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}",
@@ -1931,7 +1935,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         def_id: DefId,
         assoc_ident: Ident,
         assoc_tag: ty::AssocTag,
-    ) -> Option<(Vec, &'tcx ty::AssocItem)> {
+    ) -> Option<(Vec>, &'tcx ty::AssocItem)> {
         let trait_defines_associated_item_named = |trait_def_id: DefId| {
             tcx.associated_items(trait_def_id).find_by_ident_and_kind(
                 tcx,
@@ -1942,7 +1946,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         };
 
         use smallvec::{SmallVec, smallvec};
-        let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind; 8]>); 8]> =
+        let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind<'tcx>; 8]>); 8]> =
             smallvec![(def_id, smallvec![])];
         let mut visited: FxHashSet = FxHashSet::default();
         loop {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index d6441702b268..3515ce4ea939 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -362,7 +362,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         param_ty: Ty<'tcx>,
         hir_bounds: I,
         bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
-        bound_vars: &'tcx ty::List,
+        bound_vars: &'tcx ty::List>,
         predicate_filter: PredicateFilter,
         overlapping_assoc_constraints: OverlappingAsssocItemConstraints,
     ) where
@@ -1000,7 +1000,9 @@ impl<'tcx> TypeVisitor> for GenericParamAndBoundVarCollector<'_, 't
                             .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind));
                         return ControlFlow::Break(guar);
                     }
-                    ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
+                    ty::BoundRegionKind::NamedForPrinting(_) => {
+                        bug!("only used for pretty printing")
+                    }
                 });
             }
             _ => {}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 867c588e302d..9f84f652698b 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -2310,7 +2310,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => ty::Const::new_bound(
                 tcx,
                 debruijn,
-                ty::BoundConst { var: ty::BoundVar::from_u32(index) },
+                ty::BoundConst::new(ty::BoundVar::from_u32(index)),
             ),
             Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar),
             arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", path_hir_id),
@@ -3196,8 +3196,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     #[instrument(level = "trace", skip(self, generate_err))]
     fn validate_late_bound_regions<'cx>(
         &'cx self,
-        constrained_regions: FxIndexSet,
-        referenced_regions: FxIndexSet,
+        constrained_regions: FxIndexSet>,
+        referenced_regions: FxIndexSet>,
         generate_err: impl Fn(&str) -> Diag<'cx>,
     ) {
         for br in referenced_regions.difference(&constrained_regions) {
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index 23f6fee406a5..89ea6324d854 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -684,19 +684,19 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
                 CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]),
                 CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
                 CanonicalVarKind::PlaceholderTy(placeholder) => {
-                    CanonicalVarKind::PlaceholderTy(ty::Placeholder::new(
+                    CanonicalVarKind::PlaceholderTy(ty::PlaceholderType::new(
                         reverse_universe_map[&placeholder.universe],
                         placeholder.bound,
                     ))
                 }
                 CanonicalVarKind::PlaceholderRegion(placeholder) => {
-                    CanonicalVarKind::PlaceholderRegion(ty::Placeholder::new(
+                    CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new(
                         reverse_universe_map[&placeholder.universe],
                         placeholder.bound,
                     ))
                 }
                 CanonicalVarKind::PlaceholderConst(placeholder) => {
-                    CanonicalVarKind::PlaceholderConst(ty::Placeholder::new(
+                    CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst::new(
                         reverse_universe_map[&placeholder.universe],
                         placeholder.bound,
                     ))
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index c9ea420944e2..e15b25500bb5 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -447,7 +447,7 @@ pub enum RegionVariableOrigin<'tcx> {
 
     /// Region variables created when instantiating a binder with
     /// existential variables, e.g. when calling a function or method.
-    BoundRegion(Span, ty::BoundRegionKind, BoundRegionConversionTime),
+    BoundRegion(Span, ty::BoundRegionKind<'tcx>, BoundRegionConversionTime),
 
     UpvarRegion(ty::UpvarId, Span),
 
@@ -1300,13 +1300,13 @@ impl<'tcx> InferCtxt<'tcx> {
         }
 
         impl<'tcx> BoundVarReplacerDelegate<'tcx> for ToFreshVars<'tcx> {
-            fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
+            fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
                 self.args[br.var.index()].expect_region()
             }
-            fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
+            fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
                 self.args[bt.var.index()].expect_ty()
             }
-            fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> {
+            fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
                 self.args[bc.var.index()].expect_const()
             }
         }
diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
index 7be5daf61056..479daf67a8ba 100644
--- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
+++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs
@@ -91,7 +91,7 @@ pub(super) fn can_match_erased_ty<'tcx>(
 struct MatchAgainstHigherRankedOutlives<'tcx> {
     tcx: TyCtxt<'tcx>,
     pattern_depth: ty::DebruijnIndex,
-    map: FxHashMap>,
+    map: FxHashMap, ty::Region<'tcx>>,
 }
 
 impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> {
@@ -115,7 +115,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> {
     #[instrument(level = "trace", skip(self))]
     fn bind(
         &mut self,
-        br: ty::BoundRegion,
+        br: ty::BoundRegion<'tcx>,
         value: ty::Region<'tcx>,
     ) -> RelateResult<'tcx, ty::Region<'tcx>> {
         match self.map.entry(br) {
diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
index 7a0f70e979b8..324725a079bb 100644
--- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
+++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs
@@ -33,13 +33,13 @@ impl<'tcx> InferCtxt<'tcx> {
         let next_universe = self.create_next_universe();
 
         let delegate = FnMutDelegate {
-            regions: &mut |br: ty::BoundRegion| {
+            regions: &mut |br: ty::BoundRegion<'tcx>| {
                 ty::Region::new_placeholder(self.tcx, ty::PlaceholderRegion::new(next_universe, br))
             },
-            types: &mut |bound_ty: ty::BoundTy| {
+            types: &mut |bound_ty: ty::BoundTy<'tcx>| {
                 Ty::new_placeholder(self.tcx, ty::PlaceholderType::new(next_universe, bound_ty))
             },
-            consts: &mut |bound_const: ty::BoundConst| {
+            consts: &mut |bound_const: ty::BoundConst<'tcx>| {
                 ty::Const::new_placeholder(
                     self.tcx,
                     ty::PlaceholderConst::new(next_universe, bound_const),
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index f6c2e5946079..1cdc5e4a1b36 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -211,7 +211,7 @@ where
         // When we get into a binder, we need to add its own bound vars to the scope.
         let mut added = vec![];
         for arg in t.bound_vars() {
-            let arg: ty::BoundVariableKind = arg;
+            let arg: ty::BoundVariableKind<'tcx> = arg;
             match arg {
                 ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id))
                 | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)) => {
diff --git a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs
index 51a079e8bc12..d625e9ea08cc 100644
--- a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs
+++ b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs
@@ -48,7 +48,7 @@ pub enum ObjectLifetimeDefault {
 /// Maps the id of each bound variable reference to the variable decl
 /// that it corresponds to.
 #[derive(Debug, Default, HashStable)]
-pub struct ResolveBoundVars {
+pub struct ResolveBoundVars<'tcx> {
     // Maps from every use of a named (not anonymous) bound var to a
     // `ResolvedArg` describing how that variable is bound.
     pub defs: SortedMap,
@@ -59,7 +59,7 @@ pub struct ResolveBoundVars {
     // - closures
     // - trait refs
     // - bound types (like `T` in `for<'a> T<'a>: Foo`)
-    pub late_bound_vars: SortedMap>,
+    pub late_bound_vars: SortedMap>>,
 
     // List captured variables for each opaque type.
     pub opaque_captured_lifetimes: LocalDefIdMap>,
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 940cc30c17e6..afda0da48611 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -346,7 +346,6 @@ trivial! {
     rustc_middle::ty::AssocContainer,
     rustc_middle::ty::Asyncness,
     rustc_middle::ty::AsyncDestructor,
-    rustc_middle::ty::BoundVariableKind,
     rustc_middle::ty::AnonConstKind,
     rustc_middle::ty::Destructor,
     rustc_middle::ty::fast_reject::SimplifiedType,
@@ -415,6 +414,7 @@ tcx_lifetime! {
     rustc_middle::ty::ConstConditions,
     rustc_middle::ty::inhabitedness::InhabitedPredicate,
     rustc_middle::ty::Instance,
+    rustc_middle::ty::BoundVariableKind,
     rustc_middle::ty::InstanceKind,
     rustc_middle::ty::layout::FnAbiError,
     rustc_middle::ty::layout::LayoutError,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 2f83f3078e89..1a60cc2530d8 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -2116,7 +2116,7 @@ rustc_queries! {
     /// Does lifetime resolution on items. Importantly, we can't resolve
     /// lifetimes directly on things like trait methods, because of trait params.
     /// See `rustc_resolve::late::lifetimes` for details.
-    query resolve_bound_vars(owner_id: hir::OwnerId) -> &'tcx ResolveBoundVars {
+    query resolve_bound_vars(owner_id: hir::OwnerId) -> &'tcx ResolveBoundVars<'tcx> {
         arena_cache
         desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(owner_id) }
     }
@@ -2145,7 +2145,7 @@ rustc_queries! {
         separate_provide_extern
     }
     query late_bound_vars_map(owner_id: hir::OwnerId)
-        -> &'tcx SortedMap> {
+        -> &'tcx SortedMap>> {
         desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) }
     }
     /// For an opaque type, return the list of (captured lifetime, inner generic param).
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index 75b1317e022b..4856df3a6222 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -427,11 +427,11 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [Spanned
     }
 }
 
-impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List {
+impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List> {
     fn decode(decoder: &mut D) -> &'tcx Self {
         let len = decoder.read_usize();
         decoder.interner().mk_bound_variable_kinds_from_iter(
-            (0..len).map::(|_| Decodable::decode(decoder)),
+            (0..len).map::, _>(|_| Decodable::decode(decoder)),
         )
     }
 }
@@ -495,7 +495,7 @@ impl_decodable_via_ref! {
     &'tcx ty::List>,
     &'tcx traits::ImplSource<'tcx, ()>,
     &'tcx mir::Body<'tcx>,
-    &'tcx ty::List,
+    &'tcx ty::List>,
     &'tcx ty::List>,
     &'tcx ty::ListWithCachedTypeInfo>,
 }
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index da3caf0bb210..5581ad5669aa 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -94,7 +94,7 @@ impl<'tcx> Const<'tcx> {
     pub fn new_bound(
         tcx: TyCtxt<'tcx>,
         debruijn: ty::DebruijnIndex,
-        bound_const: ty::BoundConst,
+        bound_const: ty::BoundConst<'tcx>,
     ) -> Const<'tcx> {
         Const::new(tcx, ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const))
     }
@@ -103,7 +103,7 @@ impl<'tcx> Const<'tcx> {
     pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Const<'tcx> {
         Const::new(
             tcx,
-            ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst { var }),
+            ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst::new(var)),
         )
     }
 
@@ -183,13 +183,13 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> {
     fn new_bound(
         interner: TyCtxt<'tcx>,
         debruijn: ty::DebruijnIndex,
-        bound_const: ty::BoundConst,
+        bound_const: ty::BoundConst<'tcx>,
     ) -> Self {
         Const::new_bound(interner, debruijn, bound_const)
     }
 
     fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self {
-        Const::new_bound(tcx, debruijn, ty::BoundConst { var })
+        Const::new_bound(tcx, debruijn, ty::BoundConst::new(var))
     }
 
     fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: rustc_type_ir::BoundVar) -> Self {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index f015d0edc56c..a6960523f6cf 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -108,9 +108,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>];
     type GenericArg = ty::GenericArg<'tcx>;
     type Term = ty::Term<'tcx>;
-    type BoundVarKinds = &'tcx List;
+    type BoundVarKinds = &'tcx List>;
 
-    type BoundVarKind = ty::BoundVariableKind;
     type PredefinedOpaques = solve::PredefinedOpaques<'tcx>;
 
     fn mk_predefined_opaques_in_body(
@@ -144,10 +143,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
 
     type FnInputTys = &'tcx [Ty<'tcx>];
     type ParamTy = ParamTy;
-    type BoundTy = ty::BoundTy;
     type Symbol = Symbol;
 
-    type PlaceholderTy = ty::PlaceholderType<'tcx>;
     type ErrorGuaranteed = ErrorGuaranteed;
     type BoundExistentialPredicates = &'tcx List>;
 
@@ -157,10 +154,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type Safety = hir::Safety;
     type Abi = ExternAbi;
     type Const = ty::Const<'tcx>;
-    type PlaceholderConst = ty::PlaceholderConst<'tcx>;
 
     type ParamConst = ty::ParamConst;
-    type BoundConst = ty::BoundConst;
     type ValueConst = ty::Value<'tcx>;
     type ExprConst = ty::Expr<'tcx>;
     type ValTree = ty::ValTree<'tcx>;
@@ -169,8 +164,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type Region = Region<'tcx>;
     type EarlyParamRegion = ty::EarlyParamRegion;
     type LateParamRegion = ty::LateParamRegion;
-    type BoundRegion = ty::BoundRegion;
-    type PlaceholderRegion = ty::PlaceholderRegion<'tcx>;
 
     type RegionAssumptions = &'tcx ty::List>;
 
@@ -776,6 +769,13 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     ) -> (QueryResult<'tcx>, &'tcx inspect::Probe>) {
         self.evaluate_root_goal_for_proof_tree_raw(canonical_goal)
     }
+
+    fn item_name(self, id: DefId) -> Symbol {
+        let id = id.into_query_param();
+        self.opt_item_name(id).unwrap_or_else(|| {
+            bug!("item_name: no name for {:?}", self.def_path(id));
+        })
+    }
 }
 
 macro_rules! bidirectional_lang_item_map {
@@ -938,7 +938,7 @@ pub struct CtxtInterners<'tcx> {
     const_: InternedSet<'tcx, WithCachedTypeInfo>>,
     pat: InternedSet<'tcx, PatternKind<'tcx>>,
     const_allocation: InternedSet<'tcx, Allocation>,
-    bound_variable_kinds: InternedSet<'tcx, List>,
+    bound_variable_kinds: InternedSet<'tcx, List>>,
     layout: InternedSet<'tcx, LayoutData>,
     adt_def: InternedSet<'tcx, AdtDefData>,
     external_constraints: InternedSet<'tcx, ExternalConstraintsData>>,
@@ -2530,7 +2530,7 @@ nop_list_lift! { type_lists; Ty<'a> => Ty<'tcx> }
 nop_list_lift! {
     poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx>
 }
-nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind }
+nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind<'a> => ty::BoundVariableKind<'tcx> }
 
 // This is the impl for `&'a GenericArgs<'a>`.
 nop_list_lift! { args; GenericArg<'a> => GenericArg<'tcx> }
@@ -2817,7 +2817,7 @@ slice_interners!(
     poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>),
     projs: pub mk_projs(ProjectionKind),
     place_elems: pub mk_place_elems(PlaceElem<'tcx>),
-    bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
+    bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind<'tcx>),
     fields: pub mk_fields(FieldIdx),
     local_def_ids: intern_local_def_ids(LocalDefId),
     captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>),
@@ -3242,7 +3242,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn mk_bound_variable_kinds_from_iter(self, iter: I) -> T::Output
     where
         I: Iterator,
-        T: CollectAndApply>,
+        T: CollectAndApply, &'tcx List>>,
     {
         T::collect_and_apply(iter, |xs| self.mk_bound_variable_kinds(xs))
     }
@@ -3362,7 +3362,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self.is_late_bound_map(id.owner).is_some_and(|set| set.contains(&id.local_id))
     }
 
-    pub fn late_bound_vars(self, id: HirId) -> &'tcx List {
+    pub fn late_bound_vars(self, id: HirId) -> &'tcx List> {
         self.mk_bound_variable_kinds(
             &self
                 .late_bound_vars_map(id.owner)
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index ee29afcff638..3d9148d6ed7b 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId;
 use rustc_type_ir::data_structures::DelayedMap;
 
 use crate::ty::{
-    self, Binder, BoundConst, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    self, Binder, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
     TypeVisitableExt,
 };
 
@@ -58,28 +58,28 @@ where
 /// gets mapped to the same result. `BoundVarReplacer` caches by using
 /// a `DelayedMap` which does not cache the first few types it encounters.
 pub trait BoundVarReplacerDelegate<'tcx> {
-    fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>;
-    fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>;
-    fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx>;
+    fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx>;
+    fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx>;
+    fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx>;
 }
 
 /// A simple delegate taking 3 mutable functions. The used functions must
 /// always return the same result for each bound variable, no matter how
 /// frequently they are called.
 pub struct FnMutDelegate<'a, 'tcx> {
-    pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a),
-    pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a),
-    pub consts: &'a mut (dyn FnMut(ty::BoundConst) -> ty::Const<'tcx> + 'a),
+    pub regions: &'a mut (dyn FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx> + 'a),
+    pub types: &'a mut (dyn FnMut(ty::BoundTy<'tcx>) -> Ty<'tcx> + 'a),
+    pub consts: &'a mut (dyn FnMut(ty::BoundConst<'tcx>) -> ty::Const<'tcx> + 'a),
 }
 
 impl<'a, 'tcx> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<'a, 'tcx> {
-    fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
+    fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
         (self.regions)(br)
     }
-    fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
+    fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
         (self.types)(bt)
     }
-    fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> {
+    fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
         (self.consts)(bc)
     }
 }
@@ -207,13 +207,14 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         value: Binder<'tcx, T>,
         mut fld_r: F,
-    ) -> (T, FxIndexMap>)
+    ) -> (T, FxIndexMap, ty::Region<'tcx>>)
     where
-        F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
+        F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>,
         T: TypeFoldable>,
     {
         let mut region_map = FxIndexMap::default();
-        let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
+        let real_fld_r =
+            |br: ty::BoundRegion<'tcx>| *region_map.entry(br).or_insert_with(|| fld_r(br));
         let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
         (value, region_map)
     }
@@ -224,7 +225,7 @@ impl<'tcx> TyCtxt<'tcx> {
         mut replace_regions: F,
     ) -> T
     where
-        F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
+        F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>,
         T: TypeFoldable>,
     {
         let value = value.skip_binder();
@@ -292,14 +293,14 @@ impl<'tcx> TyCtxt<'tcx> {
         self.replace_escaping_bound_vars_uncached(
             value,
             FnMutDelegate {
-                regions: &mut |r: ty::BoundRegion| {
+                regions: &mut |r: ty::BoundRegion<'tcx>| {
                     ty::Region::new_bound(
                         self,
                         ty::INNERMOST,
                         ty::BoundRegion { var: shift_bv(r.var), kind: r.kind },
                     )
                 },
-                types: &mut |t: ty::BoundTy| {
+                types: &mut |t: ty::BoundTy<'tcx>| {
                     Ty::new_bound(
                         self,
                         ty::INNERMOST,
@@ -307,11 +308,7 @@ impl<'tcx> TyCtxt<'tcx> {
                     )
                 },
                 consts: &mut |c| {
-                    ty::Const::new_bound(
-                        self,
-                        ty::INNERMOST,
-                        ty::BoundConst { var: shift_bv(c.var) },
-                    )
+                    ty::Const::new_bound(self, ty::INNERMOST, ty::BoundConst::new(shift_bv(c.var)))
                 },
             },
         )
@@ -333,10 +330,10 @@ impl<'tcx> TyCtxt<'tcx> {
     {
         struct Anonymize<'a, 'tcx> {
             tcx: TyCtxt<'tcx>,
-            map: &'a mut FxIndexMap,
+            map: &'a mut FxIndexMap>,
         }
         impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> {
-            fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
+            fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
                 let entry = self.map.entry(br.var);
                 let index = entry.index();
                 let var = ty::BoundVar::from_usize(index);
@@ -346,7 +343,7 @@ impl<'tcx> TyCtxt<'tcx> {
                 let br = ty::BoundRegion { var, kind };
                 ty::Region::new_bound(self.tcx, ty::INNERMOST, br)
             }
-            fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
+            fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
                 let entry = self.map.entry(bt.var);
                 let index = entry.index();
                 let var = ty::BoundVar::from_usize(index);
@@ -355,12 +352,12 @@ impl<'tcx> TyCtxt<'tcx> {
                     .expect_ty();
                 Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind })
             }
-            fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> {
+            fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
                 let entry = self.map.entry(bc.var);
                 let index = entry.index();
                 let var = ty::BoundVar::from_usize(index);
                 let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
-                ty::Const::new_bound(self.tcx, ty::INNERMOST, BoundConst { var })
+                ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var))
             }
         }
 
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index ce713dcf42f5..3e3c9e27186a 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -97,13 +97,13 @@ pub use self::predicate::{
     RegionOutlivesPredicate, SubtypePredicate, TraitPredicate, TraitRef, TypeOutlivesPredicate,
 };
 pub use self::region::{
-    BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region,
-    RegionKind, RegionVid,
+    EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, RegionKind, RegionVid,
 };
 pub use self::sty::{
-    AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
-    CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst,
-    ParamTy, PolyFnSig, TyKind, TypeAndMut, TypingMode, UpvarArgs,
+    AliasTy, Article, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind,
+    BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs,
+    InlineConstArgsParts, ParamConst, ParamTy, PlaceholderConst, PlaceholderRegion,
+    PlaceholderType, PolyFnSig, TyKind, TypeAndMut, TypingMode, UpvarArgs,
 };
 pub use self::trait_def::TraitDef;
 pub use self::typeck_results::{
@@ -914,100 +914,6 @@ impl<'tcx> DefinitionSiteHiddenType<'tcx> {
     }
 }
 
-pub type PlaceholderRegion<'tcx> = ty::Placeholder, BoundRegion>;
-
-impl<'tcx> rustc_type_ir::inherent::PlaceholderLike> for PlaceholderRegion<'tcx> {
-    type Bound = BoundRegion;
-
-    fn universe(self) -> UniverseIndex {
-        self.universe
-    }
-
-    fn var(self) -> BoundVar {
-        self.bound.var
-    }
-
-    fn with_updated_universe(self, ui: UniverseIndex) -> Self {
-        ty::Placeholder::new(ui, self.bound)
-    }
-
-    fn new(ui: UniverseIndex, bound: BoundRegion) -> Self {
-        ty::Placeholder::new(ui, bound)
-    }
-
-    fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
-        ty::Placeholder::new(ui, BoundRegion { var, kind: BoundRegionKind::Anon })
-    }
-}
-
-pub type PlaceholderType<'tcx> = ty::Placeholder, BoundTy>;
-
-impl<'tcx> rustc_type_ir::inherent::PlaceholderLike> for PlaceholderType<'tcx> {
-    type Bound = BoundTy;
-
-    fn universe(self) -> UniverseIndex {
-        self.universe
-    }
-
-    fn var(self) -> BoundVar {
-        self.bound.var
-    }
-
-    fn with_updated_universe(self, ui: UniverseIndex) -> Self {
-        ty::Placeholder::new(ui, self.bound)
-    }
-
-    fn new(ui: UniverseIndex, bound: BoundTy) -> Self {
-        ty::Placeholder::new(ui, bound)
-    }
-
-    fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
-        ty::Placeholder::new(ui, BoundTy { var, kind: BoundTyKind::Anon })
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
-#[derive(TyEncodable, TyDecodable)]
-pub struct BoundConst {
-    pub var: BoundVar,
-}
-
-impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundConst {
-    fn var(self) -> BoundVar {
-        self.var
-    }
-
-    fn assert_eq(self, var: ty::BoundVariableKind) {
-        var.expect_const()
-    }
-}
-
-pub type PlaceholderConst<'tcx> = ty::Placeholder, BoundConst>;
-
-impl<'tcx> rustc_type_ir::inherent::PlaceholderLike> for PlaceholderConst<'tcx> {
-    type Bound = BoundConst;
-
-    fn universe(self) -> UniverseIndex {
-        self.universe
-    }
-
-    fn var(self) -> BoundVar {
-        self.bound.var
-    }
-
-    fn with_updated_universe(self, ui: UniverseIndex) -> Self {
-        ty::Placeholder::new(ui, self.bound)
-    }
-
-    fn new(ui: UniverseIndex, bound: BoundConst) -> Self {
-        ty::Placeholder::new(ui, bound)
-    }
-
-    fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
-        ty::Placeholder::new(ui, BoundConst { var })
-    }
-}
-
 pub type Clauses<'tcx> = &'tcx ListWithCachedTypeInfo>;
 
 impl<'tcx> rustc_type_ir::Flags for Clauses<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index de13c4f836a5..724be7b15ff2 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -199,7 +199,7 @@ pub struct RegionHighlightMode<'tcx> {
     /// This is used when you have a signature like `fn foo(x: &u32,
     /// y: &'a u32)` and we want to give a name to the region of the
     /// reference `x`.
-    highlight_bound_region: Option<(ty::BoundRegionKind, usize)>,
+    highlight_bound_region: Option<(ty::BoundRegionKind<'tcx>, usize)>,
 }
 
 impl<'tcx> RegionHighlightMode<'tcx> {
@@ -248,7 +248,7 @@ impl<'tcx> RegionHighlightMode<'tcx> {
     /// Highlight the given bound region.
     /// We can only highlight one bound region at a time. See
     /// the field `highlight_bound_region` for more detailed notes.
-    pub fn highlighting_bound_region(&mut self, br: ty::BoundRegionKind, number: usize) {
+    pub fn highlighting_bound_region(&mut self, br: ty::BoundRegionKind<'tcx>, number: usize) {
         assert!(self.highlight_bound_region.is_none());
         self.highlight_bound_region = Some((br, number));
     }
@@ -2641,12 +2641,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
 struct RegionFolder<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     current_index: ty::DebruijnIndex,
-    region_map: UnordMap>,
+    region_map: UnordMap, ty::Region<'tcx>>,
     name: &'a mut (
                 dyn FnMut(
         Option, // Debruijn index of the folded late-bound region
         ty::DebruijnIndex,         // Index corresponding to binder level
-        ty::BoundRegion,
+        ty::BoundRegion<'tcx>,
     ) -> ty::Region<'tcx>
                     + 'a
             ),
@@ -2719,7 +2719,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
         &mut self,
         value: &ty::Binder<'tcx, T>,
         mode: WrapBinderMode,
-    ) -> Result<(T, UnordMap>), fmt::Error>
+    ) -> Result<(T, UnordMap, ty::Region<'tcx>>), fmt::Error>
     where
         T: TypeFoldable>,
     {
@@ -2812,12 +2812,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
             // see issue #102392.
             let mut name = |lifetime_idx: Option,
                             binder_level_idx: ty::DebruijnIndex,
-                            br: ty::BoundRegion| {
+                            br: ty::BoundRegion<'tcx>| {
                 let (name, kind) = if let Some(name) = br.kind.get_name(tcx) {
                     (name, br.kind)
                 } else {
                     let name = next_name(self);
-                    (name, ty::BoundRegionKind::NamedAnon(name))
+                    (name, ty::BoundRegionKind::NamedForPrinting(name))
                 };
 
                 if let Some(lt_idx) = lifetime_idx {
diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs
index 61994d928dec..a497501ef19d 100644
--- a/compiler/rustc_middle/src/ty/region.rs
+++ b/compiler/rustc_middle/src/ty/region.rs
@@ -50,7 +50,7 @@ impl<'tcx> Region<'tcx> {
     pub fn new_bound(
         tcx: TyCtxt<'tcx>,
         debruijn: ty::DebruijnIndex,
-        bound_region: ty::BoundRegion,
+        bound_region: ty::BoundRegion<'tcx>,
     ) -> Region<'tcx> {
         // Use a pre-interned one when possible.
         if let ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon } = bound_region
@@ -160,7 +160,7 @@ impl<'tcx> rustc_type_ir::inherent::Region> for Region<'tcx> {
     fn new_bound(
         interner: TyCtxt<'tcx>,
         debruijn: ty::DebruijnIndex,
-        var: ty::BoundRegion,
+        var: ty::BoundRegion<'tcx>,
     ) -> Self {
         Region::new_bound(interner, debruijn, var)
     }
@@ -388,7 +388,7 @@ pub struct LateParamRegion {
     pub kind: LateParamRegionKind,
 }
 
-/// When liberating bound regions, we map their [`BoundRegionKind`]
+/// When liberating bound regions, we map their [`ty::BoundRegionKind`]
 /// to this as we need to track the index of anonymous regions. We
 /// otherwise end up liberating multiple bound regions to the same
 /// late-bound region.
@@ -397,7 +397,7 @@ pub struct LateParamRegion {
 pub enum LateParamRegionKind {
     /// An anonymous region parameter for a given fn (&T)
     ///
-    /// Unlike [`BoundRegionKind::Anon`], this tracks the index of the
+    /// Unlike [`ty::BoundRegionKind::Anon`], this tracks the index of the
     /// liberated bound region.
     ///
     /// We should ideally never liberate anonymous regions, but do so for the
@@ -418,12 +418,14 @@ pub enum LateParamRegionKind {
 }
 
 impl LateParamRegionKind {
-    pub fn from_bound(var: BoundVar, br: BoundRegionKind) -> LateParamRegionKind {
+    pub fn from_bound(var: BoundVar, br: ty::BoundRegionKind<'_>) -> LateParamRegionKind {
         match br {
-            BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()),
-            BoundRegionKind::Named(def_id) => LateParamRegionKind::Named(def_id),
-            BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv,
-            BoundRegionKind::NamedAnon(name) => LateParamRegionKind::NamedAnon(var.as_u32(), name),
+            ty::BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()),
+            ty::BoundRegionKind::Named(def_id) => LateParamRegionKind::Named(def_id),
+            ty::BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv,
+            ty::BoundRegionKind::NamedForPrinting(name) => {
+                LateParamRegionKind::NamedAnon(var.as_u32(), name)
+            }
         }
     }
 
@@ -450,81 +452,6 @@ impl LateParamRegionKind {
     }
 }
 
-#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)]
-#[derive(HashStable)]
-pub enum BoundRegionKind {
-    /// An anonymous region parameter for a given fn (&T)
-    Anon,
-
-    /// An anonymous region parameter with a `Symbol` name.
-    ///
-    /// Used to give late-bound regions names for things like pretty printing.
-    NamedAnon(Symbol),
-
-    /// Late-bound regions that appear in the AST.
-    Named(DefId),
-
-    /// Anonymous region for the implicit env pointer parameter
-    /// to a closure
-    ClosureEnv,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable)]
-pub struct BoundRegion {
-    pub var: BoundVar,
-    pub kind: BoundRegionKind,
-}
-
-impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundRegion {
-    fn var(self) -> BoundVar {
-        self.var
-    }
-
-    fn assert_eq(self, var: ty::BoundVariableKind) {
-        assert_eq!(self.kind, var.expect_region())
-    }
-}
-
-impl core::fmt::Debug for BoundRegion {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self.kind {
-            BoundRegionKind::Anon => write!(f, "{:?}", self.var),
-            BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var),
-            BoundRegionKind::Named(def) => {
-                write!(f, "{:?}.Named({:?})", self.var, def)
-            }
-            BoundRegionKind::NamedAnon(symbol) => {
-                write!(f, "{:?}.NamedAnon({:?})", self.var, symbol)
-            }
-        }
-    }
-}
-
-impl BoundRegionKind {
-    pub fn is_named(&self, tcx: TyCtxt<'_>) -> bool {
-        self.get_name(tcx).is_some()
-    }
-
-    pub fn get_name(&self, tcx: TyCtxt<'_>) -> Option {
-        match *self {
-            BoundRegionKind::Named(def_id) => {
-                let name = tcx.item_name(def_id);
-                if name != kw::UnderscoreLifetime { Some(name) } else { None }
-            }
-            BoundRegionKind::NamedAnon(name) => Some(name),
-            _ => None,
-        }
-    }
-
-    pub fn get_id(&self) -> Option {
-        match *self {
-            BoundRegionKind::Named(id) => Some(id),
-            _ => None,
-        }
-    }
-}
-
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 314d2ba39632..8707b03e4b8f 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -65,21 +65,6 @@ impl<'tcx> fmt::Debug for ty::adjustment::PatAdjustment<'tcx> {
     }
 }
 
-impl fmt::Debug for ty::BoundRegionKind {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            ty::BoundRegionKind::Anon => write!(f, "BrAnon"),
-            ty::BoundRegionKind::NamedAnon(name) => {
-                write!(f, "BrNamedAnon({name})")
-            }
-            ty::BoundRegionKind::Named(did) => {
-                write!(f, "BrNamed({did:?})")
-            }
-            ty::BoundRegionKind::ClosureEnv => write!(f, "BrEnv"),
-        }
-    }
-}
-
 impl fmt::Debug for ty::LateParamRegion {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "ReLateParam({:?}, {:?})", self.scope, self.kind)
@@ -175,15 +160,6 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
     }
 }
 
-impl fmt::Debug for ty::BoundTy {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.kind {
-            ty::BoundTyKind::Anon => write!(f, "{:?}", self.var),
-            ty::BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"),
-        }
-    }
-}
-
 impl<'tcx> fmt::Debug for GenericArg<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self.kind() {
@@ -255,7 +231,8 @@ TrivialTypeTraversalImpls! {
     crate::ty::AdtKind,
     crate::ty::AssocItem,
     crate::ty::AssocKind,
-    crate::ty::BoundRegion,
+    crate::ty::BoundRegion<'tcx>,
+    crate::ty::BoundTy<'tcx>,
     crate::ty::ScalarInt,
     crate::ty::UserTypeAnnotationIndex,
     crate::ty::abstract_const::NotConstEvaluatable,
@@ -284,7 +261,6 @@ TrivialTypeTraversalImpls! {
 TrivialTypeTraversalAndLiftImpls! {
     // tidy-alphabetical-start
     crate::mir::RuntimeChecks,
-    crate::ty::BoundTy,
     crate::ty::ParamTy,
     crate::ty::instance::ReifyReason,
     rustc_hir::def_id::DefId,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index b0b5a783b00e..4c5bd85e6b72 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -13,7 +13,7 @@ use rustc_hir as hir;
 use rustc_hir::LangItem;
 use rustc_hir::def_id::DefId;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension};
-use rustc_span::{DUMMY_SP, Span, Symbol, sym};
+use rustc_span::{DUMMY_SP, Span, Symbol, kw, sym};
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::solve::SizedTraitKind;
 use rustc_type_ir::walk::TypeWalker;
@@ -26,8 +26,8 @@ use crate::infer::canonical::Canonical;
 use crate::traits::ObligationCause;
 use crate::ty::InferTy::*;
 use crate::ty::{
-    self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv,
-    Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
+    self, AdtDef, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, Ty,
+    TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
 };
 
 // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
@@ -40,6 +40,15 @@ pub type Binder<'tcx, T> = ir::Binder, T>;
 pub type EarlyBinder<'tcx, T> = ir::EarlyBinder, T>;
 pub type TypingMode<'tcx> = ir::TypingMode>;
 pub type Placeholder<'tcx, T> = ir::Placeholder, T>;
+pub type PlaceholderRegion<'tcx> = ir::PlaceholderRegion>;
+pub type PlaceholderType<'tcx> = ir::PlaceholderType>;
+pub type PlaceholderConst<'tcx> = ir::PlaceholderConst>;
+pub type BoundTy<'tcx> = ir::BoundTy>;
+pub type BoundConst<'tcx> = ir::BoundConst>;
+pub type BoundRegion<'tcx> = ir::BoundRegion>;
+pub type BoundVariableKind<'tcx> = ir::BoundVariableKind>;
+pub type BoundRegionKind<'tcx> = ir::BoundRegionKind>;
+pub type BoundTyKind<'tcx> = ir::BoundTyKind>;
 
 pub trait Article {
     fn article(&self) -> &'static str;
@@ -257,37 +266,6 @@ impl<'tcx> InlineConstArgs<'tcx> {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
-#[derive(HashStable)]
-pub enum BoundVariableKind {
-    Ty(BoundTyKind),
-    Region(BoundRegionKind),
-    Const,
-}
-
-impl BoundVariableKind {
-    pub fn expect_region(self) -> BoundRegionKind {
-        match self {
-            BoundVariableKind::Region(lt) => lt,
-            _ => bug!("expected a region, but found another kind"),
-        }
-    }
-
-    pub fn expect_ty(self) -> BoundTyKind {
-        match self {
-            BoundVariableKind::Ty(ty) => ty,
-            _ => bug!("expected a type, but found another kind"),
-        }
-    }
-
-    pub fn expect_const(self) {
-        match self {
-            BoundVariableKind::Const => (),
-            _ => bug!("expected a const, but found another kind"),
-        }
-    }
-}
-
 pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>;
 pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>;
 
@@ -381,30 +359,6 @@ impl ParamConst {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable)]
-pub struct BoundTy {
-    pub var: BoundVar,
-    pub kind: BoundTyKind,
-}
-
-impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundTy {
-    fn var(self) -> BoundVar {
-        self.var
-    }
-
-    fn assert_eq(self, var: ty::BoundVariableKind) {
-        assert_eq!(self.kind, var.expect_ty())
-    }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
-#[derive(HashStable)]
-pub enum BoundTyKind {
-    Anon,
-    Param(DefId),
-}
-
 /// Constructors for `Ty`
 impl<'tcx> Ty<'tcx> {
     /// Avoid using this in favour of more specific `new_*` methods, where possible.
@@ -479,7 +433,7 @@ impl<'tcx> Ty<'tcx> {
     pub fn new_bound(
         tcx: TyCtxt<'tcx>,
         index: ty::DebruijnIndex,
-        bound_ty: ty::BoundTy,
+        bound_ty: ty::BoundTy<'tcx>,
     ) -> Ty<'tcx> {
         // Use a pre-interned one when possible.
         if let ty::BoundTy { var, kind: ty::BoundTyKind::Anon } = bound_ty
@@ -961,7 +915,11 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> {
         Ty::new_placeholder(tcx, placeholder)
     }
 
-    fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self {
+    fn new_bound(
+        interner: TyCtxt<'tcx>,
+        debruijn: ty::DebruijnIndex,
+        var: ty::BoundTy<'tcx>,
+    ) -> Self {
         Ty::new_bound(interner, debruijn, var)
     }
 
@@ -2135,6 +2093,12 @@ impl<'tcx> rustc_type_ir::inherent::Tys> for &'tcx ty::List rustc_type_ir::inherent::Symbol> for Symbol {
+    fn is_kw_underscore_lifetime(self) -> bool {
+        self == kw::UnderscoreLifetime
+    }
+}
+
 // Some types are used a lot. Make sure they don't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
 mod size_asserts {
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index e84ac56b31df..8f79f8e3564e 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -113,7 +113,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn collect_constrained_late_bound_regions(
         self,
         value: Binder<'tcx, T>,
-    ) -> FxIndexSet
+    ) -> FxIndexSet>
     where
         T: TypeFoldable>,
     {
@@ -124,7 +124,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn collect_referenced_late_bound_regions(
         self,
         value: Binder<'tcx, T>,
-    ) -> FxIndexSet
+    ) -> FxIndexSet>
     where
         T: TypeFoldable>,
     {
@@ -135,7 +135,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         value: Binder<'tcx, T>,
         just_constrained: bool,
-    ) -> FxIndexSet
+    ) -> FxIndexSet>
     where
         T: TypeFoldable>,
     {
@@ -149,9 +149,9 @@ impl<'tcx> TyCtxt<'tcx> {
 
 /// Collects all the late-bound regions at the innermost binding level
 /// into a hash set.
-struct LateBoundRegionsCollector {
+struct LateBoundRegionsCollector<'tcx> {
     current_index: ty::DebruijnIndex,
-    regions: FxIndexSet,
+    regions: FxIndexSet>,
 
     /// `true` if we only want regions that are known to be
     /// "constrained" when you equate this type with another type. In
@@ -163,13 +163,13 @@ struct LateBoundRegionsCollector {
     just_constrained: bool,
 }
 
-impl LateBoundRegionsCollector {
+impl LateBoundRegionsCollector<'_> {
     fn new(just_constrained: bool) -> Self {
         Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained }
     }
 }
 
-impl<'tcx> TypeVisitor> for LateBoundRegionsCollector {
+impl<'tcx> TypeVisitor> for LateBoundRegionsCollector<'tcx> {
     fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) {
         self.current_index.shift_in(1);
         t.super_visit_with(self);
diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs
index 61a2dcd226b7..ce2be24adc58 100644
--- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs
@@ -3,7 +3,8 @@ use rustc_type_ir::inherent::*;
 use rustc_type_ir::solve::{Goal, QueryInput};
 use rustc_type_ir::{
     self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, Flags, InferCtxtLike,
-    Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
+    Interner, PlaceholderConst, PlaceholderType, TypeFlags, TypeFoldable, TypeFolder,
+    TypeSuperFoldable, TypeVisitableExt,
 };
 
 use crate::delegate::SolverDelegate;
@@ -357,13 +358,13 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> {
             },
             ty::Placeholder(placeholder) => match self.canonicalize_mode {
                 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
-                    PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
+                    PlaceholderType::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
                 ),
                 CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
             },
             ty::Param(_) => match self.canonicalize_mode {
                 CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
-                    PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
+                    PlaceholderType::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
                 ),
                 CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"),
             },
@@ -513,17 +514,23 @@ impl, I: Interner> TypeFolder for Canonicaliz
                 ty::InferConst::Fresh(_) => todo!(),
             },
             ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
-                CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
-                    PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
-                ),
+                CanonicalizeMode::Input { .. } => {
+                    CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon(
+                        ty::UniverseIndex::ROOT,
+                        self.variables.len().into(),
+                    ))
+                }
                 CanonicalizeMode::Response { .. } => {
                     CanonicalVarKind::PlaceholderConst(placeholder)
                 }
             },
             ty::ConstKind::Param(_) => match self.canonicalize_mode {
-                CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
-                    PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
-                ),
+                CanonicalizeMode::Input { .. } => {
+                    CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon(
+                        ty::UniverseIndex::ROOT,
+                        self.variables.len().into(),
+                    ))
+                }
                 CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
             },
             // FIXME: See comment above -- we could fold the region separately or something.
diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs
index 96fea09013a1..1f64f09fe787 100644
--- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs
@@ -177,9 +177,9 @@ where
                 }
             }
             ty::GenericArgKind::Const(c) => {
-                if let ty::ConstKind::Bound(index_kind, bv) = c.kind() {
+                if let ty::ConstKind::Bound(index_kind, bc) = c.kind() {
                     assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical));
-                    opt_values[bv.var()] = Some(*original_value);
+                    opt_values[bc.var()] = Some(*original_value);
                 }
             }
         }
diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs
index c8016759f239..31fce0601697 100644
--- a/compiler/rustc_next_trait_solver/src/placeholder.rs
+++ b/compiler/rustc_next_trait_solver/src/placeholder.rs
@@ -3,8 +3,8 @@ use core::panic;
 use rustc_type_ir::data_structures::IndexMap;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::{
-    self as ty, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable,
-    TypeVisitableExt,
+    self as ty, InferCtxtLike, Interner, PlaceholderConst, PlaceholderRegion, PlaceholderType,
+    TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
 };
 
 pub struct BoundVarReplacer<'a, Infcx, I = ::Interner>
@@ -16,9 +16,9 @@ where
     // These three maps track the bound variable that were replaced by placeholders. It might be
     // nice to remove these since we already have the `kind` in the placeholder; we really just need
     // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
-    mapped_regions: IndexMap,
-    mapped_types: IndexMap,
-    mapped_consts: IndexMap,
+    mapped_regions: IndexMap, ty::BoundRegion>,
+    mapped_types: IndexMap, ty::BoundTy>,
+    mapped_consts: IndexMap, ty::BoundConst>,
     // The current depth relative to *this* folding, *not* the entire normalization. In other words,
     // the depth of binders we've passed here.
     current_index: ty::DebruijnIndex,
@@ -40,9 +40,9 @@ where
         value: T,
     ) -> (
         T,
-        IndexMap,
-        IndexMap,
-        IndexMap,
+        IndexMap, ty::BoundRegion>,
+        IndexMap, ty::BoundTy>,
+        IndexMap, ty::BoundConst>,
     ) {
         let mut replacer = BoundVarReplacer {
             infcx,
@@ -103,7 +103,7 @@ where
                 if debruijn >= self.current_index =>
             {
                 let universe = self.universe_for(debruijn);
-                let p = PlaceholderLike::new(universe, br);
+                let p = PlaceholderRegion::new(universe, br);
                 self.mapped_regions.insert(p, br);
                 Region::new_placeholder(self.cx(), p)
             }
@@ -126,7 +126,7 @@ where
                 if debruijn >= self.current_index =>
             {
                 let universe = self.universe_for(debruijn);
-                let p = PlaceholderLike::new(universe, bound_ty);
+                let p = PlaceholderType::new(universe, bound_ty);
                 self.mapped_types.insert(p, bound_ty);
                 Ty::new_placeholder(self.cx(), p)
             }
@@ -150,7 +150,7 @@ where
                 if debruijn >= self.current_index =>
             {
                 let universe = self.universe_for(debruijn);
-                let p = PlaceholderLike::new(universe, bound_const);
+                let p = PlaceholderConst::new(universe, bound_const);
                 self.mapped_consts.insert(p, bound_const);
                 Const::new_placeholder(self.cx(), p)
             }
diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs
index 73a3fed111b3..8594f6510041 100644
--- a/compiler/rustc_public/src/unstable/convert/internal.rs
+++ b/compiler/rustc_public/src/unstable/convert/internal.rs
@@ -433,7 +433,7 @@ where
 }
 
 impl RustcInternal for BoundVariableKind {
-    type T<'tcx> = rustc_ty::BoundVariableKind;
+    type T<'tcx> = rustc_ty::BoundVariableKind<'tcx>;
 
     fn internal<'tcx>(
         &self,
diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs
index cee144122c0e..70cdae43126c 100644
--- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs
+++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs
@@ -271,7 +271,7 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
     }
 }
 
-impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
+impl<'tcx> Stable<'tcx> for ty::BoundTyKind<'tcx> {
     type T = crate::ty::BoundTyKind;
 
     fn stable<'cx>(
@@ -290,7 +290,7 @@ impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
     }
 }
 
-impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
+impl<'tcx> Stable<'tcx> for ty::BoundRegionKind<'tcx> {
     type T = crate::ty::BoundRegionKind;
 
     fn stable<'cx>(
@@ -307,12 +307,12 @@ impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
                 cx.tcx.item_name(*def_id).to_string(),
             ),
             ty::BoundRegionKind::ClosureEnv => BoundRegionKind::BrEnv,
-            ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
+            ty::BoundRegionKind::NamedForPrinting(_) => bug!("only used for pretty printing"),
         }
     }
 }
 
-impl<'tcx> Stable<'tcx> for ty::BoundVariableKind {
+impl<'tcx> Stable<'tcx> for ty::BoundVariableKind<'tcx> {
     type T = crate::ty::BoundVariableKind;
 
     fn stable<'cx>(
@@ -546,7 +546,7 @@ impl<'tcx> Stable<'tcx> for ty::ParamTy {
     }
 }
 
-impl<'tcx> Stable<'tcx> for ty::BoundTy {
+impl<'tcx> Stable<'tcx> for ty::BoundTy<'tcx> {
     type T = crate::ty::BoundTy;
     fn stable<'cx>(
         &self,
diff --git a/compiler/rustc_public/src/unstable/internal_cx/mod.rs b/compiler/rustc_public/src/unstable/internal_cx/mod.rs
index 601ca4fb5cfd..4da86b9442f1 100644
--- a/compiler/rustc_public/src/unstable/internal_cx/mod.rs
+++ b/compiler/rustc_public/src/unstable/internal_cx/mod.rs
@@ -78,7 +78,10 @@ impl<'tcx> InternalCx<'tcx> for TyCtxt<'tcx> {
     fn mk_bound_variable_kinds_from_iter(self, iter: I) -> T::Output
     where
         I: Iterator,
-        T: ty::CollectAndApply>,
+        T: ty::CollectAndApply<
+                ty::BoundVariableKind<'tcx>,
+                &'tcx List>,
+            >,
     {
         TyCtxt::mk_bound_variable_kinds_from_iter(self, iter)
     }
diff --git a/compiler/rustc_public/src/unstable/mod.rs b/compiler/rustc_public/src/unstable/mod.rs
index 2b69fb5408cf..583493c23d66 100644
--- a/compiler/rustc_public/src/unstable/mod.rs
+++ b/compiler/rustc_public/src/unstable/mod.rs
@@ -47,7 +47,10 @@ pub trait InternalCx<'tcx>: Copy + Clone {
     fn mk_bound_variable_kinds_from_iter(self, iter: I) -> T::Output
     where
         I: Iterator,
-        T: ty::CollectAndApply>;
+        T: ty::CollectAndApply<
+                ty::BoundVariableKind<'tcx>,
+                &'tcx List>,
+            >;
 
     fn mk_place_elems(self, v: &[mir::PlaceElem<'tcx>]) -> &'tcx List>;
 
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs
index 3bab09bc587f..05a1e3fe95dd 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs
@@ -34,14 +34,18 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
                         (Some(self.tcx().def_span(def_id)), Some(self.tcx().item_name(def_id)))
                     }
                     ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => (None, None),
-                    ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
+                    ty::BoundRegionKind::NamedForPrinting(_) => {
+                        bug!("only used for pretty printing")
+                    }
                 };
                 let (sup_span, sup_symbol) = match *sup_name {
                     ty::BoundRegionKind::Named(def_id) => {
                         (Some(self.tcx().def_span(def_id)), Some(self.tcx().item_name(def_id)))
                     }
                     ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => (None, None),
-                    ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
+                    ty::BoundRegionKind::NamedForPrinting(_) => {
+                        bug!("only used for pretty printing")
+                    }
                 };
                 let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) {
                     (Some(sub_span), Some(sup_span), Some(sub_symbol), Some(sup_symbol)) => {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
index fdaf2d619dd5..b11461cd0e32 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
@@ -1052,7 +1052,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     }
 
     fn report_inference_failure(&self, var_origin: RegionVariableOrigin<'tcx>) -> Diag<'_> {
-        let br_string = |br: ty::BoundRegionKind| {
+        let br_string = |br: ty::BoundRegionKind<'tcx>| {
             let mut s = match br {
                 ty::BoundRegionKind::Named(def_id) => self.tcx.item_name(def_id).to_string(),
                 _ => String::new(),
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 2aae5f2cde1e..9f59f6c59250 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -566,7 +566,7 @@ fn plug_infer_with_placeholders<'tcx>(
                         ty,
                         Ty::new_placeholder(
                             self.infcx.tcx,
-                            ty::Placeholder::new(
+                            ty::PlaceholderType::new(
                                 self.universe,
                                 ty::BoundTy { var: self.next_var(), kind: ty::BoundTyKind::Anon },
                             ),
@@ -592,9 +592,9 @@ fn plug_infer_with_placeholders<'tcx>(
                         ct,
                         ty::Const::new_placeholder(
                             self.infcx.tcx,
-                            ty::Placeholder::new(
+                            ty::PlaceholderConst::new(
                                 self.universe,
-                                ty::BoundConst { var: self.next_var() },
+                                ty::BoundConst::new(self.next_var()),
                             ),
                         ),
                     )
@@ -623,7 +623,7 @@ fn plug_infer_with_placeholders<'tcx>(
                             r,
                             ty::Region::new_placeholder(
                                 self.infcx.tcx,
-                                ty::Placeholder::new(
+                                ty::PlaceholderRegion::new(
                                     self.universe,
                                     ty::BoundRegion {
                                         var: self.next_var(),
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 5839a8c2e295..4e027a301cc8 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -772,7 +772,7 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
                 self.idx += 1;
                 ty::Const::new_placeholder(
                     self.tcx,
-                    ty::PlaceholderConst::new(ty::UniverseIndex::ROOT, ty::BoundConst { var: idx }),
+                    ty::PlaceholderConst::new(ty::UniverseIndex::ROOT, ty::BoundConst::new(idx)),
                 )
             } else {
                 c.super_fold_with(self)
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 19ccf6a55bf1..c3f46aa51c73 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -220,9 +220,9 @@ pub fn with_replaced_escaping_bound_vars<
 /// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
 pub struct PlaceholderReplacer<'a, 'tcx> {
     infcx: &'a InferCtxt<'tcx>,
-    mapped_regions: FxIndexMap, ty::BoundRegion>,
-    mapped_types: FxIndexMap, ty::BoundTy>,
-    mapped_consts: FxIndexMap, ty::BoundConst>,
+    mapped_regions: FxIndexMap, ty::BoundRegion<'tcx>>,
+    mapped_types: FxIndexMap, ty::BoundTy<'tcx>>,
+    mapped_consts: FxIndexMap, ty::BoundConst<'tcx>>,
     universe_indices: &'a [Option],
     current_index: ty::DebruijnIndex,
 }
@@ -230,9 +230,9 @@ pub struct PlaceholderReplacer<'a, 'tcx> {
 impl<'a, 'tcx> PlaceholderReplacer<'a, 'tcx> {
     pub fn replace_placeholders>>(
         infcx: &'a InferCtxt<'tcx>,
-        mapped_regions: FxIndexMap, ty::BoundRegion>,
-        mapped_types: FxIndexMap, ty::BoundTy>,
-        mapped_consts: FxIndexMap, ty::BoundConst>,
+        mapped_regions: FxIndexMap, ty::BoundRegion<'tcx>>,
+        mapped_types: FxIndexMap, ty::BoundTy<'tcx>>,
+        mapped_consts: FxIndexMap, ty::BoundConst<'tcx>>,
         universe_indices: &'a [Option],
         value: T,
     ) -> T {
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index bb25a14ef744..d6a29aba22b9 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -210,7 +210,7 @@ struct ImplTraitInTraitFinder<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     predicates: &'a mut Vec>,
     fn_def_id: DefId,
-    bound_vars: &'tcx ty::List,
+    bound_vars: &'tcx ty::List>,
     seen: FxHashSet,
     depth: ty::DebruijnIndex,
 }
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index b5c9080154ce..fc8b39f7c01f 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -1,11 +1,14 @@
 use std::fmt;
+use std::hash::Hash;
 use std::marker::PhantomData;
 use std::ops::{ControlFlow, Deref};
 
 use derive_where::derive_where;
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
-use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic};
+use rustc_type_ir_macros::{
+    GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic,
+};
 use tracing::instrument;
 
 use crate::data_structures::SsoHashSet;
@@ -23,8 +26,11 @@ use crate::{self as ty, DebruijnIndex, Interner, UniverseIndex};
 /// for more details.
 ///
 /// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro.
-#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, T)]
+// FIXME(derive-where#136): Need to use separate `derive_where` for
+// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd`
+// impls from incorrectly relying on `T: Copy` and `T: Ord`.
 #[derive_where(Copy; I: Interner, T: Copy)]
+#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, T)]
 #[derive(GenericTypeVisitable)]
 #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
 pub struct Binder {
@@ -359,9 +365,12 @@ impl TypeVisitor for ValidateBoundVars {
 /// `instantiate`.
 ///
 /// See  for more details.
-#[derive_where(Clone, PartialEq, Ord, Hash, Debug; I: Interner, T)]
-#[derive_where(PartialOrd; I: Interner, T: Ord)]
+// FIXME(derive-where#136): Need to use separate `derive_where` for
+// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd`
+// impls from incorrectly relying on `T: Copy` and `T: Ord`.
+#[derive_where(Ord; I: Interner, T: Ord)]
 #[derive_where(Copy; I: Interner, T: Copy)]
+#[derive_where(Clone, PartialOrd, PartialEq, Hash, Debug; I: Interner, T)]
 #[derive(GenericTypeVisitable)]
 #[cfg_attr(
     feature = "nightly",
@@ -955,11 +964,12 @@ pub enum BoundVarIndexKind {
 /// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are
 /// identified by both a universe, as well as a name residing within that universe. Distinct bound
 /// regions/types/consts within the same universe simply have an unknown relationship to one
-/// another.
-#[derive_where(Clone, PartialEq, Ord, Hash; I: Interner, T)]
-#[derive_where(PartialOrd; I: Interner, T: Ord)]
-#[derive_where(Copy; I: Interner, T: Copy, T)]
-#[derive_where(Eq; T)]
+// FIXME(derive-where#136): Need to use separate `derive_where` for
+// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd`
+// impls from incorrectly relying on `T: Copy` and `T: Ord`.
+#[derive_where(Ord; I: Interner, T: Ord)]
+#[derive_where(Copy; I: Interner, T: Copy)]
+#[derive_where(Clone, PartialOrd, PartialEq, Eq, Hash; I: Interner, T)]
 #[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
 #[cfg_attr(
     feature = "nightly",
@@ -973,12 +983,6 @@ pub struct Placeholder {
     _tcx: PhantomData I>,
 }
 
-impl Placeholder {
-    pub fn new(universe: UniverseIndex, bound: T) -> Self {
-        Placeholder { universe, bound, _tcx: PhantomData }
-    }
-}
-
 impl fmt::Debug for ty::Placeholder {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if self.universe == ty::UniverseIndex::ROOT {
@@ -1003,3 +1007,321 @@ where
         })
     }
 }
+
+#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)]
+#[derive(Lift_Generic)]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
+
+pub enum BoundRegionKind {
+    /// An anonymous region parameter for a given fn (&T)
+    Anon,
+
+    /// An anonymous region parameter with a `Symbol` name.
+    ///
+    /// Used to give late-bound regions names for things like pretty printing.
+    NamedForPrinting(I::Symbol),
+
+    /// Late-bound regions that appear in the AST.
+    Named(I::DefId),
+
+    /// Anonymous region for the implicit env pointer parameter
+    /// to a closure
+    ClosureEnv,
+}
+
+impl fmt::Debug for ty::BoundRegionKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            ty::BoundRegionKind::Anon => write!(f, "BrAnon"),
+            ty::BoundRegionKind::NamedForPrinting(name) => {
+                write!(f, "BrNamedForPrinting({:?})", name)
+            }
+            ty::BoundRegionKind::Named(did) => {
+                write!(f, "BrNamed({did:?})")
+            }
+            ty::BoundRegionKind::ClosureEnv => write!(f, "BrEnv"),
+        }
+    }
+}
+
+impl BoundRegionKind {
+    pub fn is_named(&self, tcx: I) -> bool {
+        self.get_name(tcx).is_some()
+    }
+
+    pub fn get_name(&self, tcx: I) -> Option {
+        match *self {
+            ty::BoundRegionKind::Named(def_id) => {
+                let name = tcx.item_name(def_id);
+                if name.is_kw_underscore_lifetime() { None } else { Some(name) }
+            }
+            ty::BoundRegionKind::NamedForPrinting(name) => Some(name),
+            _ => None,
+        }
+    }
+
+    pub fn get_id(&self) -> Option {
+        match *self {
+            ty::BoundRegionKind::Named(id) => Some(id),
+            _ => None,
+        }
+    }
+}
+
+#[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)]
+#[derive(Lift_Generic)]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
+pub enum BoundTyKind {
+    Anon,
+    Param(I::DefId),
+}
+
+#[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)]
+#[derive(Lift_Generic)]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
+pub enum BoundVariableKind {
+    Ty(BoundTyKind),
+    Region(BoundRegionKind),
+    Const,
+}
+
+impl BoundVariableKind {
+    pub fn expect_region(self) -> BoundRegionKind {
+        match self {
+            BoundVariableKind::Region(lt) => lt,
+            _ => panic!("expected a region, but found another kind"),
+        }
+    }
+
+    pub fn expect_ty(self) -> BoundTyKind {
+        match self {
+            BoundVariableKind::Ty(ty) => ty,
+            _ => panic!("expected a type, but found another kind"),
+        }
+    }
+
+    pub fn expect_const(self) {
+        match self {
+            BoundVariableKind::Const => (),
+            _ => panic!("expected a const, but found another kind"),
+        }
+    }
+}
+
+#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, HashStable_NoContext, Decodable_NoContext)
+)]
+pub struct BoundRegion {
+    pub var: ty::BoundVar,
+    pub kind: BoundRegionKind,
+}
+
+impl core::fmt::Debug for BoundRegion {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self.kind {
+            BoundRegionKind::Anon => write!(f, "{:?}", self.var),
+            BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var),
+            BoundRegionKind::Named(def) => {
+                write!(f, "{:?}.Named({:?})", self.var, def)
+            }
+            BoundRegionKind::NamedForPrinting(symbol) => {
+                write!(f, "{:?}.NamedAnon({:?})", self.var, symbol)
+            }
+        }
+    }
+}
+
+impl BoundRegion {
+    pub fn var(self) -> ty::BoundVar {
+        self.var
+    }
+
+    pub fn assert_eq(self, var: BoundVariableKind) {
+        assert_eq!(self.kind, var.expect_region())
+    }
+}
+
+pub type PlaceholderRegion = ty::Placeholder>;
+
+impl PlaceholderRegion {
+    pub fn universe(self) -> UniverseIndex {
+        self.universe
+    }
+
+    pub fn var(self) -> ty::BoundVar {
+        self.bound.var()
+    }
+
+    pub fn with_updated_universe(self, ui: UniverseIndex) -> Self {
+        Self { universe: ui, bound: self.bound, _tcx: PhantomData }
+    }
+
+    pub fn new(ui: UniverseIndex, bound: BoundRegion) -> Self {
+        Self { universe: ui, bound, _tcx: PhantomData }
+    }
+
+    pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self {
+        let bound = BoundRegion { var, kind: BoundRegionKind::Anon };
+        Self { universe: ui, bound, _tcx: PhantomData }
+    }
+}
+
+#[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
+pub struct BoundTy {
+    pub var: ty::BoundVar,
+    pub kind: BoundTyKind,
+}
+
+impl Lift for BoundTy
+where
+    BoundTyKind: Lift>,
+{
+    type Lifted = BoundTy;
+
+    fn lift_to_interner(self, cx: U) -> Option {
+        Some(BoundTy { var: self.var, kind: self.kind.lift_to_interner(cx)? })
+    }
+}
+
+impl fmt::Debug for ty::BoundTy {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.kind {
+            ty::BoundTyKind::Anon => write!(f, "{:?}", self.var),
+            ty::BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"),
+        }
+    }
+}
+
+impl BoundTy {
+    pub fn var(self) -> ty::BoundVar {
+        self.var
+    }
+
+    pub fn assert_eq(self, var: BoundVariableKind) {
+        assert_eq!(self.kind, var.expect_ty())
+    }
+}
+
+pub type PlaceholderType = ty::Placeholder>;
+
+impl PlaceholderType {
+    pub fn universe(self) -> UniverseIndex {
+        self.universe
+    }
+
+    pub fn var(self) -> ty::BoundVar {
+        self.bound.var
+    }
+
+    pub fn with_updated_universe(self, ui: UniverseIndex) -> Self {
+        Self { universe: ui, bound: self.bound, _tcx: PhantomData }
+    }
+
+    pub fn new(ui: UniverseIndex, bound: BoundTy) -> Self {
+        Self { universe: ui, bound, _tcx: PhantomData }
+    }
+
+    pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self {
+        let bound = BoundTy { var, kind: BoundTyKind::Anon };
+        Self { universe: ui, bound, _tcx: PhantomData }
+    }
+}
+
+#[derive_where(Clone, Copy, PartialEq, Debug, Eq, Hash; I: Interner)]
+#[cfg_attr(
+    feature = "nightly",
+    derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext)
+)]
+pub struct BoundConst {
+    pub var: ty::BoundVar,
+    #[derive_where(skip(Debug))]
+    pub _tcx: PhantomData I>,
+}
+
+impl BoundConst {
+    pub fn var(self) -> ty::BoundVar {
+        self.var
+    }
+
+    pub fn assert_eq(self, var: BoundVariableKind) {
+        var.expect_const()
+    }
+
+    pub fn new(var: ty::BoundVar) -> Self {
+        Self { var, _tcx: PhantomData }
+    }
+}
+
+pub type PlaceholderConst = ty::Placeholder>;
+
+impl PlaceholderConst {
+    pub fn universe(self) -> UniverseIndex {
+        self.universe
+    }
+
+    pub fn var(self) -> ty::BoundVar {
+        self.bound.var
+    }
+
+    pub fn with_updated_universe(self, ui: UniverseIndex) -> Self {
+        Self { universe: ui, bound: self.bound, _tcx: PhantomData }
+    }
+
+    pub fn new(ui: UniverseIndex, bound: BoundConst) -> Self {
+        Self { universe: ui, bound, _tcx: PhantomData }
+    }
+
+    pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self {
+        let bound = BoundConst::new(var);
+        Self { universe: ui, bound, _tcx: PhantomData }
+    }
+
+    pub fn find_const_ty_from_env(self, env: I::ParamEnv) -> I::Ty {
+        let mut candidates = env.caller_bounds().iter().filter_map(|clause| {
+            // `ConstArgHasType` are never desugared to be higher ranked.
+            match clause.kind().skip_binder() {
+                ty::ClauseKind::ConstArgHasType(placeholder_ct, ty) => {
+                    assert!(!(placeholder_ct, ty).has_escaping_bound_vars());
+
+                    match placeholder_ct.kind() {
+                        ty::ConstKind::Placeholder(placeholder_ct) if placeholder_ct == self => {
+                            Some(ty)
+                        }
+                        _ => None,
+                    }
+                }
+                _ => None,
+            }
+        });
+
+        // N.B. it may be tempting to fix ICEs by making this function return
+        // `Option>` instead of `Ty<'tcx>`; however, this is generally
+        // considered to be a bandaid solution, since it hides more important
+        // underlying issues with how we construct generics and predicates of
+        // items. It's advised to fix the underlying issue rather than trying
+        // to modify this function.
+        let ty = candidates.next().unwrap_or_else(|| {
+            panic!("cannot find `{self:?}` in param-env: {env:#?}");
+        });
+        assert!(
+            candidates.next().is_none(),
+            "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}"
+        );
+        ty
+    }
+}
diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs
index 12d222258b0c..8e7414674c6e 100644
--- a/compiler/rustc_type_ir/src/canonical.rs
+++ b/compiler/rustc_type_ir/src/canonical.rs
@@ -108,7 +108,7 @@ pub enum CanonicalVarKind {
     Float,
 
     /// A "placeholder" that represents "any type".
-    PlaceholderTy(I::PlaceholderTy),
+    PlaceholderTy(ty::PlaceholderType),
 
     /// Region variable `'?R`.
     Region(UniverseIndex),
@@ -116,13 +116,13 @@ pub enum CanonicalVarKind {
     /// A "placeholder" that represents "any region". Created when you
     /// are solving a goal like `for<'a> T: Foo<'a>` to represent the
     /// bound region `'a`.
-    PlaceholderRegion(I::PlaceholderRegion),
+    PlaceholderRegion(ty::PlaceholderRegion),
 
     /// Some kind of const inference variable.
     Const(UniverseIndex),
 
     /// A "placeholder" that represents "any const".
-    PlaceholderConst(I::PlaceholderConst),
+    PlaceholderConst(ty::PlaceholderConst),
 }
 
 impl Eq for CanonicalVarKind {}
diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs
index 5b136ae03121..b215230ea443 100644
--- a/compiler/rustc_type_ir/src/const_kind.rs
+++ b/compiler/rustc_type_ir/src/const_kind.rs
@@ -26,10 +26,10 @@ pub enum ConstKind {
     Infer(InferConst),
 
     /// Bound const variable, used only when preparing a trait query.
-    Bound(BoundVarIndexKind, I::BoundConst),
+    Bound(BoundVarIndexKind, ty::BoundConst),
 
     /// A placeholder const - universally quantified higher-ranked const.
-    Placeholder(I::PlaceholderConst),
+    Placeholder(ty::PlaceholderConst),
 
     /// An unnormalized const item such as an anon const or assoc const or free const item.
     /// Right now anything other than anon consts does not actually work properly but this
diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs
index 502622aa50ca..eba4c7c6644a 100644
--- a/compiler/rustc_type_ir/src/error.rs
+++ b/compiler/rustc_type_ir/src/error.rs
@@ -33,7 +33,7 @@ pub enum TypeError {
     ArgCount,
 
     RegionsDoesNotOutlive(I::Region, I::Region),
-    RegionsInsufficientlyPolymorphic(I::BoundRegion, I::Region),
+    RegionsInsufficientlyPolymorphic(ty::BoundRegion, I::Region),
     RegionsPlaceholderMismatch,
 
     Sorts(ExpectedFound),
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 4323b1ca6469..89cb236d38c6 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -12,7 +12,7 @@ use crate::elaborate::Elaboratable;
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
 use crate::relate::Relate;
 use crate::solve::{AdtDestructorKind, SizedTraitKind};
-use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt};
+use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, CollectAndApply, Interner, UpcastFrom};
 
 pub trait Ty>:
@@ -42,9 +42,9 @@ pub trait Ty>:
 
     fn new_param(interner: I, param: I::ParamTy) -> Self;
 
-    fn new_placeholder(interner: I, param: I::PlaceholderTy) -> Self;
+    fn new_placeholder(interner: I, param: ty::PlaceholderType) -> Self;
 
-    fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundTy) -> Self;
+    fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self;
 
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
@@ -228,7 +228,7 @@ pub trait Region>:
     + Flags
     + Relate
 {
-    fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self;
+    fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundRegion) -> Self;
 
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
@@ -236,7 +236,7 @@ pub trait Region>:
 
     fn new_static(interner: I) -> Self;
 
-    fn new_placeholder(interner: I, var: I::PlaceholderRegion) -> Self;
+    fn new_placeholder(interner: I, var: ty::PlaceholderRegion) -> Self;
 
     fn is_bound(self) -> bool {
         matches!(self.kind(), ty::ReBound(..))
@@ -260,13 +260,13 @@ pub trait Const>:
 
     fn new_var(interner: I, var: ty::ConstVid) -> Self;
 
-    fn new_bound(interner: I, debruijn: ty::DebruijnIndex, bound_const: I::BoundConst) -> Self;
+    fn new_bound(interner: I, debruijn: ty::DebruijnIndex, bound_const: ty::BoundConst) -> Self;
 
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
     fn new_canonical_bound(interner: I, var: ty::BoundVar) -> Self;
 
-    fn new_placeholder(interner: I, param: I::PlaceholderConst) -> Self;
+    fn new_placeholder(interner: I, param: ty::PlaceholderConst) -> Self;
 
     fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst) -> Self;
 
@@ -543,68 +543,12 @@ pub trait Clauses>:
 {
 }
 
-/// Common capabilities of placeholder kinds
-pub trait PlaceholderLike: Copy + Debug + Hash + Eq {
-    fn universe(self) -> ty::UniverseIndex;
-    fn var(self) -> ty::BoundVar;
-
-    type Bound: BoundVarLike;
-    fn new(ui: ty::UniverseIndex, bound: Self::Bound) -> Self;
-    fn new_anon(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self;
-    fn with_updated_universe(self, ui: ty::UniverseIndex) -> Self;
-}
-
-pub trait PlaceholderConst: PlaceholderLike {
-    fn find_const_ty_from_env(self, env: I::ParamEnv) -> I::Ty;
-}
-impl PlaceholderConst for I::PlaceholderConst {
-    fn find_const_ty_from_env(self, env: I::ParamEnv) -> I::Ty {
-        let mut candidates = env.caller_bounds().iter().filter_map(|clause| {
-            // `ConstArgHasType` are never desugared to be higher ranked.
-            match clause.kind().skip_binder() {
-                ty::ClauseKind::ConstArgHasType(placeholder_ct, ty) => {
-                    assert!(!(placeholder_ct, ty).has_escaping_bound_vars());
-
-                    match placeholder_ct.kind() {
-                        ty::ConstKind::Placeholder(placeholder_ct) if placeholder_ct == self => {
-                            Some(ty)
-                        }
-                        _ => None,
-                    }
-                }
-                _ => None,
-            }
-        });
-
-        // N.B. it may be tempting to fix ICEs by making this function return
-        // `Option>` instead of `Ty<'tcx>`; however, this is generally
-        // considered to be a bandaid solution, since it hides more important
-        // underlying issues with how we construct generics and predicates of
-        // items. It's advised to fix the underlying issue rather than trying
-        // to modify this function.
-        let ty = candidates.next().unwrap_or_else(|| {
-            panic!("cannot find `{self:?}` in param-env: {env:#?}");
-        });
-        assert!(
-            candidates.next().is_none(),
-            "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}"
-        );
-        ty
-    }
-}
-
 pub trait IntoKind {
     type Kind;
 
     fn kind(self) -> Self::Kind;
 }
 
-pub trait BoundVarLike: Copy + Debug + Hash + Eq {
-    fn var(self) -> ty::BoundVar;
-
-    fn assert_eq(self, var: I::BoundVarKind);
-}
-
 pub trait ParamLike: Copy + Debug + Hash + Eq {
     fn index(self) -> u32;
 }
@@ -768,3 +712,7 @@ impl<'a, S: SliceLike> SliceLike for &'a S {
         (*self).as_slice()
     }
 }
+
+pub trait Symbol: Copy + Hash + PartialEq + Eq + Debug {
+    fn is_kw_underscore_lifetime(self) -> bool;
+}
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 0ab27a65c687..59ae6733fb84 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -61,8 +61,12 @@ pub trait Interner:
     type GenericArg: GenericArg;
     type Term: Term;
 
-    type BoundVarKinds: Copy + Debug + Hash + Eq + SliceLike + Default;
-    type BoundVarKind: Copy + Debug + Hash + Eq;
+    type BoundVarKinds: Copy
+        + Debug
+        + Hash
+        + Eq
+        + SliceLike>
+        + Default;
 
     type PredefinedOpaques: Copy
         + Debug
@@ -120,9 +124,7 @@ pub trait Interner:
     type Tys: Tys;
     type FnInputTys: Copy + Debug + Hash + Eq + SliceLike + TypeVisitable;
     type ParamTy: ParamLike;
-    type BoundTy: BoundVarLike;
-    type PlaceholderTy: PlaceholderLike;
-    type Symbol: Copy + Hash + PartialEq + Eq + Debug;
+    type Symbol: Symbol;
 
     // Things stored inside of tys
     type ErrorGuaranteed: Copy + Debug + Hash + Eq;
@@ -149,8 +151,6 @@ pub trait Interner:
     // Kinds of consts
     type Const: Const;
     type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
-    type BoundConst: BoundVarLike;
-    type PlaceholderConst: PlaceholderConst;
     type ValueConst: ValueConst;
     type ExprConst: ExprConst;
     type ValTree: ValTree;
@@ -160,8 +160,6 @@ pub trait Interner:
     type Region: Region;
     type EarlyParamRegion: ParamLike;
     type LateParamRegion: Copy + Debug + Hash + Eq;
-    type BoundRegion: BoundVarLike;
-    type PlaceholderRegion: PlaceholderLike;
 
     type RegionAssumptions: Copy
         + Debug
@@ -411,6 +409,8 @@ pub trait Interner:
         self,
         canonical_goal: CanonicalInput,
     ) -> (QueryResult, Self::Probe);
+
+    fn item_name(self, item_index: Self::DefId) -> Self::Symbol;
 }
 
 /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs
index c7dccea6adc1..300e5c0b4695 100644
--- a/compiler/rustc_type_ir/src/outlives.rs
+++ b/compiler/rustc_type_ir/src/outlives.rs
@@ -14,7 +14,7 @@ use crate::{self as ty, Interner};
 pub enum Component {
     Region(I::Region),
     Param(I::ParamTy),
-    Placeholder(I::PlaceholderTy),
+    Placeholder(ty::PlaceholderType),
     UnresolvedInferenceVariable(ty::InferTy),
 
     // Projections like `T::Foo` are tricky because a constraint like
diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs
index 9acf9ff8557e..e08c274f9f14 100644
--- a/compiler/rustc_type_ir/src/region_kind.rs
+++ b/compiler/rustc_type_ir/src/region_kind.rs
@@ -8,7 +8,7 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContex
 use rustc_type_ir_macros::GenericTypeVisitable;
 
 use self::RegionKind::*;
-use crate::{BoundVarIndexKind, Interner};
+use crate::{BoundRegion, BoundVarIndexKind, Interner, PlaceholderRegion};
 
 rustc_index::newtype_index! {
     /// A **region** **v**ariable **ID**.
@@ -149,7 +149,7 @@ pub enum RegionKind {
     /// Bound regions inside of types **must not** be erased, as they impact trait
     /// selection and the `TypeId` of that type. `for<'a> fn(&'a ())` and
     /// `fn(&'static ())` are different types and have to be treated as such.
-    ReBound(BoundVarIndexKind, I::BoundRegion),
+    ReBound(BoundVarIndexKind, BoundRegion),
 
     /// Late-bound function parameters are represented using a `ReBound`. When
     /// inside of a function, we convert these bound variables to placeholder
@@ -170,7 +170,7 @@ pub enum RegionKind {
     /// Should not exist outside of type inference.
     ///
     /// Used when instantiating a `forall` binder via `infcx.enter_forall`.
-    RePlaceholder(I::PlaceholderRegion),
+    RePlaceholder(PlaceholderRegion),
 
     /// Erased region, used by trait selection, in MIR and during codegen.
     ReErased,
@@ -214,9 +214,9 @@ impl fmt::Debug for RegionKind {
 impl HashStable for RegionKind
 where
     I::EarlyParamRegion: HashStable,
-    I::BoundRegion: HashStable,
     I::LateParamRegion: HashStable,
-    I::PlaceholderRegion: HashStable,
+    I::DefId: HashStable,
+    I::Symbol: HashStable,
 {
     #[inline]
     fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 498797bef653..7cb71387e868 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -233,7 +233,7 @@ pub enum TyKind {
     ///
     /// [1]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
     /// [2]: https://rustc-dev-guide.rust-lang.org/traits/canonical-queries.html
-    Bound(BoundVarIndexKind, I::BoundTy),
+    Bound(BoundVarIndexKind, ty::BoundTy),
 
     /// A placeholder type, used during higher ranked subtyping to instantiate
     /// bound variables.
@@ -243,7 +243,7 @@ pub enum TyKind {
     /// to the bound variable's index from the binder from which it was instantiated),
     /// and `U` is the universe index in which it is instantiated, or totally omitted
     /// if the universe index is zero.
-    Placeholder(I::PlaceholderTy),
+    Placeholder(ty::PlaceholderType),
 
     /// A type variable used during type checking.
     ///
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 9d6038367419..1c9154238657 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -3178,7 +3178,7 @@ fn clean_assoc_item_constraint<'tcx>(
 }
 
 fn clean_bound_vars<'tcx>(
-    bound_vars: &ty::List,
+    bound_vars: &ty::List>,
     cx: &mut DocContext<'tcx>,
 ) -> Vec {
     bound_vars
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index 2926a329ce16..0f11df98fc17 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -782,15 +782,15 @@ pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
     }
 }
 
-pub fn for_each_top_level_late_bound_region(
-    ty: Ty<'_>,
-    f: impl FnMut(BoundRegion) -> ControlFlow,
+pub fn for_each_top_level_late_bound_region<'cx, B>(
+    ty: Ty<'cx>,
+    f: impl FnMut(BoundRegion<'cx>) -> ControlFlow,
 ) -> ControlFlow {
     struct V {
         index: u32,
         f: F,
     }
-    impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow> TypeVisitor> for V {
+    impl<'tcx, B, F: FnMut(BoundRegion<'tcx>) -> ControlFlow> TypeVisitor> for V {
         type Result = ControlFlow;
         fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
             if let RegionKind::ReBound(BoundVarIndexKind::Bound(idx), bound) = r.kind()

From a333f6f93c5fe34d59e24af1269b528c6b608596 Mon Sep 17 00:00:00 2001
From: "Andrew V. Teylu" 
Date: Thu, 29 Jan 2026 12:04:30 +0000
Subject: [PATCH 348/583] Fix missing syntax context in lifetime hygiene debug
 output

`-Zunpretty=expanded,hygiene` was not printing the syntax context for
lifetimes. For example, two macro-generated lifetimes `'a` with different
hygiene would both print as `/* 2538 */` instead of `/* 2538#0 */` and
`/* 2538#1 */`, making it impossible to distinguish them.

This was fixed by changing `print_lifetime` to call `ann_post()` with
the full `Ident`, matching how regular identifiers are handled in
`print_ident`.
---
 compiler/rustc_ast_pretty/src/pprust/state.rs |  3 +-
 tests/ui/hygiene/unpretty-debug-lifetimes.rs  | 18 +++++++++++
 .../hygiene/unpretty-debug-lifetimes.stdout   | 31 +++++++++++++++++++
 3 files changed, 51 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/hygiene/unpretty-debug-lifetimes.rs
 create mode 100644 tests/ui/hygiene/unpretty-debug-lifetimes.stdout

diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index e50e31c226fd..c8874ed99dca 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1961,7 +1961,8 @@ impl<'a> State<'a> {
     }
 
     fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
-        self.print_name(lifetime.ident.name)
+        self.word(lifetime.ident.name.to_string());
+        self.ann_post(lifetime.ident)
     }
 
     fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) {
diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.rs b/tests/ui/hygiene/unpretty-debug-lifetimes.rs
new file mode 100644
index 000000000000..ee8be21b60d0
--- /dev/null
+++ b/tests/ui/hygiene/unpretty-debug-lifetimes.rs
@@ -0,0 +1,18 @@
+//@ check-pass
+//@ compile-flags: -Zunpretty=expanded,hygiene
+
+// Regression test for lifetime hygiene annotations in -Zunpretty=expanded,hygiene
+// Previously, lifetimes were missing the #N syntax context suffix.
+
+// Don't break whenever Symbol numbering changes
+//@ normalize-stdout: "\d+#" -> "0#"
+
+#![feature(decl_macro)]
+#![feature(no_core)]
+#![no_core]
+
+macro lifetime_hygiene($f:ident<$a:lifetime>) {
+    fn $f<$a, 'a>() {}
+}
+
+lifetime_hygiene!(f<'a>);
diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout
new file mode 100644
index 000000000000..28a5c70a02d7
--- /dev/null
+++ b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout
@@ -0,0 +1,31 @@
+//@ check-pass
+//@ compile-flags: -Zunpretty=expanded,hygiene
+
+// Regression test for lifetime hygiene annotations in -Zunpretty=expanded,hygiene
+// Previously, lifetimes were missing the #N syntax context suffix.
+
+// Don't break whenever Symbol numbering changes
+//@ normalize-stdout: "\d+#" -> "0#"
+
+#![feature /* 0#0 */(decl_macro)]
+#![feature /* 0#0 */(no_core)]
+#![no_core /* 0#0 */]
+
+macro lifetime_hygiene
+    /*
+    0#0
+    */ {
+    ($f:ident<$a:lifetime>) => { fn $f<$a, 'a>() {} }
+}
+fn f /* 0#0 */<'a /* 0#0 */, 'a /* 0#1 */>() {}
+
+
+/*
+Expansions:
+crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root
+crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "lifetime_hygiene")
+
+SyntaxContexts:
+#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque)
+#1: parent: #0, outer_mark: (crate0::{{expn1}}, Opaque)
+*/

From 21c99d3ad9ca533a2c4fe2a6bff19b7f74fd07f5 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Thu, 29 Jan 2026 18:00:33 +1100
Subject: [PATCH 349/583] Fix some typos of "similarity"

---
 compiler/rustc_arena/src/lib.rs | 4 ++--
 typos.toml                      | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index 5e81ec28ee35..cc18cf38ef4c 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -208,7 +208,7 @@ impl TypedArena {
         &self,
         iter: impl IntoIterator>,
     ) -> Result<&mut [T], E> {
-        // Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason
+        // Despite the similarity with `DroplessArena`, we cannot reuse their fast case. The reason
         // is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
         // reference to `self` and adding elements to the arena during iteration.
         //
@@ -584,7 +584,7 @@ impl DroplessArena {
         &self,
         iter: impl IntoIterator>,
     ) -> Result<&mut [T], E> {
-        // Despite the similarlty with `alloc_from_iter`, we cannot reuse their fast case, as we
+        // Despite the similarity with `alloc_from_iter`, we cannot reuse their fast case, as we
         // cannot know the minimum length of the iterator in this case.
         assert!(size_of::() != 0);
 
diff --git a/typos.toml b/typos.toml
index 25083174cb8f..e486a7c1722c 100644
--- a/typos.toml
+++ b/typos.toml
@@ -46,6 +46,7 @@ unstalled = "unstalled" # short for un-stalled
 #
 # tidy-alphabetical-start
 definitinon = "definition"
+similarlty = "similarity"
 # tidy-alphabetical-end
 
 [default.extend-identifiers]

From 363d3ac4382e3f46a7472bed796aebc34f76deab Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Thu, 29 Jan 2026 18:05:40 +1100
Subject: [PATCH 350/583] Document a safety condition for
 `TypedArena::alloc_raw_slice`

---
 compiler/rustc_arena/src/lib.rs | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index cc18cf38ef4c..524baf5b07fe 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -172,8 +172,22 @@ impl TypedArena {
         available_bytes >= additional_bytes
     }
 
+    /// Allocates storage for `len >= 1` values in this arena, and returns a
+    /// raw pointer to the first value's storage.
+    ///
+    /// # Safety
+    ///
+    /// Caller must initialize each of the `len` slots to a droppable value
+    /// before the arena is dropped.
+    ///
+    /// In practice, this typically means that the caller must be able to
+    /// raw-copy `len` already-initialized values into the slice without any
+    /// possibility of panicking.
+    ///
+    /// FIXME(Zalathar): This is *very* fragile; perhaps we need a different
+    /// approach to arena-allocating slices of droppable values.
     #[inline]
-    fn alloc_raw_slice(&self, len: usize) -> *mut T {
+    unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T {
         assert!(size_of::() != 0);
         assert!(len != 0);
 
@@ -229,9 +243,15 @@ impl TypedArena {
         }
         // Move the content to the arena by copying and then forgetting it.
         let len = vec.len();
-        let start_ptr = self.alloc_raw_slice(len);
+
+        // SAFETY: After allocating raw storage for exactly `len` values, we
+        // must fully initialize the storage without panicking, and we must
+        // also prevent the stale values in the vec from being dropped.
         Ok(unsafe {
+            let start_ptr = self.alloc_raw_slice(len);
+            // Initialize the newly-allocated storage without panicking.
             vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
+            // Prevent the stale values in the vec from being dropped.
             vec.set_len(0);
             slice::from_raw_parts_mut(start_ptr, len)
         })

From db74048b9a8bb6cc5092da218552a464cd1dcb52 Mon Sep 17 00:00:00 2001
From: Zalathar 
Date: Fri, 23 Jan 2026 17:02:30 +1100
Subject: [PATCH 351/583] Rename, clarify, and document code for "erasing"
 query values

---
 compiler/rustc_middle/src/query/erase.rs    | 288 ++++++++++++--------
 compiler/rustc_middle/src/query/inner.rs    |  20 +-
 compiler/rustc_middle/src/query/mod.rs      |   1 -
 compiler/rustc_middle/src/query/plumbing.rs |  27 +-
 compiler/rustc_query_impl/src/lib.rs        |   1 -
 compiler/rustc_query_impl/src/plumbing.rs   |  18 +-
 6 files changed, 206 insertions(+), 149 deletions(-)

diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 940cc30c17e6..090bcaf45c73 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -1,3 +1,10 @@
+//! To improve compile times and code size for the compiler itself, query
+//! values are "erased" in some contexts (e.g. inside in-memory cache types),
+//! to reduce the number of generic instantiations created during codegen.
+//!
+//! See  for some bootstrap-time
+//! and performance benchmarks.
+
 use std::ffi::OsStr;
 use std::intrinsics::transmute_unchecked;
 use std::mem::MaybeUninit;
@@ -14,138 +21,169 @@ use crate::ty::adjustment::CoerceUnsizedInfo;
 use crate::ty::{self, Ty, TyCtxt};
 use crate::{mir, traits};
 
+/// Internal implementation detail of [`Erased`].
 #[derive(Copy, Clone)]
-pub struct Erased {
-    // We use `MaybeUninit` here so we can store any value
-    // in `data` since we aren't actually storing a `T`.
-    data: MaybeUninit,
+pub struct ErasedData {
+    /// We use `MaybeUninit` here to make sure it's legal to store a transmuted
+    /// value that isn't actually of type `Storage`.
+    data: MaybeUninit,
 }
 
-pub trait EraseType: Copy {
-    type Result: Copy;
+/// Trait for types that can be erased into [`Erased`].
+///
+/// Erasing and unerasing values is performed by [`erase_val`] and [`restore_val`].
+///
+/// FIXME: This whole trait could potentially be replaced by `T: Copy` and the
+/// storage type `[u8; size_of::()]` when support for that is more mature.
+pub trait Erasable: Copy {
+    /// Storage type to used for erased values of this type.
+    /// Should be `[u8; N]`, where N is equal to `size_of::`.
+    ///
+    /// [`ErasedData`] wraps this storage type in `MaybeUninit` to ensure that
+    /// transmutes to/from erased storage are well-defined.
+    type Storage: Copy;
 }
 
-// Allow `type_alias_bounds` since compilation will fail without `EraseType`.
-#[allow(type_alias_bounds)]
-pub type Erase = Erased;
+/// A value of `T` that has been "erased" into some opaque storage type.
+///
+/// This is helpful for reducing the number of concrete instantiations needed
+/// during codegen when building the compiler.
+///
+/// Using an opaque type alias allows the type checker to enforce that
+/// `Erased` and `Erased` are still distinct types, while allowing
+/// monomorphization to see that they might actually use the same storage type.
+pub type Erased = ErasedData;
 
+/// Erases a value of type `T` into `Erased`.
+///
+/// `Erased` and `Erased` are type-checked as distinct types, but codegen
+/// can see whether they actually have the same storage type.
+///
+/// FIXME: This might have soundness issues with erasable types that don't
+/// implement the same auto-traits as `[u8; _]`; see
+/// 
 #[inline(always)]
-#[define_opaque(Erase)]
-pub fn erase(src: T) -> Erase {
+#[define_opaque(Erased)]
+pub fn erase_val(value: T) -> Erased {
     // Ensure the sizes match
     const {
-        if size_of::() != size_of::() {
-            panic!("size of T must match erased type T::Result")
+        if size_of::() != size_of::() {
+            panic!("size of T must match erased type ::Storage")
         }
     };
 
-    Erased::<::Result> {
+    ErasedData::<::Storage> {
         // `transmute_unchecked` is needed here because it does not have `transmute`'s size check
-        // (and thus allows to transmute between `T` and `MaybeUninit`) (we do the size
+        // (and thus allows to transmute between `T` and `MaybeUninit`) (we do the size
         // check ourselves in the `const` block above).
         //
         // `transmute_copy` is also commonly used for this (and it would work here since
-        // `EraseType: Copy`), but `transmute_unchecked` better explains the intent.
+        // `Erasable: Copy`), but `transmute_unchecked` better explains the intent.
         //
         // SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
-        data: unsafe { transmute_unchecked::>(src) },
+        data: unsafe { transmute_unchecked::>(value) },
     }
 }
 
-/// Restores an erased value.
+/// Restores an erased value to its real type.
+///
+/// This relies on the fact that `Erased` and `Erased` are type-checked
+/// as distinct types, even if they use the same storage type.
 #[inline(always)]
-#[define_opaque(Erase)]
-pub fn restore(value: Erase) -> T {
-    let value: Erased<::Result> = value;
-    // See comment in `erase` for why we use `transmute_unchecked`.
+#[define_opaque(Erased)]
+pub fn restore_val(erased_value: Erased) -> T {
+    let ErasedData { data }: ErasedData<::Storage> = erased_value;
+    // See comment in `erase_val` for why we use `transmute_unchecked`.
     //
-    // SAFETY: Due to the use of impl Trait in `Erase` the only way to safely create an instance
-    // of `Erase` is to call `erase`, so we know that `value.data` is a valid instance of `T` of
-    // the right size.
-    unsafe { transmute_unchecked::, T>(value.data) }
+    // SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance
+    // of `Erased` is to call `erase_val`, so we know that `erased_value.data` is a valid instance
+    // of `T` of the right size.
+    unsafe { transmute_unchecked::, T>(data) }
 }
 
-impl EraseType for &'_ T {
-    type Result = [u8; size_of::<&'static ()>()];
+// FIXME(#151565): Using `T: ?Sized` here should let us remove the separate
+// impls for fat reference types.
+impl Erasable for &'_ T {
+    type Storage = [u8; size_of::<&'static ()>()];
 }
 
-impl EraseType for &'_ [T] {
-    type Result = [u8; size_of::<&'static [()]>()];
+impl Erasable for &'_ [T] {
+    type Storage = [u8; size_of::<&'static [()]>()];
 }
 
-impl EraseType for &'_ OsStr {
-    type Result = [u8; size_of::<&'static OsStr>()];
+impl Erasable for &'_ OsStr {
+    type Storage = [u8; size_of::<&'static OsStr>()];
 }
 
-impl EraseType for &'_ ty::List {
-    type Result = [u8; size_of::<&'static ty::List<()>>()];
+impl Erasable for &'_ ty::List {
+    type Storage = [u8; size_of::<&'static ty::List<()>>()];
 }
 
-impl EraseType for &'_ ty::ListWithCachedTypeInfo {
-    type Result = [u8; size_of::<&'static ty::ListWithCachedTypeInfo<()>>()];
+impl Erasable for &'_ ty::ListWithCachedTypeInfo {
+    type Storage = [u8; size_of::<&'static ty::ListWithCachedTypeInfo<()>>()];
 }
 
-impl EraseType for &'_ rustc_index::IndexSlice {
-    type Result = [u8; size_of::<&'static rustc_index::IndexSlice>()];
+impl Erasable for &'_ rustc_index::IndexSlice {
+    type Storage = [u8; size_of::<&'static rustc_index::IndexSlice>()];
 }
 
-impl EraseType for Result<&'_ T, traits::query::NoSolution> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Result<&'_ T, traits::query::NoSolution> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Result<&'_ [T], traits::query::NoSolution> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Result<&'_ [T], traits::query::NoSolution> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Result<&'_ [T], rustc_errors::ErrorGuaranteed> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Result<&'_ [T], rustc_errors::ErrorGuaranteed> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Result<&'_ T, traits::CodegenObligationError> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Result<&'_ T, traits::CodegenObligationError> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> {
-    type Result = [u8; size_of::>>()];
+impl Erasable for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> {
+    type Storage = [u8; size_of::>>()];
 }
 
-impl EraseType for Result<(&'_ T, crate::thir::ExprId), rustc_errors::ErrorGuaranteed> {
-    type Result = [u8; size_of::<
+impl Erasable for Result<(&'_ T, crate::thir::ExprId), rustc_errors::ErrorGuaranteed> {
+    type Storage = [u8; size_of::<
         Result<(&'static (), crate::thir::ExprId), rustc_errors::ErrorGuaranteed>,
     >()];
 }
 
-impl EraseType for Result>, rustc_errors::ErrorGuaranteed> {
-    type Result =
+impl Erasable for Result>, rustc_errors::ErrorGuaranteed> {
+    type Storage =
         [u8; size_of::>, rustc_errors::ErrorGuaranteed>>()];
 }
 
-impl EraseType for Result {
-    type Result = [u8; size_of::>()];
+impl Erasable for Result {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType
+impl Erasable
     for Result>>, rustc_errors::ErrorGuaranteed>
 {
-    type Result = [u8; size_of::<
+    type Storage = [u8; size_of::<
         Result>>, rustc_errors::ErrorGuaranteed>,
     >()];
 }
 
-impl EraseType for Result, traits::query::NoSolution> {
-    type Result = [u8; size_of::, traits::query::NoSolution>>()];
+impl Erasable for Result, traits::query::NoSolution> {
+    type Storage = [u8; size_of::, traits::query::NoSolution>>()];
 }
 
-impl EraseType for Result> {
-    type Result = [u8; size_of::>>()];
+impl Erasable for Result> {
+    type Storage = [u8; size_of::>>()];
 }
 
-impl EraseType for Result>, &ty::layout::LayoutError<'_>> {
-    type Result = [u8; size_of::<
+impl Erasable for Result>, &ty::layout::LayoutError<'_>> {
+    type Storage = [u8; size_of::<
         Result<
             rustc_abi::TyAndLayout<'static, Ty<'static>>,
             &'static ty::layout::LayoutError<'static>,
@@ -153,35 +191,36 @@ impl EraseType for Result>, &ty::layout::Layou
     >()];
 }
 
-impl EraseType for Result, mir::interpret::ErrorHandled> {
-    type Result = [u8; size_of::, mir::interpret::ErrorHandled>>()];
+impl Erasable for Result, mir::interpret::ErrorHandled> {
+    type Storage =
+        [u8; size_of::, mir::interpret::ErrorHandled>>()];
 }
 
-impl EraseType for Result {
-    type Result = [u8; size_of::>()];
+impl Erasable for Result {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Option<(mir::ConstValue, Ty<'_>)> {
-    type Result = [u8; size_of::)>>()];
+impl Erasable for Option<(mir::ConstValue, Ty<'_>)> {
+    type Storage = [u8; size_of::)>>()];
 }
 
-impl EraseType for EvalToValTreeResult<'_> {
-    type Result = [u8; size_of::>()];
+impl Erasable for EvalToValTreeResult<'_> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Result<&'_ ty::List>, ty::util::AlwaysRequiresDrop> {
-    type Result =
+impl Erasable for Result<&'_ ty::List>, ty::util::AlwaysRequiresDrop> {
+    type Storage =
         [u8; size_of::>, ty::util::AlwaysRequiresDrop>>()];
 }
 
-impl EraseType for Result>, CyclePlaceholder> {
-    type Result = [u8; size_of::>, CyclePlaceholder>>()];
+impl Erasable for Result>, CyclePlaceholder> {
+    type Storage = [u8; size_of::>, CyclePlaceholder>>()];
 }
 
-impl EraseType
+impl Erasable
     for Result<(&'_ [Spanned>], &'_ [Spanned>]), NormalizationErrorInMono>
 {
-    type Result = [u8; size_of::<
+    type Storage = [u8; size_of::<
         Result<
             (&'static [Spanned>], &'static [Spanned>]),
             NormalizationErrorInMono,
@@ -189,86 +228,89 @@ impl EraseType
     >()];
 }
 
-impl EraseType for Result<&'_ TokenStream, ()> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Result<&'_ TokenStream, ()> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Option<&'_ T> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Option<&'_ T> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Option<&'_ [T]> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Option<&'_ [T]> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Option<&'_ OsStr> {
-    type Result = [u8; size_of::>()];
+impl Erasable for Option<&'_ OsStr> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Option> {
-    type Result = [u8; size_of::>>()];
+impl Erasable for Option> {
+    type Storage = [u8; size_of::>>()];
 }
 
-impl EraseType for ty::ImplTraitHeader<'_> {
-    type Result = [u8; size_of::>()];
+impl Erasable for ty::ImplTraitHeader<'_> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for Option>> {
-    type Result = [u8; size_of::>>>()];
+impl Erasable for Option>> {
+    type Storage = [u8; size_of::>>>()];
 }
 
-impl EraseType for rustc_hir::MaybeOwner<'_> {
-    type Result = [u8; size_of::>()];
+impl Erasable for rustc_hir::MaybeOwner<'_> {
+    type Storage = [u8; size_of::>()];
 }
 
-impl EraseType for ty::EarlyBinder<'_, T> {
-    type Result = T::Result;
+impl Erasable for ty::EarlyBinder<'_, T> {
+    type Storage = T::Storage;
 }
 
-impl EraseType for ty::Binder<'_, ty::FnSig<'_>> {
-    type Result = [u8; size_of::>>()];
+impl Erasable for ty::Binder<'_, ty::FnSig<'_>> {
+    type Storage = [u8; size_of::>>()];
 }
 
-impl EraseType for ty::Binder<'_, ty::CoroutineWitnessTypes>> {
-    type Result =
+impl Erasable for ty::Binder<'_, ty::CoroutineWitnessTypes>> {
+    type Storage =
         [u8; size_of::>>>()];
 }
 
-impl EraseType for ty::Binder<'_, &'_ ty::List>> {
-    type Result = [u8; size_of::>>>()];
+impl Erasable for ty::Binder<'_, &'_ ty::List>> {
+    type Storage = [u8; size_of::>>>()];
 }
 
-impl EraseType for (&'_ T0, &'_ T1) {
-    type Result = [u8; size_of::<(&'static (), &'static ())>()];
+impl Erasable for (&'_ T0, &'_ T1) {
+    type Storage = [u8; size_of::<(&'static (), &'static ())>()];
 }
 
-impl EraseType for (solve::QueryResult<'_>, &'_ T0) {
-    type Result = [u8; size_of::<(solve::QueryResult<'static>, &'static ())>()];
+impl Erasable for (solve::QueryResult<'_>, &'_ T0) {
+    type Storage = [u8; size_of::<(solve::QueryResult<'static>, &'static ())>()];
 }
 
-impl EraseType for (&'_ T0, &'_ [T1]) {
-    type Result = [u8; size_of::<(&'static (), &'static [()])>()];
+impl Erasable for (&'_ T0, &'_ [T1]) {
+    type Storage = [u8; size_of::<(&'static (), &'static [()])>()];
 }
 
-impl EraseType for (&'_ [T0], &'_ [T1]) {
-    type Result = [u8; size_of::<(&'static [()], &'static [()])>()];
+impl Erasable for (&'_ [T0], &'_ [T1]) {
+    type Storage = [u8; size_of::<(&'static [()], &'static [()])>()];
 }
 
-impl EraseType for (&'_ T0, Result<(), ErrorGuaranteed>) {
-    type Result = [u8; size_of::<(&'static (), Result<(), ErrorGuaranteed>)>()];
+impl Erasable for (&'_ T0, Result<(), ErrorGuaranteed>) {
+    type Storage = [u8; size_of::<(&'static (), Result<(), ErrorGuaranteed>)>()];
 }
 
-macro_rules! trivial {
+macro_rules! impl_erasable_for_simple_types {
     ($($ty:ty),+ $(,)?) => {
         $(
-            impl EraseType for $ty {
-                type Result = [u8; size_of::<$ty>()];
+            impl Erasable for $ty {
+                type Storage = [u8; size_of::<$ty>()];
             }
         )*
     }
 }
 
-trivial! {
+// For concrete types with no lifetimes, the erased storage for `Foo` is
+// `[u8; size_of::()]`.
+impl_erasable_for_simple_types! {
+    // FIXME(#151565): Add `tidy-alphabetical-{start,end}` and sort this.
     (),
     bool,
     Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>,
@@ -378,17 +420,23 @@ trivial! {
     usize,
 }
 
-macro_rules! tcx_lifetime {
+macro_rules! impl_erasable_for_single_lifetime_types {
     ($($($fake_path:ident)::+),+ $(,)?) => {
         $(
-            impl<'tcx> EraseType for $($fake_path)::+<'tcx> {
-                type Result = [u8; size_of::<$($fake_path)::+<'static>>()];
+            impl<'tcx> Erasable for $($fake_path)::+<'tcx> {
+                type Storage = [u8; size_of::<$($fake_path)::+<'static>>()];
             }
         )*
     }
 }
 
-tcx_lifetime! {
+// For types containing a single lifetime and no other generics, e.g.
+// `Foo<'tcx>`, the erased storage is `[u8; size_of::>()]`.
+//
+// FIXME(#151565): Some of the hand-written impls above that only use one
+// lifetime can probably be migrated here.
+impl_erasable_for_single_lifetime_types! {
+    // FIXME(#151565): Add `tidy-alphabetical-{start,end}` and sort this.
     rustc_middle::middle::exported_symbols::ExportedSymbol,
     rustc_middle::mir::Const,
     rustc_middle::mir::DestructuredConstant,
diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs
index ee828ae55f7a..e41d23f82f12 100644
--- a/compiler/rustc_middle/src/query/inner.rs
+++ b/compiler/rustc_middle/src/query/inner.rs
@@ -11,7 +11,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
 
 use crate::dep_graph;
 use crate::query::IntoQueryParam;
-use crate::query::erase::{self, Erase, EraseType};
+use crate::query::erase::{self, Erasable, Erased};
 use crate::ty::TyCtxt;
 
 /// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)`
@@ -63,15 +63,15 @@ pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>(
     check_cache: bool,
 ) -> Result<(), ErrorGuaranteed>
 where
-    Cache: QueryCache>>,
-    Result: EraseType,
+    Cache: QueryCache>>,
+    Result: Erasable,
 {
     let key = key.into_query_param();
     if let Some(res) = try_get_cached(tcx, query_cache, &key) {
-        erase::restore(res).map(drop)
+        erase::restore_val(res).map(drop)
     } else {
         execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache })
-            .map(erase::restore)
+            .map(erase::restore_val)
             .map(|res| res.map(drop))
             // Either we actually executed the query, which means we got a full `Result`,
             // or we can just assume the query succeeded, because it was green in the
@@ -90,17 +90,17 @@ pub(crate) fn query_feed<'tcx, Cache, Value>(
     hasher: Option, &Value) -> Fingerprint>,
     cache: &Cache,
     key: Cache::Key,
-    erased: Erase,
+    erased: Erased,
 ) where
-    Cache: QueryCache>,
+    Cache: QueryCache>,
     Cache::Key: DepNodeParams>,
-    Value: EraseType + Debug,
+    Value: Erasable + Debug,
 {
-    let value = erase::restore::(erased);
+    let value = erase::restore_val::(erased);
 
     match try_get_cached(tcx, cache, &key) {
         Some(old) => {
-            let old = erase::restore::(old);
+            let old = erase::restore_val::(old);
             if let Some(hasher) = hasher {
                 let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx
                     .with_stable_hashing_context(|mut hcx| {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 901a023c4f30..ae94c154015b 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -121,7 +121,6 @@ use crate::mir::interpret::{
 use crate::mir::mono::{
     CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono,
 };
-use crate::query::erase::{Erase, erase, restore};
 use crate::query::plumbing::CyclePlaceholder;
 use crate::traits::query::{
     CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal,
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index 0e536352563f..7b85dac41aef 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -266,6 +266,7 @@ macro_rules! define_callbacks {
         pub mod queries {
             $(pub mod $name {
                 use super::super::*;
+                use $crate::query::erase::{self, Erased};
 
                 pub type Key<'tcx> = $($K)*;
                 pub type Value<'tcx> = $V;
@@ -288,29 +289,33 @@ macro_rules! define_callbacks {
                 #[inline(always)]
                 pub fn provided_to_erased<'tcx>(
                     _tcx: TyCtxt<'tcx>,
-                    value: ProvidedValue<'tcx>,
-                ) -> Erase> {
-                    erase(query_if_arena!([$($modifiers)*]
+                    provided_value: ProvidedValue<'tcx>,
+                ) -> Erased> {
+                    // Store the provided value in an arena and get a reference
+                    // to it, for queries with `arena_cache`.
+                    let value: Value<'tcx> = query_if_arena!([$($modifiers)*]
                         {
                             use $crate::query::arena_cached::ArenaCached;
 
                             if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() {
                                 <$V as ArenaCached>::alloc_in_arena(
                                     |v| _tcx.query_system.arenas.$name.alloc(v),
-                                    value,
+                                    provided_value,
                                 )
                             } else {
                                 <$V as ArenaCached>::alloc_in_arena(
                                     |v| _tcx.arena.dropless.alloc(v),
-                                    value,
+                                    provided_value,
                                 )
                             }
                         }
-                        (value)
-                    ))
+                        // Otherwise, the provided value is the value.
+                        (provided_value)
+                    );
+                    erase::erase_val(value)
                 }
 
-                pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>;
+                pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>;
 
                 // Ensure that keys grow no larger than 88 bytes by accident.
                 // Increase this limit if necessary, but do try to keep the size low if possible
@@ -411,7 +416,9 @@ macro_rules! define_callbacks {
             #[inline(always)]
             pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V
             {
-                restore::<$V>(crate::query::inner::query_get_at(
+                use $crate::query::{erase, inner};
+
+                erase::restore_val::<$V>(inner::query_get_at(
                     self.tcx,
                     self.tcx.query_system.fns.engine.$name,
                     &self.tcx.query_system.caches.$name,
@@ -480,7 +487,7 @@ macro_rules! define_callbacks {
                 Span,
                 queries::$name::Key<'tcx>,
                 QueryMode,
-            ) -> Option>,)*
+            ) -> Option<$crate::query::erase::Erased<$V>>,)*
         }
     };
 }
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index d6310b62b275..382b8750a1ce 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -10,7 +10,6 @@ use rustc_data_structures::stable_hasher::HashStable;
 use rustc_data_structures::sync::AtomicU64;
 use rustc_middle::arena::Arena;
 use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNodeIndex};
-use rustc_middle::query::erase::{Erase, erase, restore};
 use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache};
 use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable};
 use rustc_middle::query::{
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 0223981fd55d..c71352c3fd20 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -281,7 +281,10 @@ macro_rules! feedable {
 
 macro_rules! hash_result {
     ([][$V:ty]) => {{
-        Some(|hcx, result| dep_graph::hash_result(hcx, &restore::<$V>(*result)))
+        Some(|hcx, result| {
+            let result = ::rustc_middle::query::erase::restore_val::<$V>(*result);
+            ::rustc_query_system::dep_graph::hash_result(hcx, &result)
+        })
     }};
     ([(no_hash) $($rest:tt)*][$V:ty]) => {{
         None
@@ -597,6 +600,7 @@ macro_rules! define_queries {
         pub(crate) mod query_impl { $(pub(crate) mod $name {
             use super::super::*;
             use std::marker::PhantomData;
+            use ::rustc_middle::query::erase::{self, Erased};
 
             pub(crate) mod get_query_incr {
                 use super::*;
@@ -609,7 +613,7 @@ macro_rules! define_queries {
                     span: Span,
                     key: queries::$name::Key<'tcx>,
                     mode: QueryMode,
-                ) -> Option>> {
+                ) -> Option>> {
                     #[cfg(debug_assertions)]
                     let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
                     get_query_incr(
@@ -631,7 +635,7 @@ macro_rules! define_queries {
                     span: Span,
                     key: queries::$name::Key<'tcx>,
                     __mode: QueryMode,
-                ) -> Option>> {
+                ) -> Option>> {
                     Some(get_query_non_incr(
                         QueryType::query_dispatcher(tcx),
                         QueryCtxt::new(tcx),
@@ -652,7 +656,7 @@ macro_rules! define_queries {
                     query_state: std::mem::offset_of!(QueryStates<'tcx>, $name),
                     query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name),
                     cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key),
-                    execute_query: |tcx, key| erase(tcx.$name(key)),
+                    execute_query: |tcx, key| erase::erase_val(tcx.$name(key)),
                     compute: |tcx, key| {
                         #[cfg(debug_assertions)]
                         let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
@@ -690,7 +694,7 @@ macro_rules! define_queries {
                     }),
                     value_from_cycle_error: |tcx, cycle, guar| {
                         let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar);
-                        erase(result)
+                        erase::erase_val(result)
                     },
                     loadable_from_disk: |_tcx, _key, _index| {
                         should_ever_cache_on_disk!([$($modifiers)*] {
@@ -701,7 +705,7 @@ macro_rules! define_queries {
                         })
                     },
                     hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]),
-                    format_value: |value| format!("{:?}", restore::>(*value)),
+                    format_value: |value| format!("{:?}", erase::restore_val::>(*value)),
                 }
             }
 
@@ -731,7 +735,7 @@ macro_rules! define_queries {
 
                 #[inline(always)]
                 fn restore_val(value: ::Value) -> Self::UnerasedValue {
-                    restore::>(value)
+                    erase::restore_val::>(value)
                 }
             }
 

From d3bffef0391f299f9ff96628f88f4e9a40b8a07c Mon Sep 17 00:00:00 2001
From: Tshepang Mbambo 
Date: Thu, 29 Jan 2026 15:25:15 +0200
Subject: [PATCH 352/583] bump to latest mdbook

https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md
---
 src/doc/rustc-dev-guide/.github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml
index f2f2f7ed1485..bdb70f215f83 100644
--- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml
+++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml
@@ -14,7 +14,7 @@ jobs:
     if: github.repository == 'rust-lang/rustc-dev-guide'
     runs-on: ubuntu-latest
     env:
-      MDBOOK_VERSION: 0.5.1
+      MDBOOK_VERSION: 0.5.2
       MDBOOK_LINKCHECK2_VERSION: 0.11.0
       MDBOOK_MERMAID_VERSION: 0.17.0
       MDBOOK_OUTPUT__LINKCHECK__FOLLOW_WEB_LINKS: ${{ github.event_name != 'pull_request' }}

From 989568a79fbe7c3ed8974107b503a1adc5cfdf30 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Thu, 15 Jan 2026 20:53:30 +0300
Subject: [PATCH 353/583] privacy: Fix privacy lints in RPITITs

---
 compiler/rustc_privacy/src/lib.rs             |  40 ++--
 tests/ui/privacy/private-in-public-warn.rs    |   9 +
 .../ui/privacy/private-in-public-warn.stderr  | 218 +++++++++++++-----
 tests/ui/privacy/pub-priv-dep/pub-priv1.rs    |   3 +
 .../ui/privacy/pub-priv-dep/pub-priv1.stderr  |  82 ++++---
 5 files changed, 239 insertions(+), 113 deletions(-)

diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 8656ec6e39ae..d4d0727dc479 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -1,5 +1,6 @@
 // tidy-alphabetical-start
 #![feature(associated_type_defaults)]
+#![feature(default_field_values)]
 #![feature(try_blocks)]
 // tidy-alphabetical-end
 
@@ -29,8 +30,8 @@ use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility,
 use rustc_middle::query::Providers;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{
-    self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
-    TypeVisitor,
+    self, AssocContainer, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
+    TypeVisitable, TypeVisitor,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
@@ -1353,9 +1354,9 @@ struct SearchInterfaceForPrivateItemsVisitor<'tcx> {
     /// The visitor checks that each component type is at least this visible.
     required_visibility: ty::Visibility,
     required_effective_vis: Option,
-    in_assoc_ty: bool,
-    in_primary_interface: bool,
-    skip_assoc_tys: bool,
+    hard_error: bool = false,
+    in_primary_interface: bool = true,
+    skip_assoc_tys: bool = false,
 }
 
 impl SearchInterfaceForPrivateItemsVisitor<'_> {
@@ -1427,7 +1428,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
         };
 
         let vis = self.tcx.local_visibility(local_def_id);
-        if self.in_assoc_ty && !vis.is_at_least(self.required_visibility, self.tcx) {
+        if self.hard_error && !vis.is_at_least(self.required_visibility, self.tcx) {
             let vis_descr = match vis {
                 ty::Visibility::Public => "public",
                 ty::Visibility::Restricted(vis_def_id) => {
@@ -1544,9 +1545,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
             item_def_id: def_id,
             required_visibility,
             required_effective_vis,
-            in_assoc_ty: false,
-            in_primary_interface: true,
-            skip_assoc_tys: false,
+            ..
         }
     }
 
@@ -1589,11 +1588,15 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
             ty::AssocKind::Type { .. } => (item.defaultness(self.tcx).has_value(), true),
         };
 
-        check.in_assoc_ty = is_assoc_ty;
+        check.hard_error = is_assoc_ty && !item.is_impl_trait_in_trait();
         check.generics().predicates();
         if check_ty {
             check.ty();
         }
+        if is_assoc_ty && item.container == AssocContainer::Trait {
+            check.hard_error = false;
+            check.bounds();
+        }
     }
 
     fn get(&self, def_id: LocalDefId) -> Option {
@@ -1625,20 +1628,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
                 self.check(def_id, item_visibility, effective_vis).generics().predicates();
 
                 for assoc_item in tcx.associated_items(id.owner_id).in_definition_order() {
-                    if assoc_item.is_impl_trait_in_trait() {
-                        continue;
-                    }
-
                     self.check_assoc_item(assoc_item, item_visibility, effective_vis);
-
-                    if assoc_item.is_type() {
-                        self.check(
-                            assoc_item.def_id.expect_local(),
-                            item_visibility,
-                            effective_vis,
-                        )
-                        .bounds();
-                    }
                 }
             }
             DefKind::TraitAlias => {
@@ -1712,10 +1702,6 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
                 }
 
                 for assoc_item in tcx.associated_items(id.owner_id).in_definition_order() {
-                    if assoc_item.is_impl_trait_in_trait() {
-                        continue;
-                    }
-
                     let impl_item_vis = if !of_trait {
                         min(tcx.local_visibility(assoc_item.def_id.expect_local()), impl_vis, tcx)
                     } else {
diff --git a/tests/ui/privacy/private-in-public-warn.rs b/tests/ui/privacy/private-in-public-warn.rs
index f79e4641312e..9f2958e88298 100644
--- a/tests/ui/privacy/private-in-public-warn.rs
+++ b/tests/ui/privacy/private-in-public-warn.rs
@@ -49,7 +49,10 @@ mod traits {
         fn f(arg: T) {}
         //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::f`
         fn g() -> impl PrivTr;
+        //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}`
         fn h() -> impl PrivTr {}
+        //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}`
+        //~| ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}`
     }
     impl Pub {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Pub`
     impl PubTr for Pub {} // OK, trait impl predicates
@@ -89,7 +92,13 @@ mod generics {
 
     pub trait Tr5 {
         fn required() -> impl PrivTr>;
+        //~^ ERROR trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}`
+        //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}`
         fn provided() -> impl PrivTr> {}
+        //~^ ERROR trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}`
+        //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}`
+        //~| ERROR trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}`
+        //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}`
     }
 }
 
diff --git a/tests/ui/privacy/private-in-public-warn.stderr b/tests/ui/privacy/private-in-public-warn.stderr
index edcffaf6b70a..7ec023b9f89f 100644
--- a/tests/ui/privacy/private-in-public-warn.stderr
+++ b/tests/ui/privacy/private-in-public-warn.stderr
@@ -194,8 +194,44 @@ note: but trait `traits::PrivTr` is only usable at visibility `pub(self)`
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
+error: trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:51:19
+   |
+LL |         fn g() -> impl PrivTr;
+   |                   ^^^^^^^^^^^ opaque type `traits::Tr3::g::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but trait `traits::PrivTr` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:37:5
+   |
+LL |     trait PrivTr {}
+   |     ^^^^^^^^^^^^
+
+error: trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:53:19
+   |
+LL |         fn h() -> impl PrivTr {}
+   |                   ^^^^^^^^^^^ opaque type `traits::Tr3::h::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but trait `traits::PrivTr` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:37:5
+   |
+LL |     trait PrivTr {}
+   |     ^^^^^^^^^^^^
+
+error: trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:53:19
+   |
+LL |         fn h() -> impl PrivTr {}
+   |                   ^^^^^^^^^^^ opaque type `traits::Tr3::h::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but trait `traits::PrivTr` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:37:5
+   |
+LL |     trait PrivTr {}
+   |     ^^^^^^^^^^^^
+
 error: trait `traits::PrivTr` is more private than the item `traits::Pub`
-  --> $DIR/private-in-public-warn.rs:54:5
+  --> $DIR/private-in-public-warn.rs:57:5
    |
 LL |     impl Pub {}
    |     ^^^^^^^^^^^^^^^^^^^^^^ implementation `traits::Pub` is reachable at visibility `pub(crate)`
@@ -207,103 +243,175 @@ LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits_where::PrivTr` is more private than the item `traits_where::Alias`
-  --> $DIR/private-in-public-warn.rs:63:5
+  --> $DIR/private-in-public-warn.rs:66:5
    |
 LL |     pub type Alias where T: PrivTr = T;
    |     ^^^^^^^^^^^^^^^^^ type alias `traits_where::Alias` is reachable at visibility `pub(crate)`
    |
 note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:59:5
+  --> $DIR/private-in-public-warn.rs:62:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2`
-  --> $DIR/private-in-public-warn.rs:66:5
+  --> $DIR/private-in-public-warn.rs:69:5
    |
 LL |     pub trait Tr2 where T: PrivTr {}
    |     ^^^^^^^^^^^^^^^^ trait `traits_where::Tr2` is reachable at visibility `pub(crate)`
    |
 note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:59:5
+  --> $DIR/private-in-public-warn.rs:62:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits_where::PrivTr` is more private than the item `traits_where::Tr3::f`
-  --> $DIR/private-in-public-warn.rs:69:9
+  --> $DIR/private-in-public-warn.rs:72:9
    |
 LL |         fn f(arg: T) where T: PrivTr {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function `traits_where::Tr3::f` is reachable at visibility `pub(crate)`
    |
 note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:59:5
+  --> $DIR/private-in-public-warn.rs:62:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits_where::PrivTr` is more private than the item `traits_where::Pub`
-  --> $DIR/private-in-public-warn.rs:72:5
+  --> $DIR/private-in-public-warn.rs:75:5
    |
 LL |     impl Pub where T: PrivTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation `traits_where::Pub` is reachable at visibility `pub(crate)`
    |
 note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:59:5
+  --> $DIR/private-in-public-warn.rs:62:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `generics::PrivTr` is more private than the item `generics::Tr1`
-  --> $DIR/private-in-public-warn.rs:84:5
+  --> $DIR/private-in-public-warn.rs:87:5
    |
 LL |     pub trait Tr1: PrivTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr1` is reachable at visibility `pub(crate)`
    |
 note: but trait `generics::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:80:5
+  --> $DIR/private-in-public-warn.rs:83:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^^^^
 
 error: type `generics::Priv` is more private than the item `generics::Tr2`
-  --> $DIR/private-in-public-warn.rs:86:5
+  --> $DIR/private-in-public-warn.rs:89:5
    |
 LL |     pub trait Tr2: PubTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr2` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:78:5
+  --> $DIR/private-in-public-warn.rs:81:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: type `generics::Priv` is more private than the item `generics::Tr3`
-  --> $DIR/private-in-public-warn.rs:87:5
+  --> $DIR/private-in-public-warn.rs:90:5
    |
 LL |     pub trait Tr3: PubTr<[Priv; 1]> {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr3` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:78:5
+  --> $DIR/private-in-public-warn.rs:81:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: type `generics::Priv` is more private than the item `Tr4`
-  --> $DIR/private-in-public-warn.rs:88:5
+  --> $DIR/private-in-public-warn.rs:91:5
    |
 LL |     pub trait Tr4: PubTr> {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `Tr4` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:78:5
+  --> $DIR/private-in-public-warn.rs:81:5
+   |
+LL |     struct Priv(T);
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:94:26
+   |
+LL |         fn required() -> impl PrivTr>;
+   |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:83:5
+   |
+LL |     trait PrivTr {}
+   |     ^^^^^^^^^^^^^^^
+
+error: type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:94:26
+   |
+LL |         fn required() -> impl PrivTr>;
+   |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but type `generics::Priv<()>` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:81:5
+   |
+LL |     struct Priv(T);
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:97:26
+   |
+LL |         fn provided() -> impl PrivTr> {}
+   |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:83:5
+   |
+LL |     trait PrivTr {}
+   |     ^^^^^^^^^^^^^^^
+
+error: type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:97:26
+   |
+LL |         fn provided() -> impl PrivTr> {}
+   |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but type `generics::Priv<()>` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:81:5
+   |
+LL |     struct Priv(T);
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:97:26
+   |
+LL |         fn provided() -> impl PrivTr> {}
+   |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:83:5
+   |
+LL |     trait PrivTr {}
+   |     ^^^^^^^^^^^^^^^
+
+error: type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:97:26
+   |
+LL |         fn provided() -> impl PrivTr> {}
+   |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but type `generics::Priv<()>` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:81:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error[E0446]: private type `impls::Priv` in public interface
-  --> $DIR/private-in-public-warn.rs:119:9
+  --> $DIR/private-in-public-warn.rs:128:9
    |
 LL |     struct Priv;
    |     ----------- `impls::Priv` declared as private
@@ -312,44 +420,17 @@ LL |         type Alias = Priv;
    |         ^^^^^^^^^^ can't leak private type
 
 error: type `aliases_pub::Priv` is more private than the item `aliases_pub::::f`
-  --> $DIR/private-in-public-warn.rs:190:9
+  --> $DIR/private-in-public-warn.rs:199:9
    |
 LL |         pub fn f(arg: Priv) {}
    |         ^^^^^^^^^^^^^^^^^^^ associated function `aliases_pub::::f` is reachable at visibility `pub(crate)`
    |
 note: but type `aliases_pub::Priv` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:163:5
+  --> $DIR/private-in-public-warn.rs:172:5
    |
 LL |     struct Priv;
    |     ^^^^^^^^^^^
 
-error[E0446]: private type `aliases_pub::Priv` in public interface
-  --> $DIR/private-in-public-warn.rs:193:9
-   |
-LL |     struct Priv;
-   |     ----------- `aliases_pub::Priv` declared as private
-...
-LL |         type Check = Priv;
-   |         ^^^^^^^^^^ can't leak private type
-
-error[E0446]: private type `aliases_pub::Priv` in public interface
-  --> $DIR/private-in-public-warn.rs:196:9
-   |
-LL |     struct Priv;
-   |     ----------- `aliases_pub::Priv` declared as private
-...
-LL |         type Check = Priv;
-   |         ^^^^^^^^^^ can't leak private type
-
-error[E0446]: private type `aliases_pub::Priv` in public interface
-  --> $DIR/private-in-public-warn.rs:199:9
-   |
-LL |     struct Priv;
-   |     ----------- `aliases_pub::Priv` declared as private
-...
-LL |         type Check = Priv;
-   |         ^^^^^^^^^^ can't leak private type
-
 error[E0446]: private type `aliases_pub::Priv` in public interface
   --> $DIR/private-in-public-warn.rs:202:9
    |
@@ -359,38 +440,65 @@ LL |     struct Priv;
 LL |         type Check = Priv;
    |         ^^^^^^^^^^ can't leak private type
 
+error[E0446]: private type `aliases_pub::Priv` in public interface
+  --> $DIR/private-in-public-warn.rs:205:9
+   |
+LL |     struct Priv;
+   |     ----------- `aliases_pub::Priv` declared as private
+...
+LL |         type Check = Priv;
+   |         ^^^^^^^^^^ can't leak private type
+
+error[E0446]: private type `aliases_pub::Priv` in public interface
+  --> $DIR/private-in-public-warn.rs:208:9
+   |
+LL |     struct Priv;
+   |     ----------- `aliases_pub::Priv` declared as private
+...
+LL |         type Check = Priv;
+   |         ^^^^^^^^^^ can't leak private type
+
+error[E0446]: private type `aliases_pub::Priv` in public interface
+  --> $DIR/private-in-public-warn.rs:211:9
+   |
+LL |     struct Priv;
+   |     ----------- `aliases_pub::Priv` declared as private
+...
+LL |         type Check = Priv;
+   |         ^^^^^^^^^^ can't leak private type
+
 error: trait `PrivTr1` is more private than the item `aliases_priv::Tr1`
-  --> $DIR/private-in-public-warn.rs:232:5
+  --> $DIR/private-in-public-warn.rs:241:5
    |
 LL |     pub trait Tr1: PrivUseAliasTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr1` is reachable at visibility `pub(crate)`
    |
 note: but trait `PrivTr1` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:218:5
+  --> $DIR/private-in-public-warn.rs:227:5
    |
 LL |     trait PrivTr1 {
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `PrivTr1` is more private than the item `aliases_priv::Tr2`
-  --> $DIR/private-in-public-warn.rs:234:5
+  --> $DIR/private-in-public-warn.rs:243:5
    |
 LL |     pub trait Tr2: PrivUseAliasTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr2` is reachable at visibility `pub(crate)`
    |
 note: but trait `PrivTr1` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:218:5
+  --> $DIR/private-in-public-warn.rs:227:5
    |
 LL |     trait PrivTr1 {
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: type `Priv2` is more private than the item `aliases_priv::Tr2`
-  --> $DIR/private-in-public-warn.rs:234:5
+  --> $DIR/private-in-public-warn.rs:243:5
    |
 LL |     pub trait Tr2: PrivUseAliasTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr2` is reachable at visibility `pub(crate)`
    |
 note: but type `Priv2` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:216:5
+  --> $DIR/private-in-public-warn.rs:225:5
    |
 LL |     struct Priv2;
    |     ^^^^^^^^^^^^
@@ -410,7 +518,7 @@ LL |     pub type Alias = T;
    = note: `#[warn(type_alias_bounds)]` on by default
 
 warning: where clauses on type aliases are not enforced
-  --> $DIR/private-in-public-warn.rs:63:29
+  --> $DIR/private-in-public-warn.rs:66:29
    |
 LL |     pub type Alias where T: PrivTr = T;
    |                       ------^^^^^^^^^
@@ -422,6 +530,6 @@ LL |     pub type Alias where T: PrivTr = T;
            see issue #112792  for more information
    = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-error: aborting due to 34 previous errors; 2 warnings emitted
+error: aborting due to 43 previous errors; 2 warnings emitted
 
 For more information about this error, try `rustc --explain E0446`.
diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
index eae0f9756a10..9093b0808fdb 100644
--- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
+++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
@@ -74,8 +74,11 @@ pub trait MyPubTrait {
     //~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
 
     fn required_impl_trait() -> impl OtherTrait;
+    //~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
 
     fn provided_impl_trait() -> impl OtherTrait { OtherType }
+    //~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
+    //~| ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
 
     fn required_concrete() -> OtherType;
     //~^ ERROR type `OtherType` from private dependency 'priv_dep' in public interface
diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
index e66db53f65dd..61c948c2817a 100644
--- a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
+++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
@@ -11,55 +11,55 @@ LL | #![deny(exported_private_dependencies)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: macro `m` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:156:9
+  --> $DIR/pub-priv1.rs:159:9
    |
 LL | pub use priv_dep::m;
    |         ^^^^^^^^^^^
 
 error: macro `fn_like` from private dependency 'pm' is re-exported
-  --> $DIR/pub-priv1.rs:158:9
+  --> $DIR/pub-priv1.rs:161:9
    |
 LL | pub use pm::fn_like;
    |         ^^^^^^^^^^^
 
 error: derive macro `PmDerive` from private dependency 'pm' is re-exported
-  --> $DIR/pub-priv1.rs:160:9
+  --> $DIR/pub-priv1.rs:163:9
    |
 LL | pub use pm::PmDerive;
    |         ^^^^^^^^^^^^
 
 error: attribute macro `pm_attr` from private dependency 'pm' is re-exported
-  --> $DIR/pub-priv1.rs:162:9
+  --> $DIR/pub-priv1.rs:165:9
    |
 LL | pub use pm::pm_attr;
    |         ^^^^^^^^^^^
 
 error: variant `V1` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:165:9
+  --> $DIR/pub-priv1.rs:168:9
    |
 LL | pub use priv_dep::E::V1;
    |         ^^^^^^^^^^^^^^^
 
 error: type alias `Unit` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:168:9
+  --> $DIR/pub-priv1.rs:171:9
    |
 LL | pub use priv_dep::Unit;
    |         ^^^^^^^^^^^^^^
 
 error: type alias `PubPub` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:170:9
+  --> $DIR/pub-priv1.rs:173:9
    |
 LL | pub use priv_dep::PubPub;
    |         ^^^^^^^^^^^^^^^^
 
 error: type alias `PubPriv` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:172:9
+  --> $DIR/pub-priv1.rs:175:9
    |
 LL | pub use priv_dep::PubPriv;
    |         ^^^^^^^^^^^^^^^^^
 
 error: struct `Renamed` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:174:9
+  --> $DIR/pub-priv1.rs:177:9
    |
 LL | pub use priv_dep::OtherType as Renamed;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -124,92 +124,112 @@ error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
 LL |     type Foo: OtherTrait;
    |     ^^^^^^^^^^^^^^^^^^^^
 
+error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:76:33
+   |
+LL |     fn required_impl_trait() -> impl OtherTrait;
+   |                                 ^^^^^^^^^^^^^^^
+
+error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:79:33
+   |
+LL |     fn provided_impl_trait() -> impl OtherTrait { OtherType }
+   |                                 ^^^^^^^^^^^^^^^
+
+error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:79:33
+   |
+LL |     fn provided_impl_trait() -> impl OtherTrait { OtherType }
+   |                                 ^^^^^^^^^^^^^^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:80:5
+  --> $DIR/pub-priv1.rs:83:5
    |
 LL |     fn required_concrete() -> OtherType;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:83:5
+  --> $DIR/pub-priv1.rs:86:5
    |
 LL |     fn provided_concrete() -> OtherType { OtherType }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:87:1
+  --> $DIR/pub-priv1.rs:90:1
    |
 LL | pub trait WithSuperTrait: OtherTrait {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:96:5
+  --> $DIR/pub-priv1.rs:99:5
    |
 LL |     type X = OtherType;
    |     ^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:100:1
+  --> $DIR/pub-priv1.rs:103:1
    |
 LL | pub fn in_bounds(x: T) { unimplemented!() }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:103:1
+  --> $DIR/pub-priv1.rs:106:1
    |
 LL | pub fn private_return_impl_trait() -> impl OtherTrait { OtherType }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:106:1
+  --> $DIR/pub-priv1.rs:109:1
    |
 LL | pub fn private_return() -> OtherType { OtherType }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:109:1
+  --> $DIR/pub-priv1.rs:112:1
    |
 LL | pub fn private_in_generic() -> std::num::Saturating { unimplemented!() }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:112:1
+  --> $DIR/pub-priv1.rs:115:1
    |
 LL | pub static STATIC: OtherType = OtherType;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:115:1
+  --> $DIR/pub-priv1.rs:118:1
    |
 LL | pub const CONST: OtherType = OtherType;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:118:1
+  --> $DIR/pub-priv1.rs:121:1
    |
 LL | pub type Alias = OtherType;
    | ^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:121:1
+  --> $DIR/pub-priv1.rs:124:1
    |
 LL | pub type AliasOfAlias = priv_dep::PubPub;
    | ^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:126:1
+  --> $DIR/pub-priv1.rs:129:1
    |
 LL | impl OtherTrait for PublicWithPrivateImpl {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:131:1
+  --> $DIR/pub-priv1.rs:134:1
    |
 LL | impl PubTraitOnPrivate for OtherType {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:131:1
+  --> $DIR/pub-priv1.rs:134:1
    |
 LL | impl PubTraitOnPrivate for OtherType {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -217,25 +237,25 @@ LL | impl PubTraitOnPrivate for OtherType {}
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:137:1
+  --> $DIR/pub-priv1.rs:140:1
    |
 LL | impl From for PublicWithStdImpl {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:139:5
+  --> $DIR/pub-priv1.rs:142:5
    |
 LL |     fn from(val: OtherType) -> Self { Self }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:143:1
+  --> $DIR/pub-priv1.rs:146:1
    |
 LL | impl From for OtherType {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:143:1
+  --> $DIR/pub-priv1.rs:146:1
    |
 LL | impl From for OtherType {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -243,18 +263,18 @@ LL | impl From for OtherType {
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:146:5
+  --> $DIR/pub-priv1.rs:149:5
    |
 LL |     fn from(val: PublicWithStdImpl) -> Self { Self }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:146:5
+  --> $DIR/pub-priv1.rs:149:5
    |
 LL |     fn from(val: PublicWithStdImpl) -> Self { Self }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error: aborting due to 41 previous errors
+error: aborting due to 44 previous errors
 

From 1ce89c04490746ba07f55413d7f511675e638bab Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Fri, 16 Jan 2026 20:51:41 +0300
Subject: [PATCH 354/583] privacy: Close one more hole in associated type
 visiting

---
 compiler/rustc_privacy/src/lib.rs             |  26 ++--
 tests/ui/privacy/private-in-public-warn.rs    |   3 +
 .../ui/privacy/private-in-public-warn.stderr  | 138 +++++++++++-------
 tests/ui/privacy/pub-priv-dep/pub-priv1.rs    |   1 +
 .../ui/privacy/pub-priv-dep/pub-priv1.stderr  |  74 +++++-----
 5 files changed, 148 insertions(+), 94 deletions(-)

diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index d4d0727dc479..a8046e705732 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -312,6 +312,18 @@ where
     }
 }
 
+fn assoc_has_type_of(tcx: TyCtxt<'_>, item: &ty::AssocItem) -> bool {
+    if let ty::AssocKind::Type { data: ty::AssocTypeData::Normal(..) } = item.kind
+        && let hir::Node::TraitItem(item) =
+            tcx.hir_node(tcx.local_def_id_to_hir_id(item.def_id.expect_local()))
+        && let hir::TraitItemKind::Type(_, None) = item.kind
+    {
+        false
+    } else {
+        true
+    }
+}
+
 fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visibility {
     if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 }
 }
@@ -680,10 +692,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
                         let tcx = self.tcx;
                         let mut reach = self.reach(def_id, item_ev);
                         reach.generics().predicates();
-
-                        if assoc_item.is_type() && !assoc_item.defaultness(tcx).has_value() {
-                            // No type to visit.
-                        } else {
+                        if assoc_has_type_of(tcx, assoc_item) {
                             reach.ty();
                         }
                     }
@@ -1583,14 +1592,11 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'_, 'tcx> {
     ) {
         let mut check = self.check(item.def_id.expect_local(), vis, effective_vis);
 
-        let (check_ty, is_assoc_ty) = match item.kind {
-            ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => (true, false),
-            ty::AssocKind::Type { .. } => (item.defaultness(self.tcx).has_value(), true),
-        };
-
+        let is_assoc_ty = item.is_type();
         check.hard_error = is_assoc_ty && !item.is_impl_trait_in_trait();
         check.generics().predicates();
-        if check_ty {
+        if assoc_has_type_of(self.tcx, item) {
+            check.hard_error = check.hard_error && item.defaultness(self.tcx).has_value();
             check.ty();
         }
         if is_assoc_ty && item.container == AssocContainer::Trait {
diff --git a/tests/ui/privacy/private-in-public-warn.rs b/tests/ui/privacy/private-in-public-warn.rs
index 9f2958e88298..6a0ac2b9ade7 100644
--- a/tests/ui/privacy/private-in-public-warn.rs
+++ b/tests/ui/privacy/private-in-public-warn.rs
@@ -50,6 +50,7 @@ mod traits {
         //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::f`
         fn g() -> impl PrivTr;
         //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}`
+        //~| ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}`
         fn h() -> impl PrivTr {}
         //~^ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}`
         //~| ERROR trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}`
@@ -94,6 +95,8 @@ mod generics {
         fn required() -> impl PrivTr>;
         //~^ ERROR trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}`
         //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}`
+        //~| ERROR trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}`
+        //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}`
         fn provided() -> impl PrivTr> {}
         //~^ ERROR trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}`
         //~| ERROR type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}`
diff --git a/tests/ui/privacy/private-in-public-warn.stderr b/tests/ui/privacy/private-in-public-warn.stderr
index 7ec023b9f89f..1fa415e27c16 100644
--- a/tests/ui/privacy/private-in-public-warn.stderr
+++ b/tests/ui/privacy/private-in-public-warn.stderr
@@ -206,8 +206,20 @@ note: but trait `traits::PrivTr` is only usable at visibility `pub(self)`
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
+error: trait `traits::PrivTr` is more private than the item `traits::Tr3::g::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:51:19
+   |
+LL |         fn g() -> impl PrivTr;
+   |                   ^^^^^^^^^^^ opaque type `traits::Tr3::g::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but trait `traits::PrivTr` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:37:5
+   |
+LL |     trait PrivTr {}
+   |     ^^^^^^^^^^^^
+
 error: trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}`
-  --> $DIR/private-in-public-warn.rs:53:19
+  --> $DIR/private-in-public-warn.rs:54:19
    |
 LL |         fn h() -> impl PrivTr {}
    |                   ^^^^^^^^^^^ opaque type `traits::Tr3::h::{anon_assoc#0}` is reachable at visibility `pub(crate)`
@@ -219,7 +231,7 @@ LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits::PrivTr` is more private than the item `traits::Tr3::h::{anon_assoc#0}`
-  --> $DIR/private-in-public-warn.rs:53:19
+  --> $DIR/private-in-public-warn.rs:54:19
    |
 LL |         fn h() -> impl PrivTr {}
    |                   ^^^^^^^^^^^ opaque type `traits::Tr3::h::{anon_assoc#0}` is reachable at visibility `pub(crate)`
@@ -231,7 +243,7 @@ LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits::PrivTr` is more private than the item `traits::Pub`
-  --> $DIR/private-in-public-warn.rs:57:5
+  --> $DIR/private-in-public-warn.rs:58:5
    |
 LL |     impl Pub {}
    |     ^^^^^^^^^^^^^^^^^^^^^^ implementation `traits::Pub` is reachable at visibility `pub(crate)`
@@ -243,175 +255,199 @@ LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits_where::PrivTr` is more private than the item `traits_where::Alias`
-  --> $DIR/private-in-public-warn.rs:66:5
+  --> $DIR/private-in-public-warn.rs:67:5
    |
 LL |     pub type Alias where T: PrivTr = T;
    |     ^^^^^^^^^^^^^^^^^ type alias `traits_where::Alias` is reachable at visibility `pub(crate)`
    |
 note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:62:5
+  --> $DIR/private-in-public-warn.rs:63:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2`
-  --> $DIR/private-in-public-warn.rs:69:5
+  --> $DIR/private-in-public-warn.rs:70:5
    |
 LL |     pub trait Tr2 where T: PrivTr {}
    |     ^^^^^^^^^^^^^^^^ trait `traits_where::Tr2` is reachable at visibility `pub(crate)`
    |
 note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:62:5
+  --> $DIR/private-in-public-warn.rs:63:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits_where::PrivTr` is more private than the item `traits_where::Tr3::f`
-  --> $DIR/private-in-public-warn.rs:72:9
+  --> $DIR/private-in-public-warn.rs:73:9
    |
 LL |         fn f(arg: T) where T: PrivTr {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function `traits_where::Tr3::f` is reachable at visibility `pub(crate)`
    |
 note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:62:5
+  --> $DIR/private-in-public-warn.rs:63:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `traits_where::PrivTr` is more private than the item `traits_where::Pub`
-  --> $DIR/private-in-public-warn.rs:75:5
+  --> $DIR/private-in-public-warn.rs:76:5
    |
 LL |     impl Pub where T: PrivTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation `traits_where::Pub` is reachable at visibility `pub(crate)`
    |
 note: but trait `traits_where::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:62:5
+  --> $DIR/private-in-public-warn.rs:63:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^
 
 error: trait `generics::PrivTr` is more private than the item `generics::Tr1`
-  --> $DIR/private-in-public-warn.rs:87:5
+  --> $DIR/private-in-public-warn.rs:88:5
    |
 LL |     pub trait Tr1: PrivTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr1` is reachable at visibility `pub(crate)`
    |
 note: but trait `generics::PrivTr` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:83:5
+  --> $DIR/private-in-public-warn.rs:84:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^^^^
 
 error: type `generics::Priv` is more private than the item `generics::Tr2`
-  --> $DIR/private-in-public-warn.rs:89:5
+  --> $DIR/private-in-public-warn.rs:90:5
    |
 LL |     pub trait Tr2: PubTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr2` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:81:5
+  --> $DIR/private-in-public-warn.rs:82:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: type `generics::Priv` is more private than the item `generics::Tr3`
-  --> $DIR/private-in-public-warn.rs:90:5
+  --> $DIR/private-in-public-warn.rs:91:5
    |
 LL |     pub trait Tr3: PubTr<[Priv; 1]> {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `generics::Tr3` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:81:5
+  --> $DIR/private-in-public-warn.rs:82:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: type `generics::Priv` is more private than the item `Tr4`
-  --> $DIR/private-in-public-warn.rs:91:5
+  --> $DIR/private-in-public-warn.rs:92:5
    |
 LL |     pub trait Tr4: PubTr> {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `Tr4` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:81:5
+  --> $DIR/private-in-public-warn.rs:82:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}`
-  --> $DIR/private-in-public-warn.rs:94:26
+  --> $DIR/private-in-public-warn.rs:95:26
    |
 LL |         fn required() -> impl PrivTr>;
    |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)`
    |
 note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:83:5
+  --> $DIR/private-in-public-warn.rs:84:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^^^^
 
 error: type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}`
-  --> $DIR/private-in-public-warn.rs:94:26
+  --> $DIR/private-in-public-warn.rs:95:26
    |
 LL |         fn required() -> impl PrivTr>;
    |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv<()>` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:81:5
+  --> $DIR/private-in-public-warn.rs:82:5
+   |
+LL |     struct Priv(T);
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: trait `generics::PrivTr>` is more private than the item `Tr5::required::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:95:26
+   |
+LL |         fn required() -> impl PrivTr>;
+   |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:84:5
+   |
+LL |     trait PrivTr {}
+   |     ^^^^^^^^^^^^^^^
+
+error: type `generics::Priv<()>` is more private than the item `Tr5::required::{anon_assoc#0}`
+  --> $DIR/private-in-public-warn.rs:95:26
+   |
+LL |         fn required() -> impl PrivTr>;
+   |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::required::{anon_assoc#0}` is reachable at visibility `pub(crate)`
+   |
+note: but type `generics::Priv<()>` is only usable at visibility `pub(self)`
+  --> $DIR/private-in-public-warn.rs:82:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}`
-  --> $DIR/private-in-public-warn.rs:97:26
+  --> $DIR/private-in-public-warn.rs:100:26
    |
 LL |         fn provided() -> impl PrivTr> {}
    |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)`
    |
 note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:83:5
+  --> $DIR/private-in-public-warn.rs:84:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^^^^
 
 error: type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}`
-  --> $DIR/private-in-public-warn.rs:97:26
+  --> $DIR/private-in-public-warn.rs:100:26
    |
 LL |         fn provided() -> impl PrivTr> {}
    |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv<()>` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:81:5
+  --> $DIR/private-in-public-warn.rs:82:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: trait `generics::PrivTr>` is more private than the item `Tr5::provided::{anon_assoc#0}`
-  --> $DIR/private-in-public-warn.rs:97:26
+  --> $DIR/private-in-public-warn.rs:100:26
    |
 LL |         fn provided() -> impl PrivTr> {}
    |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)`
    |
 note: but trait `generics::PrivTr>` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:83:5
+  --> $DIR/private-in-public-warn.rs:84:5
    |
 LL |     trait PrivTr {}
    |     ^^^^^^^^^^^^^^^
 
 error: type `generics::Priv<()>` is more private than the item `Tr5::provided::{anon_assoc#0}`
-  --> $DIR/private-in-public-warn.rs:97:26
+  --> $DIR/private-in-public-warn.rs:100:26
    |
 LL |         fn provided() -> impl PrivTr> {}
    |                          ^^^^^^^^^^^^^^^^^^^^^ opaque type `Tr5::provided::{anon_assoc#0}` is reachable at visibility `pub(crate)`
    |
 note: but type `generics::Priv<()>` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:81:5
+  --> $DIR/private-in-public-warn.rs:82:5
    |
 LL |     struct Priv(T);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error[E0446]: private type `impls::Priv` in public interface
-  --> $DIR/private-in-public-warn.rs:128:9
+  --> $DIR/private-in-public-warn.rs:131:9
    |
 LL |     struct Priv;
    |     ----------- `impls::Priv` declared as private
@@ -420,26 +456,17 @@ LL |         type Alias = Priv;
    |         ^^^^^^^^^^ can't leak private type
 
 error: type `aliases_pub::Priv` is more private than the item `aliases_pub::::f`
-  --> $DIR/private-in-public-warn.rs:199:9
+  --> $DIR/private-in-public-warn.rs:202:9
    |
 LL |         pub fn f(arg: Priv) {}
    |         ^^^^^^^^^^^^^^^^^^^ associated function `aliases_pub::::f` is reachable at visibility `pub(crate)`
    |
 note: but type `aliases_pub::Priv` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:172:5
+  --> $DIR/private-in-public-warn.rs:175:5
    |
 LL |     struct Priv;
    |     ^^^^^^^^^^^
 
-error[E0446]: private type `aliases_pub::Priv` in public interface
-  --> $DIR/private-in-public-warn.rs:202:9
-   |
-LL |     struct Priv;
-   |     ----------- `aliases_pub::Priv` declared as private
-...
-LL |         type Check = Priv;
-   |         ^^^^^^^^^^ can't leak private type
-
 error[E0446]: private type `aliases_pub::Priv` in public interface
   --> $DIR/private-in-public-warn.rs:205:9
    |
@@ -467,38 +494,47 @@ LL |     struct Priv;
 LL |         type Check = Priv;
    |         ^^^^^^^^^^ can't leak private type
 
+error[E0446]: private type `aliases_pub::Priv` in public interface
+  --> $DIR/private-in-public-warn.rs:214:9
+   |
+LL |     struct Priv;
+   |     ----------- `aliases_pub::Priv` declared as private
+...
+LL |         type Check = Priv;
+   |         ^^^^^^^^^^ can't leak private type
+
 error: trait `PrivTr1` is more private than the item `aliases_priv::Tr1`
-  --> $DIR/private-in-public-warn.rs:241:5
+  --> $DIR/private-in-public-warn.rs:244:5
    |
 LL |     pub trait Tr1: PrivUseAliasTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr1` is reachable at visibility `pub(crate)`
    |
 note: but trait `PrivTr1` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:227:5
+  --> $DIR/private-in-public-warn.rs:230:5
    |
 LL |     trait PrivTr1 {
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `PrivTr1` is more private than the item `aliases_priv::Tr2`
-  --> $DIR/private-in-public-warn.rs:243:5
+  --> $DIR/private-in-public-warn.rs:246:5
    |
 LL |     pub trait Tr2: PrivUseAliasTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr2` is reachable at visibility `pub(crate)`
    |
 note: but trait `PrivTr1` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:227:5
+  --> $DIR/private-in-public-warn.rs:230:5
    |
 LL |     trait PrivTr1 {
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: type `Priv2` is more private than the item `aliases_priv::Tr2`
-  --> $DIR/private-in-public-warn.rs:243:5
+  --> $DIR/private-in-public-warn.rs:246:5
    |
 LL |     pub trait Tr2: PrivUseAliasTr {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait `aliases_priv::Tr2` is reachable at visibility `pub(crate)`
    |
 note: but type `Priv2` is only usable at visibility `pub(self)`
-  --> $DIR/private-in-public-warn.rs:225:5
+  --> $DIR/private-in-public-warn.rs:228:5
    |
 LL |     struct Priv2;
    |     ^^^^^^^^^^^^
@@ -518,7 +554,7 @@ LL |     pub type Alias = T;
    = note: `#[warn(type_alias_bounds)]` on by default
 
 warning: where clauses on type aliases are not enforced
-  --> $DIR/private-in-public-warn.rs:66:29
+  --> $DIR/private-in-public-warn.rs:67:29
    |
 LL |     pub type Alias where T: PrivTr = T;
    |                       ------^^^^^^^^^
@@ -530,6 +566,6 @@ LL |     pub type Alias where T: PrivTr = T;
            see issue #112792  for more information
    = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-error: aborting due to 43 previous errors; 2 warnings emitted
+error: aborting due to 46 previous errors; 2 warnings emitted
 
 For more information about this error, try `rustc --explain E0446`.
diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
index 9093b0808fdb..ae86be19cf96 100644
--- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
+++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
@@ -75,6 +75,7 @@ pub trait MyPubTrait {
 
     fn required_impl_trait() -> impl OtherTrait;
     //~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
+    //~| ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
 
     fn provided_impl_trait() -> impl OtherTrait { OtherType }
     //~^ ERROR trait `OtherTrait` from private dependency 'priv_dep' in public interface
diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
index 61c948c2817a..609dbd77f9c1 100644
--- a/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
+++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.stderr
@@ -11,55 +11,55 @@ LL | #![deny(exported_private_dependencies)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: macro `m` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:159:9
+  --> $DIR/pub-priv1.rs:160:9
    |
 LL | pub use priv_dep::m;
    |         ^^^^^^^^^^^
 
 error: macro `fn_like` from private dependency 'pm' is re-exported
-  --> $DIR/pub-priv1.rs:161:9
+  --> $DIR/pub-priv1.rs:162:9
    |
 LL | pub use pm::fn_like;
    |         ^^^^^^^^^^^
 
 error: derive macro `PmDerive` from private dependency 'pm' is re-exported
-  --> $DIR/pub-priv1.rs:163:9
+  --> $DIR/pub-priv1.rs:164:9
    |
 LL | pub use pm::PmDerive;
    |         ^^^^^^^^^^^^
 
 error: attribute macro `pm_attr` from private dependency 'pm' is re-exported
-  --> $DIR/pub-priv1.rs:165:9
+  --> $DIR/pub-priv1.rs:166:9
    |
 LL | pub use pm::pm_attr;
    |         ^^^^^^^^^^^
 
 error: variant `V1` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:168:9
+  --> $DIR/pub-priv1.rs:169:9
    |
 LL | pub use priv_dep::E::V1;
    |         ^^^^^^^^^^^^^^^
 
 error: type alias `Unit` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:171:9
+  --> $DIR/pub-priv1.rs:172:9
    |
 LL | pub use priv_dep::Unit;
    |         ^^^^^^^^^^^^^^
 
 error: type alias `PubPub` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:173:9
+  --> $DIR/pub-priv1.rs:174:9
    |
 LL | pub use priv_dep::PubPub;
    |         ^^^^^^^^^^^^^^^^
 
 error: type alias `PubPriv` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:175:9
+  --> $DIR/pub-priv1.rs:176:9
    |
 LL | pub use priv_dep::PubPriv;
    |         ^^^^^^^^^^^^^^^^^
 
 error: struct `Renamed` from private dependency 'priv_dep' is re-exported
-  --> $DIR/pub-priv1.rs:177:9
+  --> $DIR/pub-priv1.rs:178:9
    |
 LL | pub use priv_dep::OtherType as Renamed;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -131,13 +131,21 @@ LL |     fn required_impl_trait() -> impl OtherTrait;
    |                                 ^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:79:33
+  --> $DIR/pub-priv1.rs:76:33
+   |
+LL |     fn required_impl_trait() -> impl OtherTrait;
+   |                                 ^^^^^^^^^^^^^^^
+   |
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
+  --> $DIR/pub-priv1.rs:80:33
    |
 LL |     fn provided_impl_trait() -> impl OtherTrait { OtherType }
    |                                 ^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:79:33
+  --> $DIR/pub-priv1.rs:80:33
    |
 LL |     fn provided_impl_trait() -> impl OtherTrait { OtherType }
    |                                 ^^^^^^^^^^^^^^^
@@ -145,91 +153,91 @@ LL |     fn provided_impl_trait() -> impl OtherTrait { OtherType }
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:83:5
+  --> $DIR/pub-priv1.rs:84:5
    |
 LL |     fn required_concrete() -> OtherType;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:86:5
+  --> $DIR/pub-priv1.rs:87:5
    |
 LL |     fn provided_concrete() -> OtherType { OtherType }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:90:1
+  --> $DIR/pub-priv1.rs:91:1
    |
 LL | pub trait WithSuperTrait: OtherTrait {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:99:5
+  --> $DIR/pub-priv1.rs:100:5
    |
 LL |     type X = OtherType;
    |     ^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:103:1
+  --> $DIR/pub-priv1.rs:104:1
    |
 LL | pub fn in_bounds(x: T) { unimplemented!() }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:106:1
+  --> $DIR/pub-priv1.rs:107:1
    |
 LL | pub fn private_return_impl_trait() -> impl OtherTrait { OtherType }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:109:1
+  --> $DIR/pub-priv1.rs:110:1
    |
 LL | pub fn private_return() -> OtherType { OtherType }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:112:1
+  --> $DIR/pub-priv1.rs:113:1
    |
 LL | pub fn private_in_generic() -> std::num::Saturating { unimplemented!() }
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:115:1
+  --> $DIR/pub-priv1.rs:116:1
    |
 LL | pub static STATIC: OtherType = OtherType;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:118:1
+  --> $DIR/pub-priv1.rs:119:1
    |
 LL | pub const CONST: OtherType = OtherType;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:121:1
+  --> $DIR/pub-priv1.rs:122:1
    |
 LL | pub type Alias = OtherType;
    | ^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:124:1
+  --> $DIR/pub-priv1.rs:125:1
    |
 LL | pub type AliasOfAlias = priv_dep::PubPub;
    | ^^^^^^^^^^^^^^^^^^^^^
 
 error: trait `OtherTrait` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:129:1
+  --> $DIR/pub-priv1.rs:130:1
    |
 LL | impl OtherTrait for PublicWithPrivateImpl {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:134:1
+  --> $DIR/pub-priv1.rs:135:1
    |
 LL | impl PubTraitOnPrivate for OtherType {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:134:1
+  --> $DIR/pub-priv1.rs:135:1
    |
 LL | impl PubTraitOnPrivate for OtherType {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -237,25 +245,25 @@ LL | impl PubTraitOnPrivate for OtherType {}
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:140:1
+  --> $DIR/pub-priv1.rs:141:1
    |
 LL | impl From for PublicWithStdImpl {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:142:5
+  --> $DIR/pub-priv1.rs:143:5
    |
 LL |     fn from(val: OtherType) -> Self { Self }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:146:1
+  --> $DIR/pub-priv1.rs:147:1
    |
 LL | impl From for OtherType {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:146:1
+  --> $DIR/pub-priv1.rs:147:1
    |
 LL | impl From for OtherType {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -263,18 +271,18 @@ LL | impl From for OtherType {
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:149:5
+  --> $DIR/pub-priv1.rs:150:5
    |
 LL |     fn from(val: PublicWithStdImpl) -> Self { Self }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: type `OtherType` from private dependency 'priv_dep' in public interface
-  --> $DIR/pub-priv1.rs:149:5
+  --> $DIR/pub-priv1.rs:150:5
    |
 LL |     fn from(val: PublicWithStdImpl) -> Self { Self }
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error: aborting due to 44 previous errors
+error: aborting due to 45 previous errors
 

From 88f2e79b77879ea87a28c28b0f5a111721c4b27a Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Sun, 18 Jan 2026 14:32:52 +0300
Subject: [PATCH 355/583] Add test case from issue 151284

---
 .../missing-mir-priv-bounds-extern.rs         | 40 +++++++++++++++++++
 tests/ui/privacy/missing-mir-priv-bounds.rs   | 26 ++++++++++++
 .../ui/privacy/missing-mir-priv-bounds.stderr | 10 +++++
 3 files changed, 76 insertions(+)
 create mode 100644 tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern.rs
 create mode 100644 tests/ui/privacy/missing-mir-priv-bounds.rs
 create mode 100644 tests/ui/privacy/missing-mir-priv-bounds.stderr

diff --git a/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern.rs b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern.rs
new file mode 100644
index 000000000000..4cb7bfcc6a3f
--- /dev/null
+++ b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern.rs
@@ -0,0 +1,40 @@
+pub trait ToPriv {
+    type AssocPriv;
+}
+
+pub trait PubTr {
+    #[expect(private_bounds)]
+    type Assoc: ToPriv;
+}
+
+struct Dummy;
+struct DummyToPriv;
+impl PubTr for Dummy {
+    type Assoc = DummyToPriv;
+}
+impl ToPriv for DummyToPriv {
+    type AssocPriv = Priv;
+}
+
+pub fn get_dummy() -> impl PubTr {
+    Dummy
+}
+
+struct Priv;
+
+pub trait GetUnreachable {
+    type Assoc;
+}
+
+mod m {
+    pub struct Unreachable;
+
+    impl Unreachable {
+        #[expect(dead_code)]
+        pub fn generic() {}
+    }
+
+    impl crate::GetUnreachable for crate::Priv {
+        type Assoc = Unreachable;
+    }
+}
diff --git a/tests/ui/privacy/missing-mir-priv-bounds.rs b/tests/ui/privacy/missing-mir-priv-bounds.rs
new file mode 100644
index 000000000000..07783a2ef858
--- /dev/null
+++ b/tests/ui/privacy/missing-mir-priv-bounds.rs
@@ -0,0 +1,26 @@
+// Test case from issue #151284.
+// A private associated type bound allows to leak another private type and result in missing MIR.
+
+//@ build-fail
+//@ aux-crate:dep=missing-mir-priv-bounds-extern.rs
+
+extern crate dep;
+use dep::{GetUnreachable, PubTr, ToPriv, get_dummy};
+
+fn main() {
+    wut(get_dummy());
+}
+
+fn wut(_: T) {
+    ::AccessAssoc::generic::();
+}
+
+trait Access: PubTr {
+    type AccessAssoc;
+}
+
+impl Access for T {
+    type AccessAssoc = <::AssocPriv as GetUnreachable>::Assoc;
+}
+
+//~? ERROR missing optimized MIR
diff --git a/tests/ui/privacy/missing-mir-priv-bounds.stderr b/tests/ui/privacy/missing-mir-priv-bounds.stderr
new file mode 100644
index 000000000000..68eca33332cb
--- /dev/null
+++ b/tests/ui/privacy/missing-mir-priv-bounds.stderr
@@ -0,0 +1,10 @@
+error: missing optimized MIR for `dep::m::Unreachable::generic::` in the crate `missing_mir_priv_bounds_extern`
+   |
+note: missing optimized MIR for this item (was the crate `missing_mir_priv_bounds_extern` compiled with `--emit=metadata`?)
+  --> $DIR/auxiliary/missing-mir-priv-bounds-extern.rs:34:9
+   |
+LL |         pub fn generic() {}
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+

From 7b5a4d865381345b9f273a08315075426e5511d5 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Sun, 18 Jan 2026 15:01:51 +0300
Subject: [PATCH 356/583] privacy: Synchronize
 PrivateItemsInPublicInterfacesChecker and EmbargoVisitor

---
 compiler/rustc_privacy/src/lib.rs             | 53 +++++++++----------
 .../duplicate-bound-err.stderr                | 24 ++++-----
 .../ui/delegation/unsupported.current.stderr  |  6 +--
 tests/ui/delegation/unsupported.next.stderr   |  2 +-
 .../ui/lint/lint-stability-deprecated.stderr  | 20 +++----
 tests/ui/lint/lint-stability.stderr           | 18 +++----
 6 files changed, 59 insertions(+), 64 deletions(-)

diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index a8046e705732..7c3312291198 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -652,6 +652,19 @@ impl<'tcx> EmbargoVisitor<'tcx> {
 }
 
 impl<'tcx> EmbargoVisitor<'tcx> {
+    fn check_assoc_item(&mut self, item: &ty::AssocItem, item_ev: EffectiveVisibility) {
+        let def_id = item.def_id.expect_local();
+        let tcx = self.tcx;
+        let mut reach = self.reach(def_id, item_ev);
+        reach.generics().predicates();
+        if assoc_has_type_of(tcx, item) {
+            reach.ty();
+        }
+        if item.is_type() && item.container == AssocContainer::Trait {
+            reach.bounds();
+        }
+    }
+
     fn check_def_id(&mut self, owner_id: OwnerId) {
         // Update levels of nested things and mark all items
         // in interfaces of reachable items as reachable.
@@ -682,19 +695,10 @@ impl<'tcx> EmbargoVisitor<'tcx> {
                     self.reach(owner_id.def_id, item_ev).generics().predicates();
 
                     for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() {
-                        if assoc_item.is_impl_trait_in_trait() {
-                            continue;
-                        }
-
                         let def_id = assoc_item.def_id.expect_local();
                         self.update(def_id, item_ev, Level::Reachable);
 
-                        let tcx = self.tcx;
-                        let mut reach = self.reach(def_id, item_ev);
-                        reach.generics().predicates();
-                        if assoc_has_type_of(tcx, assoc_item) {
-                            reach.ty();
-                        }
+                        self.check_assoc_item(assoc_item, item_ev);
                     }
                 }
             }
@@ -732,17 +736,13 @@ impl<'tcx> EmbargoVisitor<'tcx> {
                 }
 
                 for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() {
-                    if assoc_item.is_impl_trait_in_trait() {
-                        continue;
-                    }
-
                     let def_id = assoc_item.def_id.expect_local();
                     let max_vis =
                         if of_trait { None } else { Some(self.tcx.local_visibility(def_id)) };
                     self.update_eff_vis(def_id, item_ev, max_vis, Level::Direct);
 
                     if let Some(impl_item_ev) = self.get(def_id) {
-                        self.reach(def_id, impl_item_ev).generics().predicates().ty();
+                        self.check_assoc_item(assoc_item, impl_item_ev);
                     }
                 }
             }
@@ -834,7 +834,12 @@ impl ReachEverythingInTheInterfaceVisitor<'_, '_> {
     }
 
     fn predicates(&mut self) -> &mut Self {
-        self.visit_predicates(self.ev.tcx.predicates_of(self.item_def_id));
+        self.visit_predicates(self.ev.tcx.explicit_predicates_of(self.item_def_id));
+        self
+    }
+
+    fn bounds(&mut self) -> &mut Self {
+        self.visit_clauses(self.ev.tcx.explicit_item_bounds(self.item_def_id).skip_binder());
         self
     }
 
@@ -1372,17 +1377,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
     fn generics(&mut self) -> &mut Self {
         self.in_primary_interface = true;
         for param in &self.tcx.generics_of(self.item_def_id).own_params {
-            match param.kind {
-                GenericParamDefKind::Lifetime => {}
-                GenericParamDefKind::Type { has_default, .. } => {
-                    if has_default {
-                        let _ = self.visit(self.tcx.type_of(param.def_id).instantiate_identity());
-                    }
-                }
-                // FIXME(generic_const_exprs): May want to look inside const here
-                GenericParamDefKind::Const { .. } => {
-                    let _ = self.visit(self.tcx.type_of(param.def_id).instantiate_identity());
-                }
+            if let GenericParamDefKind::Const { .. } = param.kind {
+                let _ = self.visit(self.tcx.type_of(param.def_id).instantiate_identity());
+            }
+            if let Some(default) = param.default_value(self.tcx) {
+                let _ = self.visit(default.instantiate_identity());
             }
         }
         self
diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr
index 80db2cdb933f..695bb8ad6066 100644
--- a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr
+++ b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr
@@ -108,6 +108,18 @@ LL |     fn foo() -> impl Iterator {
 LL |         [2u32].into_iter()
    |         ------------------ return type was inferred to be `std::array::IntoIter` here
 
+error[E0271]: expected `impl Iterator` to be an iterator that yields `i32`, but it yields `u32`
+  --> $DIR/duplicate-bound-err.rs:111:17
+   |
+LL |     fn foo() -> impl Iterator {
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
+   |
+note: required by a bound in `Trait3::foo::{anon_assoc#0}`
+  --> $DIR/duplicate-bound-err.rs:107:31
+   |
+LL |     fn foo() -> impl Iterator;
+   |                               ^^^^^^^^^^ required by this bound in `Trait3::foo::{anon_assoc#0}`
+
 error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified
   --> $DIR/duplicate-bound-err.rs:87:42
    |
@@ -158,18 +170,6 @@ LL | type MustFail4 = dyn Trait2;
    |                             |
    |                             `ASSOC` bound here first
 
-error[E0271]: expected `impl Iterator` to be an iterator that yields `i32`, but it yields `u32`
-  --> $DIR/duplicate-bound-err.rs:111:17
-   |
-LL |     fn foo() -> impl Iterator {
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
-   |
-note: required by a bound in `Trait3::foo::{anon_assoc#0}`
-  --> $DIR/duplicate-bound-err.rs:107:31
-   |
-LL |     fn foo() -> impl Iterator;
-   |                               ^^^^^^^^^^ required by this bound in `Trait3::foo::{anon_assoc#0}`
-
 error[E0271]: expected `Empty` to be an iterator that yields `i32`, but it yields `u32`
   --> $DIR/duplicate-bound-err.rs:119:16
    |
diff --git a/tests/ui/delegation/unsupported.current.stderr b/tests/ui/delegation/unsupported.current.stderr
index 2bb0633621f2..9bc2eec068fe 100644
--- a/tests/ui/delegation/unsupported.current.stderr
+++ b/tests/ui/delegation/unsupported.current.stderr
@@ -29,11 +29,7 @@ note: ...which requires comparing an impl and trait method signature, inferring
 LL |         reuse ToReuse::opaque_ret;
    |                        ^^^^^^^^^^
    = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle
-note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition
-  --> $DIR/unsupported.rs:33:24
-   |
-LL |         reuse ToReuse::opaque_ret;
-   |                        ^^^^^^^^^^
+   = note: cycle used when checking effective visibilities
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error[E0283]: type annotations needed
diff --git a/tests/ui/delegation/unsupported.next.stderr b/tests/ui/delegation/unsupported.next.stderr
index 1665d1f39d6d..08bc49513bad 100644
--- a/tests/ui/delegation/unsupported.next.stderr
+++ b/tests/ui/delegation/unsupported.next.stderr
@@ -25,7 +25,7 @@ note: ...which requires comparing an impl and trait method signature, inferring
 LL |         reuse ToReuse::opaque_ret;
    |                        ^^^^^^^^^^
    = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle
-   = note: cycle used when computing implied outlives bounds for `::opaque_ret::{anon_assoc#0}` (hack disabled = false)
+   = note: cycle used when checking effective visibilities
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error[E0283]: type annotations needed
diff --git a/tests/ui/lint/lint-stability-deprecated.stderr b/tests/ui/lint/lint-stability-deprecated.stderr
index bda4ee82d1fc..ce0bc36f6292 100644
--- a/tests/ui/lint/lint-stability-deprecated.stderr
+++ b/tests/ui/lint/lint-stability-deprecated.stderr
@@ -1,8 +1,8 @@
-warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text
-  --> $DIR/lint-stability-deprecated.rs:97:48
+warning: use of deprecated function `lint_stability::deprecated`: text
+  --> $DIR/lint-stability-deprecated.rs:24:9
    |
-LL |         struct S2(T::TypeDeprecated);
-   |                                                ^^^^^^^^^^^^^^^^^
+LL |         deprecated();
+   |         ^^^^^^^^^^
    |
 note: the lint level is defined here
   --> $DIR/lint-stability-deprecated.rs:6:9
@@ -10,12 +10,6 @@ note: the lint level is defined here
 LL | #![warn(deprecated)]
    |         ^^^^^^^^^^
 
-warning: use of deprecated function `lint_stability::deprecated`: text
-  --> $DIR/lint-stability-deprecated.rs:24:9
-   |
-LL |         deprecated();
-   |         ^^^^^^^^^^
-
 warning: use of deprecated method `lint_stability::Trait::trait_deprecated`: text
   --> $DIR/lint-stability-deprecated.rs:29:16
    |
@@ -322,6 +316,12 @@ warning: use of deprecated function `this_crate::MethodTester::test_method_body:
 LL |             fn_in_body();
    |             ^^^^^^^^^^
 
+warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text
+  --> $DIR/lint-stability-deprecated.rs:97:48
+   |
+LL |         struct S2(T::TypeDeprecated);
+   |                                                ^^^^^^^^^^^^^^^^^
+
 warning: use of deprecated associated type `lint_stability::TraitWithAssociatedTypes::TypeDeprecated`: text
   --> $DIR/lint-stability-deprecated.rs:101:13
    |
diff --git a/tests/ui/lint/lint-stability.stderr b/tests/ui/lint/lint-stability.stderr
index 249f3ccaa542..fd57908a77b5 100644
--- a/tests/ui/lint/lint-stability.stderr
+++ b/tests/ui/lint/lint-stability.stderr
@@ -1,12 +1,3 @@
-error[E0658]: use of unstable library feature `unstable_test_feature`
-  --> $DIR/lint-stability.rs:88:48
-   |
-LL |         struct S1(T::TypeUnstable);
-   |                                                ^^^^^^^^^^^^^^^
-   |
-   = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
 error[E0658]: use of unstable library feature `unstable_test_feature`
   --> $DIR/lint-stability.rs:17:5
    |
@@ -376,6 +367,15 @@ LL |         let _ = Unstable::StableVariant;
    = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
+error[E0658]: use of unstable library feature `unstable_test_feature`
+  --> $DIR/lint-stability.rs:88:48
+   |
+LL |         struct S1(T::TypeUnstable);
+   |                                                ^^^^^^^^^^^^^^^
+   |
+   = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
 error[E0658]: use of unstable library feature `unstable_test_feature`
   --> $DIR/lint-stability.rs:92:13
    |

From 7c9d14993643fff92b8cf1351422b76e468d6a27 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Thu, 29 Jan 2026 16:44:05 +0300
Subject: [PATCH 357/583] Add 2 more test cases from 151284 and 151479

---
 .../missing-mir-priv-bounds-extern-2.rs       | 46 +++++++++++++++++++
 .../missing-mir-priv-bounds-extern-3.rs       | 35 ++++++++++++++
 tests/ui/privacy/missing-mir-priv-bounds-2.rs | 29 ++++++++++++
 .../privacy/missing-mir-priv-bounds-2.stderr  | 10 ++++
 tests/ui/privacy/missing-mir-priv-bounds-3.rs | 30 ++++++++++++
 .../privacy/missing-mir-priv-bounds-3.stderr  | 10 ++++
 6 files changed, 160 insertions(+)
 create mode 100644 tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-2.rs
 create mode 100644 tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-3.rs
 create mode 100644 tests/ui/privacy/missing-mir-priv-bounds-2.rs
 create mode 100644 tests/ui/privacy/missing-mir-priv-bounds-2.stderr
 create mode 100644 tests/ui/privacy/missing-mir-priv-bounds-3.rs
 create mode 100644 tests/ui/privacy/missing-mir-priv-bounds-3.stderr

diff --git a/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-2.rs b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-2.rs
new file mode 100644
index 000000000000..38e3cdded5f2
--- /dev/null
+++ b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-2.rs
@@ -0,0 +1,46 @@
+// Doesn't involve `impl Trait` unlike missing-mir-priv-bounds-extern.rs
+
+pub trait ToPriv {
+    type AssocPriv;
+}
+
+pub trait PubTr {
+    #[expect(private_bounds)]
+    type Assoc: ToPriv;
+}
+
+// Dummy and DummyToPriv are only used in call_handler
+struct Dummy;
+struct DummyToPriv;
+impl PubTr for Dummy {
+    type Assoc = DummyToPriv;
+}
+impl ToPriv for DummyToPriv {
+    type AssocPriv = Priv;
+}
+
+pub trait PubTrHandler {
+    fn handle();
+}
+pub fn call_handler() {
+    T::handle::();
+}
+
+struct Priv;
+
+pub trait GetUnreachable {
+    type Assoc;
+}
+
+mod m {
+    pub struct Unreachable;
+
+    impl Unreachable {
+        #[expect(dead_code)]
+        pub fn generic() {}
+    }
+
+    impl crate::GetUnreachable for crate::Priv {
+        type Assoc = Unreachable;
+    }
+}
diff --git a/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-3.rs b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-3.rs
new file mode 100644
index 000000000000..807abe2c4ad8
--- /dev/null
+++ b/tests/ui/privacy/auxiliary/missing-mir-priv-bounds-extern-3.rs
@@ -0,0 +1,35 @@
+struct Priv;
+
+pub trait Super {
+    type AssocSuper: GetUnreachable;
+}
+#[expect(private_bounds)]
+pub trait Sub: Super {}
+
+// This Dummy type is only used in call_handler
+struct Dummy;
+impl Super for Dummy {
+    type AssocSuper = Priv;
+}
+impl Sub for Dummy {}
+
+pub trait SubHandler {
+    fn handle();
+}
+pub fn call_handler() {
+    ::handle::();
+}
+
+pub trait GetUnreachable {
+    type Assoc;
+}
+mod m {
+    pub struct Unreachable;
+    impl Unreachable {
+        #[expect(dead_code)]
+        pub fn generic() {}
+    }
+    impl crate::GetUnreachable for crate::Priv {
+        type Assoc = Unreachable;
+    }
+}
diff --git a/tests/ui/privacy/missing-mir-priv-bounds-2.rs b/tests/ui/privacy/missing-mir-priv-bounds-2.rs
new file mode 100644
index 000000000000..7676fdb4af4b
--- /dev/null
+++ b/tests/ui/privacy/missing-mir-priv-bounds-2.rs
@@ -0,0 +1,29 @@
+// Test case from issue #151284.
+// A private associated type bound allows to leak another private type and result in missing MIR.
+
+//@ build-fail
+//@ aux-crate:dep=missing-mir-priv-bounds-extern-2.rs
+
+extern crate dep;
+use dep::{GetUnreachable, PubTr, PubTrHandler, ToPriv, call_handler};
+
+fn main() {
+    call_handler::();
+}
+
+struct Handler;
+impl PubTrHandler for Handler {
+    fn handle() {
+        ::AccessAssoc::generic::();
+    }
+}
+
+trait Access: PubTr {
+    type AccessAssoc;
+}
+
+impl Access for T {
+    type AccessAssoc = <::AssocPriv as GetUnreachable>::Assoc;
+}
+
+//~? ERROR missing optimized MIR
diff --git a/tests/ui/privacy/missing-mir-priv-bounds-2.stderr b/tests/ui/privacy/missing-mir-priv-bounds-2.stderr
new file mode 100644
index 000000000000..29a10eb1223b
--- /dev/null
+++ b/tests/ui/privacy/missing-mir-priv-bounds-2.stderr
@@ -0,0 +1,10 @@
+error: missing optimized MIR for `dep::m::Unreachable::generic::` in the crate `missing_mir_priv_bounds_extern_2`
+   |
+note: missing optimized MIR for this item (was the crate `missing_mir_priv_bounds_extern_2` compiled with `--emit=metadata`?)
+  --> $DIR/auxiliary/missing-mir-priv-bounds-extern-2.rs:40:9
+   |
+LL |         pub fn generic() {}
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/privacy/missing-mir-priv-bounds-3.rs b/tests/ui/privacy/missing-mir-priv-bounds-3.rs
new file mode 100644
index 000000000000..1d277265451e
--- /dev/null
+++ b/tests/ui/privacy/missing-mir-priv-bounds-3.rs
@@ -0,0 +1,30 @@
+// Test case from issue #151479.
+// A private associated type bound allows to leak another private type and result in missing MIR.
+
+//@ build-fail
+//@ aux-crate:dep=missing-mir-priv-bounds-extern-3.rs
+
+extern crate dep;
+use dep::{GetUnreachable, Sub, SubHandler, Super, call_handler};
+
+fn main() {
+    call_handler::();
+}
+
+struct Handler;
+impl SubHandler for Handler {
+    fn handle() {
+        ::AccessAssoc::generic::();
+    }
+}
+
+// Without this indirection, Handler::handle notices that
+// it's mentioning dep::Priv.
+trait Access: Super {
+    type AccessAssoc;
+}
+impl Access for T {
+    type AccessAssoc = <::AssocSuper as GetUnreachable>::Assoc;
+}
+
+//~? ERROR missing optimized MIR
diff --git a/tests/ui/privacy/missing-mir-priv-bounds-3.stderr b/tests/ui/privacy/missing-mir-priv-bounds-3.stderr
new file mode 100644
index 000000000000..ef464de08cb9
--- /dev/null
+++ b/tests/ui/privacy/missing-mir-priv-bounds-3.stderr
@@ -0,0 +1,10 @@
+error: missing optimized MIR for `dep::m::Unreachable::generic::` in the crate `missing_mir_priv_bounds_extern_3`
+   |
+note: missing optimized MIR for this item (was the crate `missing_mir_priv_bounds_extern_3` compiled with `--emit=metadata`?)
+  --> $DIR/auxiliary/missing-mir-priv-bounds-extern-3.rs:30:9
+   |
+LL |         pub fn generic() {}
+   |         ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+

From 02bd3e86c0ed79e6293d83082f60d78a3c56550a Mon Sep 17 00:00:00 2001
From: Jamie Hill-Daniel 
Date: Thu, 29 Jan 2026 14:52:09 +0000
Subject: [PATCH 358/583] Use `Rustc` prefix for `rustc` attrs in
 `AttributeKind`

---
 .../src/attributes/allow_unstable.rs          |   2 +-
 .../src/attributes/codegen_attrs.rs           |   4 +-
 .../src/attributes/confusables.rs             |   2 +-
 .../src/attributes/dummy.rs                   |   2 +-
 .../src/attributes/link_attrs.rs              |   2 +-
 .../src/attributes/lint_helpers.rs            |   6 +-
 .../src/attributes/stability.rs               |   6 +-
 .../src/attributes/traits.rs                  |  20 ++-
 .../src/attributes/transparency.rs            |   2 +-
 .../rustc_codegen_ssa/src/codegen_attrs.rs    |   6 +-
 .../rustc_const_eval/src/check_consts/mod.rs  |   2 +-
 compiler/rustc_expand/src/base.rs             |   7 +-
 compiler/rustc_expand/src/mbe/macro_rules.rs  |   2 +-
 .../rustc_hir/src/attrs/data_structures.rs    | 149 +++++++++---------
 .../rustc_hir/src/attrs/encode_cross_crate.rs |  42 ++---
 .../rustc_hir_analysis/src/check/check.rs     |   5 +-
 .../src/coherence/inherent_impls.rs           |   4 +-
 compiler/rustc_hir_analysis/src/collect.rs    |  25 +--
 .../rustc_hir_typeck/src/method/suggest.rs    |   2 +-
 compiler/rustc_lint/src/dangling.rs           |   2 +-
 compiler/rustc_lint/src/pass_by_value.rs      |   7 +-
 compiler/rustc_passes/src/check_attr.rs       |  42 ++---
 compiler/rustc_passes/src/lib_features.rs     |   4 +-
 compiler/rustc_passes/src/stability.rs        |  16 +-
 compiler/rustc_privacy/src/lib.rs             |   2 +-
 25 files changed, 187 insertions(+), 176 deletions(-)

diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
index 7323db06a8f1..d825d770fa35 100644
--- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
@@ -57,7 +57,7 @@ impl CombineAttributeParser for AllowConstFnUnstableParser {
     const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
     type Item = Symbol;
     const CONVERT: ConvertFn =
-        |items, first_span| AttributeKind::AllowConstFnUnstable(items, first_span);
+        |items, first_span| AttributeKind::RustcAllowConstFnUnstable(items, first_span);
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
         Allow(Target::Fn),
         Allow(Target::Method(MethodKind::Inherent)),
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 063fa12d3896..485307622291 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -181,7 +181,7 @@ impl SingleAttributeParser for ObjcClassParser {
             cx.emit_err(NullOnObjcClass { span: nv.value_span });
             return None;
         }
-        Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
+        Some(AttributeKind::RustcObjcClass { classname, span: cx.attr_span })
     }
 }
 
@@ -213,7 +213,7 @@ impl SingleAttributeParser for ObjcSelectorParser {
             cx.emit_err(NullOnObjcSelector { span: nv.value_span });
             return None;
         }
-        Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
+        Some(AttributeKind::RustcObjcSelector { methname, span: cx.attr_span })
     }
 }
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
index 0b7ac989346a..1897fed1e250 100644
--- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
@@ -43,7 +43,7 @@ impl AttributeParser for ConfusablesParser {
             return None;
         }
 
-        Some(AttributeKind::Confusables {
+        Some(AttributeKind::RustcConfusables {
             symbols: self.confusables,
             first_span: self.first_span.unwrap(),
         })
diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs
index 0a7d95f31799..9f97af48afa0 100644
--- a/compiler/rustc_attr_parsing/src/attributes/dummy.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs
@@ -16,6 +16,6 @@ impl SingleAttributeParser for DummyParser {
     const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really
 
     fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option {
-        Some(AttributeKind::Dummy)
+        Some(AttributeKind::RustcDummy)
     }
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
index a636b449ca56..548f53a986b8 100644
--- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
@@ -529,7 +529,7 @@ impl NoArgsAttributeParser for StdInternalSymbolParser {
         Allow(Target::Static),
         Allow(Target::ForeignStatic),
     ]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStdInternalSymbol;
 }
 
 pub(crate) struct LinkOrdinalParser;
diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
index 8ca5f0f7228e..60e83a6083ed 100644
--- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
@@ -11,7 +11,7 @@ impl NoArgsAttributeParser for AsPtrParser {
         Allow(Target::Method(MethodKind::Trait { body: true })),
         Allow(Target::Method(MethodKind::TraitImpl)),
     ]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::AsPtr;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAsPtr;
 }
 
 pub(crate) struct PubTransparentParser;
@@ -23,7 +23,7 @@ impl NoArgsAttributeParser for PubTransparentParser {
         Allow(Target::Enum),
         Allow(Target::Union),
     ]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::PubTransparent;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent;
 }
 
 pub(crate) struct PassByValueParser;
@@ -35,7 +35,7 @@ impl NoArgsAttributeParser for PassByValueParser {
         Allow(Target::Enum),
         Allow(Target::TyAlias),
     ]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::PassByValue;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassByValue;
 }
 
 pub(crate) struct RustcShouldNotBeCalledOnConstItems;
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index 8d27e2e276dd..1f01cadcb43e 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -173,7 +173,7 @@ impl AttributeParser for BodyStabilityParser {
     fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option {
         let (stability, span) = self.stability?;
 
-        Some(AttributeKind::BodyStability { stability, span })
+        Some(AttributeKind::RustcBodyStability { stability, span })
     }
 }
 
@@ -185,7 +185,7 @@ impl NoArgsAttributeParser for ConstStabilityIndirectParser {
         Allow(Target::Fn),
         Allow(Target::Method(MethodKind::Inherent)),
     ]);
-    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ConstStabilityIndirect;
+    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStabilityIndirect;
 }
 
 #[derive(Default)]
@@ -258,7 +258,7 @@ impl AttributeParser for ConstStabilityParser {
 
         let (stability, span) = self.stability?;
 
-        Some(AttributeKind::ConstStability { stability, span })
+        Some(AttributeKind::RustcConstStability { stability, span })
     }
 }
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs
index c0db5b4d442a..bfea02e789a0 100644
--- a/compiler/rustc_attr_parsing/src/attributes/traits.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs
@@ -50,7 +50,11 @@ impl SingleAttributeParser for SkipDuringMethodDispatchParser {
                 cx.duplicate_key(arg.span(), key);
             }
         }
-        Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span })
+        Some(AttributeKind::RustcSkipDuringMethodDispatch {
+            array,
+            boxed_slice,
+            span: cx.attr_span,
+        })
     }
 }
 
@@ -59,7 +63,7 @@ impl NoArgsAttributeParser for ParenSugarParser {
     const PATH: &[Symbol] = &[sym::rustc_paren_sugar];
     const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::ParenSugar;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcParenSugar;
 }
 
 pub(crate) struct TypeConstParser;
@@ -91,7 +95,7 @@ impl NoArgsAttributeParser for DenyExplicitImplParser {
     const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl];
     const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDenyExplicitImpl;
 }
 
 pub(crate) struct DynIncompatibleTraitParser;
@@ -99,7 +103,7 @@ impl NoArgsAttributeParser for DynIncompatibleTraitParser {
     const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait];
     const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::DynIncompatibleTrait;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDynIncompatibleTrait;
 }
 
 // Specialization
@@ -109,7 +113,7 @@ impl NoArgsAttributeParser for SpecializationTraitParser {
     const PATH: &[Symbol] = &[sym::rustc_specialization_trait];
     const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::SpecializationTrait;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcSpecializationTrait;
 }
 
 pub(crate) struct UnsafeSpecializationMarkerParser;
@@ -117,7 +121,7 @@ impl NoArgsAttributeParser for UnsafeSpecializationMarkerParser {
     const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker];
     const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::UnsafeSpecializationMarker;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcUnsafeSpecializationMarker;
 }
 
 // Coherence
@@ -127,7 +131,7 @@ impl NoArgsAttributeParser for CoinductiveParser {
     const PATH: &[Symbol] = &[sym::rustc_coinductive];
     const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::Coinductive;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoinductive;
 }
 
 pub(crate) struct AllowIncoherentImplParser;
@@ -136,7 +140,7 @@ impl NoArgsAttributeParser for AllowIncoherentImplParser {
     const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error;
     const ALLOWED_TARGETS: AllowedTargets =
         AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]);
-    const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl;
+    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAllowIncoherentImpl;
 }
 
 pub(crate) struct FundamentalParser;
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index 5b8b3cd151eb..7fa4487b7c69 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -32,6 +32,6 @@ impl SingleAttributeParser for TransparencyParser {
             }
             None => None,
         }
-        .map(AttributeKind::MacroTransparency)
+        .map(AttributeKind::RustcMacroTransparency)
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 4cb390e7d569..9ecb7ab9fd45 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -189,7 +189,7 @@ fn process_builtin_attrs(
             },
             AttributeKind::FfiConst(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
             AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
-            AttributeKind::StdInternalSymbol(_) => {
+            AttributeKind::RustcStdInternalSymbol(_) => {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
             }
             AttributeKind::Linkage(linkage, span) => {
@@ -217,10 +217,10 @@ fn process_builtin_attrs(
             AttributeKind::Sanitize { span, .. } => {
                 interesting_spans.sanitize = Some(*span);
             }
-            AttributeKind::ObjcClass { classname, .. } => {
+            AttributeKind::RustcObjcClass { classname, .. } => {
                 codegen_fn_attrs.objc_class = Some(*classname);
             }
-            AttributeKind::ObjcSelector { methname, .. } => {
+            AttributeKind::RustcObjcSelector { methname, .. } => {
                 codegen_fn_attrs.objc_selector = Some(*methname);
             }
             AttributeKind::EiiForeignItem => {
diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs
index e9824400ab7a..1adba200caaf 100644
--- a/compiler/rustc_const_eval/src/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/check_consts/mod.rs
@@ -83,7 +83,7 @@ pub fn rustc_allow_const_fn_unstable(
 ) -> bool {
     let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
 
-    find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms, _) if syms.contains(&feature_gate))
+    find_attr!(attrs, AttributeKind::RustcAllowConstFnUnstable(syms, _) if syms.contains(&feature_gate))
 }
 
 /// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable".
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 653f2071d898..6b3c7fe97932 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -963,13 +963,16 @@ impl SyntaxExtension {
         let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability);
 
         // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
-        if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability { span, .. } => *span) {
+        if let Some(sp) =
+            find_attr!(attrs, AttributeKind::RustcConstStability { span, .. } => *span)
+        {
             sess.dcx().emit_err(errors::MacroConstStability {
                 span: sp,
                 head_span: sess.source_map().guess_head_span(span),
             });
         }
-        if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{ span, .. } => *span) {
+        if let Some(sp) = find_attr!(attrs, AttributeKind::RustcBodyStability{ span, .. } => *span)
+        {
             sess.dcx().emit_err(errors::MacroBodyStability {
                 span: sp,
                 head_span: sess.source_map().guess_head_span(span),
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 8d2cc23f9763..7cd96211de50 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -819,7 +819,7 @@ pub fn compile_declarative_macro(
     }
     assert!(!kinds.is_empty());
 
-    let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
+    let transparency = find_attr!(attrs, AttributeKind::RustcMacroTransparency(x) => *x)
         .unwrap_or(Transparency::fallback(macro_rules));
 
     if let Some(guar) = guar {
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
index 8a7dee15d4f4..ef9ee4b6fb2d 100644
--- a/compiler/rustc_hir/src/attrs/data_structures.rs
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -746,31 +746,15 @@ pub enum AttributeKind {
     // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
     Align { align: Align, span: Span },
 
-    /// Represents `#[rustc_allow_const_fn_unstable]`.
-    AllowConstFnUnstable(ThinVec, Span),
-
-    /// Represents `#[rustc_allow_incoherent_impl]`.
-    AllowIncoherentImpl(Span),
-
     /// Represents `#[allow_internal_unsafe]`.
     AllowInternalUnsafe(Span),
 
     /// Represents `#[allow_internal_unstable]`.
     AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
 
-    /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
-    AsPtr(Span),
-
     /// Represents `#[automatically_derived]`
     AutomaticallyDerived(Span),
 
-    /// Represents `#[rustc_default_body_unstable]`.
-    BodyStability {
-        stability: DefaultBodyStability,
-        /// Span of the `#[rustc_default_body_unstable(...)]` attribute
-        span: Span,
-    },
-
     /// Represents the trace attribute of `#[cfg_attr]`
     CfgAttrTrace,
 
@@ -780,9 +764,6 @@ pub enum AttributeKind {
     /// Represents `#[cfi_encoding]`
     CfiEncoding { encoding: Symbol },
 
-    /// Represents `#[rustc_coinductive]`.
-    Coinductive(Span),
-
     /// Represents `#[cold]`.
     Cold(Span),
 
@@ -792,26 +773,9 @@ pub enum AttributeKind {
     /// Represents `#[compiler_builtins]`.
     CompilerBuiltins,
 
-    /// Represents `#[rustc_confusables]`.
-    Confusables {
-        symbols: ThinVec,
-        // FIXME(jdonszelmann): remove when target validation code is moved
-        first_span: Span,
-    },
-
     /// Represents `#[const_continue]`.
     ConstContinue(Span),
 
-    /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
-    ConstStability {
-        stability: PartialConstStability,
-        /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
-        span: Span,
-    },
-
-    /// Represents `#[rustc_const_stable_indirect]`.
-    ConstStabilityIndirect,
-
     /// Represents `#[coroutine]`.
     Coroutine(Span),
 
@@ -830,9 +794,6 @@ pub enum AttributeKind {
     /// Represents `#[debugger_visualizer]`.
     DebuggerVisualizer(ThinVec),
 
-    /// Represents `#[rustc_deny_explicit_impl]`.
-    DenyExplicitImpl(Span),
-
     /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
     Deprecation { deprecation: Deprecation, span: Span },
 
@@ -848,12 +809,6 @@ pub enum AttributeKind {
     /// i.e. doc comments.
     DocComment { style: AttrStyle, kind: DocFragmentKind, span: Span, comment: Symbol },
 
-    /// Represents `#[rustc_dummy]`.
-    Dummy,
-
-    /// Represents `#[rustc_dyn_incompatible_trait]`.
-    DynIncompatibleTrait(Span),
-
     /// Implementation detail of `#[eii]`
     EiiDeclaration(EiiDecl),
 
@@ -920,9 +875,6 @@ pub enum AttributeKind {
     /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path).
     MacroExport { span: Span, local_inner_macros: bool },
 
-    /// Represents `#[rustc_macro_transparency]`.
-    MacroTransparency(Transparency),
-
     /// Represents `#[macro_use]`.
     MacroUse { span: Span, arguments: MacroUseArgs },
 
@@ -978,24 +930,12 @@ pub enum AttributeKind {
     /// Represents `#[non_exhaustive]`
     NonExhaustive(Span),
 
-    /// Represents `#[rustc_objc_class]`
-    ObjcClass { classname: Symbol, span: Span },
-
-    /// Represents `#[rustc_objc_selector]`
-    ObjcSelector { methname: Symbol, span: Span },
-
     /// Represents `#[optimize(size|speed)]`
     Optimize(OptimizeAttr, Span),
 
     /// Represents `#[panic_runtime]`
     PanicRuntime,
 
-    /// Represents `#[rustc_paren_sugar]`.
-    ParenSugar(Span),
-
-    /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
-    PassByValue(Span),
-
     /// Represents `#[patchable_function_entry]`
     PatchableFunctionEntry { prefix: u8, entry: u8 },
 
@@ -1023,9 +963,6 @@ pub enum AttributeKind {
     /// Represents `#[profiler_runtime]`
     ProfilerRuntime,
 
-    /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
-    PubTransparent(Span),
-
     /// Represents [`#[recursion_limit]`](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute)
     RecursionLimit { attr_span: Span, limit_span: Span, limit: Limit },
 
@@ -1041,15 +978,55 @@ pub enum AttributeKind {
     /// Represents `#[rustc_allocator_zeroed_variant]`
     RustcAllocatorZeroedVariant { name: Symbol },
 
+    /// Represents `#[rustc_allow_const_fn_unstable]`.
+    RustcAllowConstFnUnstable(ThinVec, Span),
+
+    /// Represents `#[rustc_allow_incoherent_impl]`.
+    RustcAllowIncoherentImpl(Span),
+
+    /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
+    RustcAsPtr(Span),
+
+    /// Represents `#[rustc_default_body_unstable]`.
+    RustcBodyStability {
+        stability: DefaultBodyStability,
+        /// Span of the `#[rustc_default_body_unstable(...)]` attribute
+        span: Span,
+    },
     /// Represents `#[rustc_builtin_macro]`.
     RustcBuiltinMacro { builtin_name: Option, helper_attrs: ThinVec, span: Span },
 
     /// Represents `#[rustc_coherence_is_core]`
     RustcCoherenceIsCore(Span),
 
+    /// Represents `#[rustc_coinductive]`.
+    RustcCoinductive(Span),
+
+    /// Represents `#[rustc_confusables]`.
+    RustcConfusables {
+        symbols: ThinVec,
+        // FIXME(jdonszelmann): remove when target validation code is moved
+        first_span: Span,
+    },
+    /// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
+    RustcConstStability {
+        stability: PartialConstStability,
+        /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
+        span: Span,
+    },
+
+    /// Represents `#[rustc_const_stable_indirect]`.
+    RustcConstStabilityIndirect,
+
     /// Represents `#[rustc_deallocator]`
     RustcDeallocator,
 
+    /// Represents `#[rustc_deny_explicit_impl]`.
+    RustcDenyExplicitImpl(Span),
+
+    /// Represents `#[rustc_dummy]`.
+    RustcDummy,
+
     /// Represents `#[rustc_dump_def_parents]`
     RustcDumpDefParents,
 
@@ -1065,6 +1042,9 @@ pub enum AttributeKind {
     /// Represents `#[rustc_dump_vtable]`
     RustcDumpVtable(Span),
 
+    /// Represents `#[rustc_dyn_incompatible_trait]`.
+    RustcDynIncompatibleTrait(Span),
+
     /// Represents `#[rustc_has_incoherent_inherent_impls]`
     RustcHasIncoherentInherentImpls,
 
@@ -1089,6 +1069,9 @@ pub enum AttributeKind {
     /// Represents `#[rustc_lint_untracked_query_information]`
     RustcLintUntrackedQueryInformation,
 
+    /// Represents `#[rustc_macro_transparency]`.
+    RustcMacroTransparency(Transparency),
+
     /// Represents `#[rustc_main]`.
     RustcMain,
 
@@ -1104,15 +1087,30 @@ pub enum AttributeKind {
     /// Represents `#[rustc_nounwind]`
     RustcNounwind,
 
+    /// Represents `#[rustc_objc_class]`
+    RustcObjcClass { classname: Symbol, span: Span },
+
+    /// Represents `#[rustc_objc_selector]`
+    RustcObjcSelector { methname: Symbol, span: Span },
+
     /// Represents `#[rustc_object_lifetime_default]`.
     RustcObjectLifetimeDefault,
 
     /// Represents `#[rustc_offload_kernel]`
     RustcOffloadKernel,
 
+    /// Represents `#[rustc_paren_sugar]`.
+    RustcParenSugar(Span),
+
+    /// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
+    RustcPassByValue(Span),
+
     /// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]`
     RustcPassIndirectlyInNonRusticAbis(Span),
 
+    /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
+    RustcPubTransparent(Span),
+
     /// Represents `#[rustc_reallocator]`
     RustcReallocator,
 
@@ -1130,6 +1128,18 @@ pub enum AttributeKind {
     /// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
     RustcSimdMonomorphizeLaneLimit(Limit),
 
+    /// Represents `#[rustc_skip_during_method_dispatch]`.
+    RustcSkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
+
+    /// Represents `#[rustc_specialization_trait]`.
+    RustcSpecializationTrait(Span),
+
+    /// Represents `#[rustc_std_internal_symbol]`.
+    RustcStdInternalSymbol(Span),
+
+    /// Represents `#[rustc_unsafe_specialization_marker]`.
+    RustcUnsafeSpecializationMarker(Span),
+
     /// Represents `#[rustc_variance]`
     RustcVariance,
 
@@ -1151,22 +1161,12 @@ pub enum AttributeKind {
     /// Represents `#[should_panic]`
     ShouldPanic { reason: Option, span: Span },
 
-    /// Represents `#[rustc_skip_during_method_dispatch]`.
-    SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
-
-    /// Represents `#[rustc_specialization_trait]`.
-    SpecializationTrait(Span),
-
     /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
     Stability {
         stability: Stability,
         /// Span of the attribute.
         span: Span,
     },
-
-    /// Represents `#[rustc_std_internal_symbol]`.
-    StdInternalSymbol(Span),
-
     /// Represents `#[target_feature(enable = "...")]` and
     /// `#[unsafe(force_target_feature(enable = "...")]`.
     TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool },
@@ -1183,9 +1183,6 @@ pub enum AttributeKind {
     /// Represents `#[type_length_limit]`
     TypeLengthLimit { attr_span: Span, limit_span: Span, limit: Limit },
 
-    /// Represents `#[rustc_unsafe_specialization_marker]`.
-    UnsafeSpecializationMarker(Span),
-
     /// Represents `#[unstable_feature_bound]`.
     UnstableFeatureBound(ThinVec<(Symbol, Span)>),
 
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
index 356575416ade..631b07ff1da2 100644
--- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -19,37 +19,26 @@ impl AttributeKind {
         match self {
             // tidy-alphabetical-start
             Align { .. } => No,
-            AllowConstFnUnstable(..) => No,
-            AllowIncoherentImpl(..) => No,
             AllowInternalUnsafe(..) => Yes,
             AllowInternalUnstable(..) => Yes,
-            AsPtr(..) => Yes,
             AutomaticallyDerived(..) => Yes,
-            BodyStability { .. } => No,
             CfgAttrTrace => Yes,
             CfgTrace(..) => Yes,
             CfiEncoding { .. } => Yes,
-            Coinductive(..) => No,
             Cold(..) => No,
             CollapseDebugInfo(..) => Yes,
             CompilerBuiltins => No,
-            Confusables { .. } => Yes,
             ConstContinue(..) => No,
-            ConstStability { .. } => Yes,
-            ConstStabilityIndirect => No,
             Coroutine(..) => No,
             Coverage(..) => No,
             CrateName { .. } => No,
             CrateType(_) => No,
             CustomMir(_, _, _) => Yes,
             DebuggerVisualizer(..) => No,
-            DenyExplicitImpl(..) => No,
             Deprecation { .. } => Yes,
             DoNotRecommend { .. } => Yes,
             Doc(_) => Yes,
             DocComment { .. } => Yes,
-            Dummy => No,
-            DynIncompatibleTrait(..) => No,
             EiiDeclaration(_) => Yes,
             EiiForeignItem => No,
             EiiImpls(..) => No,
@@ -69,7 +58,6 @@ impl AttributeKind {
             LoopMatch(..) => No,
             MacroEscape(..) => No,
             MacroExport { .. } => Yes,
-            MacroTransparency(..) => Yes,
             MacroUse { .. } => No,
             Marker(..) => No,
             MayDangle(..) => No,
@@ -87,12 +75,8 @@ impl AttributeKind {
             NoMangle(..) => Yes, // Needed for rustdoc
             NoStd(..) => No,
             NonExhaustive(..) => Yes, // Needed for rustdoc
-            ObjcClass { .. } => No,
-            ObjcSelector { .. } => No,
             Optimize(..) => No,
             PanicRuntime => No,
-            ParenSugar(..) => No,
-            PassByValue(..) => Yes,
             PatchableFunctionEntry { .. } => Yes,
             Path(..) => No,
             PatternComplexityLimit { .. } => No,
@@ -102,20 +86,30 @@ impl AttributeKind {
             ProcMacroAttribute(..) => No,
             ProcMacroDerive { .. } => No,
             ProfilerRuntime => No,
-            PubTransparent(..) => Yes,
             RecursionLimit { .. } => No,
             Repr { .. } => No,
             RustcAllocator => No,
             RustcAllocatorZeroed => No,
             RustcAllocatorZeroedVariant { .. } => Yes,
+            RustcAllowConstFnUnstable(..) => No,
+            RustcAllowIncoherentImpl(..) => No,
+            RustcAsPtr(..) => Yes,
+            RustcBodyStability { .. } => No,
             RustcBuiltinMacro { .. } => Yes,
             RustcCoherenceIsCore(..) => No,
+            RustcCoinductive(..) => No,
+            RustcConfusables { .. } => Yes,
+            RustcConstStability { .. } => Yes,
+            RustcConstStabilityIndirect => No,
             RustcDeallocator => No,
+            RustcDenyExplicitImpl(..) => No,
+            RustcDummy => No,
             RustcDumpDefParents => No,
             RustcDumpItemBounds => No,
             RustcDumpPredicates => No,
             RustcDumpUserArgs => No,
             RustcDumpVtable(..) => No,
+            RustcDynIncompatibleTrait(..) => No,
             RustcHasIncoherentInherentImpls => Yes,
             RustcLayoutScalarValidRangeEnd(..) => Yes,
             RustcLayoutScalarValidRangeStart(..) => Yes,
@@ -124,32 +118,38 @@ impl AttributeKind {
             RustcLintOptTy => Yes,
             RustcLintQueryInstability => Yes,
             RustcLintUntrackedQueryInformation => Yes,
+            RustcMacroTransparency(..) => Yes,
             RustcMain => No,
             RustcMustImplementOneOf { .. } => No,
             RustcNeverReturnsNullPointer => Yes,
             RustcNoImplicitAutorefs => Yes,
             RustcNounwind => No,
+            RustcObjcClass { .. } => No,
+            RustcObjcSelector { .. } => No,
             RustcObjectLifetimeDefault => No,
             RustcOffloadKernel => Yes,
+            RustcParenSugar(..) => No,
+            RustcPassByValue(..) => Yes,
             RustcPassIndirectlyInNonRusticAbis(..) => No,
+            RustcPubTransparent(..) => Yes,
             RustcReallocator => No,
             RustcScalableVector { .. } => Yes,
             RustcShouldNotBeCalledOnConstItems(..) => Yes,
             RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
+            RustcSkipDuringMethodDispatch { .. } => No,
+            RustcSpecializationTrait(..) => No,
+            RustcStdInternalSymbol(..) => No,
+            RustcUnsafeSpecializationMarker(..) => No,
             RustcVariance => No,
             RustcVarianceOfOpaques => No,
             Sanitize { .. } => No,
             ShouldPanic { .. } => No,
-            SkipDuringMethodDispatch { .. } => No,
-            SpecializationTrait(..) => No,
             Stability { .. } => Yes,
-            StdInternalSymbol(..) => No,
             TargetFeature { .. } => No,
             ThreadLocal => No,
             TrackCaller(..) => Yes,
             TypeConst(..) => Yes,
             TypeLengthLimit { .. } => No,
-            UnsafeSpecializationMarker(..) => No,
             UnstableFeatureBound(..) => No,
             Used { .. } => No,
             WindowsSubsystem(..) => No,
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index f2b06e1cde71..c00122bce559 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1701,7 +1701,10 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
             ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty),
             ty::Adt(def, args) => {
                 if !def.did().is_local()
-                    && !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_))
+                    && !find_attr!(
+                        tcx.get_all_attrs(def.did()),
+                        AttributeKind::RustcPubTransparent(_)
+                    )
                 {
                     let non_exhaustive = def.is_variant_list_non_exhaustive()
                         || def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive);
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index fe47f3258846..edaf33e493c0 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -91,7 +91,7 @@ impl<'tcx> InherentCollect<'tcx> {
             for &impl_item in items {
                 if !find_attr!(
                     self.tcx.get_all_attrs(impl_item),
-                    AttributeKind::AllowIncoherentImpl(_)
+                    AttributeKind::RustcAllowIncoherentImpl(_)
                 ) {
                     let impl_span = self.tcx.def_span(impl_def_id);
                     return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideRelevant {
@@ -125,7 +125,7 @@ impl<'tcx> InherentCollect<'tcx> {
                 for &impl_item in items {
                     if !find_attr!(
                         self.tcx.get_all_attrs(impl_item),
-                        AttributeKind::AllowIncoherentImpl(_)
+                        AttributeKind::RustcAllowIncoherentImpl(_)
                     ) {
                         let span = self.tcx.def_span(impl_def_id);
                         return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsidePrimitive {
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index dd399f9d90de..f50aff187f25 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -889,7 +889,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
 
     let attrs = tcx.get_all_attrs(def_id);
 
-    let paren_sugar = find_attr!(attrs, AttributeKind::ParenSugar(_));
+    let paren_sugar = find_attr!(attrs, AttributeKind::RustcParenSugar(_));
     if paren_sugar && !tcx.features().unboxed_closures() {
         tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span });
     }
@@ -897,22 +897,23 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
     // Only regular traits can be marker.
     let is_marker = !is_alias && find_attr!(attrs, AttributeKind::Marker(_));
 
-    let rustc_coinductive = find_attr!(attrs, AttributeKind::Coinductive(_));
+    let rustc_coinductive = find_attr!(attrs, AttributeKind::RustcCoinductive(_));
     let is_fundamental = find_attr!(attrs, AttributeKind::Fundamental);
 
     let [skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch] = find_attr!(
         attrs,
-        AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice]
+        AttributeKind::RustcSkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice]
     )
     .unwrap_or([false; 2]);
 
-    let specialization_kind = if find_attr!(attrs, AttributeKind::UnsafeSpecializationMarker(_)) {
-        ty::trait_def::TraitSpecializationKind::Marker
-    } else if find_attr!(attrs, AttributeKind::SpecializationTrait(_)) {
-        ty::trait_def::TraitSpecializationKind::AlwaysApplicable
-    } else {
-        ty::trait_def::TraitSpecializationKind::None
-    };
+    let specialization_kind =
+        if find_attr!(attrs, AttributeKind::RustcUnsafeSpecializationMarker(_)) {
+            ty::trait_def::TraitSpecializationKind::Marker
+        } else if find_attr!(attrs, AttributeKind::RustcSpecializationTrait(_)) {
+            ty::trait_def::TraitSpecializationKind::AlwaysApplicable
+        } else {
+            ty::trait_def::TraitSpecializationKind::None
+        };
 
     let must_implement_one_of = find_attr!(
         attrs,
@@ -923,9 +924,9 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
                 .collect::>()
     );
 
-    let deny_explicit_impl = find_attr!(attrs, AttributeKind::DenyExplicitImpl(_));
+    let deny_explicit_impl = find_attr!(attrs, AttributeKind::RustcDenyExplicitImpl(_));
     let force_dyn_incompatible =
-        find_attr!(attrs, AttributeKind::DynIncompatibleTrait(span) => *span);
+        find_attr!(attrs, AttributeKind::RustcDynIncompatibleTrait(span) => *span);
 
     ty::TraitDef {
         def_id: def_id.to_def_id(),
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 5673d044ad2c..c6241c6a3992 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -2103,7 +2103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 for inherent_method in
                     self.tcx.associated_items(inherent_impl_did).in_definition_order()
                 {
-                    if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols)
+                    if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::RustcConfusables{symbols, ..} => symbols)
                         && candidates.contains(&item_name.name)
                         && inherent_method.is_fn()
                     {
diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs
index af4457f4314b..71ea801a408e 100644
--- a/compiler/rustc_lint/src/dangling.rs
+++ b/compiler/rustc_lint/src/dangling.rs
@@ -268,7 +268,7 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
         && let ty = cx.typeck_results().expr_ty(receiver)
         && owns_allocation(cx.tcx, ty)
         && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
-        && find_attr!(cx.tcx.get_all_attrs(fn_id), AttributeKind::AsPtr(_))
+        && find_attr!(cx.tcx.get_all_attrs(fn_id), AttributeKind::RustcAsPtr(_))
     {
         // FIXME: use `emit_node_lint` when `#[primary_span]` is added.
         cx.tcx.emit_node_span_lint(
diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs
index f4a506d50a41..3823c9aa1861 100644
--- a/compiler/rustc_lint/src/pass_by_value.rs
+++ b/compiler/rustc_lint/src/pass_by_value.rs
@@ -44,7 +44,7 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option
+                if find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcPassByValue(_)) =>
             {
                 let name = cx.tcx.item_ident(def_id);
                 let path_segment = path.segments.last().unwrap();
@@ -52,7 +52,10 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option {
                 if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() {
-                    if find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::PassByValue(_)) {
+                    if find_attr!(
+                        cx.tcx.get_all_attrs(adt.did()),
+                        AttributeKind::RustcPassByValue(_)
+                    ) {
                         return Some(cx.tcx.def_path_str_with_args(adt.did(), args));
                     }
                 }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index cdd141f9233e..541870607b63 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -150,7 +150,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         span: attr_span,
                         stability: Stability { level, feature },
                     }
-                    | AttributeKind::ConstStability {
+                    | AttributeKind::RustcConstStability {
                         span: attr_span,
                         stability: PartialConstStability { level, feature, .. },
                     },
@@ -168,7 +168,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::AllowInternalUnsafe(attr_span) | AttributeKind::AllowInternalUnstable(.., attr_span)) => {
                     self.check_macro_only_attr(*attr_span, span, target, attrs)
                 }
-                Attribute::Parsed(AttributeKind::AllowConstFnUnstable(_, first_span)) => {
+                Attribute::Parsed(AttributeKind::RustcAllowConstFnUnstable(_, first_span)) => {
                     self.check_rustc_allow_const_fn_unstable(hir_id, *first_span, span, target)
                 }
                 Attribute::Parsed(AttributeKind::Deprecation {span: attr_span, .. }) => {
@@ -180,7 +180,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => {
                     self.check_object_lifetime_default(hir_id);
                 }
-                &Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => {
+                &Attribute::Parsed(AttributeKind::RustcPubTransparent(attr_span)) => {
                     self.check_rustc_pub_transparent(attr_span, span, attrs)
                 }
                 Attribute::Parsed(AttributeKind::Align { align, span: attr_span }) => {
@@ -226,29 +226,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)},
                 Attribute::Parsed(
                     // tidy-alphabetical-start
-                    AttributeKind::AllowIncoherentImpl(..)
-                    | AttributeKind::AsPtr(..)
+                    AttributeKind::RustcAllowIncoherentImpl(..)
                     | AttributeKind::AutomaticallyDerived(..)
-                    | AttributeKind::BodyStability { .. }
                     | AttributeKind::CfgAttrTrace
                     | AttributeKind::CfgTrace(..)
                     | AttributeKind::CfiEncoding { .. }
-                    | AttributeKind::Coinductive(..)
                     | AttributeKind::Cold(..)
                     | AttributeKind::CollapseDebugInfo(..)
                     | AttributeKind::CompilerBuiltins
-                    | AttributeKind::Confusables { .. }
-                    | AttributeKind::ConstStabilityIndirect
                     | AttributeKind::Coroutine(..)
                     | AttributeKind::Coverage (..)
                     | AttributeKind::CrateName { .. }
                     | AttributeKind::CrateType(..)
                     | AttributeKind::DebuggerVisualizer(..)
-                    | AttributeKind::DenyExplicitImpl(..)
                     // `#[doc]` is actually a lot more than just doc comments, so is checked below
                     | AttributeKind::DocComment {..}
-                    | AttributeKind::Dummy
-                    | AttributeKind::DynIncompatibleTrait(..)
                     | AttributeKind::EiiDeclaration { .. }
                     | AttributeKind::EiiForeignItem
                     | AttributeKind::ExportName { .. }
@@ -262,7 +254,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     | AttributeKind::LinkSection { .. }
                     | AttributeKind::Linkage(..)
                     | AttributeKind::MacroEscape( .. )
-                    | AttributeKind::MacroTransparency(_)
                     | AttributeKind::MacroUse { .. }
                     | AttributeKind::Marker(..)
                     | AttributeKind::MoveSizeLimit { .. }
@@ -277,12 +268,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     | AttributeKind::NoMain
                     | AttributeKind::NoMangle(..)
                     | AttributeKind::NoStd { .. }
-                    | AttributeKind::ObjcClass { .. }
-                    | AttributeKind::ObjcSelector { .. }
                     | AttributeKind::Optimize(..)
                     | AttributeKind::PanicRuntime
-                    | AttributeKind::ParenSugar(..)
-                    | AttributeKind::PassByValue (..)
                     | AttributeKind::PatchableFunctionEntry { .. }
                     | AttributeKind::Path(..)
                     | AttributeKind::PatternComplexityLimit { .. }
@@ -295,14 +282,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     | AttributeKind::RustcAllocator
                     | AttributeKind::RustcAllocatorZeroed
                     | AttributeKind::RustcAllocatorZeroedVariant { .. }
+                    | AttributeKind::RustcAsPtr(..)
+                    | AttributeKind::RustcBodyStability { .. }
                     | AttributeKind::RustcBuiltinMacro { .. }
                     | AttributeKind::RustcCoherenceIsCore(..)
+                    | AttributeKind::RustcCoinductive(..)
+                    | AttributeKind::RustcConfusables { .. }
+                    | AttributeKind::RustcConstStabilityIndirect
                     | AttributeKind::RustcDeallocator
+                    | AttributeKind::RustcDenyExplicitImpl(..)
+                    | AttributeKind::RustcDummy
                     | AttributeKind::RustcDumpDefParents
                     | AttributeKind::RustcDumpItemBounds
                     | AttributeKind::RustcDumpPredicates
                     | AttributeKind::RustcDumpUserArgs
                     | AttributeKind::RustcDumpVtable(..)
+                    | AttributeKind::RustcDynIncompatibleTrait(..)
                     | AttributeKind::RustcHasIncoherentInherentImpls
                     | AttributeKind::RustcLayoutScalarValidRangeEnd(..)
                     | AttributeKind::RustcLayoutScalarValidRangeStart(..)
@@ -310,26 +305,31 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     | AttributeKind::RustcLintOptTy
                     | AttributeKind::RustcLintQueryInstability
                     | AttributeKind::RustcLintUntrackedQueryInformation
+                    | AttributeKind::RustcMacroTransparency(_)
                     | AttributeKind::RustcMain
                     | AttributeKind::RustcNeverReturnsNullPointer
                     | AttributeKind::RustcNoImplicitAutorefs
                     | AttributeKind::RustcNounwind
+                    | AttributeKind::RustcObjcClass { .. }
+                    | AttributeKind::RustcObjcSelector { .. }
                     | AttributeKind::RustcOffloadKernel
+                    | AttributeKind::RustcParenSugar(..)
+                    | AttributeKind::RustcPassByValue (..)
                     | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)
                     | AttributeKind::RustcReallocator
                     | AttributeKind::RustcScalableVector { .. }
                     | AttributeKind::RustcShouldNotBeCalledOnConstItems(..)
                     | AttributeKind::RustcSimdMonomorphizeLaneLimit(..)
+                    | AttributeKind::RustcSkipDuringMethodDispatch { .. }
+                    | AttributeKind::RustcSpecializationTrait(..)
+                    | AttributeKind::RustcStdInternalSymbol (..)
+                    | AttributeKind::RustcUnsafeSpecializationMarker(..)
                     | AttributeKind::RustcVariance
                     | AttributeKind::RustcVarianceOfOpaques
                     | AttributeKind::ShouldPanic { .. }
-                    | AttributeKind::SkipDuringMethodDispatch { .. }
-                    | AttributeKind::SpecializationTrait(..)
-                    | AttributeKind::StdInternalSymbol (..)
                     | AttributeKind::ThreadLocal
                     | AttributeKind::TypeConst{..}
                     | AttributeKind::TypeLengthLimit { .. }
-                    | AttributeKind::UnsafeSpecializationMarker(..)
                     | AttributeKind::UnstableFeatureBound(..)
                     | AttributeKind::Used { .. }
                     | AttributeKind::WindowsSubsystem(..)
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 8d0ef88610af..1f92643815fd 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -30,10 +30,10 @@ impl<'tcx> LibFeatureCollector<'tcx> {
             Attribute::Parsed(AttributeKind::Stability { stability, span }) => {
                 (stability.feature, stability.level, *span)
             }
-            Attribute::Parsed(AttributeKind::ConstStability { stability, span }) => {
+            Attribute::Parsed(AttributeKind::RustcConstStability { stability, span }) => {
                 (stability.feature, stability.level, *span)
             }
-            Attribute::Parsed(AttributeKind::BodyStability { stability, span }) => {
+            Attribute::Parsed(AttributeKind::RustcBodyStability { stability, span }) => {
                 (stability.feature, stability.level, *span)
             }
             _ => return None,
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 9d94c4cc6225..657b362d5ca1 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -197,7 +197,7 @@ fn lookup_default_body_stability(
 
     let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
     // FIXME: check that this item can have body stability
-    find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability)
+    find_attr!(attrs, AttributeKind::RustcBodyStability { stability, .. } => *stability)
 }
 
 #[instrument(level = "debug", skip(tcx))]
@@ -214,7 +214,7 @@ fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option, def_id: LocalDefId) -> Option *stability);
+        find_attr!(attrs, AttributeKind::RustcConstStability { stability, span: _ } => *stability);
 
     // After checking the immediate attributes, get rid of the span and compute implied
     // const stability: inherit feature gate from regular stability.
@@ -393,7 +393,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
         if let Some(fn_sig) = fn_sig
             && !fn_sig.header.is_const()
             && const_stab.is_some()
-            && find_attr_span!(ConstStability).is_some()
+            && find_attr_span!(RustcConstStability).is_some()
         {
             self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span });
         }
@@ -403,7 +403,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
             && let Some(fn_sig) = fn_sig
             && const_stab.is_const_stable()
             && !stab.is_some_and(|s| s.is_stable())
-            && let Some(const_span) = find_attr_span!(ConstStability)
+            && let Some(const_span) = find_attr_span!(RustcConstStability)
         {
             self.tcx
                 .dcx()
@@ -413,7 +413,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
         if let Some(stab) = &const_stab
             && stab.is_const_stable()
             && stab.const_stable_indirect
-            && let Some(span) = find_attr_span!(ConstStability)
+            && let Some(span) = find_attr_span!(RustcConstStability)
         {
             self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span });
         }
@@ -602,7 +602,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                     let stab = find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span));
 
                     // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
-                    let const_stab = find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability);
+                    let const_stab = find_attr!(attrs, AttributeKind::RustcConstStability{stability, ..} => *stability);
 
                     let unstable_feature_stab =
                         find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i)
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 8656ec6e39ae..73446ddcf7e4 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -497,7 +497,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
         let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id);
         let attrs = self.tcx.hir_attrs(hir_id);
 
-        if find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
+        if find_attr!(attrs, AttributeKind::RustcMacroTransparency(x) => *x)
             .unwrap_or(Transparency::fallback(md.macro_rules))
             != Transparency::Opaque
         {

From 87f75df0b80b038daf307f56e0dde73b8514d195 Mon Sep 17 00:00:00 2001
From: yukang 
Date: Thu, 29 Jan 2026 23:10:46 +0800
Subject: [PATCH 359/583] Fix unused lint error in macro

---
 compiler/rustc_lint/src/unused.rs             | 21 ++++++++++++++-----
 .../lint-unsed-in-macro-issue-151269.rs       | 15 +++++++++++++
 .../lint-unsed-in-macro-issue-151269.stderr   | 19 +++++++++++++++++
 3 files changed, 50 insertions(+), 5 deletions(-)
 create mode 100644 tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.rs
 create mode 100644 tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.stderr

diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 506a16355e22..5516298c5e85 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -539,10 +539,19 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                     );
                 }
                 MustUsePath::Def(span, def_id, reason) => {
-                    let span = span.find_ancestor_not_from_macro().unwrap_or(*span);
+                    let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span);
+                    let is_redundant_let_ignore = cx
+                        .sess()
+                        .source_map()
+                        .span_to_prev_source(ancenstor_span)
+                        .ok()
+                        .map(|prev| prev.trim_end().ends_with("let _ ="))
+                        .unwrap_or(false);
+                    let suggestion_span =
+                        if is_redundant_let_ignore { *span } else { ancenstor_span };
                     cx.emit_span_lint(
                         UNUSED_MUST_USE,
-                        span,
+                        ancenstor_span,
                         UnusedDef {
                             pre: descr_pre,
                             post: descr_post,
@@ -551,11 +560,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                             note: *reason,
                             suggestion: (!is_inner).then_some(if expr_is_from_block {
                                 UnusedDefSuggestion::BlockTailExpr {
-                                    before_span: span.shrink_to_lo(),
-                                    after_span: span.shrink_to_hi(),
+                                    before_span: suggestion_span.shrink_to_lo(),
+                                    after_span: suggestion_span.shrink_to_hi(),
                                 }
                             } else {
-                                UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
+                                UnusedDefSuggestion::NormalExpr {
+                                    span: suggestion_span.shrink_to_lo(),
+                                }
                             }),
                         },
                     );
diff --git a/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.rs b/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.rs
new file mode 100644
index 000000000000..65b8a22d383a
--- /dev/null
+++ b/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.rs
@@ -0,0 +1,15 @@
+#![deny(unused_must_use)]
+
+fn error() -> Result<(), ()> {
+    Err(())
+}
+
+macro_rules! foo {
+    () => {{
+        error();
+    }};
+}
+
+fn main() {
+    let _ = foo!(); //~ ERROR unused `Result` that must be used
+}
diff --git a/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.stderr b/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.stderr
new file mode 100644
index 000000000000..12cedf58974b
--- /dev/null
+++ b/tests/ui/lint/unused/lint-unsed-in-macro-issue-151269.stderr
@@ -0,0 +1,19 @@
+error: unused `Result` that must be used
+  --> $DIR/lint-unsed-in-macro-issue-151269.rs:14:13
+   |
+LL |     let _ = foo!();
+   |             ^^^^^^
+   |
+   = note: this `Result` may be an `Err` variant, which should be handled
+note: the lint level is defined here
+  --> $DIR/lint-unsed-in-macro-issue-151269.rs:1:9
+   |
+LL | #![deny(unused_must_use)]
+   |         ^^^^^^^^^^^^^^^
+help: use `let _ = ...` to ignore the resulting value
+   |
+LL |         let _ = error();
+   |         +++++++
+
+error: aborting due to 1 previous error
+

From 9928723bffc9b3dae673462a78b97077a0bde200 Mon Sep 17 00:00:00 2001
From: Max Heller 
Date: Thu, 29 Jan 2026 10:20:34 -0500
Subject: [PATCH 360/583] Implement `BinaryHeap::pop_if()`

---
 .../alloc/src/collections/binary_heap/mod.rs  | 26 +++++++++++++++++++
 library/alloctests/lib.rs                     |  1 +
 .../tests/collections/binary_heap.rs          | 12 +++++++++
 3 files changed, 39 insertions(+)

diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs
index 5710b2a3a91d..65c8d5213f16 100644
--- a/library/alloc/src/collections/binary_heap/mod.rs
+++ b/library/alloc/src/collections/binary_heap/mod.rs
@@ -649,6 +649,32 @@ impl BinaryHeap {
         })
     }
 
+    /// Removes and returns the greatest item from the binary heap if the predicate
+    /// returns `true`, or [`None`] if the predicate returns false or the heap
+    /// is empty (the predicate will not be called in that case).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::collections::BinaryHeap;
+    /// let mut heap = BinaryHeap::from([1, 2]);
+    /// let pred = |x: &i32| *x % 2 == 0;
+    ///
+    /// assert_eq!(heap.pop_if(pred), Some(2));
+    /// assert_eq!(heap, BinaryHeap::from([1]));
+    /// assert_eq!(heap.pop_if(pred), None);
+    /// assert_eq!(heap, BinaryHeap::from([1]));
+    /// ```
+    ///
+    /// # Time complexity
+    ///
+    /// The worst case cost of `pop_if` on a heap containing *n* elements is *O*(log(*n*)).
+    #[unstable(feature = "binary_heap_pop_if", issue = "151828")]
+    pub fn pop_if(&mut self, predicate: impl FnOnce(&T) -> bool) -> Option {
+        let first = self.data.first()?;
+        if predicate(first) { self.pop() } else { None }
+    }
+
     /// Pushes an item onto the binary heap.
     ///
     /// # Examples
diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs
index fe14480102e3..9806c59ce0e1 100644
--- a/library/alloctests/lib.rs
+++ b/library/alloctests/lib.rs
@@ -17,6 +17,7 @@
 #![feature(allocator_api)]
 #![feature(array_into_iter_constructors)]
 #![feature(assert_matches)]
+#![feature(binary_heap_pop_if)]
 #![feature(box_vec_non_null)]
 #![feature(char_internals)]
 #![feature(const_alloc_error)]
diff --git a/library/alloctests/tests/collections/binary_heap.rs b/library/alloctests/tests/collections/binary_heap.rs
index 95f4c3e614f5..1b6afec7f355 100644
--- a/library/alloctests/tests/collections/binary_heap.rs
+++ b/library/alloctests/tests/collections/binary_heap.rs
@@ -136,6 +136,18 @@ fn test_peek_and_pop() {
     }
 }
 
+#[test]
+fn test_pop_if() {
+    let data = vec![9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
+    let mut sorted = data.clone();
+    sorted.sort();
+    let mut heap = BinaryHeap::from(data);
+    while let Some(popped) = heap.pop_if(|x| *x > 2) {
+        assert_eq!(popped, sorted.pop().unwrap());
+    }
+    assert_eq!(heap.into_sorted_vec(), vec![1, 2]);
+}
+
 #[test]
 fn test_peek_mut() {
     let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];

From b226583d9454d11ff141d258cd194256168842ac Mon Sep 17 00:00:00 2001
From: Jamie Hill-Daniel 
Date: Thu, 29 Jan 2026 15:51:43 +0000
Subject: [PATCH 361/583] Treat unions as 'data types' in attr parsing
 diagnostics

---
 .../rustc_attr_parsing/src/target_checking.rs |  2 +-
 tests/ui/attributes/attr-on-mac-call.stderr   |  6 +-
 .../deprecated-expr-precedence.stderr         |  2 +-
 .../ui/deprecation/deprecation-sanity.stderr  |  2 +-
 ...issue-43106-gating-of-builtin-attrs.stderr | 10 +--
 tests/ui/lint/fn_must_use.stderr              |  2 +-
 .../lint/must_not_suspend/other_items.stderr  |  2 +-
 tests/ui/lint/must_not_suspend/return.stderr  |  2 +-
 .../unused/unused_attributes-must_use.stderr  | 44 +++++------
 tests/ui/pin-ergonomics/pin_v2-attr.stderr    | 76 +++++++++----------
 .../invalid-attribute.stderr                  |  2 +-
 .../param-attrs-builtin-attrs.stderr          | 16 ++--
 .../unsupported_attribute.stderr              |  4 +-
 13 files changed, 85 insertions(+), 85 deletions(-)

diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs
index 79aa06e9475c..fb005477f0fa 100644
--- a/compiler/rustc_attr_parsing/src/target_checking.rs
+++ b/compiler/rustc_attr_parsing/src/target_checking.rs
@@ -205,7 +205,7 @@ pub(crate) fn allowed_targets_applied(
     ];
     const IMPL_LIKE: &[Target] =
         &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
-    const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
+    const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union];
 
     let mut added_fake_targets = Vec::new();
     filter_targets(
diff --git a/tests/ui/attributes/attr-on-mac-call.stderr b/tests/ui/attributes/attr-on-mac-call.stderr
index 1aeec463c71c..3bb50f2d6f65 100644
--- a/tests/ui/attributes/attr-on-mac-call.stderr
+++ b/tests/ui/attributes/attr-on-mac-call.stderr
@@ -55,7 +55,7 @@ LL |     #[deprecated]
    |     ^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements
+   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements
 
 warning: `#[inline]` attribute cannot be used on macro calls
   --> $DIR/attr-on-mac-call.rs:24:5
@@ -136,7 +136,7 @@ LL |     #[deprecated]
    |     ^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements
+   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements
 
 warning: `#[automatically_derived]` attribute cannot be used on macro calls
   --> $DIR/attr-on-mac-call.rs:51:5
@@ -163,7 +163,7 @@ LL |     #[must_use]
    |     ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[no_implicit_prelude]` attribute cannot be used on macro calls
   --> $DIR/attr-on-mac-call.rs:60:5
diff --git a/tests/ui/deprecation/deprecated-expr-precedence.stderr b/tests/ui/deprecation/deprecated-expr-precedence.stderr
index c3124cf86ef4..bd575e6b6276 100644
--- a/tests/ui/deprecation/deprecated-expr-precedence.stderr
+++ b/tests/ui/deprecation/deprecated-expr-precedence.stderr
@@ -5,7 +5,7 @@ LL |     #[deprecated] 0
    |     ^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements
+   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements
    = note: requested on the command line with `-W unused-attributes`
 
 error[E0308]: mismatched types
diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr
index a4dc9f23d3d2..427d14d89c19 100644
--- a/tests/ui/deprecation/deprecation-sanity.stderr
+++ b/tests/ui/deprecation/deprecation-sanity.stderr
@@ -83,7 +83,7 @@ LL | #[deprecated = "hello"]
    | ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements
+   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements
    = note: `#[deny(useless_deprecated)]` on by default
 
 error: aborting due to 10 previous errors
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
index fd90fe4c837b..d69daf45b352 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
@@ -1055,7 +1055,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[must_use]` attribute cannot be used on modules
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:766:17
@@ -1064,7 +1064,7 @@ LL |     mod inner { #![must_use] }
    |                 ^^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[must_use]` attribute cannot be used on type aliases
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:775:5
@@ -1073,7 +1073,7 @@ LL |     #[must_use] type T = S;
    |     ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[must_use]` attribute cannot be used on inherent impl blocks
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5
@@ -1082,7 +1082,7 @@ LL |     #[must_use] impl S { }
    |     ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![windows_subsystem]`
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:786:1
@@ -1635,7 +1635,7 @@ LL | #![must_use]
    | ^^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: 175 warnings emitted
 
diff --git a/tests/ui/lint/fn_must_use.stderr b/tests/ui/lint/fn_must_use.stderr
index 0e8da873e7c3..bdf4eb6de4a5 100644
--- a/tests/ui/lint/fn_must_use.stderr
+++ b/tests/ui/lint/fn_must_use.stderr
@@ -5,7 +5,7 @@ LL |     #[must_use]
    |     ^^^^^^^^^^^
    |
    = 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
+   = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, and traits
    = note: requested on the command line with `-W unused-attributes`
 
 warning: unused return value of `need_to_use_this_value` that must be used
diff --git a/tests/ui/lint/must_not_suspend/other_items.stderr b/tests/ui/lint/must_not_suspend/other_items.stderr
index 999a9d2fb3dc..289230b027ad 100644
--- a/tests/ui/lint/must_not_suspend/other_items.stderr
+++ b/tests/ui/lint/must_not_suspend/other_items.stderr
@@ -4,7 +4,7 @@ error: `#[must_not_suspend]` attribute cannot be used on modules
 LL | #[must_not_suspend]
    | ^^^^^^^^^^^^^^^^^^^
    |
-   = help: `#[must_not_suspend]` can be applied to data types, traits, and unions
+   = help: `#[must_not_suspend]` can be applied to data types and traits
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lint/must_not_suspend/return.stderr b/tests/ui/lint/must_not_suspend/return.stderr
index 1a81b1a39f0c..b041491128e1 100644
--- a/tests/ui/lint/must_not_suspend/return.stderr
+++ b/tests/ui/lint/must_not_suspend/return.stderr
@@ -4,7 +4,7 @@ error: `#[must_not_suspend]` attribute cannot be used on functions
 LL | #[must_not_suspend]
    | ^^^^^^^^^^^^^^^^^^^
    |
-   = help: `#[must_not_suspend]` can be applied to data types, traits, and unions
+   = help: `#[must_not_suspend]` can be applied to data types and traits
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/lint/unused/unused_attributes-must_use.stderr b/tests/ui/lint/unused/unused_attributes-must_use.stderr
index 3192c0994548..3fc340b5188f 100644
--- a/tests/ui/lint/unused/unused_attributes-must_use.stderr
+++ b/tests/ui/lint/unused/unused_attributes-must_use.stderr
@@ -5,7 +5,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 note: the lint level is defined here
   --> $DIR/unused_attributes-must_use.rs:4:9
    |
@@ -19,7 +19,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on modules
   --> $DIR/unused_attributes-must_use.rs:11:1
@@ -28,7 +28,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on use statements
   --> $DIR/unused_attributes-must_use.rs:15:1
@@ -37,7 +37,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on constants
   --> $DIR/unused_attributes-must_use.rs:19:1
@@ -46,7 +46,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on statics
   --> $DIR/unused_attributes-must_use.rs:22:1
@@ -55,7 +55,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on inherent impl blocks
   --> $DIR/unused_attributes-must_use.rs:40:1
@@ -64,7 +64,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on foreign modules
   --> $DIR/unused_attributes-must_use.rs:55:1
@@ -73,7 +73,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on foreign statics
   --> $DIR/unused_attributes-must_use.rs:59:5
@@ -82,7 +82,7 @@ LL |     #[must_use]
    |     ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on type aliases
   --> $DIR/unused_attributes-must_use.rs:73:1
@@ -91,7 +91,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on type parameters
   --> $DIR/unused_attributes-must_use.rs:77:8
@@ -100,7 +100,7 @@ LL | fn qux<#[must_use] T>(_: T) {}
    |        ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on associated consts
   --> $DIR/unused_attributes-must_use.rs:82:5
@@ -109,7 +109,7 @@ LL |     #[must_use]
    |     ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on associated types
   --> $DIR/unused_attributes-must_use.rs:85:5
@@ -118,7 +118,7 @@ LL |     #[must_use]
    |     ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on trait impl blocks
   --> $DIR/unused_attributes-must_use.rs:95:1
@@ -127,7 +127,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on trait methods in impl blocks
   --> $DIR/unused_attributes-must_use.rs:100:5
@@ -136,7 +136,7 @@ LL |     #[must_use]
    |     ^^^^^^^^^^^
    |
    = 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
+   = help: `#[must_use]` can be applied to data types, foreign functions, functions, inherent methods, provided trait methods, required trait methods, and traits
 
 error: `#[must_use]` attribute cannot be used on trait aliases
   --> $DIR/unused_attributes-must_use.rs:107:1
@@ -145,7 +145,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on macro defs
   --> $DIR/unused_attributes-must_use.rs:111:1
@@ -154,7 +154,7 @@ LL | #[must_use]
    | ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on statements
   --> $DIR/unused_attributes-must_use.rs:120:5
@@ -163,7 +163,7 @@ LL |     #[must_use]
    |     ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on closures
   --> $DIR/unused_attributes-must_use.rs:125:13
@@ -172,7 +172,7 @@ LL |     let x = #[must_use]
    |             ^^^^^^^^^^^
    |
    = 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, methods, traits, and unions
+   = help: `#[must_use]` can be applied to data types, foreign functions, functions, methods, and traits
 
 error: `#[must_use]` attribute cannot be used on match arms
   --> $DIR/unused_attributes-must_use.rs:148:9
@@ -181,7 +181,7 @@ LL |         #[must_use]
    |         ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on struct fields
   --> $DIR/unused_attributes-must_use.rs:157:28
@@ -190,7 +190,7 @@ LL |     let s = PatternField { #[must_use]  foo: 123 };
    |                            ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: `#[must_use]` attribute cannot be used on pattern fields
   --> $DIR/unused_attributes-must_use.rs:159:24
@@ -199,7 +199,7 @@ LL |     let PatternField { #[must_use] foo } = s;
    |                        ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 error: unused `X` that must be used
   --> $DIR/unused_attributes-must_use.rs:130:5
diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.stderr b/tests/ui/pin-ergonomics/pin_v2-attr.stderr
index c297afa87a73..8f8a9f3b3a19 100644
--- a/tests/ui/pin-ergonomics/pin_v2-attr.stderr
+++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr
@@ -4,7 +4,7 @@ error: `#[pin_v2]` attribute cannot be used on macro calls
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters
   --> $DIR/pin_v2-attr.rs:84:12
@@ -18,7 +18,7 @@ error: `#[pin_v2]` attribute cannot be used on crates
 LL | #![pin_v2]
    | ^^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on type parameters
   --> $DIR/pin_v2-attr.rs:27:10
@@ -26,7 +26,7 @@ error: `#[pin_v2]` attribute cannot be used on type parameters
 LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> {
    |          ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on type parameters
   --> $DIR/pin_v2-attr.rs:27:23
@@ -34,7 +34,7 @@ error: `#[pin_v2]` attribute cannot be used on type parameters
 LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> {
    |                       ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on enum variants
   --> $DIR/pin_v2-attr.rs:30:5
@@ -42,7 +42,7 @@ error: `#[pin_v2]` attribute cannot be used on enum variants
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on struct fields
   --> $DIR/pin_v2-attr.rs:32:18
@@ -50,7 +50,7 @@ error: `#[pin_v2]` attribute cannot be used on struct fields
 LL |     TupleVariant(#[pin_v2] T),
    |                  ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on struct fields
   --> $DIR/pin_v2-attr.rs:34:9
@@ -58,7 +58,7 @@ error: `#[pin_v2]` attribute cannot be used on struct fields
 LL |         #[pin_v2]
    |         ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on traits
   --> $DIR/pin_v2-attr.rs:39:1
@@ -66,7 +66,7 @@ error: `#[pin_v2]` attribute cannot be used on traits
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on associated consts
   --> $DIR/pin_v2-attr.rs:41:5
@@ -74,7 +74,7 @@ error: `#[pin_v2]` attribute cannot be used on associated consts
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on associated types
   --> $DIR/pin_v2-attr.rs:43:5
@@ -82,7 +82,7 @@ error: `#[pin_v2]` attribute cannot be used on associated types
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on required trait methods
   --> $DIR/pin_v2-attr.rs:46:5
@@ -90,7 +90,7 @@ error: `#[pin_v2]` attribute cannot be used on required trait methods
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on provided trait methods
   --> $DIR/pin_v2-attr.rs:48:5
@@ -98,7 +98,7 @@ error: `#[pin_v2]` attribute cannot be used on provided trait methods
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on trait aliases
   --> $DIR/pin_v2-attr.rs:52:1
@@ -106,7 +106,7 @@ error: `#[pin_v2]` attribute cannot be used on trait aliases
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on inherent impl blocks
   --> $DIR/pin_v2-attr.rs:55:1
@@ -114,7 +114,7 @@ error: `#[pin_v2]` attribute cannot be used on inherent impl blocks
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on delegations
   --> $DIR/pin_v2-attr.rs:58:5
@@ -122,7 +122,7 @@ error: `#[pin_v2]` attribute cannot be used on delegations
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on inherent methods
   --> $DIR/pin_v2-attr.rs:61:5
@@ -130,7 +130,7 @@ error: `#[pin_v2]` attribute cannot be used on inherent methods
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on trait impl blocks
   --> $DIR/pin_v2-attr.rs:65:1
@@ -138,7 +138,7 @@ error: `#[pin_v2]` attribute cannot be used on trait impl blocks
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on trait methods in impl blocks
   --> $DIR/pin_v2-attr.rs:67:5
@@ -146,7 +146,7 @@ error: `#[pin_v2]` attribute cannot be used on trait methods in impl blocks
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on extern crates
   --> $DIR/pin_v2-attr.rs:71:1
@@ -154,7 +154,7 @@ error: `#[pin_v2]` attribute cannot be used on extern crates
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on use statements
   --> $DIR/pin_v2-attr.rs:74:1
@@ -162,7 +162,7 @@ error: `#[pin_v2]` attribute cannot be used on use statements
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on statics
   --> $DIR/pin_v2-attr.rs:77:1
@@ -170,7 +170,7 @@ error: `#[pin_v2]` attribute cannot be used on statics
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on constants
   --> $DIR/pin_v2-attr.rs:80:1
@@ -178,7 +178,7 @@ error: `#[pin_v2]` attribute cannot be used on constants
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on functions
   --> $DIR/pin_v2-attr.rs:83:1
@@ -186,7 +186,7 @@ error: `#[pin_v2]` attribute cannot be used on functions
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on function params
   --> $DIR/pin_v2-attr.rs:84:12
@@ -194,7 +194,7 @@ error: `#[pin_v2]` attribute cannot be used on function params
 LL | fn f(#[pin_v2] param: Foo)
    |            ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on closures
   --> $DIR/pin_v2-attr.rs:92:5
@@ -202,7 +202,7 @@ error: `#[pin_v2]` attribute cannot be used on closures
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on expressions
   --> $DIR/pin_v2-attr.rs:94:5
@@ -210,7 +210,7 @@ error: `#[pin_v2]` attribute cannot be used on expressions
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on struct fields
   --> $DIR/pin_v2-attr.rs:98:9
@@ -218,7 +218,7 @@ error: `#[pin_v2]` attribute cannot be used on struct fields
 LL |         #[pin_v2]
    |         ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on statements
   --> $DIR/pin_v2-attr.rs:96:5
@@ -226,7 +226,7 @@ error: `#[pin_v2]` attribute cannot be used on statements
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on match arms
   --> $DIR/pin_v2-attr.rs:102:9
@@ -234,7 +234,7 @@ error: `#[pin_v2]` attribute cannot be used on match arms
 LL |         #[pin_v2]
    |         ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on pattern fields
   --> $DIR/pin_v2-attr.rs:106:13
@@ -242,7 +242,7 @@ error: `#[pin_v2]` attribute cannot be used on pattern fields
 LL |             #[pin_v2]
    |             ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on where predicates
   --> $DIR/pin_v2-attr.rs:88:5
@@ -250,7 +250,7 @@ error: `#[pin_v2]` attribute cannot be used on where predicates
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on modules
   --> $DIR/pin_v2-attr.rs:112:1
@@ -258,7 +258,7 @@ error: `#[pin_v2]` attribute cannot be used on modules
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on foreign modules
   --> $DIR/pin_v2-attr.rs:115:1
@@ -266,7 +266,7 @@ error: `#[pin_v2]` attribute cannot be used on foreign modules
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on foreign types
   --> $DIR/pin_v2-attr.rs:117:5
@@ -274,7 +274,7 @@ error: `#[pin_v2]` attribute cannot be used on foreign types
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on foreign statics
   --> $DIR/pin_v2-attr.rs:120:5
@@ -282,7 +282,7 @@ error: `#[pin_v2]` attribute cannot be used on foreign statics
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on foreign functions
   --> $DIR/pin_v2-attr.rs:123:5
@@ -290,7 +290,7 @@ error: `#[pin_v2]` attribute cannot be used on foreign functions
 LL |     #[pin_v2]
    |     ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on type aliases
   --> $DIR/pin_v2-attr.rs:127:1
@@ -298,7 +298,7 @@ error: `#[pin_v2]` attribute cannot be used on type aliases
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: `#[pin_v2]` attribute cannot be used on macro defs
   --> $DIR/pin_v2-attr.rs:130:1
@@ -306,7 +306,7 @@ error: `#[pin_v2]` attribute cannot be used on macro defs
 LL | #[pin_v2]
    | ^^^^^^^^^
    |
-   = help: `#[pin_v2]` can be applied to data types and unions
+   = help: `#[pin_v2]` can only be applied to data types
 
 error: aborting due to 39 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr
index 98e8f1235e6b..d711c3f2eb12 100644
--- a/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr
+++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr
@@ -21,7 +21,7 @@ error: `#[non_exhaustive]` attribute cannot be used on unions
 LL | #[non_exhaustive]
    | ^^^^^^^^^^^^^^^^^
    |
-   = help: `#[non_exhaustive]` can be applied to data types and enum variants
+   = help: `#[non_exhaustive]` can be applied to enum variants, enums, and structs
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr
index cc08307a18d0..c234cb31bc56 100644
--- a/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr
+++ b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr
@@ -389,7 +389,7 @@ LL |     #[must_use]
    |     ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
    = note: requested on the command line with `-W unused-attributes`
 
 warning: `#[no_mangle]` attribute cannot be used on function params
@@ -408,7 +408,7 @@ LL |         #[must_use]
    |         ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[no_mangle]` attribute cannot be used on function params
   --> $DIR/param-attrs-builtin-attrs.rs:70:9
@@ -426,7 +426,7 @@ LL |         #[must_use]
    |         ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[no_mangle]` attribute cannot be used on function params
   --> $DIR/param-attrs-builtin-attrs.rs:89:9
@@ -444,7 +444,7 @@ LL |         #[must_use]
    |         ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[no_mangle]` attribute cannot be used on function params
   --> $DIR/param-attrs-builtin-attrs.rs:114:9
@@ -462,7 +462,7 @@ LL |         #[must_use]
    |         ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[no_mangle]` attribute cannot be used on function params
   --> $DIR/param-attrs-builtin-attrs.rs:137:9
@@ -480,7 +480,7 @@ LL |         #[must_use]
    |         ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[no_mangle]` attribute cannot be used on function params
   --> $DIR/param-attrs-builtin-attrs.rs:156:9
@@ -498,7 +498,7 @@ LL |         #[must_use]
    |         ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[no_mangle]` attribute cannot be used on function params
   --> $DIR/param-attrs-builtin-attrs.rs:180:9
@@ -516,7 +516,7 @@ LL |         #[must_use]
    |         ^^^^^^^^^^^
    |
    = 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, functions, traits, and unions
+   = help: `#[must_use]` can be applied to data types, functions, and traits
 
 warning: `#[no_mangle]` attribute cannot be used on function params
   --> $DIR/param-attrs-builtin-attrs.rs:201:9
diff --git a/tests/ui/where-clauses/unsupported_attribute.stderr b/tests/ui/where-clauses/unsupported_attribute.stderr
index 9ebc14c40a14..e69eff976c3f 100644
--- a/tests/ui/where-clauses/unsupported_attribute.stderr
+++ b/tests/ui/where-clauses/unsupported_attribute.stderr
@@ -64,7 +64,7 @@ error: `#[deprecated]` attribute cannot be used on where predicates
 LL |     #[deprecated] T: Trait,
    |     ^^^^^^^^^^^^^
    |
-   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements
+   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements
 
 error: `#[deprecated]` attribute cannot be used on where predicates
   --> $DIR/unsupported_attribute.rs:25:5
@@ -72,7 +72,7 @@ error: `#[deprecated]` attribute cannot be used on where predicates
 LL |     #[deprecated] 'a: 'static,
    |     ^^^^^^^^^^^^^
    |
-   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, unions, and use statements
+   = help: `#[deprecated]` can be applied to associated consts, associated types, constants, crates, data types, enum variants, foreign statics, functions, inherent impl blocks, macro defs, modules, statics, struct fields, traits, type aliases, and use statements
 
 error: `#[automatically_derived]` attribute cannot be used on where predicates
   --> $DIR/unsupported_attribute.rs:26:5

From 63973953621c9f1aa84ceae6887d1ce818a4d98a Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Thu, 29 Jan 2026 17:08:19 +0100
Subject: [PATCH 362/583] Update `askama` version to `0.15.4`

---
 Cargo.lock                              | 16 ++++++++--------
 src/ci/citool/Cargo.toml                |  2 +-
 src/librustdoc/Cargo.toml               |  2 +-
 src/tools/generate-copyright/Cargo.toml |  2 +-
 4 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 35ad4472e6a3..6be4565374af 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -184,9 +184,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
 [[package]]
 name = "askama"
-version = "0.15.3"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a800c6f7c005e5bcb76ff0b9e61c9e54ad379ce4e83a88ed14ff487a73776d"
+checksum = "08e1676b346cadfec169374f949d7490fd80a24193d37d2afce0c047cf695e57"
 dependencies = [
  "askama_macros",
  "itoa",
@@ -197,9 +197,9 @@ dependencies = [
 
 [[package]]
 name = "askama_derive"
-version = "0.15.3"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cb7657165bac49b5c533850e7cd67c1c60059aefc31088f89aa431c8a90d5d9"
+checksum = "7661ff56517787343f376f75db037426facd7c8d3049cef8911f1e75016f3a37"
 dependencies = [
  "askama_parser",
  "basic-toml",
@@ -214,18 +214,18 @@ dependencies = [
 
 [[package]]
 name = "askama_macros"
-version = "0.15.3"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e55eacd3e54d32483cd10d0a881a0f28a40f3a763704ac9b8693edc39d7321c7"
+checksum = "713ee4dbfd1eb719c2dab859465b01fa1d21cb566684614a713a6b7a99a4e47b"
 dependencies = [
  "askama_derive",
 ]
 
 [[package]]
 name = "askama_parser"
-version = "0.15.3"
+version = "0.15.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20c3df8886ab5acdcd76eee93b3e2df1ef734251438b5b942b5fea22c50d2a0f"
+checksum = "1d62d674238a526418b30c0def480d5beadb9d8964e7f38d635b03bf639c704c"
 dependencies = [
  "rustc-hash 2.1.1",
  "serde",
diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml
index c2a926b3eaef..0b1f2fe79bf7 100644
--- a/src/ci/citool/Cargo.toml
+++ b/src/ci/citool/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2024"
 
 [dependencies]
 anyhow = "1"
-askama = "0.15.3"
+askama = "0.15.4"
 clap = { version = "4.5", features = ["derive"] }
 csv = "1"
 diff = "0.1"
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index efc44a8a2d51..033d3ecdd03d 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -10,7 +10,7 @@ path = "lib.rs"
 [dependencies]
 # tidy-alphabetical-start
 arrayvec = { version = "0.7", default-features = false }
-askama = { version = "0.15.3", default-features = false, features = ["alloc", "config", "derive"] }
+askama = { version = "0.15.4", default-features = false, features = ["alloc", "config", "derive"] }
 base64 = "0.21.7"
 indexmap = { version = "2", features = ["serde"] }
 itertools = "0.12"
diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml
index 965f7f992e75..17b5c727964f 100644
--- a/src/tools/generate-copyright/Cargo.toml
+++ b/src/tools/generate-copyright/Cargo.toml
@@ -8,7 +8,7 @@ description = "Produces a manifest of all the copyrighted materials in the Rust
 
 [dependencies]
 anyhow = "1.0.65"
-askama = "0.15.3"
+askama = "0.15.4"
 cargo_metadata = "0.21"
 serde = { version = "1.0.147", features = ["derive"] }
 serde_json = "1.0.85"

From 959595fb45ac706b1a2c4e7774f69d18ae6441f3 Mon Sep 17 00:00:00 2001
From: IonicMC 
Date: Thu, 29 Jan 2026 20:26:06 +0200
Subject: [PATCH 363/583] Fix typo for Maybe dangling docs

Boxe's -> Box's
---
 library/core/src/mem/maybe_dangling.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/core/src/mem/maybe_dangling.rs b/library/core/src/mem/maybe_dangling.rs
index 3c5437757e97..a5f77e667f97 100644
--- a/library/core/src/mem/maybe_dangling.rs
+++ b/library/core/src/mem/maybe_dangling.rs
@@ -29,7 +29,7 @@ use crate::{mem, ptr};
 /// mem::forget(boxed); // <-- this is UB!
 /// ```
 ///
-/// Even though the `Box`e's destructor is not run (and thus we don't have a double free bug), this
+/// Even though the `Box`'s destructor is not run (and thus we don't have a double free bug), this
 /// code is still UB. This is because when moving `boxed` into `forget`, its validity invariants
 /// are asserted, causing UB since the `Box` is dangling. The safety comment is as such wrong, as
 /// moving the `boxed` variable as part of the `forget` call *is* a use.

From 2b0ce429ff81543e1a312a5a966302faf539f6b1 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 30 Jan 2026 00:09:31 +0530
Subject: [PATCH 364/583] remove codec and framing

---
 .../crates/proc-macro-api/src/transport/codec.rs  | 15 ---------------
 .../proc-macro-api/src/transport/framing.rs       | 14 --------------
 2 files changed, 29 deletions(-)
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs
deleted file mode 100644
index c9afad260a56..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//! Protocol codec
-
-use std::io;
-
-use serde::de::DeserializeOwned;
-
-use crate::transport::framing::Framing;
-
-pub mod json;
-pub mod postcard;
-
-pub trait Codec: Framing {
-    fn encode(msg: &T) -> io::Result;
-    fn decode(buf: &mut Self::Buf) -> io::Result;
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs
deleted file mode 100644
index 56c3b68e8cd2..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//! Protocol framing
-
-use std::io::{self, BufRead, Write};
-
-pub trait Framing {
-    type Buf: Default + Send + Sync;
-
-    fn read<'a, R: BufRead + ?Sized>(
-        inp: &mut R,
-        buf: &'a mut Self::Buf,
-    ) -> io::Result>;
-
-    fn write(out: &mut W, buf: &Self::Buf) -> io::Result<()>;
-}

From 06c0fde840a2d369eb917b6410111a9c32d046e9 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 30 Jan 2026 00:10:09 +0530
Subject: [PATCH 365/583] move json and postcard as top level api's

---
 .../src/transport/codec/json.rs               | 58 -------------------
 .../src/transport/codec/postcard.rs           | 40 -------------
 .../proc-macro-api/src/transport/json.rs      | 48 +++++++++++++++
 .../proc-macro-api/src/transport/postcard.rs  | 30 ++++++++++
 4 files changed, 78 insertions(+), 98 deletions(-)
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs
 create mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs
 create mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs
deleted file mode 100644
index 96db802e0bfd..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-//! Protocol functions for json.
-use std::io::{self, BufRead, Write};
-
-use serde::{Serialize, de::DeserializeOwned};
-
-use crate::{Codec, transport::framing::Framing};
-
-pub struct JsonProtocol;
-
-impl Framing for JsonProtocol {
-    type Buf = String;
-
-    fn read<'a, R: BufRead + ?Sized>(
-        inp: &mut R,
-        buf: &'a mut String,
-    ) -> io::Result> {
-        loop {
-            buf.clear();
-
-            inp.read_line(buf)?;
-            buf.pop(); // Remove trailing '\n'
-
-            if buf.is_empty() {
-                return Ok(None);
-            }
-
-            // Some ill behaved macro try to use stdout for debugging
-            // We ignore it here
-            if !buf.starts_with('{') {
-                tracing::error!("proc-macro tried to print : {}", buf);
-                continue;
-            }
-
-            return Ok(Some(buf));
-        }
-    }
-
-    fn write(out: &mut W, buf: &String) -> io::Result<()> {
-        tracing::debug!("> {}", buf);
-        out.write_all(buf.as_bytes())?;
-        out.write_all(b"\n")?;
-        out.flush()
-    }
-}
-
-impl Codec for JsonProtocol {
-    fn encode(msg: &T) -> io::Result {
-        Ok(serde_json::to_string(msg)?)
-    }
-
-    fn decode(buf: &mut String) -> io::Result {
-        let mut deserializer = serde_json::Deserializer::from_str(buf);
-        // Note that some proc-macro generate very deep syntax tree
-        // We have to disable the current limit of serde here
-        deserializer.disable_recursion_limit();
-        Ok(T::deserialize(&mut deserializer)?)
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs
deleted file mode 100644
index 6f5319e75b37..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-//! Postcard encode and decode implementations.
-
-use std::io::{self, BufRead, Write};
-
-use serde::{Serialize, de::DeserializeOwned};
-
-use crate::{Codec, transport::framing::Framing};
-
-pub struct PostcardProtocol;
-
-impl Framing for PostcardProtocol {
-    type Buf = Vec;
-
-    fn read<'a, R: BufRead + ?Sized>(
-        inp: &mut R,
-        buf: &'a mut Vec,
-    ) -> io::Result>> {
-        buf.clear();
-        let n = inp.read_until(0, buf)?;
-        if n == 0 {
-            return Ok(None);
-        }
-        Ok(Some(buf))
-    }
-
-    fn write(out: &mut W, buf: &Vec) -> io::Result<()> {
-        out.write_all(buf)?;
-        out.flush()
-    }
-}
-
-impl Codec for PostcardProtocol {
-    fn encode(msg: &T) -> io::Result> {
-        postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
-    }
-
-    fn decode(buf: &mut Self::Buf) -> io::Result {
-        postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs
new file mode 100644
index 000000000000..da79dc5309b5
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs
@@ -0,0 +1,48 @@
+//! Protocol functions for json.
+use std::io::{self, BufRead, Write};
+
+use serde::{Serialize, de::DeserializeOwned};
+
+pub fn read<'a, R: BufRead + ?Sized>(
+    inp: &mut R,
+    buf: &'a mut String,
+) -> io::Result> {
+    loop {
+        buf.clear();
+
+        inp.read_line(buf)?;
+        buf.pop(); // Remove trailing '\n'
+
+        if buf.is_empty() {
+            return Ok(None);
+        }
+
+        // Some ill behaved macro try to use stdout for debugging
+        // We ignore it here
+        if !buf.starts_with('{') {
+            tracing::error!("proc-macro tried to print : {}", buf);
+            continue;
+        }
+
+        return Ok(Some(buf));
+    }
+}
+
+pub fn write(out: &mut W, buf: &String) -> io::Result<()> {
+    tracing::debug!("> {}", buf);
+    out.write_all(buf.as_bytes())?;
+    out.write_all(b"\n")?;
+    out.flush()
+}
+
+pub fn encode(msg: &T) -> io::Result {
+    Ok(serde_json::to_string(msg)?)
+}
+
+pub fn decode(buf: &mut str) -> io::Result {
+    let mut deserializer = serde_json::Deserializer::from_str(buf);
+    // Note that some proc-macro generate very deep syntax tree
+    // We have to disable the current limit of serde here
+    deserializer.disable_recursion_limit();
+    Ok(T::deserialize(&mut deserializer)?)
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs
new file mode 100644
index 000000000000..ddd5f405d5dc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs
@@ -0,0 +1,30 @@
+//! Postcard encode and decode implementations.
+
+use std::io::{self, BufRead, Write};
+
+use serde::{Serialize, de::DeserializeOwned};
+
+pub fn read<'a, R: BufRead + ?Sized>(
+    inp: &mut R,
+    buf: &'a mut Vec,
+) -> io::Result>> {
+    buf.clear();
+    let n = inp.read_until(0, buf)?;
+    if n == 0 {
+        return Ok(None);
+    }
+    Ok(Some(buf))
+}
+
+pub fn write(out: &mut W, buf: &[u8]) -> io::Result<()> {
+    out.write_all(buf)?;
+    out.flush()
+}
+
+pub fn encode(msg: &T) -> io::Result> {
+    postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
+}
+
+pub fn decode(buf: &mut [u8]) -> io::Result {
+    postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
+}

From dfb22b94069a58ac2c98fc1af0d03b7cb64473e2 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 30 Jan 2026 00:11:52 +0530
Subject: [PATCH 366/583] adapt proc-macro-api to remove codec abstraction

---
 .../src/bidirectional_protocol.rs             | 23 +++++------
 .../src/bidirectional_protocol/msg.rs         | 21 +++++++++-
 .../proc-macro-api/src/legacy_protocol.rs     | 12 +++---
 .../proc-macro-api/src/legacy_protocol/msg.rs | 38 ++++++++++++++-----
 .../crates/proc-macro-api/src/lib.rs          |  1 -
 .../crates/proc-macro-api/src/process.rs      | 21 +++++-----
 .../crates/proc-macro-api/src/transport.rs    |  4 +-
 7 files changed, 77 insertions(+), 43 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index a13bff7d7d02..8311df23d718 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -9,7 +9,7 @@ use paths::AbsPath;
 use span::Span;
 
 use crate::{
-    Codec, ProcMacro, ProcMacroKind, ServerError,
+    ProcMacro, ProcMacroKind, ServerError,
     bidirectional_protocol::msg::{
         BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response,
         SubRequest, SubResponse,
@@ -22,25 +22,25 @@ use crate::{
         },
     },
     process::ProcMacroServerProcess,
-    transport::codec::postcard::PostcardProtocol,
+    transport::postcard,
 };
 
 pub mod msg;
 
 pub type SubCallback<'a> = &'a dyn Fn(SubRequest) -> Result;
 
-pub fn run_conversation(
+pub fn run_conversation(
     writer: &mut dyn Write,
     reader: &mut dyn BufRead,
-    buf: &mut C::Buf,
+    buf: &mut Vec,
     msg: BidirectionalMessage,
     callback: SubCallback<'_>,
 ) -> Result {
-    let encoded = C::encode(&msg).map_err(wrap_encode)?;
-    C::write(writer, &encoded).map_err(wrap_io("failed to write initial request"))?;
+    let encoded = postcard::encode(&msg).map_err(wrap_encode)?;
+    postcard::write(writer, &encoded).map_err(wrap_io("failed to write initial request"))?;
 
     loop {
-        let maybe_buf = C::read(reader, buf).map_err(wrap_io("failed to read message"))?;
+        let maybe_buf = postcard::read(reader, buf).map_err(wrap_io("failed to read message"))?;
         let Some(b) = maybe_buf else {
             return Err(ServerError {
                 message: "proc-macro server closed the stream".into(),
@@ -48,7 +48,7 @@ pub fn run_conversation(
             });
         };
 
-        let msg: BidirectionalMessage = C::decode(b).map_err(wrap_decode)?;
+        let msg: BidirectionalMessage = postcard::decode(b).map_err(wrap_decode)?;
 
         match msg {
             BidirectionalMessage::Response(response) => {
@@ -57,8 +57,9 @@ pub fn run_conversation(
             BidirectionalMessage::SubRequest(sr) => {
                 let resp = callback(sr)?;
                 let reply = BidirectionalMessage::SubResponse(resp);
-                let encoded = C::encode(&reply).map_err(wrap_encode)?;
-                C::write(writer, &encoded).map_err(wrap_io("failed to write sub-response"))?;
+                let encoded = postcard::encode(&reply).map_err(wrap_encode)?;
+                postcard::write(writer, &encoded)
+                    .map_err(wrap_io("failed to write sub-response"))?;
             }
             _ => {
                 return Err(ServerError {
@@ -207,7 +208,7 @@ fn run_request(
     if let Some(err) = srv.exited() {
         return Err(err.clone());
     }
-    srv.run_bidirectional::(msg, callback)
+    srv.run_bidirectional(msg, callback)
 }
 
 pub fn reject_subrequests(req: SubRequest) -> Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
index d030498e59c4..1df0c68379a5 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
@@ -1,6 +1,9 @@
 //! Bidirectional protocol messages
 
-use std::ops::Range;
+use std::{
+    io::{self, BufRead, Write},
+    ops::Range,
+};
 
 use paths::Utf8PathBuf;
 use serde::{Deserialize, Serialize};
@@ -8,6 +11,7 @@ use serde::{Deserialize, Serialize};
 use crate::{
     ProcMacroKind,
     legacy_protocol::msg::{FlatTree, Message, PanicMessage, ServerConfig},
+    transport::postcard,
 };
 
 #[derive(Debug, Serialize, Deserialize)]
@@ -97,4 +101,17 @@ pub struct ExpnGlobals {
     pub mixed_site: usize,
 }
 
-impl Message for BidirectionalMessage {}
+impl Message for BidirectionalMessage {
+    type Buf = Vec;
+
+    fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> {
+        Ok(match postcard::read(inp, buf)? {
+            None => None,
+            Some(buf) => Some(postcard::decode(buf)?),
+        })
+    }
+    fn write(self, out: &mut dyn Write) -> io::Result<()> {
+        let value = postcard::encode(&self)?;
+        postcard::write(out, &value)
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index eedf66d46086..ee1795d39c2e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -18,8 +18,6 @@ use crate::{
         flat::serialize_span_data_index_map,
     },
     process::ProcMacroServerProcess,
-    transport::codec::Codec,
-    transport::codec::json::JsonProtocol,
     version,
 };
 
@@ -149,21 +147,21 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result(send_request::, req)
+    srv.send_task_legacy::<_, _>(send_request, req)
 }
 
 /// Sends a request to the server and reads the response.
-fn send_request(
+fn send_request(
     mut writer: &mut dyn Write,
     mut reader: &mut dyn BufRead,
     req: Request,
-    buf: &mut P::Buf,
+    buf: &mut String,
 ) -> Result, ServerError> {
-    req.write::

(&mut writer).map_err(|err| ServerError { + req.write(&mut writer).map_err(|err| ServerError { message: "failed to write request".into(), io: Some(Arc::new(err)), })?; - let res = Response::read::

(self, predicate: P) -> Filter where Self: Sized, @@ -939,6 +953,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn filter_map(self, f: F) -> FilterMap where Self: Sized, @@ -986,6 +1001,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "enumerate_method"] + #[rustc_non_const_trait_method] fn enumerate(self) -> Enumerate where Self: Sized, @@ -1057,6 +1073,7 @@ pub trait Iterator { /// [`next`]: Iterator::next #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn peekable(self) -> Peekable where Self: Sized, @@ -1122,6 +1139,7 @@ pub trait Iterator { #[inline] #[doc(alias = "drop_while")] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn skip_while

(self, predicate: P) -> SkipWhile where Self: Sized, @@ -1200,6 +1218,7 @@ pub trait Iterator { /// the iteration should stop, but wasn't placed back into the iterator. #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn take_while

(self, predicate: P) -> TakeWhile where Self: Sized, @@ -1288,6 +1307,7 @@ pub trait Iterator { /// [`fuse`]: Iterator::fuse #[inline] #[stable(feature = "iter_map_while", since = "1.57.0")] + #[rustc_non_const_trait_method] fn map_while(self, predicate: P) -> MapWhile where Self: Sized, @@ -1317,6 +1337,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn skip(self, n: usize) -> Skip where Self: Sized, @@ -1389,6 +1410,7 @@ pub trait Iterator { #[doc(alias = "limit")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn take(self, n: usize) -> Take where Self: Sized, @@ -1436,6 +1458,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn scan(self, initial_state: St, f: F) -> Scan where Self: Sized, @@ -1474,6 +1497,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn flat_map(self, f: F) -> FlatMap where Self: Sized, @@ -1558,6 +1582,7 @@ pub trait Iterator { /// [`flat_map()`]: Iterator::flat_map #[inline] #[stable(feature = "iterator_flatten", since = "1.29.0")] + #[rustc_non_const_trait_method] fn flatten(self) -> Flatten where Self: Sized, @@ -1714,6 +1739,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_map_windows", issue = "87155")] + #[rustc_non_const_trait_method] fn map_windows(self, f: F) -> MapWindows where Self: Sized, @@ -1776,6 +1802,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn fuse(self) -> Fuse where Self: Sized, @@ -1860,6 +1887,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn inspect(self, f: F) -> Inspect where Self: Sized, @@ -2019,6 +2047,7 @@ pub trait Iterator { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"] #[rustc_diagnostic_item = "iterator_collect_fn"] + #[rustc_non_const_trait_method] fn collect>(self) -> B where Self: Sized, @@ -2106,6 +2135,7 @@ pub trait Iterator { /// [`collect`]: Iterator::collect #[inline] #[unstable(feature = "iterator_try_collect", issue = "94047")] + #[rustc_non_const_trait_method] fn try_collect(&mut self) -> ChangeOutputType where Self: Sized, @@ -2178,6 +2208,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_collect_into", issue = "94780")] + #[rustc_non_const_trait_method] fn collect_into>(self, collection: &mut E) -> &mut E where Self: Sized, @@ -2210,6 +2241,7 @@ pub trait Iterator { /// assert_eq!(odd, [1, 3]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn partition(self, f: F) -> (B, B) where Self: Sized, @@ -2272,6 +2304,7 @@ pub trait Iterator { /// assert!(a[i..].iter().all(|n| n % 2 == 1)); // odds /// ``` #[unstable(feature = "iter_partition_in_place", issue = "62543")] + #[rustc_non_const_trait_method] fn partition_in_place<'a, T: 'a, P>(mut self, ref mut predicate: P) -> usize where Self: Sized + DoubleEndedIterator, @@ -2329,6 +2362,7 @@ pub trait Iterator { /// assert!(!"IntoIterator".chars().is_partitioned(char::is_uppercase)); /// ``` #[unstable(feature = "iter_is_partitioned", issue = "62544")] + #[rustc_non_const_trait_method] fn is_partitioned

(mut self, mut predicate: P) -> bool where Self: Sized, @@ -2423,6 +2457,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_try_fold", since = "1.27.0")] + #[rustc_non_const_trait_method] fn try_fold(&mut self, init: B, mut f: F) -> R where Self: Sized, @@ -2481,6 +2516,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_try_fold", since = "1.27.0")] + #[rustc_non_const_trait_method] fn try_for_each(&mut self, f: F) -> R where Self: Sized, @@ -2600,6 +2636,7 @@ pub trait Iterator { #[doc(alias = "inject", alias = "foldl")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn fold(mut self, init: B, mut f: F) -> B where Self: Sized, @@ -2637,6 +2674,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_fold_self", since = "1.51.0")] + #[rustc_non_const_trait_method] fn reduce(mut self, f: F) -> Option where Self: Sized, @@ -2708,6 +2746,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iterator_try_reduce", issue = "87053")] + #[rustc_non_const_trait_method] fn try_reduce( &mut self, f: impl FnMut(Self::Item, Self::Item) -> R, @@ -2766,6 +2805,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn all(&mut self, f: F) -> bool where Self: Sized, @@ -2819,6 +2859,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn any(&mut self, f: F) -> bool where Self: Sized, @@ -2892,6 +2933,7 @@ pub trait Iterator { /// Note that `iter.find(f)` is equivalent to `iter.filter(f).next()`. #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn find

(&mut self, predicate: P) -> Option where Self: Sized, @@ -2923,6 +2965,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_find_map", since = "1.30.0")] + #[rustc_non_const_trait_method] fn find_map(&mut self, f: F) -> Option where Self: Sized, @@ -2981,6 +3024,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "try_find", issue = "63178")] + #[rustc_non_const_trait_method] fn try_find( &mut self, f: impl FnMut(&Self::Item) -> R, @@ -3064,6 +3108,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn position

(&mut self, predicate: P) -> Option where Self: Sized, @@ -3129,6 +3174,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn rposition

(&mut self, predicate: P) -> Option where P: FnMut(Self::Item) -> bool, @@ -3178,6 +3224,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn max(self) -> Option where Self: Sized, @@ -3214,6 +3261,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn min(self) -> Option where Self: Sized, @@ -3236,6 +3284,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_cmp_by_key", since = "1.6.0")] + #[rustc_non_const_trait_method] fn max_by_key(self, f: F) -> Option where Self: Sized, @@ -3269,6 +3318,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_max_by", since = "1.15.0")] + #[rustc_non_const_trait_method] fn max_by(self, compare: F) -> Option where Self: Sized, @@ -3296,6 +3346,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_cmp_by_key", since = "1.6.0")] + #[rustc_non_const_trait_method] fn min_by_key(self, f: F) -> Option where Self: Sized, @@ -3329,6 +3380,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_min_by", since = "1.15.0")] + #[rustc_non_const_trait_method] fn min_by(self, compare: F) -> Option where Self: Sized, @@ -3366,6 +3418,7 @@ pub trait Iterator { #[inline] #[doc(alias = "reverse")] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn rev(self) -> Rev where Self: Sized + DoubleEndedIterator, @@ -3402,6 +3455,7 @@ pub trait Iterator { /// assert_eq!(z, [3, 6]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn unzip(self) -> (FromA, FromB) where FromA: Default + Extend, @@ -3433,6 +3487,7 @@ pub trait Iterator { /// ``` #[stable(feature = "iter_copied", since = "1.36.0")] #[rustc_diagnostic_item = "iter_copied"] + #[rustc_non_const_trait_method] fn copied<'a, T>(self) -> Copied where T: Copy + 'a, @@ -3481,6 +3536,7 @@ pub trait Iterator { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "iter_cloned"] + #[rustc_non_const_trait_method] fn cloned<'a, T>(self) -> Cloned where T: Clone + 'a, @@ -3512,6 +3568,7 @@ pub trait Iterator { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[rustc_non_const_trait_method] fn cycle(self) -> Cycle where Self: Sized + Clone, @@ -3555,6 +3612,7 @@ pub trait Iterator { /// ``` #[track_caller] #[unstable(feature = "iter_array_chunks", issue = "100450")] + #[rustc_non_const_trait_method] fn array_chunks(self) -> ArrayChunks where Self: Sized, @@ -3591,6 +3649,7 @@ pub trait Iterator { /// assert_eq!(sum, -0.0_f32); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] + #[rustc_non_const_trait_method] fn sum(self) -> S where Self: Sized, @@ -3623,6 +3682,7 @@ pub trait Iterator { /// assert_eq!(factorial(5), 120); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] + #[rustc_non_const_trait_method] fn product

(self) -> P where Self: Sized, @@ -3644,6 +3704,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().cmp([1].iter()), Ordering::Greater); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn cmp(self, other: I) -> Ordering where I: IntoIterator, @@ -3671,6 +3732,7 @@ pub trait Iterator { /// assert_eq!(xs.into_iter().cmp_by(ys, |x, y| (2 * x).cmp(&y)), Ordering::Greater); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn cmp_by(self, other: I, cmp: F) -> Ordering where Self: Sized, @@ -3727,6 +3789,7 @@ pub trait Iterator { /// ``` /// #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn partial_cmp(self, other: I) -> Option where I: IntoIterator, @@ -3763,6 +3826,7 @@ pub trait Iterator { /// ); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn partial_cmp_by(self, other: I, partial_cmp: F) -> Option where Self: Sized, @@ -3796,6 +3860,7 @@ pub trait Iterator { /// assert_eq!([1].iter().eq([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn eq(self, other: I) -> bool where I: IntoIterator, @@ -3819,6 +3884,7 @@ pub trait Iterator { /// assert!(xs.iter().eq_by(ys, |x, y| x * x == y)); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn eq_by(self, other: I, eq: F) -> bool where Self: Sized, @@ -3848,6 +3914,7 @@ pub trait Iterator { /// assert_eq!([1].iter().ne([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn ne(self, other: I) -> bool where I: IntoIterator, @@ -3869,6 +3936,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().lt([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn lt(self, other: I) -> bool where I: IntoIterator, @@ -3890,6 +3958,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().le([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn le(self, other: I) -> bool where I: IntoIterator, @@ -3911,6 +3980,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().gt([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn gt(self, other: I) -> bool where I: IntoIterator, @@ -3932,6 +4002,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().ge([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn ge(self, other: I) -> bool where I: IntoIterator, @@ -3961,6 +4032,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted(self) -> bool where Self: Sized, @@ -3987,6 +4059,7 @@ pub trait Iterator { /// assert!(std::iter::empty::().is_sorted_by(|a, b| true)); /// ``` #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted_by(mut self, compare: F) -> bool where Self: Sized, @@ -4031,6 +4104,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted_by_key(self, f: F) -> bool where Self: Sized, @@ -4046,6 +4120,7 @@ pub trait Iterator { #[inline] #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] + #[rustc_non_const_trait_method] unsafe fn __iterator_get_unchecked(&mut self, _idx: usize) -> Self::Item where Self: TrustedRandomAccessNoCoerce, diff --git a/library/core/src/option.rs b/library/core/src/option.rs index eb4f978b7c19..b7c805f5855e 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2257,7 +2257,8 @@ impl const Default for Option { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for Option { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const IntoIterator for Option { type Item = T; type IntoIter = IntoIter; @@ -2429,7 +2430,8 @@ struct Item { opt: Option, } -impl Iterator for Item { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const Iterator for Item { type Item = A; #[inline] @@ -2439,7 +2441,7 @@ impl Iterator for Item { #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.len(); + let len = self.opt.len(); (len, Some(len)) } } @@ -2563,7 +2565,8 @@ pub struct IntoIter { } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const Iterator for IntoIter { type Item = A; #[inline] diff --git a/library/coretests/tests/iter/mod.rs b/library/coretests/tests/iter/mod.rs index 5b2769d04698..f300f421f7c1 100644 --- a/library/coretests/tests/iter/mod.rs +++ b/library/coretests/tests/iter/mod.rs @@ -99,3 +99,18 @@ pub fn extend_for_unit() { } assert_eq!(x, 5); } + +#[test] +pub fn test_const_iter() { + const X: bool = { + let it = Some(42); + let mut run = false; + #[expect(for_loops_over_fallibles)] + for x in it { + assert!(x == 42); + run = true; + } + run + }; + assert_eq!(true, X); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 8cca714b7393..a24c83834edb 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -27,6 +27,7 @@ #![feature(const_drop_in_place)] #![feature(const_eval_select)] #![feature(const_index)] +#![feature(const_iter)] #![feature(const_ops)] #![feature(const_option_ops)] #![feature(const_ref_cell)] From 02e10b2d90c8d4f0cb3f0f2b4a6a8a3283efa9c3 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 26 Jan 2026 13:00:58 -0500 Subject: [PATCH 416/583] fix issues and ui tests, address reviews --- .../src/check_consts/check.rs | 4 +-- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- .../src/check/compare_impl_item.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 5 ++- src/doc/rustc-dev-guide/src/SUMMARY.md | 2 +- src/doc/rustc-dev-guide/src/effects.md | 17 +++++++++- .../const-traits/partial/attr-gate.stderr | 2 +- .../const-traits/partial/no-const-callers.rs | 17 ++++++++++ .../partial/no-const-callers.stderr | 32 +++++++++++++++++-- .../typeck_type_placeholder_item.stderr | 14 -------- 10 files changed, 72 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 2abbf75984a4..57396b657a3f 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -774,8 +774,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Attempting to call a trait method? if let Some(trait_did) = tcx.trait_of_assoc(callee) { - // We can't determine the actual callee here, so we have to do different checks - // than usual. + // We can't determine the actual callee (the underlying impl of the trait) here, so we have + // to do different checks than usual. trace!("attempting to call a trait method"); let is_const = tcx.constness(callee) == hir::Constness::Const; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 8f447e1477b3..182ef6816337 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1333,7 +1333,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_non_const_trait_method, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods \ - as non-const to allow large traits to easier transition to const" + as non-const to allow large traits an easier transition to const" ), BuiltinAttribute { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index dd7b03c9dac3..9e07d5260d20 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -218,7 +218,7 @@ fn compare_method_predicate_entailment<'tcx>( trait_m_predicates.instantiate_own(tcx, trait_to_impl_args).map(|(predicate, _)| predicate), ); - let is_conditionally_const = tcx.is_conditionally_const(impl_def_id); + let is_conditionally_const = tcx.is_conditionally_const(impl_m.def_id); if is_conditionally_const { // Augment the hybrid param-env with the const conditions // of the impl header and the trait method. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 044c99ffeb21..4e33b8ebb6de 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2087,7 +2087,10 @@ impl<'tcx> TyCtxt<'tcx> { self.constness(def_id) == hir::Constness::Const } DefKind::Impl { of_trait: true } => { - self.constness(self.trait_item_of(def_id).unwrap()) == hir::Constness::Const + let Some(trait_method_did) = self.trait_item_of(def_id) else { + return false; + }; + self.constness(trait_method_did) == hir::Constness::Const && self.is_conditionally_const(parent_def_id) } DefKind::Trait => { diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 1f9e0aac9d0b..5342c54607b7 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -199,7 +199,7 @@ - [Inference details](./opaque-types-impl-trait-inference.md) - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md) - [Region inference restrictions](./borrow-check/opaque-types-region-inference-restrictions.md) -- [Const condition checking](./effects.md) +- [Const traits and const condition checking](./effects.md) - [Pattern and exhaustiveness checking](./pat-exhaustive-checking.md) - [Unsafety checking](./unsafety-checking.md) - [MIR dataflow](./mir/dataflow.md) diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index 87b0103a7bc4..732ba7153116 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -1,4 +1,4 @@ -# Effects and const condition checking +# Effects, const traits, and const condition checking ## The `HostEffect` predicate @@ -154,3 +154,18 @@ be dropped at compile time. [old solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_trait_selection/traits/effects.rs.html [new trait solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_next_trait_solver/solve/effect_goals.rs.html + +## More on const traits + +To be expanded later. + +### The `#[rustc_non_const_trait_method]` attribute + +This is intended for internal (standard library) usage only. With this attribute +applied to a trait method, the compiler will not check the default body of this +method for ability to run in compile time. Users of the trait will also not be +allowed to use this trait method in const contexts. This attribute is primarily +used for constifying large traits such as `Iterator` without having to make all +its methods `const` at the same time. + +This attribute should not be present while stabilizing the trait as `const`. diff --git a/tests/ui/traits/const-traits/partial/attr-gate.stderr b/tests/ui/traits/const-traits/partial/attr-gate.stderr index bc86cbe4edf7..e46e35d036be 100644 --- a/tests/ui/traits/const-traits/partial/attr-gate.stderr +++ b/tests/ui/traits/const-traits/partial/attr-gate.stderr @@ -6,7 +6,7 @@ LL | #[rustc_non_const_trait_method] | = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = note: the `#[rustc_non_const_trait_method]` attribute is an internal implementation detail that will never be stable - = note: `#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods as non-const to allow large traits to easier transition to const + = note: `#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods as non-const to allow large traits an easier transition to const error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/partial/no-const-callers.rs b/tests/ui/traits/const-traits/partial/no-const-callers.rs index efcdea36f1ca..7c198f41ce42 100644 --- a/tests/ui/traits/const-traits/partial/no-const-callers.rs +++ b/tests/ui/traits/const-traits/partial/no-const-callers.rs @@ -10,6 +10,17 @@ impl const A for () { fn a() {} } +impl const A for u8 { + fn a() {} + fn b() { println!("hello"); } + //~^ ERROR: cannot call non-const function +} + +impl const A for i8 { + fn a() {} + fn b() {} +} + const fn foo() { T::a(); T::b(); @@ -17,6 +28,12 @@ const fn foo() { <()>::a(); <()>::b(); //~^ ERROR: cannot call non-const associated function + u8::a(); + u8::b(); + //~^ ERROR: cannot call non-const associated function + i8::a(); + i8::b(); + //~^ ERROR: cannot call non-const associated function } fn main() {} diff --git a/tests/ui/traits/const-traits/partial/no-const-callers.stderr b/tests/ui/traits/const-traits/partial/no-const-callers.stderr index 31179161c5cd..bbb4495943ed 100644 --- a/tests/ui/traits/const-traits/partial/no-const-callers.stderr +++ b/tests/ui/traits/const-traits/partial/no-const-callers.stderr @@ -1,5 +1,15 @@ +error[E0015]: cannot call non-const function `std::io::_print` in constant functions + --> $DIR/no-const-callers.rs:15:14 + | +LL | fn b() { println!("hello"); } + | ^^^^^^^^^^^^^^^^^ + | +note: function `_print` is not const + --> $SRC_DIR/std/src/io/stdio.rs:LL:COL + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + error[E0015]: cannot call non-const associated function `::b` in constant functions - --> $DIR/no-const-callers.rs:15:5 + --> $DIR/no-const-callers.rs:26:5 | LL | T::b(); | ^^^^^^ @@ -7,13 +17,29 @@ LL | T::b(); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants error[E0015]: cannot call non-const associated function `<() as A>::b` in constant functions - --> $DIR/no-const-callers.rs:18:5 + --> $DIR/no-const-callers.rs:29:5 | LL | <()>::b(); | ^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 2 previous errors +error[E0015]: cannot call non-const associated function `::b` in constant functions + --> $DIR/no-const-callers.rs:32:5 + | +LL | u8::b(); + | ^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const associated function `::b` in constant functions + --> $DIR/no-const-callers.rs:35:5 + | +LL | i8::b(); + | ^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index 0b70ac97fd43..2772d55f953a 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -684,13 +684,6 @@ error[E0015]: cannot call non-const method ` as Iterator>:: LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^ | -note: method `filter` is not const because trait `Iterator` is not const - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this trait is not const - ::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this method is not const = note: calls in constants are limited to constant functions, tuple structs and tuple variants error[E0015]: cannot call non-const method `, {closure@$DIR/typeck_type_placeholder_item.rs:240:29: 240:32}> as Iterator>::map::` in constants @@ -699,13 +692,6 @@ error[E0015]: cannot call non-const method `, {closu LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^ | -note: method `map` is not const because trait `Iterator` is not const - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this trait is not const - ::: $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - | - = note: this method is not const = note: calls in constants are limited to constant functions, tuple structs and tuple variants error: aborting due to 83 previous errors From 0271b6b156e602dd8157e3284d37933377c1476c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Fri, 30 Jan 2026 18:25:19 +0100 Subject: [PATCH 417/583] regression test for alias-relate changes in lub --- .../generalize/relate-alias-in-lub.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs diff --git a/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs new file mode 100644 index 000000000000..0a948989f981 --- /dev/null +++ b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs @@ -0,0 +1,30 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +// Reproduces https://github.com/rust-lang/rust/pull/151746#issuecomment-3822930803. +// +// The change we tried to make there caused relating a type variable with an alias inside lub, +// In 5bd20bbd0ba6c0285664e55a1ffc677d7487c98b, we moved around code +// that adds an alias-relate predicate to be earlier, from one shared codepath into several +// distinct code paths. However, we forgot one codepath, through lub, causing an ICE in serde. +// In the end we dropped said commit, but the reproducer is still a useful as test. + +use std::marker::PhantomData; + +pub trait Trait { + type Error; +} + +pub struct Struct(PhantomData); + +impl Trait for () { + type Error = (); +} + +fn main() { + let _: Struct<<() as Trait>::Error> = match loop {} { + b => loop {}, + a => Struct(PhantomData), + }; +} From ea0ee492bdeb17a8e2df9e84ef14d1e12cb93884 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 30 Jan 2026 19:29:00 +0300 Subject: [PATCH 418/583] resolve: Remove `force` parameter from `resolve_ident_in_scope` --- compiler/rustc_resolve/src/diagnostics.rs | 3 -- compiler/rustc_resolve/src/ident.rs | 34 ++++--------------- compiler/rustc_resolve/src/imports.rs | 1 - compiler/rustc_resolve/src/macros.rs | 7 ++-- ...ro-rules-as-derive-or-attr-issue-132928.rs | 1 - ...ules-as-derive-or-attr-issue-132928.stderr | 13 +------ 6 files changed, 10 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 5c401c3bf828..6f2d3e79d10a 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1709,7 +1709,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), parent_scope, None, - false, None, None, ) else { @@ -2546,7 +2545,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns_to_try), parent_scope, None, - false, ignore_decl, ignore_import, ) @@ -2650,7 +2648,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ValueNS), parent_scope, None, - false, ignore_decl, ignore_import, ) { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 26dca5e6e508..4fbde60d8679 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -350,7 +350,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Module(ns, module), parent_scope, finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), - finalize.is_some(), ignore_decl, None, ) @@ -368,7 +367,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), parent_scope, finalize, - finalize.is_some(), ignore_decl, None, ) @@ -396,12 +394,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, finalize: Option, - force: bool, ignore_decl: Option>, ignore_import: Option>, ) -> Result, Determinacy> { - assert!(force || finalize.is_none()); // `finalize` implies `force` - // Make sure `self`, `super` etc produce an error when passed to here. if !matches!(scope_set, ScopeSet::Module(..)) && orig_ident.is_path_segment_keyword() { return Err(Determinacy::Determined); @@ -451,7 +446,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope, // Shadowed decls don't need to be marked as used or non-speculatively loaded. if innermost_results.is_empty() { finalize } else { None }, - force, ignore_decl, ignore_import, ) { @@ -509,7 +503,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Scope visiting walked all the scopes and maybe found something in one of them. match innermost_results.first() { Some(&(decl, ..)) => Ok(decl), - None => Err(Determinacy::determined(determinacy == Determinacy::Determined || force)), + None => Err(determinacy), } } @@ -523,7 +517,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, finalize: Option, - force: bool, ignore_decl: Option>, ignore_import: Option>, ) -> Result, ControlFlow> { @@ -546,7 +539,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match self.reborrow().resolve_derive_macro_path( derive, parent_scope, - force, + false, ignore_import, ) { Ok((Some(ext), _)) => { @@ -617,11 +610,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Ok(decl) } Err(ControlFlow::Continue(determinacy)) => Err(determinacy), - Err(ControlFlow::Break(determinacy)) => { - return Err(ControlFlow::Break(Determinacy::determined( - determinacy == Determinacy::Determined || force, - ))); - } + Err(ControlFlow::Break(..)) => return decl, } } Scope::ModuleGlobs(module, derive_fallback_lint_id) => { @@ -668,11 +657,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Ok(binding) } Err(ControlFlow::Continue(determinacy)) => Err(determinacy), - Err(ControlFlow::Break(determinacy)) => { - return Err(ControlFlow::Break(Determinacy::determined( - determinacy == Determinacy::Determined || force, - ))); - } + Err(ControlFlow::Break(..)) => return binding, } } Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() { @@ -715,7 +700,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Module(ns, prelude), parent_scope, None, - false, ignore_decl, ignore_import, ) @@ -951,7 +935,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Module(ns, module), parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ), @@ -960,7 +943,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::ModuleAndExternPrelude(ns, module), parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ), @@ -973,7 +955,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::ExternPrelude, parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ) @@ -996,7 +977,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ) @@ -1180,7 +1160,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Module(ns, module), adjusted_parent_scope, None, - false, ignore_decl, ignore_import, ); @@ -1881,7 +1860,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), parent_scope, finalize, - finalize.is_some(), ignore_decl, ignore_import, ) @@ -1957,8 +1935,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ); } } - Err(Undetermined) => return PathResult::Indeterminate, - Err(Determined) => { + Err(Undetermined) if finalize.is_none() => return PathResult::Indeterminate, + Err(Determined | Undetermined) => { if let Some(ModuleOrUniformRoot::Module(module)) = module && opt_ns.is_some() && !module.is_normal() diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 7ff24bc612df..057340028085 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1498,7 +1498,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::All(ns), &import.parent_scope, None, - false, decls[ns].get().decl(), None, ) { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index b7d83d9e130c..b933c2b9d036 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -799,10 +799,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Macro(kind), parent_scope, None, - force, None, None, ); + let binding = binding.map_err(|determinacy| { + Determinacy::determined(determinacy == Determinacy::Determined || force) + }); if let Err(Determinacy::Undetermined) = binding { return Err(Determinacy::Undetermined); } @@ -958,7 +960,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Macro(kind), &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), - true, None, None, ) { @@ -1013,7 +1014,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Macro(MacroKind::Attr), &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), - true, None, None, ); @@ -1117,7 +1117,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ScopeSet::Macro(MacroKind::Bang), &ParentScope { macro_rules: no_macro_rules, ..*parent_scope }, None, - false, None, None, ); diff --git a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.rs b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.rs index a2e1398c61e6..a556983e204d 100644 --- a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.rs +++ b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.rs @@ -5,5 +5,4 @@ macro_rules! sample { () => {} } #[sample] //~ ERROR cannot find attribute `sample` in this scope #[derive(sample)] //~ ERROR cannot find derive macro `sample` in this scope //~| ERROR cannot find derive macro `sample` in this scope - //~| ERROR cannot find derive macro `sample` in this scope pub struct S {} diff --git a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr index aad4a844ec17..a3c21df43e75 100644 --- a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr +++ b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr @@ -1,12 +1,3 @@ -error: cannot find derive macro `sample` in this scope - --> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10 - | -LL | macro_rules! sample { () => {} } - | ------ `sample` exists, but has no `derive` rules -... -LL | #[derive(sample)] - | ^^^^^^ - error: cannot find attribute `sample` in this scope --> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:5:3 | @@ -24,8 +15,6 @@ LL | macro_rules! sample { () => {} } ... LL | #[derive(sample)] | ^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: cannot find derive macro `sample` in this scope --> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10 @@ -38,5 +27,5 @@ LL | #[derive(sample)] | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors From a3f169c75b7625e15e07e4ed7855647871207fd1 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 27 Jan 2026 08:46:08 -0800 Subject: [PATCH 419/583] Use `Bound::copied` instead of `Bound::cloned` --- library/core/src/ops/range.rs | 2 +- library/core/src/slice/index.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index a0b74ff383ea..c15c8f20c16b 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -773,7 +773,7 @@ impl Bound<&T> { /// ``` #[unstable(feature = "bound_copied", issue = "145966")] #[must_use] - pub fn copied(self) -> Bound { + pub const fn copied(self) -> Bound { match self { Bound::Unbounded => Bound::Unbounded, Bound::Included(x) => Bound::Included(*x), diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 097310dc8fb0..59802989c18f 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -910,7 +910,7 @@ where R: [const] ops::RangeBounds + [const] Destruct, { let len = bounds.end; - into_slice_range(len, (range.start_bound().cloned(), range.end_bound().cloned())) + into_slice_range(len, (range.start_bound().copied(), range.end_bound().copied())) } /// Performs bounds checking of a range without panicking. @@ -950,7 +950,7 @@ where R: ops::RangeBounds, { let len = bounds.end; - let r = into_range(len, (range.start_bound().cloned(), range.end_bound().cloned()))?; + let r = into_range(len, (range.start_bound().copied(), range.end_bound().copied()))?; if r.start > r.end || r.end > len { None } else { Some(r) } } @@ -977,6 +977,7 @@ pub(crate) const fn into_range_unchecked( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Returns `None` on overflowing indices. #[rustc_const_unstable(feature = "const_range", issue = "none")] +#[inline] pub(crate) const fn into_range( len: usize, (start, end): (ops::Bound, ops::Bound), @@ -1002,6 +1003,7 @@ pub(crate) const fn into_range( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Panics on overflowing indices. +#[inline] pub(crate) const fn into_slice_range( len: usize, (start, end): (ops::Bound, ops::Bound), From 5f76adf7b368b4bec6fb3771fe645ab762ef8236 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 31 Jan 2026 07:37:52 +0900 Subject: [PATCH 420/583] fix adk installer link --- src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md index d2680d40853f..2267f9b490f7 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md @@ -22,8 +22,8 @@ specifically designed to make analyzing rustc easier. You can install WPR and WPA as part of the Windows Performance Toolkit which itself is an option as part of downloading the Windows Assessment and Deployment Kit (ADK). You can download the ADK -installer [here](https://go.microsoft.com/fwlink/?linkid=2086042). Make sure to select the Windows -Performance Toolkit (you don't need to select anything else). +installer [here](https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install). Make +sure to select the Windows Performance Toolkit (you don't need to select anything else). ## Recording From 8869b7b356d1bf1766427bbcfb075756adebbfaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 31 Jan 2026 00:29:30 +0100 Subject: [PATCH 421/583] Pass `DepNode` by reference more places --- .../rustc_query_system/src/dep_graph/graph.rs | 10 +++++----- .../rustc_query_system/src/dep_graph/mod.rs | 4 ++-- .../src/dep_graph/serialized.rs | 18 +++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 6d46d144d0f1..cf3d211d1cc7 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -662,7 +662,7 @@ impl DepGraphData { } #[inline] - pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> DepNode { + pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> &DepNode { self.previous.index_to_node(prev_index) } @@ -783,7 +783,7 @@ impl DepGraphData { #[cfg(debug_assertions)] self.current.record_edge( dep_node_index, - self.previous.index_to_node(prev_index), + *self.previous.index_to_node(prev_index), self.previous.fingerprint_by_index(prev_index), ); @@ -918,7 +918,7 @@ impl DepGraphData { DepNodeColor::Unknown => {} } - let dep_dep_node = &get_dep_dep_node(); + let dep_dep_node = get_dep_dep_node(); // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. @@ -989,7 +989,7 @@ impl DepGraphData { // We never try to mark eval_always nodes as green debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); - debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), *dep_node); + debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), dep_node); let prev_deps = self.previous.edge_targets_from(prev_dep_node_index); @@ -1450,7 +1450,7 @@ fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepN // previous session and has been marked green for prev_index in data.colors.values.indices() { if data.colors.current(prev_index) == Some(dep_node_index) { - dep_node = Some(data.previous.index_to_node(prev_index)); + dep_node = Some(*data.previous.index_to_node(prev_index)); break; } } diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 874b41cbf3b1..8f714a2c96d6 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -83,9 +83,9 @@ pub trait DepContext: Copy { } /// Load data from the on-disk cache. - fn try_load_from_on_disk_cache(self, dep_node: DepNode) { + fn try_load_from_on_disk_cache(self, dep_node: &DepNode) { if let Some(try_load_fn) = self.dep_kind_vtable(dep_node.kind).try_load_from_on_disk_cache { - try_load_fn(self, dep_node) + try_load_fn(self, *dep_node) } } diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 403394674a02..aa4c928d3cdc 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -133,8 +133,8 @@ impl SerializedDepGraph { } #[inline] - pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> DepNode { - self.nodes[dep_node_index] + pub fn index_to_node(&self, dep_node_index: SerializedDepNodeIndex) -> &DepNode { + &self.nodes[dep_node_index] } #[inline] @@ -346,7 +346,7 @@ impl SerializedNodeHeader { #[inline] fn new( - node: DepNode, + node: &DepNode, index: DepNodeIndex, fingerprint: Fingerprint, edge_max_index: u32, @@ -379,7 +379,7 @@ impl SerializedNodeHeader { { let res = Self { bytes, _marker: PhantomData }; assert_eq!(fingerprint, res.fingerprint()); - assert_eq!(node, res.node()); + assert_eq!(*node, res.node()); if let Some(len) = res.len() { assert_eq!(edge_count, len as usize); } @@ -452,7 +452,7 @@ struct NodeInfo { impl NodeInfo { fn encode(&self, e: &mut MemEncoder, index: DepNodeIndex) { - let NodeInfo { node, fingerprint, ref edges } = *self; + let NodeInfo { ref node, fingerprint, ref edges } = *self; let header = SerializedNodeHeader::::new( node, index, @@ -482,7 +482,7 @@ impl NodeInfo { #[inline] fn encode_promoted( e: &mut MemEncoder, - node: DepNode, + node: &DepNode, index: DepNodeIndex, fingerprint: Fingerprint, prev_index: SerializedDepNodeIndex, @@ -604,7 +604,7 @@ impl EncoderState { #[inline] fn record( &self, - node: DepNode, + node: &DepNode, index: DepNodeIndex, edge_count: usize, edges: impl FnOnce(&Self) -> Vec, @@ -622,7 +622,7 @@ impl EncoderState { outline(move || { // Do not ICE when a query is called from within `with_query`. if let Some(record_graph) = &mut record_graph.try_lock() { - record_graph.push(index, node, &edges); + record_graph.push(index, *node, &edges); } }); } @@ -661,7 +661,7 @@ impl EncoderState { node.encode::(&mut local.encoder, index); self.flush_mem_encoder(&mut *local); self.record( - node.node, + &node.node, index, node.edges.len(), |_| node.edges[..].to_vec(), From f7ffe3139006ea4d6fdf99c160a8b62b373922e9 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 27 Jan 2026 17:06:12 -0500 Subject: [PATCH 422/583] Update cargo submodule also fixed tests/run-make-cargo/apple-slow-tls/rmake.rs accordingly for the new `-Zjson-target-spec` requirement --- src/tools/cargo | 2 +- tests/run-make-cargo/apple-slow-tls/rmake.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index efcd9f58636c..fe2f314aef06 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit efcd9f58636c1990393d495159045d9c35e43b8f +Subproject commit fe2f314aef06e688a9517da1ac0577bb1854d01f diff --git a/tests/run-make-cargo/apple-slow-tls/rmake.rs b/tests/run-make-cargo/apple-slow-tls/rmake.rs index 231e0b1668e9..476414bcfa1e 100644 --- a/tests/run-make-cargo/apple-slow-tls/rmake.rs +++ b/tests/run-make-cargo/apple-slow-tls/rmake.rs @@ -26,6 +26,7 @@ fn main() { "--target", "t.json", "-Zbuild-std=std,core,panic_abort", + "-Zjson-target-spec", ]) .run(); From 5cb916370bf2b770b15ee9b915fbe8f99b36890e Mon Sep 17 00:00:00 2001 From: "R. Yin" <88910093+thealtofwar@users.noreply.github.com> Date: Fri, 30 Jan 2026 20:02:48 -0600 Subject: [PATCH 423/583] Fix typo in debugging.md --- src/doc/rustc-dev-guide/src/autodiff/debugging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/debugging.md b/src/doc/rustc-dev-guide/src/autodiff/debugging.md index 7c7af8589868..3b2278f1a075 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/debugging.md +++ b/src/doc/rustc-dev-guide/src/autodiff/debugging.md @@ -16,7 +16,7 @@ Before generating the llvm-ir, keep in mind two techniques that can help ensure ## 1) Generate an llvm-ir reproducer ```sh -RUSTFLAGS="-Z autodiff=Enable,PrintModbefore" cargo +enzyme build --release &> out.ll +RUSTFLAGS="-Z autodiff=Enable,PrintModBefore" cargo +enzyme build --release &> out.ll ``` This also captures a few warnings and info messages above and below your module. open out.ll and remove every line above `; moduleid = `. Now look at the end of the file and remove everything that's not part of llvm-ir, i.e. remove errors and warnings. The last line of your llvm-ir should now start with `! = `, i.e. `!40831 = !{i32 0, i32 1037508, i32 1037538, i32 1037559}` or `!43760 = !dilocation(line: 297, column: 5, scope: !43746)`. From c8d2f3e6f16ff797d8789fe9818f2c6a144153dc Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Sat, 31 Jan 2026 02:06:09 +0000 Subject: [PATCH 424/583] Prepare for merging from rust-lang/rust This updates the rust-version file to 44e34e1ac6d7e69b40856cf1403d3da145319c30. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 795271ee0ef0..209f4226eae7 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -370143facfb348ad3b29749c0393402d76b280c3 +44e34e1ac6d7e69b40856cf1403d3da145319c30 From 04e6afe20a6b5ea3b1a0e17a25df486c43a66df4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 26 Jan 2026 14:15:15 +1100 Subject: [PATCH 425/583] Re-export `hashbrown::hash_table` from `rustc_data_structures` --- Cargo.lock | 2 -- compiler/rustc_data_structures/src/lib.rs | 4 ++++ compiler/rustc_data_structures/src/sharded.rs | 4 ++-- compiler/rustc_mir_transform/Cargo.toml | 1 - compiler/rustc_mir_transform/src/gvn.rs | 2 +- compiler/rustc_query_system/Cargo.toml | 5 ----- compiler/rustc_query_system/src/query/plumbing.rs | 5 ++--- 7 files changed, 9 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6be4565374af..b63bb619b64e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4354,7 +4354,6 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", - "hashbrown 0.16.1", "itertools", "rustc_abi", "rustc_arena", @@ -4565,7 +4564,6 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ - "hashbrown 0.16.1", "parking_lot", "rustc_abi", "rustc_ast", diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 85877d73519a..4467a2811181 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -49,6 +49,10 @@ pub use std::{assert_matches, debug_assert_matches}; pub use atomic_ref::AtomicRef; pub use ena::{snapshot_vec, undo_log, unify}; +// Re-export `hashbrown::hash_table`, because it's part of our API +// (via `ShardedHashMap`), and because it lets other compiler crates use the +// lower-level `HashTable` API without a tricky `hashbrown` dependency. +pub use hashbrown::hash_table; pub use rustc_index::static_assert_size; // Re-export some data-structure crates which are part of our public API. pub use {either, indexmap, smallvec, thin_vec}; diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs index 5de9413cf15d..e10ccccad5bb 100644 --- a/compiler/rustc_data_structures/src/sharded.rs +++ b/compiler/rustc_data_structures/src/sharded.rs @@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher}; use std::{iter, mem}; use either::Either; -use hashbrown::hash_table::{Entry, HashTable}; +use hashbrown::hash_table::{self, Entry, HashTable}; use crate::fx::FxHasher; use crate::sync::{CacheAligned, Lock, LockGuard, Mode, is_dyn_thread_safe}; @@ -140,7 +140,7 @@ pub fn shards() -> usize { 1 } -pub type ShardedHashMap = Sharded>; +pub type ShardedHashMap = Sharded>; impl ShardedHashMap { pub fn with_capacity(cap: usize) -> Self { diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 22de197d374a..404531eb3c91 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" -hashbrown = { version = "0.16.1", default-features = false } itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 820998eed100..e06b3625aa7b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -88,7 +88,6 @@ use std::borrow::Cow; use std::hash::{Hash, Hasher}; use either::Either; -use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use rustc_arena::DroplessArena; @@ -99,6 +98,7 @@ use rustc_const_eval::interpret::{ }; use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::hash_table::{Entry, HashTable}; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexVec, newtype_index}; diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 73ab03576dec..0ad8143c5a4f 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -23,8 +23,3 @@ rustc_thread_pool = { path = "../rustc_thread_pool" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end - -[dependencies.hashbrown] -version = "0.16.1" -default-features = false -features = ["nightly"] # for may_dangle diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 04902d0ab10c..7e34957364e4 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -7,9 +7,8 @@ use std::fmt::Debug; use std::hash::Hash; use std::mem; -use hashbrown::HashTable; -use hashbrown::hash_table::Entry; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::hash_table::{self, Entry, HashTable}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::LockGuard; @@ -35,7 +34,7 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { } pub struct QueryState<'tcx, K> { - active: Sharded)>>, + active: Sharded)>>, } /// Indicates the state of a query for a given key in a query map. From 9cdcd0c3fa2ec52547d196101d350c5f2e50ead6 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Sat, 31 Jan 2026 10:15:19 +0530 Subject: [PATCH 426/583] Document enum types used as values for E0423 --- compiler/rustc_error_codes/src/error_codes/E0423.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/compiler/rustc_error_codes/src/error_codes/E0423.md b/compiler/rustc_error_codes/src/error_codes/E0423.md index a98ada17a469..eb5243b59847 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0423.md +++ b/compiler/rustc_error_codes/src/error_codes/E0423.md @@ -44,3 +44,16 @@ fn h1() -> i32 { // did you mean `a::I`? } ``` + + + +### Enum types used as values + +Enums are types and cannot be used directly as values. + +```compile_fail,E0423 +fn main() { + let x = Option::; + //~^ ERROR expected value, found enum `Option` +} +``` \ No newline at end of file From daaff44cbc25c35fbe59878bef315252fbba8a62 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Sat, 31 Jan 2026 10:46:58 +0530 Subject: [PATCH 427/583] Fix tidy formatting manually for E0423.md --- compiler/rustc_error_codes/src/error_codes/E0423.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0423.md b/compiler/rustc_error_codes/src/error_codes/E0423.md index eb5243b59847..4af17b1221b9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0423.md +++ b/compiler/rustc_error_codes/src/error_codes/E0423.md @@ -45,15 +45,13 @@ fn h1() -> i32 { } ``` - - ### Enum types used as values Enums are types and cannot be used directly as values. ```compile_fail,E0423 -fn main() { +fn main(){ let x = Option::; - //~^ ERROR expected value, found enum `Option` + //~^ ERROR expected value,found enum `Option` } -``` \ No newline at end of file +``` From 85ae47f83e0750e82e75229ca8adeace7d0e1239 Mon Sep 17 00:00:00 2001 From: delta17920 Date: Sat, 31 Jan 2026 06:44:24 +0000 Subject: [PATCH 428/583] moved 7 tests to organized locations --- .../issue-2074.rs => closures/local-enums-in-closure-2074.rs} | 0 .../struct-function-same-name-2445-b.rs} | 0 .../issue-2445.rs => resolve/struct-function-same-name-2445.rs} | 0 .../struct-function-same-name-2487-a.rs} | 0 .../issue-2502.rs => resolve/struct-function-same-name-2502.rs} | 0 .../issue-2550.rs => resolve/struct-function-same-name-2550.rs} | 0 .../issue-2463.rs => structs/struct-update-syntax-2463.rs} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-2074.rs => closures/local-enums-in-closure-2074.rs} (100%) rename tests/ui/{issues/issue-2445-b.rs => resolve/struct-function-same-name-2445-b.rs} (100%) rename tests/ui/{issues/issue-2445.rs => resolve/struct-function-same-name-2445.rs} (100%) rename tests/ui/{issues/issue-2487-a.rs => resolve/struct-function-same-name-2487-a.rs} (100%) rename tests/ui/{issues/issue-2502.rs => resolve/struct-function-same-name-2502.rs} (100%) rename tests/ui/{issues/issue-2550.rs => resolve/struct-function-same-name-2550.rs} (100%) rename tests/ui/{issues/issue-2463.rs => structs/struct-update-syntax-2463.rs} (100%) diff --git a/tests/ui/issues/issue-2074.rs b/tests/ui/closures/local-enums-in-closure-2074.rs similarity index 100% rename from tests/ui/issues/issue-2074.rs rename to tests/ui/closures/local-enums-in-closure-2074.rs diff --git a/tests/ui/issues/issue-2445-b.rs b/tests/ui/resolve/struct-function-same-name-2445-b.rs similarity index 100% rename from tests/ui/issues/issue-2445-b.rs rename to tests/ui/resolve/struct-function-same-name-2445-b.rs diff --git a/tests/ui/issues/issue-2445.rs b/tests/ui/resolve/struct-function-same-name-2445.rs similarity index 100% rename from tests/ui/issues/issue-2445.rs rename to tests/ui/resolve/struct-function-same-name-2445.rs diff --git a/tests/ui/issues/issue-2487-a.rs b/tests/ui/resolve/struct-function-same-name-2487-a.rs similarity index 100% rename from tests/ui/issues/issue-2487-a.rs rename to tests/ui/resolve/struct-function-same-name-2487-a.rs diff --git a/tests/ui/issues/issue-2502.rs b/tests/ui/resolve/struct-function-same-name-2502.rs similarity index 100% rename from tests/ui/issues/issue-2502.rs rename to tests/ui/resolve/struct-function-same-name-2502.rs diff --git a/tests/ui/issues/issue-2550.rs b/tests/ui/resolve/struct-function-same-name-2550.rs similarity index 100% rename from tests/ui/issues/issue-2550.rs rename to tests/ui/resolve/struct-function-same-name-2550.rs diff --git a/tests/ui/issues/issue-2463.rs b/tests/ui/structs/struct-update-syntax-2463.rs similarity index 100% rename from tests/ui/issues/issue-2463.rs rename to tests/ui/structs/struct-update-syntax-2463.rs From a77b9e62ccf1f5fd93c6d899a37d074dd84d3eac Mon Sep 17 00:00:00 2001 From: Ada Alakbarova <58857108+ada4a@users.noreply.github.com> Date: Sat, 31 Jan 2026 07:58:24 +0100 Subject: [PATCH 429/583] Fix tense --- src/doc/rustc-dev-guide/src/offload/internals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/offload/internals.md b/src/doc/rustc-dev-guide/src/offload/internals.md index 77a4cadbcb98..520c48d95896 100644 --- a/src/doc/rustc-dev-guide/src/offload/internals.md +++ b/src/doc/rustc-dev-guide/src/offload/internals.md @@ -13,4 +13,4 @@ We use a single-source, two-pass compilation approach. First we compile all functions that should be offloaded for the device (e.g nvptx64, amdgcn-amd-amdhsa, intel in the future). Currently we require cumbersome `#cfg(target_os="")` annotations, but we intend to recognize those in the future based on our offload intrinsic. -We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. On the host side, we generate calls to the openmp offload runtime, to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. `&[f32;1024]`), from the device, or both (e.g. `&mut [f64]`). We then launched the kernel, after which we inform the runtime to end this environment and move data back (as far as needed). +We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. On the host side, we generate calls to the openmp offload runtime, to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. `&[f32;1024]`), from the device, or both (e.g. `&mut [f64]`). We then launch the kernel, after which we inform the runtime to end this environment and move data back (as far as needed). From 65f075792bc92bc8027806e3152a8fd396954eab Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 25 Jan 2026 23:22:43 +0300 Subject: [PATCH 430/583] resolve: Use `IdentKey` in `resolve_ident_in_scope_set` --- compiler/rustc_resolve/src/diagnostics.rs | 18 +++-- compiler/rustc_resolve/src/ident.rs | 71 +++++++++++++------ .../rustc_resolve/src/late/diagnostics.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 5 +- 4 files changed, 65 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6f2d3e79d10a..6884ed1891a3 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -32,7 +32,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::{SourceMap, Spanned}; -use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, SyntaxContext, kw, sym}; +use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, instrument}; @@ -41,6 +41,7 @@ use crate::errors::{ ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, MaybeMissingMacroRulesName, }; +use crate::hygiene::Macros20NormalizedSyntaxContext; use crate::imports::{Import, ImportKind}; use crate::late::{DiagMetadata, PatternSource, Rib}; use crate::{ @@ -1163,11 +1164,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestions: &mut Vec, scope_set: ScopeSet<'ra>, ps: &ParentScope<'ra>, - ctxt: SyntaxContext, + sp: Span, filter_fn: &impl Fn(Res) -> bool, ) { - let ctxt = DUMMY_SP.with_ctxt(ctxt); - self.cm().visit_scopes(scope_set, ps, ctxt, None, |this, scope, use_prelude, _| { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { match scope { Scope::DeriveHelpers(expn_id) => { let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); @@ -1269,8 +1270,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { filter_fn: &impl Fn(Res) -> bool, ) -> Option { let mut suggestions = Vec::new(); - let ctxt = ident.span.ctxt(); - self.add_scope_set_candidates(&mut suggestions, scope_set, parent_scope, ctxt, filter_fn); + self.add_scope_set_candidates( + &mut suggestions, + scope_set, + parent_scope, + ident.span, + filter_fn, + ); // Make sure error reporting is deterministic. suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 4fbde60d8679..5c85f721c0b8 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -54,9 +54,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { mut self: CmResolver<'r, 'ra, 'tcx>, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, - // Location of the span is not significant, but pass a `Span` instead of `SyntaxContext` - // to avoid extracting and re-packaging the syntax context unnecessarily. - orig_ctxt: Span, + mut ctxt: Macros20NormalizedSyntaxContext, + orig_ident_span: Span, derive_fallback_lint_id: Option, mut visitor: impl FnMut( CmResolver<'_, 'ra, 'tcx>, @@ -128,7 +127,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { TypeNS | ValueNS => Scope::ModuleNonGlobs(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; - let mut ctxt = Macros20NormalizedSyntaxContext::new(orig_ctxt.ctxt()); let mut use_prelude = !module.no_implicit_prelude; loop { @@ -153,7 +151,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { true } Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..) => true, - Scope::MacroUsePrelude => use_prelude || orig_ctxt.edition().is_rust_2015(), + Scope::MacroUsePrelude => use_prelude || orig_ident_span.is_rust_2015(), Scope::BuiltinAttrs => true, Scope::ExternPreludeItems | Scope::ExternPreludeFlags => { use_prelude || module_and_extern_prelude || extern_prelude @@ -396,9 +394,30 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize: Option, ignore_decl: Option>, ignore_import: Option>, + ) -> Result, Determinacy> { + self.resolve_ident_in_scope_set_inner( + IdentKey::new(orig_ident), + orig_ident.span, + scope_set, + parent_scope, + finalize, + ignore_decl, + ignore_import, + ) + } + + fn resolve_ident_in_scope_set_inner<'r>( + self: CmResolver<'r, 'ra, 'tcx>, + ident: IdentKey, + orig_ident_span: Span, + scope_set: ScopeSet<'ra>, + parent_scope: &ParentScope<'ra>, + finalize: Option, + ignore_decl: Option>, + ignore_import: Option>, ) -> Result, Determinacy> { // Make sure `self`, `super` etc produce an error when passed to here. - if !matches!(scope_set, ScopeSet::Module(..)) && orig_ident.is_path_segment_keyword() { + if !matches!(scope_set, ScopeSet::Module(..)) && ident.name.is_path_segment_keyword() { return Err(Determinacy::Determined); } @@ -432,13 +451,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let break_result = self.visit_scopes( scope_set, parent_scope, - orig_ident.span, + ident.ctxt, + orig_ident_span, derive_fallback_lint_id, |mut this, scope, use_prelude, ctxt| { - let ident = IdentKey { name: orig_ident.name, ctxt }; + let ident = IdentKey { name: ident.name, ctxt }; let res = match this.reborrow().resolve_ident_in_scope( ident, - orig_ident.span, + orig_ident_span, ns, scope, use_prelude, @@ -472,7 +492,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(&(innermost_decl, _)) = innermost_results.first() { // Found another solution, if the first one was "weak", report an error. if this.get_mut().maybe_push_ambiguity( - orig_ident, + ident, + orig_ident_span, ns, scope_set, parent_scope, @@ -695,8 +716,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::StdLibPrelude => { let mut result = Err(Determinacy::Determined); if let Some(prelude) = self.prelude - && let Ok(decl) = self.reborrow().resolve_ident_in_scope_set( - ident.orig(orig_ident_span.with_ctxt(*ident.ctxt)), + && let Ok(decl) = self.reborrow().resolve_ident_in_scope_set_inner( + ident, + orig_ident_span, ScopeSet::Module(ns, prelude), parent_scope, None, @@ -749,7 +771,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn maybe_push_ambiguity( &mut self, - orig_ident: Ident, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, @@ -775,7 +798,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else if innermost_res == derive_helper_compat { Some(AmbiguityKind::DeriveHelper) } else if res == derive_helper_compat && innermost_res != derive_helper { - span_bug!(orig_ident.span, "impossible inner resolution kind") + span_bug!(orig_ident_span, "impossible inner resolution kind") } else if matches!(innermost_scope, Scope::MacroRules(_)) && matches!(scope, Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..)) && !self.disambiguate_macro_rules_vs_modularized(innermost_decl, decl) @@ -790,7 +813,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // we visit all macro_rules scopes (e.g. textual scope macros) // before we visit any modules (e.g. path-based scope macros) span_bug!( - orig_ident.span, + orig_ident_span, "ambiguous scoped macro resolutions with path-based \ scope resolution as first candidate" ) @@ -839,8 +862,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { // Turn ambiguity errors for core vs std panic into warnings. // FIXME: Remove with lang team approval. - let is_issue_147319_hack = orig_ident.span.edition() <= Edition::Edition2024 - && matches!(orig_ident.name, sym::panic) + let is_issue_147319_hack = orig_ident_span.edition() <= Edition::Edition2024 + && matches!(ident.name, sym::panic) && matches!(scope, Scope::StdLibPrelude) && matches!(innermost_scope, Scope::ModuleGlobs(_, _)) && ((self.is_specific_builtin_macro(res, sym::std_panic) @@ -852,7 +875,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.ambiguity_errors.push(AmbiguityError { kind, - ident: orig_ident, + ident: ident.orig(orig_ident_span), b1: innermost_decl, b2: decl, scope1: innermost_scope, @@ -1145,8 +1168,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => return Err(ControlFlow::Continue(Undetermined)), }; let tmp_parent_scope; - let (mut adjusted_parent_scope, mut ctxt) = (parent_scope, *ident.ctxt); - match ctxt.glob_adjust(module.expansion, glob_import.span) { + let (mut adjusted_parent_scope, mut adjusted_ident) = (parent_scope, ident); + match adjusted_ident + .ctxt + .update_unchecked(|ctxt| ctxt.glob_adjust(module.expansion, glob_import.span)) + { Some(Some(def)) => { tmp_parent_scope = ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; @@ -1155,8 +1181,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(None) => {} None => continue, }; - let result = self.reborrow().resolve_ident_in_scope_set( - ident.orig(orig_ident_span.with_ctxt(ctxt)), + let result = self.reborrow().resolve_ident_in_scope_set_inner( + adjusted_ident, + orig_ident_span, ScopeSet::Module(ns, module), adjusted_parent_scope, None, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a3ee17ec4a9a..8794c4ff8b02 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2677,7 +2677,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut names, ScopeSet::All(ns), parent_scope, - ctxt, + segment.ident.span.with_ctxt(ctxt), filter_fn, ); break; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 5fe1be039a88..dd5ac55d30d8 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1923,7 +1923,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &mut self, current_trait: Option>, parent_scope: &ParentScope<'ra>, - ctxt: Span, + sp: Span, assoc_item: Option<(Symbol, Namespace)>, ) -> Vec { let mut found_traits = Vec::new(); @@ -1940,7 +1940,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let scope_set = ScopeSet::All(TypeNS); - self.cm().visit_scopes(scope_set, parent_scope, ctxt, None, |mut this, scope, _, _| { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, parent_scope, ctxt, sp, None, |mut this, scope, _, _| { match scope { Scope::ModuleNonGlobs(module, _) => { this.get_mut().traits_in_module(module, assoc_item, &mut found_traits); From 726a0a68b1d9867f1beed0168bd2a9a87aad4e81 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 30 Jan 2026 16:36:42 +0300 Subject: [PATCH 431/583] resolve: Inline `resolve_ident_in_virt_module_unadjusted` into `resolve_ident_in_module` --- compiler/rustc_resolve/src/ident.rs | 56 +++++++---------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 5c85f721c0b8..5b3fd805ebb8 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -912,55 +912,24 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_decl: Option>, ignore_import: Option>, ) -> Result, Determinacy> { - let tmp_parent_scope; - let mut adjusted_parent_scope = parent_scope; match module { - ModuleOrUniformRoot::Module(m) => { - if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(m.expansion) { + ModuleOrUniformRoot::Module(module) => { + let tmp_parent_scope; + let mut adjusted_parent_scope = parent_scope; + if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(module.expansion) { tmp_parent_scope = ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; adjusted_parent_scope = &tmp_parent_scope; } + self.resolve_ident_in_scope_set( + ident, + ScopeSet::Module(ns, module), + &adjusted_parent_scope, + finalize, + ignore_decl, + ignore_import, + ) } - ModuleOrUniformRoot::ExternPrelude => { - ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); - } - ModuleOrUniformRoot::ModuleAndExternPrelude(..) | ModuleOrUniformRoot::CurrentScope => { - // No adjustments - } - } - self.resolve_ident_in_virt_module_unadjusted( - module, - ident, - ns, - adjusted_parent_scope, - finalize, - ignore_decl, - ignore_import, - ) - } - - /// Attempts to resolve `ident` in namespace `ns` of `module`. - #[instrument(level = "debug", skip(self))] - fn resolve_ident_in_virt_module_unadjusted<'r>( - self: CmResolver<'r, 'ra, 'tcx>, - module: ModuleOrUniformRoot<'ra>, - ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'ra>, - finalize: Option, - ignore_decl: Option>, - ignore_import: Option>, - ) -> Result, Determinacy> { - match module { - ModuleOrUniformRoot::Module(module) => self.resolve_ident_in_scope_set( - ident, - ScopeSet::Module(ns, module), - parent_scope, - finalize, - ignore_decl, - ignore_import, - ), ModuleOrUniformRoot::ModuleAndExternPrelude(module) => self.resolve_ident_in_scope_set( ident, ScopeSet::ModuleAndExternPrelude(ns, module), @@ -973,6 +942,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if ns != TypeNS { Err(Determined) } else { + ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); self.resolve_ident_in_scope_set( ident, ScopeSet::ExternPrelude, From 161fcdeac00c9bc8b284bd62eef207dd5698cc05 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 30 Jan 2026 17:05:57 +0300 Subject: [PATCH 432/583] resolve: Avoid double normalization in `resolve_ident_in_module` --- compiler/rustc_resolve/src/ident.rs | 25 ++++++++++++------------- compiler/rustc_resolve/src/lib.rs | 17 ++++++++++++++++- compiler/rustc_span/src/hygiene.rs | 2 +- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 5b3fd805ebb8..0e73c349c8cd 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -905,7 +905,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn resolve_ident_in_module<'r>( self: CmResolver<'r, 'ra, 'tcx>, module: ModuleOrUniformRoot<'ra>, - mut ident: Ident, + ident: Ident, ns: Namespace, parent_scope: &ParentScope<'ra>, finalize: Option, @@ -914,15 +914,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Result, Determinacy> { match module { ModuleOrUniformRoot::Module(module) => { - let tmp_parent_scope; - let mut adjusted_parent_scope = parent_scope; - if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(module.expansion) { - tmp_parent_scope = - ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; - adjusted_parent_scope = &tmp_parent_scope; - } - self.resolve_ident_in_scope_set( - ident, + let (ident_key, def) = IdentKey::new_adjusted(ident, module.expansion); + let adjusted_parent_scope = match def { + Some(def) => ParentScope { module: self.expn_def_scope(def), ..*parent_scope }, + None => *parent_scope, + }; + self.resolve_ident_in_scope_set_inner( + ident_key, + ident.span, ScopeSet::Module(ns, module), &adjusted_parent_scope, finalize, @@ -942,9 +941,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if ns != TypeNS { Err(Determined) } else { - ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); - self.resolve_ident_in_scope_set( - ident, + self.resolve_ident_in_scope_set_inner( + IdentKey::new_adjusted(ident, ExpnId::root()).0, + ident.span, ScopeSet::ExternPrelude, parent_scope, finalize, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index dd5ac55d30d8..75bd2c62f9b4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -575,6 +575,12 @@ impl IdentKey { IdentKey { name: ident.name, ctxt: Macros20NormalizedSyntaxContext::new(ident.span.ctxt()) } } + #[inline] + fn new_adjusted(ident: Ident, expn_id: ExpnId) -> (IdentKey, Option) { + let (ctxt, def) = Macros20NormalizedSyntaxContext::new_adjusted(ident.span.ctxt(), expn_id); + (IdentKey { name: ident.name, ctxt }, def) + } + #[inline] fn with_root_ctxt(name: Symbol) -> Self { let ctxt = Macros20NormalizedSyntaxContext::new_unchecked(SyntaxContext::root()); @@ -2724,7 +2730,7 @@ mod ref_mut { } mod hygiene { - use rustc_span::SyntaxContext; + use rustc_span::{ExpnId, SyntaxContext}; /// A newtype around `SyntaxContext` that can only keep contexts produced by /// [SyntaxContext::normalize_to_macros_2_0]. @@ -2737,6 +2743,15 @@ mod hygiene { Macros20NormalizedSyntaxContext(ctxt.normalize_to_macros_2_0()) } + #[inline] + pub(crate) fn new_adjusted( + mut ctxt: SyntaxContext, + expn_id: ExpnId, + ) -> (Macros20NormalizedSyntaxContext, Option) { + let def = ctxt.normalize_to_macros_2_0_and_adjust(expn_id); + (Macros20NormalizedSyntaxContext(ctxt), def) + } + #[inline] pub(crate) fn new_unchecked(ctxt: SyntaxContext) -> Macros20NormalizedSyntaxContext { debug_assert_eq!(ctxt, ctxt.normalize_to_macros_2_0()); diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 2e8037b3f9e4..05f4c8e268a9 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -804,7 +804,7 @@ impl SyntaxContext { /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. #[inline] - pub(crate) fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { + pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { HygieneData::with(|data| { *self = data.normalize_to_macros_2_0(*self); data.adjust(self, expn_id) From 54f88be3cd4b8e7a3f4cae9ad495d047bfb51d33 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 31 Jan 2026 07:07:53 +0000 Subject: [PATCH 433/583] Revert "Enable `outline-atomics` by default on AArch64 FreeBSD" This reverts commit 383053e01602a1a754a170d7c201cc43fff87739. --- .../rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs index 47775f968400..69a65e6b0f02 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { - features: "+v8a,+outline-atomics".into(), + features: "+v8a".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::ADDRESS From 63e16520a245d69dae38428aede40303dfc0e410 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 31 Jan 2026 07:07:59 +0000 Subject: [PATCH 434/583] Revert "Enable `outline-atomics` by default on AArch64 OpenBSD" This reverts commit 66c150c1fa29d15e1c5c06dfe708c8443faf42c3. --- .../rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs index 14a2f007f1e8..e5e40cb38b91 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_openbsd.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { - features: "+v8a,+outline-atomics".into(), + features: "+v8a".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, ..base::openbsd::opts() From a9da102e20153c03e963c3cdf5160cde7c7d81af Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 31 Jan 2026 07:08:05 +0000 Subject: [PATCH 435/583] Revert "Enable `outline-atomics` by default on AArch64 Fuchsia" This reverts commit 21525f862d621bbeb9a131f3631111c402e1a447. --- .../rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs index 8a07f98075a8..6489e2bda809 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs @@ -5,7 +5,7 @@ use crate::spec::{ pub(crate) fn target() -> Target { let mut base = base::fuchsia::opts(); base.cpu = "generic".into(); - base.features = "+v8a,+crc,+aes,+sha2,+neon,+outline-atomics".into(); + base.features = "+v8a,+crc,+aes,+sha2,+neon".into(); base.max_atomic_width = Some(128); base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS From 1173034b7bd007d68931cf58cb656deec8af994d Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 31 Jan 2026 07:08:11 +0000 Subject: [PATCH 436/583] Revert "Enable `outline-atomics` by default on AArch64 Android" This reverts commit c45590397843909a1f36d4fd9d89cfc1e7551652. --- compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs index d1810b6fa486..3b158c13521e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_linux_android.rs @@ -21,7 +21,7 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(128), // As documented in https://developer.android.com/ndk/guides/cpu-features.html // the neon (ASIMD) and FP must exist on all android aarch64 targets. - features: "+v8a,+neon,+outline-atomics".into(), + features: "+v8a,+neon".into(), // the AAPCS64 expects use of non-leaf frame pointers per // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not From 9fb68d60efc842614a5ce63eb4d8ecfa6f7deee3 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 31 Jan 2026 07:08:17 +0000 Subject: [PATCH 437/583] Revert "Enable `outline-atomics` by default on AArch64 Windows platforms" This reverts commit 1ed1b6e2674e0884479042ae1b6aac431f1c217c. --- .../rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs | 2 +- .../rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs index db3cf83ddc99..ce17280b153d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs @@ -3,7 +3,7 @@ use crate::spec::{Arch, Cc, FramePointer, LinkerFlavor, Lld, Target, TargetMetad pub(crate) fn target() -> Target { let mut base = base::windows_gnullvm::opts(); base.max_atomic_width = Some(128); - base.features = "+v8a,+neon,+outline-atomics".into(); + base.features = "+v8a,+neon".into(); base.linker = Some("aarch64-w64-mingw32-clang".into()); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &["-m", "arm64pe"]); diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs index e9f7f51a7a35..0d06bec21f4a 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_msvc.rs @@ -3,7 +3,7 @@ use crate::spec::{Arch, FramePointer, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); base.max_atomic_width = Some(128); - base.features = "+v8a,+neon,+outline-atomics".into(); + base.features = "+v8a,+neon".into(); // Microsoft recommends enabling frame pointers on Arm64 Windows. // From https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#integer-registers From 996425b295bdc783725f42df6ad25311886a1bc8 Mon Sep 17 00:00:00 2001 From: Frank King Date: Fri, 30 Jan 2026 11:06:27 +0800 Subject: [PATCH 438/583] refactor: add an `enum DerefAdjustKind` in favor of `Option` --- compiler/rustc_hir_typeck/src/autoderef.rs | 22 ++++++++++--------- compiler/rustc_hir_typeck/src/coercion.rs | 9 ++++---- .../rustc_hir_typeck/src/expr_use_visitor.rs | 9 ++++---- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 8 ++++--- compiler/rustc_hir_typeck/src/place_op.rs | 7 +++--- compiler/rustc_lint/src/autorefs.rs | 10 ++++++--- compiler/rustc_lint/src/noop_method_call.rs | 7 ++++-- compiler/rustc_middle/src/ty/adjustment.rs | 8 ++++++- compiler/rustc_mir_build/src/thir/cx/expr.rs | 6 ++--- .../error_reporting/infer/need_type_info.rs | 4 ++-- .../clippy/clippy_lints/src/eta_reduction.rs | 4 ++-- .../clippy/clippy_lints/src/format_args.rs | 6 ++--- .../src/loops/while_let_on_iterator.rs | 4 ++-- .../clippy_lints/src/methods/map_clone.rs | 4 ++-- .../src/methods/option_as_ref_deref.rs | 2 +- .../src/methods/swap_with_temporary.rs | 4 ++-- .../src/methods/type_id_on_box.rs | 4 ++-- .../src/methods/unnecessary_to_owned.rs | 10 ++++----- .../clippy_lints/src/methods/useless_asref.rs | 4 ++-- .../clippy/clippy_lints/src/non_copy_const.rs | 4 ++-- .../clippy/clippy_utils/src/eager_or_lazy.rs | 11 +++------- src/tools/clippy/clippy_utils/src/lib.rs | 10 +++++---- src/tools/clippy/clippy_utils/src/ty/mod.rs | 5 +++-- 23 files changed, 90 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 4fe77278706e..f7700179d2a3 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind}; use rustc_infer::infer::InferOk; use rustc_infer::traits::PredicateObligations; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -45,22 +45,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty())); let steps: Vec<_> = steps .iter() - .map(|&(source, kind)| { - if let AutoderefKind::Overloaded = kind { - self.try_overloaded_deref(autoderef.span(), source).and_then( - |InferOk { value: method, obligations: o }| { + .map(|&(source, kind)| match kind { + AutoderefKind::Overloaded => { + self.try_overloaded_deref(autoderef.span(), source) + .and_then(|InferOk { value: method, obligations: o }| { obligations.extend(o); // FIXME: we should assert the sig is &T here... there's no reason for this to be fallible. if let ty::Ref(_, _, mutbl) = *method.sig.output().kind() { - Some(OverloadedDeref { mutbl, span: autoderef.span() }) + Some(DerefAdjustKind::Overloaded(OverloadedDeref { + mutbl, + span: autoderef.span(), + })) } else { None } - }, - ) - } else { - None + }) + .unwrap_or(DerefAdjustKind::Builtin) } + AutoderefKind::Builtin => DerefAdjustKind::Builtin, }) .zip_eq(targets) .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 0e87b90dc6c1..36a07b361d9d 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -50,7 +50,8 @@ use rustc_infer::traits::{ }; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + PointerCoercion, }; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; @@ -595,7 +596,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), @@ -606,7 +607,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { coerce_mutbls(mt_a, mt_b)?; Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), target: Ty::new_ptr(self.tcx, ty_a, mt_b), @@ -936,7 +937,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and( a_raw, b, - [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], + [Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: mt_a.ty }], Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), ForceLeakCheck::No, ) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ad34994526ed..171184d3a562 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -22,6 +22,7 @@ use rustc_middle::hir::place::ProjectionKind; pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; use rustc_middle::mir::FakeReadCause; use rustc_middle::thir::DerefPatBorrowMode; +use rustc_middle::ty::adjustment::DerefAdjustKind; use rustc_middle::ty::{ self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment, }; @@ -733,14 +734,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.consume_or_copy(&place_with_id, place_with_id.hir_id); } - adjustment::Adjust::Deref(None) => {} + adjustment::Adjust::Deref(DerefAdjustKind::Builtin) => {} // Autoderefs for overloaded Deref calls in fact reference // their receiver. That is, if we have `(*x)` where `x` // is of type `Rc`, then this in fact is equivalent to // `x.deref()`. Since `deref()` is declared with `&self`, // this is an autoref of `x`. - adjustment::Adjust::Deref(Some(ref deref)) => { + adjustment::Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => { let bk = ty::BorrowKind::from_mutbl(deref.mutbl); self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); } @@ -1272,9 +1273,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx { let target = self.cx.resolve_vars_if_possible(adjustment.target); match adjustment.kind { - adjustment::Adjust::Deref(overloaded) => { + adjustment::Adjust::Deref(deref_kind) => { // Equivalent to *expr or something similar. - let base = if let Some(deref) = overloaded { + let base = if let DerefAdjustKind::Overloaded(deref) = deref_kind { let ref_ty = Ty::new_ref( self.cx.tcx(), self.cx.tcx().lifetimes.re_erased, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 91f91d911444..67007523a067 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -20,7 +20,9 @@ use rustc_hir_analysis::hir_ty_lowering::{ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, +}; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, @@ -266,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target); } } - Adjust::Deref(Some(overloaded_deref)) => { + Adjust::Deref(DerefAdjustKind::Overloaded(overloaded_deref)) => { self.enforce_context_effects( None, expr.span, @@ -274,7 +276,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_args(&[expr_ty.into()]), ); } - Adjust::Deref(None) => { + Adjust::Deref(DerefAdjustKind::Builtin) => { // FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here. } Adjust::Pointer(_pointer_coercion) => { diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index a48db2cc855c..c33f8bdaffe2 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -4,8 +4,8 @@ use rustc_infer::infer::InferOk; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref, - PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + OverloadedDeref, PointerCoercion, }; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; @@ -298,7 +298,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id); if let Some(mut adjustments) = previous_adjustments { for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind + if let Adjust::Deref(DerefAdjustKind::Overloaded(ref mut deref)) = + adjustment.kind && let Some(ok) = self.try_mutable_overloaded_place_op( expr.span, source, diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index ed7ac0e33244..5bb7df80ffb3 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -1,7 +1,9 @@ use rustc_ast::{BorrowKind, UnOp}; use rustc_hir::attrs::AttributeKind; use rustc_hir::{Expr, ExprKind, Mutability, find_attr}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, DerefAdjustKind, OverloadedDeref, +}; use rustc_session::{declare_lint, declare_lint_pass}; use crate::lints::{ @@ -165,12 +167,14 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen /// an implicit borrow (or has an implicit borrow via an overloaded deref). fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> { match kind { - &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)), + &Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { mutbl, .. })) => { + Some((mutbl, true)) + } &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)), Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::ReborrowPin(..) - | Adjust::Deref(None) + | Adjust::Deref(DerefAdjustKind::Builtin) | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, } } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 24682c4562a0..66b4e3c16e93 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -1,7 +1,7 @@ use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -114,7 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { // If there is any user defined auto-deref step, then we don't want to warn. // https://github.com/rust-lang/rust-clippy/issues/9272 - if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) { + if arg_adjustments + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) + { return; } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index c806366b518a..6d546aede4cf 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -97,7 +97,7 @@ pub enum Adjust { NeverToAny, /// Dereference once, producing a place. - Deref(Option), + Deref(DerefAdjustKind), /// Take the address and produce either a `&` or `*` pointer. Borrow(AutoBorrow), @@ -108,6 +108,12 @@ pub enum Adjust { ReborrowPin(hir::Mutability), } +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub enum DerefAdjustKind { + Builtin, + Overloaded(OverloadedDeref), +} + /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8e02424706ee..0117a10e3a8c 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -14,7 +14,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, @@ -140,11 +140,11 @@ impl<'tcx> ThirBuildCx<'tcx> { } Adjust::NeverToAny if adjustment.target.is_never() => return expr, Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) }, - Adjust::Deref(None) => { + Adjust::Deref(DerefAdjustKind::Builtin) => { adjust_span(&mut expr); ExprKind::Deref { arg: self.thir.exprs.push(expr) } } - Adjust::Deref(Some(deref)) => { + Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => { // We don't need to do call adjust_span here since // deref coercions always start with a built-in deref. let call_def_id = deref.method_call(self.tcx); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index e3c8bfe4a452..ca846bca874e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -12,7 +12,7 @@ use rustc_hir::{ }; use rustc_middle::bug; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{ self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Term, TermKind, @@ -615,7 +615,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // first adjustment was not a builtin deref. let adjustment = match typeck_results.expr_adjustments(receiver) { [ - Adjustment { kind: Adjust::Deref(None), target: _ }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: _ }, .., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, ] => "", diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 21385ee4fdc7..3e46c370fb70 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -10,7 +10,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::{ self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; @@ -236,7 +236,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx .iter() .rev() .fold(0, |acc, adjustment| match adjustment.kind { - Adjust::Deref(Some(_)) => acc + 1, + Adjust::Deref(DerefAdjustKind::Overloaded(_)) => acc + 1, Adjust::Deref(_) if acc > 0 => acc + 1, _ => acc, }) diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 011cbf8c5d41..5fb1a0b80f1a 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -24,7 +24,7 @@ use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; use rustc_hir::attrs::AttributeKind; use rustc_hir::{Expr, ExprKind, LangItem, RustcVersion, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition::Edition2021; @@ -704,12 +704,12 @@ where let mut n_needed = 0; loop { if let Some(Adjustment { - kind: Adjust::Deref(overloaded_deref), + kind: Adjust::Deref(deref), target, }) = iter.next() { n_total += 1; - if overloaded_deref.is_some() { + if let DerefAdjustKind::Overloaded(..) = deref { n_needed = n_total; } ty = *target; diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 6c95c7b54c50..fc0789894cc2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::Symbol; use rustc_span::symbol::sym; @@ -88,7 +88,7 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option, e: &hir::Expr<'_>, recv: &hir::Expr<'_ && cx.tcx.lang_items().clone_trait() == Some(trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + .any(|a| matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(..)))) { let obj_ty = cx.typeck_results().expr_ty(obj); if let ty::Ref(_, ty, mutability) = obj_ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 3d489075ce8a..1239d8927acf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -58,7 +58,7 @@ pub(super) fn check( .iter() .map(|x| &x.kind) .collect::>() - && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + && let [ty::adjustment::Adjust::Deref(ty::adjustment::DerefAdjustKind::Builtin), ty::adjustment::Adjust::Borrow(_)] = *adj && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs index e378cbd6ae0a..0b8f6ae9f277 100644 --- a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs +++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs @@ -4,7 +4,7 @@ use rustc_ast::BorrowKind; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Node, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::sym; use super::SWAP_WITH_TEMPORARY; @@ -47,7 +47,7 @@ impl<'tcx> ArgKind<'tcx> { && let adjustments = cx.typeck_results().expr_adjustments(arg) && adjustments .first() - .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None))) + .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Builtin))) && adjustments .last() .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs index e67ba5c4d314..98f319be7a45 100644 --- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs +++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, ExistentialPredicate, Ty}; use rustc_span::{Span, sym}; @@ -58,7 +58,7 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span) |diag| { let derefs = recv_adjusts .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(None))) + .filter(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Builtin))) .count(); diag.note( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 74e8dbc15a6c..607aaef9627b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -14,7 +14,7 @@ use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; @@ -78,7 +78,7 @@ fn check_addr_of_expr( // For matching uses of `Cow::from` [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { @@ -89,7 +89,7 @@ fn check_addr_of_expr( // For matching uses of arrays | [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { @@ -104,11 +104,11 @@ fn check_addr_of_expr( // For matching everything else | [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { - kind: Adjust::Deref(Some(OverloadedDeref { .. })), + kind: Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { .. })), .. }, Adjustment { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index f852080d0f2a..3737249a40cb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir, LangItem, Node}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::{Span, Symbol, sym}; @@ -161,7 +161,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { && cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + .any(|a| matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(..)))) && obj.res_local_id() == Some(local_id) { true diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 9b0008a29c6b..f99748127a8f 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -33,7 +33,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::mir::{ConstValue, UnevaluatedConst}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{ self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeckResults, TypingEnv, @@ -907,7 +907,7 @@ fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option { match adjust.kind { Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow), // Custom deref calls `::deref(&x)` resulting in a borrow. - Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref), + Adjust::Deref(DerefAdjustKind::Overloaded(_)) => Some(BorrowCause::AutoDeref), // All other adjustments read the value. _ => None, } diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 2bdd5739a557..d184744162e4 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -19,7 +19,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::Symbol; use std::{cmp, ops}; @@ -132,7 +132,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .typeck_results() .expr_adjustments(e) .iter() - .any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) + .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) { self.eagerness |= NoChange; return; @@ -211,12 +211,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Custom `Deref` impl might have side effects ExprKind::Unary(UnOp::Deref, e) - if self - .cx - .typeck_results() - .expr_ty(e) - .builtin_deref(true) - .is_none() => + if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => { self.eagerness |= NoChange; }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 6b10f4b51442..16bedf199e20 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -108,7 +108,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, @@ -477,8 +477,8 @@ pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Optio .expr_adjustments(e) .iter() .find_map(|a| match a.kind { - Adjust::Deref(Some(d)) => Some(Some(d.mutbl)), - Adjust::Deref(None) => None, + Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)), + Adjust::Deref(DerefAdjustKind::Builtin) => None, _ => Some(None), }) .and_then(|x| x) @@ -3537,7 +3537,9 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) cx.typeck_results().expr_adjustments(expr).iter().any(|adj| { matches!( adj.kind, - Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny + Adjust::Deref(DerefAdjustKind::Overloaded(_)) + | Adjust::Pointer(PointerCoercion::Unsize) + | Adjust::NeverToAny ) }) } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 0f11df98fc17..46456528fdf8 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -17,7 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, @@ -1345,6 +1345,7 @@ pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { pub fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { adjustments.iter().any(|a| { let ty = mem::replace(&mut ty, a.target); - matches!(a.kind, Adjust::Deref(Some(op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) + matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(op)) if op.mutbl == Mutability::Mut) + && is_manually_drop(ty) }) } From 565b772fa5ed898981f7db684c2756338e8daa77 Mon Sep 17 00:00:00 2001 From: ThanhNguyxn Date: Thu, 22 Jan 2026 18:28:54 +0700 Subject: [PATCH 439/583] Add regression test for ICE with undefined type in async fn --- tests/ui/mir/gvn-opt-138225.rs | 16 ++++++++++++++++ tests/ui/mir/gvn-opt-138225.stderr | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/ui/mir/gvn-opt-138225.rs create mode 100644 tests/ui/mir/gvn-opt-138225.stderr diff --git a/tests/ui/mir/gvn-opt-138225.rs b/tests/ui/mir/gvn-opt-138225.rs new file mode 100644 index 000000000000..46359e044e2e --- /dev/null +++ b/tests/ui/mir/gvn-opt-138225.rs @@ -0,0 +1,16 @@ +//! Regression test for + +pub struct A { + name: NestedOption>, + //~^ ERROR cannot find type `NestedOption` in this scope +} + +impl A { + pub async fn func1() -> &'static A { + //~^ ERROR `async fn` is not permitted in Rust 2015 + static RES: A = A { name: None }; + &RES + } +} + +fn main() {} diff --git a/tests/ui/mir/gvn-opt-138225.stderr b/tests/ui/mir/gvn-opt-138225.stderr new file mode 100644 index 000000000000..b2e3d4476bf8 --- /dev/null +++ b/tests/ui/mir/gvn-opt-138225.stderr @@ -0,0 +1,19 @@ +error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/gvn-opt-138225.rs:9:9 + | +LL | pub async fn func1() -> &'static A { + | ^^^^^ to use `async fn`, switch to Rust 2018 or later + | + = help: pass `--edition 2024` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0425]: cannot find type `NestedOption` in this scope + --> $DIR/gvn-opt-138225.rs:4:11 + | +LL | name: NestedOption>, + | ^^^^^^^^^^^^ not found in this scope + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0425, E0670. +For more information about an error, try `rustc --explain E0425`. From dc48704f98f77822e8f6aea7fdac255c5aea101f Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 31 Jan 2026 02:05:07 +0000 Subject: [PATCH 440/583] Fix ICE when parsing frontmatter without newline --- compiler/rustc_parse/src/lexer/mod.rs | 2 +- .../frontmatter-no-trailing-newline/rmake.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/frontmatter-no-trailing-newline/rmake.rs diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f9bf50de091a..76f610df1eb0 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -623,7 +623,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span }); } - let last_line_start = real_s.rfind('\n').map_or(0, |i| i + 1); + let last_line_start = real_s.rfind('\n').map_or(line_end, |i| i + 1); let content = &real_s[line_end..last_line_start]; if let Some(cr_offset) = content.find('\r') { diff --git a/tests/run-make/frontmatter-no-trailing-newline/rmake.rs b/tests/run-make/frontmatter-no-trailing-newline/rmake.rs new file mode 100644 index 000000000000..204062201ad3 --- /dev/null +++ b/tests/run-make/frontmatter-no-trailing-newline/rmake.rs @@ -0,0 +1,19 @@ +// Regression test for issue #151882 +// See https://github.com/rust-lang/rust/issues/151882 + +//@ only-nightly +//@ needs-target-std + +use run_make_support::{rfs, rustc}; + +fn main() { + rfs::write("test.rs", b"----"); + + // Ensure rustc does not ICE when parsing a file with frontmatter syntax + // that has no trailing newline + rustc() + .input("test.rs") + .run_fail() + .assert_stderr_contains("invalid infostring for frontmatter") + .assert_stderr_not_contains("unexpectedly panicked"); +} From 1689fcd1cc8a7288b0d3ae88211549ad8de9df7d Mon Sep 17 00:00:00 2001 From: kouhe3 <25522053+kouhe3@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:23:06 +0800 Subject: [PATCH 441/583] feat(windows): add Unix domain socket support This commit introduces initial, unstable support for Unix domain sockets (UDS) on Windows, behind the `windows_unix_domain_sockets` feature gate Added types: - `std::os::windows::net::SocketAddr`: represents a UDS address with support for pathname addresses (abstract and unnamed are parsed but not yet fully supported). - `std::os::windows::net::UnixListener`: server-side UDS listener. - `std::os::windows::net::UnixStream`: client/server stream for UDS. Key features: - Binding and connecting using filesystem paths. - Basic I/O via `Read`/`Write`. - Address querying (`local_addr`, `peer_addr`). - Non-blocking mode, timeouts, and socket duplication. - Includes basic test coverage for smoke, echo, path length, and bind reuse. --- library/std/src/os/windows/mod.rs | 2 + library/std/src/os/windows/net/addr.rs | 173 +++++++++ library/std/src/os/windows/net/listener.rs | 342 +++++++++++++++++ library/std/src/os/windows/net/mod.rs | 6 + library/std/src/os/windows/net/stream.rs | 421 +++++++++++++++++++++ library/std/src/sys/pal/windows/mod.rs | 5 + library/std/tests/windows_unix_socket.rs | 86 +++++ 7 files changed, 1035 insertions(+) create mode 100644 library/std/src/os/windows/net/addr.rs create mode 100644 library/std/src/os/windows/net/listener.rs create mode 100644 library/std/src/os/windows/net/mod.rs create mode 100644 library/std/src/os/windows/net/stream.rs create mode 100644 library/std/tests/windows_unix_socket.rs diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index f452403ee842..53c33d17a9f6 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -29,6 +29,8 @@ pub mod ffi; pub mod fs; pub mod io; +#[unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +pub mod net; pub mod process; pub mod raw; pub mod thread; diff --git a/library/std/src/os/windows/net/addr.rs b/library/std/src/os/windows/net/addr.rs new file mode 100644 index 000000000000..ef2263edcf61 --- /dev/null +++ b/library/std/src/os/windows/net/addr.rs @@ -0,0 +1,173 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::bstr::ByteStr; +use crate::ffi::OsStr; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{AF_UNIX, SOCKADDR, SOCKADDR_UN}; +use crate::sys::cvt_nz; +use crate::{fmt, io, mem, ptr}; + +#[cfg(not(doc))] +pub fn sockaddr_un(path: &Path) -> io::Result<(SOCKADDR_UN, usize)> { + // SAFETY: All zeros is a valid representation for `sockaddr_un`. + let mut addr: SOCKADDR_UN = unsafe { mem::zeroed() }; + addr.sun_family = AF_UNIX; + + // path to UTF-8 bytes + let bytes = path + .to_str() + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "path must be valid UTF-8"))? + .as_bytes(); + if bytes.len() >= addr.sun_path.len() { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "path too long")); + } + // SAFETY: `bytes` and `addr.sun_path` are not overlapping and + // both point to valid memory. + // NOTE: We zeroed the memory above, so the path is already null + // terminated. + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) + }; + + let len = SUN_PATH_OFFSET + bytes.len() + 1; + Ok((addr, len)) +} +#[cfg(not(doc))] +const SUN_PATH_OFFSET: usize = mem::offset_of!(SOCKADDR_UN, sun_path); +pub struct SocketAddr { + #[cfg(not(doc))] + pub(super) addr: SOCKADDR_UN, + pub(super) len: u32, // Use u32 here as same as libc::socklen_t +} +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.address() { + AddressKind::Unnamed => write!(fmt, "(unnamed)"), + AddressKind::Abstract(name) => write!(fmt, "{name:?} (abstract)"), + AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"), + } + } +} + +impl SocketAddr { + #[cfg(not(doc))] + pub(super) fn new(f: F) -> io::Result + where + F: FnOnce(*mut SOCKADDR, *mut i32) -> i32, + { + unsafe { + let mut addr: SOCKADDR_UN = mem::zeroed(); + let mut len = mem::size_of::() as i32; + cvt_nz(f(&raw mut addr as *mut _, &mut len))?; + SocketAddr::from_parts(addr, len) + } + } + #[cfg(not(doc))] + pub(super) fn from_parts(addr: SOCKADDR_UN, len: i32) -> io::Result { + if addr.sun_family != AF_UNIX { + Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address family")) + } else if len < SUN_PATH_OFFSET as _ || len > mem::size_of::() as _ { + Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address length")) + } else { + Ok(SocketAddr { addr, len: len as _ }) + } + } + + /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// use std::path::Path; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// Ok(()) + /// } + /// ``` + pub fn as_pathname(&self) -> Option<&Path> { + if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } + } + + /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. + /// + /// # Errors + /// + /// Returns an error if the path is longer than `SUN_LEN` or if it contains + /// NULL bytes. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::SocketAddr; + /// use std::path::Path; + /// + /// # fn main() -> std::io::Result<()> { + /// let address = SocketAddr::from_pathname("/path/to/socket")?; + /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); + /// # Ok(()) + /// # } + /// ``` + /// + /// Creating a `SocketAddr` with a NULL byte results in an error. + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::SocketAddr; + /// + /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err()); + /// ``` + pub fn from_pathname

(path: P) -> io::Result + where + P: AsRef, + { + sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len: len as _ }) + } + fn address(&self) -> AddressKind<'_> { + let len = self.len as usize - SUN_PATH_OFFSET; + let path = unsafe { mem::transmute::<&[i8], &[u8]>(&self.addr.sun_path) }; + + if len == 0 { + AddressKind::Unnamed + } else if self.addr.sun_path[0] == 0 { + AddressKind::Abstract(ByteStr::from_bytes(&path[1..len])) + } else { + AddressKind::Pathname(unsafe { + OsStr::from_encoded_bytes_unchecked(&path[..len - 1]).as_ref() + }) + } + } + + /// Returns `true` if the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// Ok(()) + /// } + /// ``` + pub fn is_unnamed(&self) -> bool { + matches!(self.address(), AddressKind::Unnamed) + } +} +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a ByteStr), +} diff --git a/library/std/src/os/windows/net/listener.rs b/library/std/src/os/windows/net/listener.rs new file mode 100644 index 000000000000..332b116ee1a3 --- /dev/null +++ b/library/std/src/os/windows/net/listener.rs @@ -0,0 +1,342 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; +use crate::os::windows::net::{SocketAddr, UnixStream}; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{AF_UNIX, SOCK_STREAM, SOCKADDR_UN, bind, getsockname, listen}; +use crate::sys::net::Socket; +#[cfg(not(doc))] +use crate::sys::winsock::startup; +use crate::sys::{AsInner, cvt_nz}; +use crate::{fmt, io}; + +/// A structure representing a Unix domain socket server. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::thread; +/// use std::os::windows::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// // accept connections and process them, spawning a new thread for each one +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// /* connection succeeded */ +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// /* connection failed */ +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub struct UnixListener(Socket); + +impl fmt::Debug for UnixListener { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixListener"); + builder.field("sock", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + builder.finish() + } +} +impl UnixListener { + /// Creates a new `UnixListener` bound to the specified socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// let listener = match UnixListener::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return + /// } + /// }; + /// ``` + pub fn bind>(path: P) -> io::Result { + let socket_addr = SocketAddr::from_pathname(path)?; + Self::bind_addr(&socket_addr) + } + + /// Creates a new `UnixListener` bound to the specified [`socket address`]. + /// + /// [`socket address`]: crate::os::windows::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::{UnixListener}; + /// + /// fn main() -> std::io::Result<()> { + /// let listener1 = UnixListener::bind("path/to/socket")?; + /// let addr = listener1.local_addr()?; + /// + /// let listener2 = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {err:?}"); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { + startup(); + let inner = Socket::new(AF_UNIX as _, SOCK_STREAM)?; + unsafe { + cvt_nz(bind(inner.as_raw(), &raw const socket_addr.addr as _, socket_addr.len as _))?; + cvt_nz(listen(inner.as_raw(), 128))?; + } + Ok(UnixListener(inner)) + } + + /// Accepts a new incoming connection to this listener. + /// + /// This function will block the calling thread until a new Unix connection + /// is established. When established, the corresponding [`UnixStream`] and + /// the remote peer's address will be returned. + /// + /// [`UnixStream`]: crate::os::windows::net::UnixStream + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {addr:?}"), + /// Err(e) => println!("accept function failed: {e:?}"), + /// } + /// Ok(()) + /// } + /// ``` + pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { + let mut storage = SOCKADDR_UN::default(); + let mut len = size_of::() as _; + let inner = self.0.accept(&raw mut storage as *mut _, &raw mut len)?; + let addr = SocketAddr::from_parts(storage, len)?; + Ok((UnixStream(inner), addr)) + } + + /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getsockname(self.0.as_raw(), addr, len) }) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixListener` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// Ok(()) + /// } + /// ``` + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixListener) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// This will result in the `accept` operation becoming nonblocking, + /// i.e., immediately returning from their calls. If the IO operation is + /// successful, `Ok` is returned and no further action is required. If the + /// IO operation could not be completed and needs to be retried, an error + /// with kind [`io::ErrorKind::WouldBlock`] is returned. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// Ok(()) + /// } + /// ``` + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/tmp/sock")?; + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {err:?}"); + /// } + /// Ok(()) + /// } + /// ``` + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Returns an iterator over incoming connections. + /// + /// The iterator will never return [`None`] and will also not yield the + /// peer's [`SocketAddr`] structure. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::thread; + /// use std::os::windows::net::{UnixStream, UnixListener}; + /// + /// fn handle_client(stream: UnixStream) { + /// // ... + /// } + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// Ok(()) + /// } + /// ``` + pub fn incoming(&self) -> Incoming<'_> { + Incoming { listener: self } + } +} + +/// An iterator over incoming connections to a [`UnixListener`]. +/// +/// It will never return [`None`]. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::thread; +/// use std::os::windows::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub struct Incoming<'a> { + listener: &'a UnixListener, +} + +impl<'a> Iterator for Incoming<'a> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + Some(self.listener.accept().map(|s| s.0)) + } + + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } +} + +impl AsRawSocket for UnixListener { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl FromRawSocket for UnixListener { + #[inline] + unsafe fn from_raw_socket(sock: RawSocket) -> Self { + UnixListener(unsafe { Socket::from_raw_socket(sock) }) + } +} + +impl IntoRawSocket for UnixListener { + #[inline] + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} + +impl<'a> IntoIterator for &'a UnixListener { + type Item = io::Result; + type IntoIter = Incoming<'a>; + + fn into_iter(self) -> Incoming<'a> { + self.incoming() + } +} diff --git a/library/std/src/os/windows/net/mod.rs b/library/std/src/os/windows/net/mod.rs new file mode 100644 index 000000000000..6b3f062b8ab4 --- /dev/null +++ b/library/std/src/os/windows/net/mod.rs @@ -0,0 +1,6 @@ +mod addr; +mod listener; +mod stream; +pub use addr::*; +pub use listener::*; +pub use stream::*; diff --git a/library/std/src/os/windows/net/stream.rs b/library/std/src/os/windows/net/stream.rs new file mode 100644 index 000000000000..c31f03fdf53f --- /dev/null +++ b/library/std/src/os/windows/net/stream.rs @@ -0,0 +1,421 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::net::Shutdown; +use crate::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, RawSocket, +}; +use crate::os::windows::net::SocketAddr; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{ + AF_UNIX, SO_RCVTIMEO, SO_SNDTIMEO, SOCK_STREAM, connect, getpeername, getsockname, +}; +use crate::sys::net::Socket; +#[cfg(not(doc))] +use crate::sys::winsock::startup; +use crate::sys::{AsInner, cvt_nz}; +use crate::time::Duration; +use crate::{fmt, io}; +/// A Unix stream socket. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::os::windows::net::UnixStream; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut stream = UnixStream::connect("/path/to/my/socket")?; +/// stream.write_all(b"hello world")?; +/// let mut response = String::new(); +/// stream.read_to_string(&mut response)?; +/// println!("{response}"); +/// Ok(()) +/// } +/// ``` +pub struct UnixStream(pub(super) Socket); +impl fmt::Debug for UnixStream { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixStream"); + builder.field("sock", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + if let Ok(addr) = self.peer_addr() { + builder.field("peer", &addr); + } + builder.finish() + } +} +impl UnixStream { + /// Connects to the socket named by `path`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// let socket = match UnixStream::connect("/tmp/sock") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return + /// } + /// }; + /// ``` + pub fn connect>(path: P) -> io::Result { + let socket_addr = SocketAddr::from_pathname(path)?; + Self::connect_addr(&socket_addr) + } + + /// Connects to the socket specified by [`address`]. + /// + /// [`address`]: crate::os::windows::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::{UnixListener, UnixStream}; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr()?; + /// + /// let sock = match UnixStream::connect_addr(&addr) { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return Err(e) + /// } + /// }; + /// Ok(()) + /// } + /// ```` + pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { + startup(); + let inner = Socket::new(AF_UNIX as _, SOCK_STREAM)?; + unsafe { + cvt_nz(connect( + inner.as_raw(), + &raw const socket_addr.addr as *const _, + socket_addr.len as _, + ))?; + } + Ok(UnixStream(inner)) + } + + /// Returns the socket address of the local half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getsockname(self.0.as_raw(), addr, len) }) + } + + /// Returns the socket address of the remote half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// Ok(()) + /// } + /// ``` + pub fn peer_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getpeername(self.0.as_raw(), addr, len) }) + } + + /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + pub fn read_timeout(&self) -> io::Result> { + self.0.timeout(SO_RCVTIMEO) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// Ok(()) + /// } + /// ``` + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Sets the read timeout for the socket. + /// + /// If the provided value is [`None`], then [`read`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::io; + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_timeout(dur, SO_RCVTIMEO) + } + + /// Sets the write timeout for the socket. + /// + /// If the provided value is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::io; + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_timeout(dur, SO_SNDTIMEO) + } + + /// Shuts down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O calls on the + /// specified portions to immediately return with an appropriate value + /// (see the documentation of [`Shutdown`]). + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::net::Shutdown; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// Ok(()) + /// } + /// ``` + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {err:?}"); + /// } + /// Ok(()) + /// } + /// ``` + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixStream` is a reference to the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// Ok(()) + /// } + /// ``` + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixStream) + } + + /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + pub fn write_timeout(&self) -> io::Result> { + self.0.timeout(SO_SNDTIMEO) + } +} + +impl io::Read for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + io::Read::read(&mut &*self, buf) + } +} + +impl<'a> io::Read for &'a UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } +} + +impl io::Write for UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + io::Write::write(&mut &*self, buf) + } + + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut &*self) + } +} +impl<'a> io::Write for &'a UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_vectored(&[io::IoSlice::new(buf)]) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +impl AsSocket for UnixStream { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } +} + +impl AsRawSocket for UnixStream { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl FromRawSocket for UnixStream { + #[inline] + unsafe fn from_raw_socket(sock: RawSocket) -> Self { + unsafe { UnixStream(Socket::from_raw_socket(sock)) } + } +} + +impl IntoRawSocket for UnixStream { + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 17e3cdbecd5c..0cd915261471 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -233,6 +233,11 @@ pub fn cvt(i: I) -> io::Result { if i.is_zero() { Err(io::Error::last_os_error()) } else { Ok(i) } } +#[allow(dead_code)] +pub fn cvt_nz(i: I) -> crate::io::Result<()> { + if i.is_zero() { Ok(()) } else { Err(crate::io::Error::last_os_error()) } +} + pub fn dur2timeout(dur: Duration) -> u32 { // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the // timeouts in windows APIs are typically u32 milliseconds. To translate, we diff --git a/library/std/tests/windows_unix_socket.rs b/library/std/tests/windows_unix_socket.rs new file mode 100644 index 000000000000..18f4c52e72c2 --- /dev/null +++ b/library/std/tests/windows_unix_socket.rs @@ -0,0 +1,86 @@ +#![cfg(windows)] +#![feature(windows_unix_domain_sockets)] +// Now only test windows_unix_domain_sockets feature +// in the future, will test both unix and windows uds +use std::io::{Read, Write}; +use std::os::windows::net::{UnixListener, UnixStream}; +use std::thread; + +#[test] +fn win_uds_smoke_bind_connect() { + let tmp = std::env::temp_dir(); + let sock_path = tmp.join("rust-test-uds-smoke.sock"); + let _ = std::fs::remove_file(&sock_path); + let listener = UnixListener::bind(&sock_path).expect("bind failed"); + let sock_path_clone = sock_path.clone(); + let tx = thread::spawn(move || { + let mut stream = UnixStream::connect(&sock_path_clone).expect("connect failed"); + stream.write_all(b"hello").expect("write failed"); + }); + + let (mut stream, _) = listener.accept().expect("accept failed"); + let mut buf = [0; 5]; + stream.read_exact(&mut buf).expect("read failed"); + assert_eq!(&buf, b"hello"); + + tx.join().unwrap(); + + drop(listener); + let _ = std::fs::remove_file(&sock_path); +} + +#[test] +fn win_uds_echo() { + let tmp = std::env::temp_dir(); + let sock_path = tmp.join("rust-test-uds-echo.sock"); + let _ = std::fs::remove_file(&sock_path); + + let listener = UnixListener::bind(&sock_path).expect("bind failed"); + let srv = thread::spawn(move || { + let (mut stream, _) = listener.accept().expect("accept failed"); + let mut buf = [0u8; 128]; + loop { + let n = match stream.read(&mut buf) { + Ok(0) => break, + Ok(n) => n, + Err(e) => panic!("read error: {}", e), + }; + stream.write_all(&buf[..n]).expect("write_all failed"); + } + }); + + let sock_path_clone = sock_path.clone(); + let cli = thread::spawn(move || { + let mut stream = UnixStream::connect(&sock_path_clone).expect("connect failed"); + let req = b"hello windows uds"; + stream.write_all(req).expect("write failed"); + let mut resp = vec![0u8; req.len()]; + stream.read_exact(&mut resp).expect("read failed"); + assert_eq!(resp, req); + }); + + cli.join().unwrap(); + srv.join().unwrap(); + + let _ = std::fs::remove_file(&sock_path); +} + +#[test] +fn win_uds_path_too_long() { + let tmp = std::env::temp_dir(); + let long_path = tmp.join("a".repeat(200)); + let result = UnixListener::bind(&long_path); + assert!(result.is_err()); + let _ = std::fs::remove_file(&long_path); +} +#[test] +fn win_uds_existing_bind() { + let tmp = std::env::temp_dir(); + let sock_path = tmp.join("rust-test-uds-existing.sock"); + let _ = std::fs::remove_file(&sock_path); + let listener = UnixListener::bind(&sock_path).expect("bind failed"); + let result = UnixListener::bind(&sock_path); + assert!(result.is_err()); + drop(listener); + let _ = std::fs::remove_file(&sock_path); +} From 8c0d93bc54f857bf04c294a25bdbfbe1e29b0900 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 31 Jan 2026 11:01:45 +0000 Subject: [PATCH 442/583] Skip overlapping spans in argument error suggestions --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 10 ++- .../disjoint-spans-issue-151607.rs | 16 ++++ .../disjoint-spans-issue-151607.stderr | 85 +++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs create mode 100644 tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c07cbfae256d..2e85410c8960 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2647,7 +2647,15 @@ impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> { // To suggest a multipart suggestion when encountering `foo(1, "")` where the def // was `fn foo(())`. let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; - suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + // Check if the new suggestion would overlap with any existing suggestion. + // This can happen when we have both removal suggestions (which may include + // adjacent commas) and type replacement suggestions for the same span. + let dominated = suggestions + .iter() + .any(|(span, _)| span.contains(*arg_span) || arg_span.overlaps(*span)); + if !dominated { + suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + } } } } diff --git a/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs new file mode 100644 index 000000000000..31390faa488b --- /dev/null +++ b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs @@ -0,0 +1,16 @@ +// Regression test for #151607 +// The ICE was "all spans must be disjoint" when emitting diagnostics +// with overlapping suggestion spans. + +struct B; +struct D; +struct F; +fn foo(g: F, y: F, e: &E) { + //~^ ERROR cannot find type `E` in this scope + foo(B, g, D, E, F, G) + //~^ ERROR this function takes 3 arguments but 6 arguments were supplied + //~| ERROR cannot find value `E` in this scope + //~| ERROR cannot find value `G` in this scope +} + +fn main() {} diff --git a/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr new file mode 100644 index 000000000000..f1bda1203347 --- /dev/null +++ b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr @@ -0,0 +1,85 @@ +error[E0425]: cannot find type `E` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:8:24 + | +LL | struct B; + | --------- similarly named struct `B` defined here +... +LL | fn foo(g: F, y: F, e: &E) { + | ^ + | +help: a struct with a similar name exists + | +LL - fn foo(g: F, y: F, e: &E) { +LL + fn foo(g: F, y: F, e: &B) { + | +help: you might be missing a type parameter + | +LL | fn foo(g: F, y: F, e: &E) { + | +++ + +error[E0425]: cannot find value `E` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:10:18 + | +LL | foo(B, g, D, E, F, G) + | ^ + | +help: a local variable with a similar name exists + | +LL - foo(B, g, D, E, F, G) +LL + foo(B, g, D, e, F, G) + | +help: consider importing one of these constants + | +LL + use std::f128::consts::E; + | +LL + use std::f16::consts::E; + | +LL + use std::f32::consts::E; + | +LL + use std::f64::consts::E; + | + +error[E0425]: cannot find value `G` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:10:24 + | +LL | foo(B, g, D, E, F, G) + | ^ + | +help: a local variable with a similar name exists + | +LL - foo(B, g, D, E, F, G) +LL + foo(B, g, D, E, F, g) + | +help: you might be missing a const parameter + | +LL | fn foo(g: F, y: F, e: &E) { + | +++++++++++++++++++++ + +error[E0061]: this function takes 3 arguments but 6 arguments were supplied + --> $DIR/disjoint-spans-issue-151607.rs:10:5 + | +LL | foo(B, g, D, E, F, G) + | ^^^ - - - unexpected argument #4 + | | | + | | unexpected argument #3 of type `D` + | unexpected argument #1 of type `B` + | +note: function defined here + --> $DIR/disjoint-spans-issue-151607.rs:8:4 + | +LL | fn foo(g: F, y: F, e: &E) { + | ^^^ ----- +help: consider borrowing here + | +LL | foo(B, g, D, E, F, &G) + | + +help: remove the extra arguments + | +LL - foo(B, g, D, E, F, G) +LL + foo(, g, F, G) + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0061, E0425. +For more information about an error, try `rustc --explain E0061`. From e60c475471ab934c6047465e00d5d2849c3711e3 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 31 Jan 2026 22:56:49 +1100 Subject: [PATCH 443/583] Remove unused method `DroplessArena::contains_slice` --- compiler/rustc_arena/src/lib.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 524baf5b07fe..6217bf46a942 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -510,19 +510,6 @@ impl DroplessArena { } } - /// Used by `Lift` to check whether this slice is allocated - /// in this arena. - #[inline] - pub fn contains_slice(&self, slice: &[T]) -> bool { - for chunk in self.chunks.borrow_mut().iter_mut() { - let ptr = slice.as_ptr().cast::().cast_mut(); - if chunk.start() <= ptr && chunk.end() >= ptr { - return true; - } - } - false - } - /// Allocates a string slice that is copied into the `DroplessArena`, returning a /// reference to it. Will panic if passed an empty string. /// From 4df307acc1ff152518eab3cc142fbd0a394143e4 Mon Sep 17 00:00:00 2001 From: zedddie Date: Fri, 30 Jan 2026 16:27:13 +0100 Subject: [PATCH 444/583] move some tests --- .../associated-const-access.rs} | 0 .../issue-49824.rs => closures/nested-closure-escape-borrow.rs} | 0 .../nested-closure-escape-borrow.stderr} | 0 .../ui/{issues/issue-17651.rs => closures/unsized_value_move.rs} | 0 .../issue-17651.stderr => closures/unsized_value_move.stderr} | 0 tests/ui/{issues/issue-18767.rs => deref/deref-in-for-loop.rs} | 0 .../ui/{issues/issue-34569.rs => match/closure-in-match-guard.rs} | 0 tests/ui/{issues/issue-29740.rs => match/large-match-mir-gen.rs} | 0 .../issue-32004.rs => pattern/constructor-type-mismatch.rs} | 0 .../constructor-type-mismatch.stderr} | 0 .../{issues/issue-3038.rs => pattern/multiple-bindings-on-var.rs} | 0 .../issue-3038.stderr => pattern/multiple-bindings-on-var.stderr} | 0 .../issue-2849.rs => pattern/or-pattern-binding-mismatch.rs} | 0 .../or-pattern-binding-mismatch.stderr} | 0 .../or-pattern-mismatched-variable-and-variant.rs} | 0 .../or-pattern-mismatched-variable-and-variant.stderr} | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-31267.rs => associated-consts/associated-const-access.rs} (100%) rename tests/ui/{issues/issue-49824.rs => closures/nested-closure-escape-borrow.rs} (100%) rename tests/ui/{issues/issue-49824.stderr => closures/nested-closure-escape-borrow.stderr} (100%) rename tests/ui/{issues/issue-17651.rs => closures/unsized_value_move.rs} (100%) rename tests/ui/{issues/issue-17651.stderr => closures/unsized_value_move.stderr} (100%) rename tests/ui/{issues/issue-18767.rs => deref/deref-in-for-loop.rs} (100%) rename tests/ui/{issues/issue-34569.rs => match/closure-in-match-guard.rs} (100%) rename tests/ui/{issues/issue-29740.rs => match/large-match-mir-gen.rs} (100%) rename tests/ui/{issues/issue-32004.rs => pattern/constructor-type-mismatch.rs} (100%) rename tests/ui/{issues/issue-32004.stderr => pattern/constructor-type-mismatch.stderr} (100%) rename tests/ui/{issues/issue-3038.rs => pattern/multiple-bindings-on-var.rs} (100%) rename tests/ui/{issues/issue-3038.stderr => pattern/multiple-bindings-on-var.stderr} (100%) rename tests/ui/{issues/issue-2849.rs => pattern/or-pattern-binding-mismatch.rs} (100%) rename tests/ui/{issues/issue-2849.stderr => pattern/or-pattern-binding-mismatch.stderr} (100%) rename tests/ui/{issues/issue-2848.rs => pattern/or-pattern-mismatched-variable-and-variant.rs} (100%) rename tests/ui/{issues/issue-2848.stderr => pattern/or-pattern-mismatched-variable-and-variant.stderr} (100%) diff --git a/tests/ui/issues/issue-31267.rs b/tests/ui/associated-consts/associated-const-access.rs similarity index 100% rename from tests/ui/issues/issue-31267.rs rename to tests/ui/associated-consts/associated-const-access.rs diff --git a/tests/ui/issues/issue-49824.rs b/tests/ui/closures/nested-closure-escape-borrow.rs similarity index 100% rename from tests/ui/issues/issue-49824.rs rename to tests/ui/closures/nested-closure-escape-borrow.rs diff --git a/tests/ui/issues/issue-49824.stderr b/tests/ui/closures/nested-closure-escape-borrow.stderr similarity index 100% rename from tests/ui/issues/issue-49824.stderr rename to tests/ui/closures/nested-closure-escape-borrow.stderr diff --git a/tests/ui/issues/issue-17651.rs b/tests/ui/closures/unsized_value_move.rs similarity index 100% rename from tests/ui/issues/issue-17651.rs rename to tests/ui/closures/unsized_value_move.rs diff --git a/tests/ui/issues/issue-17651.stderr b/tests/ui/closures/unsized_value_move.stderr similarity index 100% rename from tests/ui/issues/issue-17651.stderr rename to tests/ui/closures/unsized_value_move.stderr diff --git a/tests/ui/issues/issue-18767.rs b/tests/ui/deref/deref-in-for-loop.rs similarity index 100% rename from tests/ui/issues/issue-18767.rs rename to tests/ui/deref/deref-in-for-loop.rs diff --git a/tests/ui/issues/issue-34569.rs b/tests/ui/match/closure-in-match-guard.rs similarity index 100% rename from tests/ui/issues/issue-34569.rs rename to tests/ui/match/closure-in-match-guard.rs diff --git a/tests/ui/issues/issue-29740.rs b/tests/ui/match/large-match-mir-gen.rs similarity index 100% rename from tests/ui/issues/issue-29740.rs rename to tests/ui/match/large-match-mir-gen.rs diff --git a/tests/ui/issues/issue-32004.rs b/tests/ui/pattern/constructor-type-mismatch.rs similarity index 100% rename from tests/ui/issues/issue-32004.rs rename to tests/ui/pattern/constructor-type-mismatch.rs diff --git a/tests/ui/issues/issue-32004.stderr b/tests/ui/pattern/constructor-type-mismatch.stderr similarity index 100% rename from tests/ui/issues/issue-32004.stderr rename to tests/ui/pattern/constructor-type-mismatch.stderr diff --git a/tests/ui/issues/issue-3038.rs b/tests/ui/pattern/multiple-bindings-on-var.rs similarity index 100% rename from tests/ui/issues/issue-3038.rs rename to tests/ui/pattern/multiple-bindings-on-var.rs diff --git a/tests/ui/issues/issue-3038.stderr b/tests/ui/pattern/multiple-bindings-on-var.stderr similarity index 100% rename from tests/ui/issues/issue-3038.stderr rename to tests/ui/pattern/multiple-bindings-on-var.stderr diff --git a/tests/ui/issues/issue-2849.rs b/tests/ui/pattern/or-pattern-binding-mismatch.rs similarity index 100% rename from tests/ui/issues/issue-2849.rs rename to tests/ui/pattern/or-pattern-binding-mismatch.rs diff --git a/tests/ui/issues/issue-2849.stderr b/tests/ui/pattern/or-pattern-binding-mismatch.stderr similarity index 100% rename from tests/ui/issues/issue-2849.stderr rename to tests/ui/pattern/or-pattern-binding-mismatch.stderr diff --git a/tests/ui/issues/issue-2848.rs b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs similarity index 100% rename from tests/ui/issues/issue-2848.rs rename to tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs diff --git a/tests/ui/issues/issue-2848.stderr b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr similarity index 100% rename from tests/ui/issues/issue-2848.stderr rename to tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr From 086dc0258bd5d12570a79f23b73ea030c0acbd00 Mon Sep 17 00:00:00 2001 From: zedddie Date: Fri, 30 Jan 2026 16:27:49 +0100 Subject: [PATCH 445/583] clean up some tests --- tests/ui/associated-consts/associated-const-access.rs | 2 +- tests/ui/closures/nested-closure-escape-borrow.rs | 1 + tests/ui/closures/nested-closure-escape-borrow.stderr | 2 +- tests/ui/closures/unsized_value_move.rs | 1 + tests/ui/closures/unsized_value_move.stderr | 2 +- tests/ui/deref/deref-in-for-loop.rs | 1 + tests/ui/match/closure-in-match-guard.rs | 1 + tests/ui/match/large-match-mir-gen.rs | 5 +++-- tests/ui/pattern/constructor-type-mismatch.rs | 1 + tests/ui/pattern/constructor-type-mismatch.stderr | 4 ++-- tests/ui/pattern/multiple-bindings-on-var.rs | 1 + tests/ui/pattern/multiple-bindings-on-var.stderr | 6 +++--- tests/ui/pattern/or-pattern-binding-mismatch.rs | 1 + tests/ui/pattern/or-pattern-binding-mismatch.stderr | 2 +- .../pattern/or-pattern-mismatched-variable-and-variant.rs | 1 + .../or-pattern-mismatched-variable-and-variant.stderr | 4 ++-- 16 files changed, 22 insertions(+), 13 deletions(-) diff --git a/tests/ui/associated-consts/associated-const-access.rs b/tests/ui/associated-consts/associated-const-access.rs index d6081bb87439..d6508d4922ae 100644 --- a/tests/ui/associated-consts/associated-const-access.rs +++ b/tests/ui/associated-consts/associated-const-access.rs @@ -1,5 +1,5 @@ +//! regression test for //@ run-pass -// Regression test for issue #31267 struct Foo; diff --git a/tests/ui/closures/nested-closure-escape-borrow.rs b/tests/ui/closures/nested-closure-escape-borrow.rs index bc1cd6856bc9..afd440ba4250 100644 --- a/tests/ui/closures/nested-closure-escape-borrow.rs +++ b/tests/ui/closures/nested-closure-escape-borrow.rs @@ -1,3 +1,4 @@ +//! regression test for fn main() { let mut x = 0; || { diff --git a/tests/ui/closures/nested-closure-escape-borrow.stderr b/tests/ui/closures/nested-closure-escape-borrow.stderr index 1c77090de27b..5a77652fa376 100644 --- a/tests/ui/closures/nested-closure-escape-borrow.stderr +++ b/tests/ui/closures/nested-closure-escape-borrow.stderr @@ -1,5 +1,5 @@ error: captured variable cannot escape `FnMut` closure body - --> $DIR/issue-49824.rs:4:9 + --> $DIR/nested-closure-escape-borrow.rs:5:9 | LL | let mut x = 0; | ----- variable defined here diff --git a/tests/ui/closures/unsized_value_move.rs b/tests/ui/closures/unsized_value_move.rs index 7629a5a3be1e..da39cc0f35f4 100644 --- a/tests/ui/closures/unsized_value_move.rs +++ b/tests/ui/closures/unsized_value_move.rs @@ -1,3 +1,4 @@ +//! regression test for // Test that moves of unsized values within closures are caught // and rejected. diff --git a/tests/ui/closures/unsized_value_move.stderr b/tests/ui/closures/unsized_value_move.stderr index 9519507320d8..a9a26a42d167 100644 --- a/tests/ui/closures/unsized_value_move.stderr +++ b/tests/ui/closures/unsized_value_move.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time - --> $DIR/issue-17651.rs:5:18 + --> $DIR/unsized_value_move.rs:6:18 | LL | (|| Box::new(*(&[0][..])))(); | -------- ^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/deref/deref-in-for-loop.rs b/tests/ui/deref/deref-in-for-loop.rs index 87762406da60..26921c3f1dda 100644 --- a/tests/ui/deref/deref-in-for-loop.rs +++ b/tests/ui/deref/deref-in-for-loop.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass // Test that regionck uses the right memcat for patterns in for loops // and doesn't ICE. diff --git a/tests/ui/match/closure-in-match-guard.rs b/tests/ui/match/closure-in-match-guard.rs index 25b2e7fbe160..c3f16dad4bfb 100644 --- a/tests/ui/match/closure-in-match-guard.rs +++ b/tests/ui/match/closure-in-match-guard.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass //@ compile-flags:-g diff --git a/tests/ui/match/large-match-mir-gen.rs b/tests/ui/match/large-match-mir-gen.rs index e26e2c882dc3..bb084a9ef0d0 100644 --- a/tests/ui/match/large-match-mir-gen.rs +++ b/tests/ui/match/large-match-mir-gen.rs @@ -1,7 +1,8 @@ +//! regression test for //@ check-pass #![allow(dead_code)] -// Regression test for #29740. Inefficient MIR matching algorithms -// generated way too much code for this sort of case, leading to OOM. +// Inefficient MIR matching algorithms generated way +// too much code for this sort of case, leading to OOM. #![allow(non_snake_case)] pub mod KeyboardEventConstants { diff --git a/tests/ui/pattern/constructor-type-mismatch.rs b/tests/ui/pattern/constructor-type-mismatch.rs index b3493508c5a9..77b8e5312dca 100644 --- a/tests/ui/pattern/constructor-type-mismatch.rs +++ b/tests/ui/pattern/constructor-type-mismatch.rs @@ -1,3 +1,4 @@ +//! regression test for enum Foo { Bar(i32), Baz diff --git a/tests/ui/pattern/constructor-type-mismatch.stderr b/tests/ui/pattern/constructor-type-mismatch.stderr index fcbec97661b4..6610e12cdc75 100644 --- a/tests/ui/pattern/constructor-type-mismatch.stderr +++ b/tests/ui/pattern/constructor-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0532]: expected unit struct, unit variant or constant, found tuple variant `Foo::Bar` - --> $DIR/issue-32004.rs:10:9 + --> $DIR/constructor-type-mismatch.rs:11:9 | LL | Bar(i32), | -------- `Foo::Bar` defined here @@ -20,7 +20,7 @@ LL + Foo::Baz => {} | error[E0532]: expected tuple struct or tuple variant, found unit struct `S` - --> $DIR/issue-32004.rs:16:9 + --> $DIR/constructor-type-mismatch.rs:17:9 | LL | struct S; | --------- `S` defined here diff --git a/tests/ui/pattern/multiple-bindings-on-var.rs b/tests/ui/pattern/multiple-bindings-on-var.rs index cf3ba009f00c..f84276257b67 100644 --- a/tests/ui/pattern/multiple-bindings-on-var.rs +++ b/tests/ui/pattern/multiple-bindings-on-var.rs @@ -1,3 +1,4 @@ +//! regression test for enum F { G(isize, isize) } enum H { I(J, K) } diff --git a/tests/ui/pattern/multiple-bindings-on-var.stderr b/tests/ui/pattern/multiple-bindings-on-var.stderr index 210da2ceff9b..c2ec11c43465 100644 --- a/tests/ui/pattern/multiple-bindings-on-var.stderr +++ b/tests/ui/pattern/multiple-bindings-on-var.stderr @@ -1,17 +1,17 @@ error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:12:15 + --> $DIR/multiple-bindings-on-var.rs:13:15 | LL | F::G(x, x) => { println!("{}", x + x); } | ^ used in a pattern more than once error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:17:32 + --> $DIR/multiple-bindings-on-var.rs:18:32 | LL | H::I(J::L(x, _), K::M(_, x)) | ^ used in a pattern more than once error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:23:13 + --> $DIR/multiple-bindings-on-var.rs:24:13 | LL | (x, x) => { x } | ^ used in a pattern more than once diff --git a/tests/ui/pattern/or-pattern-binding-mismatch.rs b/tests/ui/pattern/or-pattern-binding-mismatch.rs index 787ab0e28960..a207c3f81899 100644 --- a/tests/ui/pattern/or-pattern-binding-mismatch.rs +++ b/tests/ui/pattern/or-pattern-binding-mismatch.rs @@ -1,3 +1,4 @@ +//! regression test for enum Foo { Alpha, Beta(isize) } fn main() { diff --git a/tests/ui/pattern/or-pattern-binding-mismatch.stderr b/tests/ui/pattern/or-pattern-binding-mismatch.stderr index ef5cdb42e610..46984fac03ce 100644 --- a/tests/ui/pattern/or-pattern-binding-mismatch.stderr +++ b/tests/ui/pattern/or-pattern-binding-mismatch.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `i` is not bound in all patterns - --> $DIR/issue-2849.rs:5:7 + --> $DIR/or-pattern-binding-mismatch.rs:6:7 | LL | Foo::Alpha | Foo::Beta(i) => {} | ^^^^^^^^^^ - variable not in all patterns diff --git a/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs index 8499459cec26..0447fd2ad589 100644 --- a/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs +++ b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs @@ -1,3 +1,4 @@ +//! regression test for #[allow(non_camel_case_types)] mod bar { diff --git a/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr index 1cef27c34635..66c7d7d348c9 100644 --- a/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr +++ b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `beta` is not bound in all patterns - --> $DIR/issue-2848.rs:14:7 + --> $DIR/or-pattern-mismatched-variable-and-variant.rs:15:7 | LL | alpha | beta => {} | ^^^^^ ---- variable not in all patterns @@ -7,7 +7,7 @@ LL | alpha | beta => {} | pattern doesn't bind `beta` error[E0170]: pattern binding `beta` is named the same as one of the variants of the type `bar::foo` - --> $DIR/issue-2848.rs:14:15 + --> $DIR/or-pattern-mismatched-variable-and-variant.rs:15:15 | LL | alpha | beta => {} | ^^^^ help: to match on the variant, qualify the path: `bar::foo::beta` From 7bb156395edb2bdc591b4774bc2cea9dd28d94da Mon Sep 17 00:00:00 2001 From: JayanAXHF Date: Sat, 31 Jan 2026 23:37:56 +0530 Subject: [PATCH 446/583] fix: fix `handle_explain` to always respect `--color always` --- compiler/rustc_driver_impl/src/lib.rs | 37 ++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..2b837cc8442b 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -491,10 +491,18 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col } text.push('\n'); } + + // If output is a terminal, use a pager to display the content. if io::stdout().is_terminal() { show_md_content_with_pager(&text, color); } else { - safe_print!("{text}"); + // Otherwise, if the user has requested colored output + // print the content in color, else print the md content. + if color == ColorConfig::Always { + show_colored_md_content(&text); + } else { + safe_print!("{text}"); + } } } else { early_dcx.early_fatal(format!("{code} is not a valid error code")); @@ -564,6 +572,33 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { safe_print!("{content}"); } +/// Prints the markdown content with colored output. +/// +/// This function is used when the output is not a terminal, +/// but the user has requested colored output with `--color=always`. +fn show_colored_md_content(content: &str) { + // Try to prettify the raw markdown text. + let mut pretty_data = { + let mdstream = markdown::MdStream::parse_str(content); + let bufwtr = markdown::create_stdout_bufwtr(); + let mut mdbuf = Vec::new(); + if mdstream.write_anstream_buf(&mut mdbuf, Some(&highlighter::highlight)).is_ok() { + Some((bufwtr, mdbuf)) + } else { + None + } + }; + + if let Some((bufwtr, mdbuf)) = &mut pretty_data + && bufwtr.write_all(&mdbuf).is_ok() + { + return; + } + + // Everything failed. Print the raw markdown text. + safe_print!("{content}"); +} + fn process_rlink(sess: &Session, compiler: &interface::Compiler) { assert!(sess.opts.unstable_opts.link_only); let dcx = sess.dcx(); From a3e94c843398b096b79c05506ee248638afd1e6c Mon Sep 17 00:00:00 2001 From: JayanAXHF Date: Sun, 1 Feb 2026 00:23:04 +0530 Subject: [PATCH 447/583] test: add tests to ensure that `--color always` is always respected --- tests/ui/explain/ensure-color-always-works.rs | 2 + .../explain/ensure-color-always-works.stdout | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/ui/explain/ensure-color-always-works.rs create mode 100644 tests/ui/explain/ensure-color-always-works.stdout diff --git a/tests/ui/explain/ensure-color-always-works.rs b/tests/ui/explain/ensure-color-always-works.rs new file mode 100644 index 000000000000..81ee716e78b4 --- /dev/null +++ b/tests/ui/explain/ensure-color-always-works.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --explain E0591 --color always --error-format=human +//@ check-pass diff --git a/tests/ui/explain/ensure-color-always-works.stdout b/tests/ui/explain/ensure-color-always-works.stdout new file mode 100644 index 000000000000..7e5358bcfb7e --- /dev/null +++ b/tests/ui/explain/ensure-color-always-works.stdout @@ -0,0 +1,50 @@ +Per ]8;;https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md\RFC 401]8;;\, if you have a function declaration foo: + +struct S; + +// For the purposes of this explanation, all of these +// different kinds of `fn` declarations are equivalent: + +fn foo(x: S) { /* ... */ } +extern "C" { + fn foo(x: S); +} +impl S { + fn foo(self) { /* ... */ } +} + +the type of foo is not fn(S), as one might expect. Rather, it is a unique, zero-sized marker type written here as typeof(foo). However, typeof(foo) + can be coerced to a function pointer fn(S), so you rarely notice this: + +let x: fn(S) = foo; // OK, coerces + +The reason that this matter is that the type fn(S) is not specific to any particular function: it's a function pointer. So calling x() +results in a virtual call, whereas foo() is statically dispatched, because the type of foo tells us precisely what function is being called. + +As noted above, coercions mean that most code doesn't have to be concerned with this distinction. However, you can tell the difference when +using transmute to convert a fn item into a fn pointer. + +This is sometimes done as part of an FFI: + +extern "C" fn foo(userdata: Box<i32>) { + /* ... */ +} + +unsafe { + let f: extern "C" fn(*mut i32) = transmute(foo); + callback(f); +} + +Here, transmute is being used to convert the types of the fn arguments. This pattern is incorrect because the type of foo is a function item +(typeof(foo)), which is zero-sized, and the target type (fn()) is a function pointer, which is not zero-sized. This pattern should be +rewritten. There are a few possible ways to do this: + +* change the original fn declaration to match the expected signature, and do the cast in the fn body (the preferred option) +* cast the fn item of a fn pointer before calling transmute, as shown here: + +let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_)); +let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too + +The same applies to transmutes to *mut fn(), which were observed in practice. Note though that use of this type is generally incorrect. The +intention is typically to describe a function pointer, but just fn() alone suffices for that. *mut fn() is a pointer to a fn pointer. (Since these +values are typically just passed to C code, however, this rarely makes a difference in practice.) From 49f9fe75ee257070af2d689265ccd16f1c9ddf76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 31 Jan 2026 00:15:00 +0100 Subject: [PATCH 448/583] Remove the `dep_node` argument from `try_mark_previous_green` --- .../rustc_query_system/src/dep_graph/graph.rs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index cf3d211d1cc7..36f5ccfc68f4 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -872,6 +872,8 @@ impl DepGraphData { // Return None if the dep node didn't exist in the previous session let prev_index = self.previous.node_to_index_opt(dep_node)?; + debug_assert_eq!(self.previous.index_to_node(prev_index), dep_node); + match self.colors.get(prev_index) { DepNodeColor::Green(dep_node_index) => Some((prev_index, dep_node_index)), DepNodeColor::Red => None, @@ -880,7 +882,7 @@ impl DepGraphData { // in the previous compilation session too, so we can try to // mark it as green by recursively marking all of its // dependencies green. - self.try_mark_previous_green(qcx, prev_index, dep_node, None) + self.try_mark_previous_green(qcx, prev_index, None) .map(|dep_node_index| (prev_index, dep_node_index)) } } @@ -928,8 +930,7 @@ impl DepGraphData { dep_dep_node, dep_dep_node.hash, ); - let node_index = - self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, Some(frame)); + let node_index = self.try_mark_previous_green(qcx, parent_dep_node_index, Some(frame)); if node_index.is_some() { debug!("managed to MARK dependency {dep_dep_node:?} as green"); @@ -981,15 +982,15 @@ impl DepGraphData { &self, qcx: Qcx, prev_dep_node_index: SerializedDepNodeIndex, - dep_node: &DepNode, frame: Option<&MarkFrame<'_>>, ) -> Option { let frame = MarkFrame { index: prev_dep_node_index, parent: frame }; // We never try to mark eval_always nodes as green - debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); - - debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), dep_node); + debug_assert!( + !qcx.dep_context() + .is_eval_always(self.previous.index_to_node(prev_dep_node_index).kind) + ); let prev_deps = self.previous.edge_targets_from(prev_dep_node_index); @@ -1010,7 +1011,10 @@ impl DepGraphData { // ... and finally storing a "Green" entry in the color map. // Multiple threads can all write the same color here - debug!("successfully marked {dep_node:?} as green"); + debug!( + "successfully marked {:?} as green", + self.previous.index_to_node(prev_dep_node_index) + ); Some(dep_node_index) } } From 4cf181bfa40957696c46b7637e6d2250fa18617b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 31 Jan 2026 21:55:02 +0200 Subject: [PATCH 449/583] suggested.md: improve a bit It is awkward to continue a sentence after a code block --- src/doc/rustc-dev-guide/src/building/suggested.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index b5c9b9b4e3d1..5eb214fac904 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -277,14 +277,14 @@ This often helps reviewing. ## Configuring `rustup` to use nightly Some parts of the bootstrap process uses pinned, nightly versions of tools like rustfmt. -To make things like `cargo fmt` work correctly in your repo, run +To make things like `cargo fmt` work correctly in your repo, +[install a nightly toolchain] with rustup, then run this command: ```console cd rustup override set nightly ``` -After [installing a nightly toolchain] with `rustup`. Don't forget to do this for all directories you have [setup a worktree for]. You may need to use the pinned nightly version from `src/stage0`, but often the normal `nightly` channel will work. @@ -297,7 +297,7 @@ toolchain for your bootstrapped compiler You still have to use `x` to work on the compiler or standard library, this just lets you use `cargo fmt`. -[installing a nightly toolchain]: https://rust-lang.github.io/rustup/concepts/channels.html?highlight=nightl#working-with-nightly-rust +[install a nightly toolchain]: https://rust-lang.github.io/rustup/concepts/channels.html?highlight=nightl#working-with-nightly-rust [setup a worktree for]: ./suggested.md#working-on-multiple-branches-at-the-same-time [the section on vscode]: suggested.md#configuring-rust-analyzer-for-rustc [the section on rustup]: how-to-build-and-run.md?highlight=rustup#creating-a-rustup-toolchain From c8ce14018f082a747307a5bbc5b46ec24c2642dd Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 1 Feb 2026 05:27:13 +0900 Subject: [PATCH 450/583] check if ProjectionTy or ProjectionConst fmt --- .../src/error_reporting/traits/fulfillment_errors.rs | 2 ++ .../opaque-alias-relate-issue-151331.rs | 10 ++++++++++ .../opaque-alias-relate-issue-151331.stderr | 9 +++++++++ 3 files changed, 21 insertions(+) create mode 100644 tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs create mode 100644 tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 6872d038fb7f..45e4f5fe23a6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1607,6 +1607,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if let Some(lhs) = lhs.to_alias_term() + && let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst = lhs.kind(self.tcx) && let Some((better_type_err, expected_term)) = derive_better_type_error(lhs, rhs) { @@ -1615,6 +1616,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { better_type_err, ) } else if let Some(rhs) = rhs.to_alias_term() + && let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst = rhs.kind(self.tcx) && let Some((better_type_err, expected_term)) = derive_better_type_error(rhs, lhs) { diff --git a/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs new file mode 100644 index 000000000000..684f2498d584 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -Znext-solver=globally +#![feature(type_alias_impl_trait)] + +type Foo = Vec; + +#[define_opaque(Foo)] +fn make_foo() -> Foo {} +//~^ ERROR type mismatch resolving + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr new file mode 100644 index 000000000000..dd73ed1a247c --- /dev/null +++ b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `Foo == ()` + --> $DIR/opaque-alias-relate-issue-151331.rs:7:18 + | +LL | fn make_foo() -> Foo {} + | ^^^ types differ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. From 4e02b6db0aac951f02d13fa316ea7d7e3acc0e98 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sun, 1 Feb 2026 00:00:26 +0200 Subject: [PATCH 451/583] typeck_root_def_id: improve doc comment This was initially written to be exhaustive, but one more type that can only be type-checked with its containing item has since been added, and was not mentioned. So, make it future-proof by mentioning just the one example. Also, a previous refactor left this less readable. --- compiler/rustc_middle/src/ty/util.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index c4212eee8e40..d6e6ffae7300 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -642,12 +642,8 @@ impl<'tcx> TyCtxt<'tcx> { /// has its own type-checking context or "inference environment". /// /// For example, a closure has its own `DefId`, but it is type-checked - /// with the containing item. Similarly, an inline const block has its - /// own `DefId` but it is type-checked together with the containing item. - /// - /// Therefore, when we fetch the - /// `typeck` the closure, for example, we really wind up - /// fetching the `typeck` the enclosing fn item. + /// with the containing item. Therefore, when we fetch the `typeck` of the closure, + /// for example, we really wind up fetching the `typeck` of the enclosing fn item. pub fn typeck_root_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; while self.is_typeck_child(def_id) { From 16844173269ade1ea68f302c24ac1ebc8efedbb4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sun, 1 Feb 2026 00:31:55 +0200 Subject: [PATCH 452/583] ty::context: clean some code a little The explicit `return` was not needed --- compiler/rustc_middle/src/ty/context.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a6960523f6cf..41e1388e3146 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -3470,10 +3470,9 @@ impl<'tcx> TyCtxt<'tcx> { pub fn intrinsic(self, def_id: impl IntoQueryParam + Copy) -> Option { match self.def_kind(def_id) { - DefKind::Fn | DefKind::AssocFn => {} - _ => return None, + DefKind::Fn | DefKind::AssocFn => self.intrinsic_raw(def_id), + _ => None, } - self.intrinsic_raw(def_id) } pub fn next_trait_solver_globally(self) -> bool { From f0b76d5a1591fc39d8a6f9c35772695a3fad0036 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 31 Jan 2026 18:57:32 -0600 Subject: [PATCH 453/583] Use with_capacity in query_key_hash_verify --- .../rustc_data_structures/src/vec_cache.rs | 4 ++++ compiler/rustc_query_impl/src/plumbing.rs | 3 +-- .../rustc_query_system/src/query/caches.rs | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index 599970663db8..c3baae204f66 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -331,6 +331,10 @@ where } } } + + pub fn len(&self) -> usize { + self.len.load(Ordering::Acquire) + } } #[cfg(test)] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 2d4e10a0380c..55c5ff911c31 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -415,9 +415,8 @@ pub(crate) fn query_key_hash_verify<'tcx>( ) { let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name()); - let mut map = UnordMap::default(); - let cache = query.query_cache(qcx); + let mut map = UnordMap::with_capacity(cache.len()); cache.iter(&mut |key, _, _| { let node = DepNode::construct(qcx.tcx, query.dep_kind(), key); if let Some(other_key) = map.insert(node, *key) { diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 30b5d7e59549..67ad767d4d31 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -30,6 +30,8 @@ pub trait QueryCache: Sized { fn complete(&self, key: Self::Key, value: Self::Value, index: DepNodeIndex); fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)); + + fn len(&self) -> usize; } /// In-memory cache for queries whose keys aren't suitable for any of the @@ -71,6 +73,10 @@ where } } } + + fn len(&self) -> usize { + self.cache.len() + } } /// In-memory cache for queries whose key type only has one value (e.g. `()`). @@ -107,6 +113,10 @@ where f(&(), &value.0, value.1) } } + + fn len(&self) -> usize { + self.cache.get().is_some().into() + } } /// In-memory cache for queries whose key is a [`DefId`]. @@ -157,6 +167,10 @@ where }); self.foreign.iter(f); } + + fn len(&self) -> usize { + self.local.len() + self.foreign.len() + } } impl QueryCache for VecCache @@ -180,4 +194,8 @@ where fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { self.iter(f) } + + fn len(&self) -> usize { + self.len() + } } From e251fba475714560d60555a26b9c1a4264776bc8 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Sun, 1 Feb 2026 10:42:57 +0900 Subject: [PATCH 454/583] Replace plain text link with md link to cargo-pgo repo --- src/doc/rustc/src/profile-guided-optimization.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md index 4050494793a3..fa6e0912f7f0 100644 --- a/src/doc/rustc/src/profile-guided-optimization.md +++ b/src/doc/rustc/src/profile-guided-optimization.md @@ -151,7 +151,9 @@ to use PGO with Rust. As an alternative to directly using the compiler for Profile-Guided Optimization, you may choose to go with `cargo-pgo`, which has an intuitive command-line API and saves you the trouble of doing all the manual work. You can read more about -it in their repository accessible from this link: https://github.com/Kobzol/cargo-pgo +it in [their repository][cargo-pgo]. + +[cargo-pgo]: https://github.com/Kobzol/cargo-pgo For the sake of completeness, here are the corresponding steps using `cargo-pgo`: From 9b04184cbc7f1387f38a35e2e219cd88f7089cf6 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Sun, 1 Feb 2026 10:46:02 +0900 Subject: [PATCH 455/583] reword --- src/doc/rustc/src/profile-guided-optimization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md index fa6e0912f7f0..eeeeffe65a02 100644 --- a/src/doc/rustc/src/profile-guided-optimization.md +++ b/src/doc/rustc/src/profile-guided-optimization.md @@ -151,7 +151,7 @@ to use PGO with Rust. As an alternative to directly using the compiler for Profile-Guided Optimization, you may choose to go with `cargo-pgo`, which has an intuitive command-line API and saves you the trouble of doing all the manual work. You can read more about -it in [their repository][cargo-pgo]. +it in [cargo-pgo repository][cargo-pgo]. [cargo-pgo]: https://github.com/Kobzol/cargo-pgo From 60f567916cc39add8a9b6571c1865577204ca729 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 31 Jan 2026 19:11:06 -0600 Subject: [PATCH 456/583] Add capacity to PlaceholderExpander --- compiler/rustc_expand/src/expand.rs | 4 +++- compiler/rustc_expand/src/placeholders.rs | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cfa7725c7400..dc2f28992105 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -508,6 +508,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // Unresolved macros produce dummy outputs as a recovery measure. invocations.reverse(); let mut expanded_fragments = Vec::new(); + let mut expanded_fragments_len = 0; let mut undetermined_invocations = Vec::new(); let (mut progress, mut force) = (false, !self.monotonic); loop { @@ -602,6 +603,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { expanded_fragments.push(Vec::new()); } expanded_fragments[depth - 1].push((expn_id, expanded_fragment)); + expanded_fragments_len += 1; invocations.extend(derive_invocations.into_iter().rev()); } ExpandResult::Retry(invoc) => { @@ -622,7 +624,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.force_mode = orig_force_mode; // Finally incorporate all the expanded macros into the input AST fragment. - let mut placeholder_expander = PlaceholderExpander::default(); + let mut placeholder_expander = PlaceholderExpander::with_capacity(expanded_fragments_len); while let Some(expanded_fragments) = expanded_fragments.pop() { for (expn_id, expanded_fragment) in expanded_fragments.into_iter().rev() { placeholder_expander diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 05f9a5aa43f7..2db18429a521 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -218,12 +218,17 @@ pub(crate) fn placeholder( } } -#[derive(Default)] pub(crate) struct PlaceholderExpander { expanded_fragments: FxHashMap, } impl PlaceholderExpander { + pub(crate) fn with_capacity(capacity: usize) -> Self { + PlaceholderExpander { + expanded_fragments: FxHashMap::with_capacity_and_hasher(capacity, Default::default()), + } + } + pub(crate) fn add(&mut self, id: ast::NodeId, mut fragment: AstFragment) { fragment.mut_visit_with(self); self.expanded_fragments.insert(id, fragment); From bf2536ae6498d04029ee9afac1cc28f7895bf725 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 31 Jan 2026 22:17:10 +1100 Subject: [PATCH 457/583] Rename `QueryResult` to `ActiveKeyStatus` --- .../rustc_query_system/src/query/plumbing.rs | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 7e34957364e4..98bbd3ebc4a0 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -33,13 +33,24 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { move |x| x.0 == *k } +/// For a particular query, keeps track of "active" keys, i.e. keys whose +/// evaluation has started but has not yet finished successfully. +/// +/// (Successful query evaluation for a key is represented by an entry in the +/// query's in-memory cache.) pub struct QueryState<'tcx, K> { - active: Sharded)>>, + active: Sharded)>>, } -/// Indicates the state of a query for a given key in a query map. -enum QueryResult<'tcx> { - /// An already executing query. The query job can be used to await for its completion. +/// For a particular query and key, tracks the status of a query evaluation +/// that has started, but has not yet finished successfully. +/// +/// (Successful query evaluation for a key is represented by an entry in the +/// query's in-memory cache.) +enum ActiveKeyStatus<'tcx> { + /// Some thread is already evaluating the query for this key. + /// + /// The enclosed [`QueryJob`] can be used to wait for it to finish. Started(QueryJob<'tcx>), /// The query panicked. Queries trying to wait on this will raise a fatal error which will @@ -47,8 +58,9 @@ enum QueryResult<'tcx> { Poisoned, } -impl<'tcx> QueryResult<'tcx> { - /// Unwraps the query job expecting that it has started. +impl<'tcx> ActiveKeyStatus<'tcx> { + /// Obtains the enclosed [`QueryJob`], or panics if this query evaluation + /// was poisoned by a panic. fn expect_job(self) -> QueryJob<'tcx> { match self { Self::Started(job) => job, @@ -76,9 +88,9 @@ where ) -> Option<()> { let mut active = Vec::new(); - let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult<'tcx>)>>| { + let mut collect = |iter: LockGuard<'_, HashTable<(K, ActiveKeyStatus<'tcx>)>>| { for (k, v) in iter.iter() { - if let QueryResult::Started(ref job) = *v { + if let ActiveKeyStatus::Started(ref job) = *v { active.push((*k, job.clone())); } } @@ -222,7 +234,7 @@ where Err(_) => panic!(), Ok(occupied) => { let ((key, value), vacant) = occupied.remove(); - vacant.insert((key, QueryResult::Poisoned)); + vacant.insert((key, ActiveKeyStatus::Poisoned)); value.expect_job() } } @@ -319,7 +331,7 @@ where let shard = query.query_state(qcx).active.lock_shard_by_hash(key_hash); match shard.find(key_hash, equivalent_key(&key)) { // The query we waited on panicked. Continue unwinding here. - Some((_, QueryResult::Poisoned)) => FatalError.raise(), + Some((_, ActiveKeyStatus::Poisoned)) => FatalError.raise(), _ => panic!( "query '{}' result must be in the cache or the query must be poisoned after a wait", query.name() @@ -373,7 +385,7 @@ where // state map. let id = qcx.next_job_id(); let job = QueryJob::new(id, span, current_job_id); - entry.insert((key, QueryResult::Started(job))); + entry.insert((key, ActiveKeyStatus::Started(job))); // Drop the lock before we start executing the query drop(state_lock); @@ -382,7 +394,7 @@ where } Entry::Occupied(mut entry) => { match &mut entry.get_mut().1 { - QueryResult::Started(job) => { + ActiveKeyStatus::Started(job) => { if sync::is_dyn_thread_safe() { // Get the latch out let latch = job.latch(); @@ -400,7 +412,7 @@ where // so we just return the error. cycle_error(query, qcx, id, span) } - QueryResult::Poisoned => FatalError.raise(), + ActiveKeyStatus::Poisoned => FatalError.raise(), } } } From b4bf57b7aa46ae7dfd31c9a7468102342f91c713 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 25 Jan 2026 14:25:18 +1100 Subject: [PATCH 458/583] Move the `fingerprint_style` special case into `DepKindVTable` creation --- compiler/rustc_query_impl/src/plumbing.rs | 8 ++++++-- compiler/rustc_query_system/src/dep_graph/dep_node.rs | 5 +++-- compiler/rustc_query_system/src/dep_graph/mod.rs | 11 +++++------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 2d4e10a0380c..cfcfffa0fdfb 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -24,7 +24,7 @@ use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::print::with_reduced_queries; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext}; +use rustc_query_system::dep_graph::{DepNodeParams, FingerprintStyle, HasDepContext}; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ QueryCache, QueryContext, QueryDispatcher, QueryJobId, QueryMap, QuerySideEffect, @@ -519,7 +519,11 @@ pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>( where Q: QueryDispatcherUnerased<'tcx>, { - let fingerprint_style = ::Key::fingerprint_style(); + let fingerprint_style = if is_anon { + FingerprintStyle::Opaque + } else { + ::Key::fingerprint_style() + }; if is_anon || !fingerprint_style.reconstructible() { return DepKindVTable { diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 72bdcd2d534d..b9d35bc5a937 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -237,8 +237,9 @@ pub struct DepKindVTable { /// cached within one compiler invocation. pub is_eval_always: bool, - /// Whether the query key can be recovered from the hashed fingerprint. - /// See [DepNodeParams] trait for the behaviour of each key type. + /// Indicates whether and how the query key can be recovered from its hashed fingerprint. + /// + /// The [`DepNodeParams`] trait determines the fingerprint style for each key type. pub fingerprint_style: FingerprintStyle, /// The red/green evaluation system will try to mark a specific DepNode in the diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 8f714a2c96d6..c3ac1c7bc2d5 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -39,11 +39,7 @@ pub trait DepContext: Copy { #[inline(always)] fn fingerprint_style(self, kind: DepKind) -> FingerprintStyle { - let vtable = self.dep_kind_vtable(kind); - if vtable.is_anon { - return FingerprintStyle::Opaque; - } - vtable.fingerprint_style + self.dep_kind_vtable(kind).fingerprint_style } #[inline(always)] @@ -148,6 +144,9 @@ impl HasDepContext for (T, Q) { } /// Describes the contents of the fingerprint generated by a given query. +/// +/// This is mainly for determining whether and how we can reconstruct a key +/// from the fingerprint. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum FingerprintStyle { /// The fingerprint is actually a DefPathHash. @@ -156,7 +155,7 @@ pub enum FingerprintStyle { HirId, /// Query key was `()` or equivalent, so fingerprint is just zero. Unit, - /// Some opaque hash. + /// The fingerprint is an opaque hash, and a key cannot be reconstructed from it. Opaque, } From a7dea5504bd3e706bf37bb158b25af8d5fe37c3c Mon Sep 17 00:00:00 2001 From: delta17920 Date: Sat, 31 Jan 2026 06:52:17 +0000 Subject: [PATCH 459/583] rename various regression tests --- .../closures/local-enums-in-closure-2074.rs | 13 ++++++-- .../struct-function-same-name-2445-b.rs | 30 ------------------- .../resolve/struct-function-same-name-2445.rs | 8 ++--- ...a.rs => struct-function-same-name-2487.rs} | 17 ++++++----- .../resolve/struct-function-same-name-2502.rs | 14 ++++----- .../resolve/struct-function-same-name-2550.rs | 10 +++---- tests/ui/structs/struct-update-syntax-2463.rs | 25 ++++++---------- 7 files changed, 41 insertions(+), 76 deletions(-) delete mode 100644 tests/ui/resolve/struct-function-same-name-2445-b.rs rename tests/ui/resolve/{struct-function-same-name-2487-a.rs => struct-function-same-name-2487.rs} (64%) diff --git a/tests/ui/closures/local-enums-in-closure-2074.rs b/tests/ui/closures/local-enums-in-closure-2074.rs index b6e3fb1fa23a..29cd6f13e3bb 100644 --- a/tests/ui/closures/local-enums-in-closure-2074.rs +++ b/tests/ui/closures/local-enums-in-closure-2074.rs @@ -1,15 +1,22 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2074 + //@ run-pass #![allow(non_camel_case_types)] pub fn main() { let one = || { - enum r { a } + enum r { + a, + } r::a as usize }; let two = || { - enum r { a } + enum r { + a, + } r::a as usize }; - one(); two(); + one(); + two(); } diff --git a/tests/ui/resolve/struct-function-same-name-2445-b.rs b/tests/ui/resolve/struct-function-same-name-2445-b.rs deleted file mode 100644 index 3a54c62a771b..000000000000 --- a/tests/ui/resolve/struct-function-same-name-2445-b.rs +++ /dev/null @@ -1,30 +0,0 @@ -//@ run-pass -#![allow(dead_code)] -#![allow(non_camel_case_types)] - - -struct c1 { - x: T, -} - -impl c1 { - pub fn f1(&self, _x: isize) { - } -} - -fn c1(x: T) -> c1 { - c1 { - x: x - } -} - -impl c1 { - pub fn f2(&self, _x: isize) { - } -} - - -pub fn main() { - c1::(3).f1(4); - c1::(3).f2(4); -} diff --git a/tests/ui/resolve/struct-function-same-name-2445.rs b/tests/ui/resolve/struct-function-same-name-2445.rs index e6c33a8fd016..8a0490efa3aa 100644 --- a/tests/ui/resolve/struct-function-same-name-2445.rs +++ b/tests/ui/resolve/struct-function-same-name-2445.rs @@ -1,8 +1,9 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2445 + //@ run-pass #![allow(dead_code)] #![allow(non_camel_case_types)] - struct c1 { x: T, } @@ -12,16 +13,13 @@ impl c1 { } fn c1(x: T) -> c1 { - c1 { - x: x - } + c1 { x } } impl c1 { pub fn f2(&self, _x: T) {} } - pub fn main() { c1::(3).f1(4); c1::(3).f2(4); diff --git a/tests/ui/resolve/struct-function-same-name-2487-a.rs b/tests/ui/resolve/struct-function-same-name-2487.rs similarity index 64% rename from tests/ui/resolve/struct-function-same-name-2487-a.rs rename to tests/ui/resolve/struct-function-same-name-2487.rs index d38616929fae..5f9a61c3260b 100644 --- a/tests/ui/resolve/struct-function-same-name-2487-a.rs +++ b/tests/ui/resolve/struct-function-same-name-2487.rs @@ -2,10 +2,8 @@ #![allow(dead_code)] #![allow(non_camel_case_types)] - struct socket { sock: isize, - } impl Drop for socket { @@ -13,19 +11,22 @@ impl Drop for socket { } impl socket { - pub fn set_identity(&self) { + pub fn set_identity(&self) { closure(|| setsockopt_bytes(self.sock.clone())) } } fn socket() -> socket { - socket { - sock: 1 - } + socket { sock: 1 } } -fn closure(f: F) where F: FnOnce() { f() } +fn closure(f: F) +where + F: FnOnce(), +{ + f() +} -fn setsockopt_bytes(_sock: isize) { } +fn setsockopt_bytes(_sock: isize) {} pub fn main() {} diff --git a/tests/ui/resolve/struct-function-same-name-2502.rs b/tests/ui/resolve/struct-function-same-name-2502.rs index 98a52a3b5a7d..5305c7d0a96f 100644 --- a/tests/ui/resolve/struct-function-same-name-2502.rs +++ b/tests/ui/resolve/struct-function-same-name-2502.rs @@ -1,11 +1,11 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2502 + //@ check-pass #![allow(dead_code)] #![allow(non_camel_case_types)] - - struct font<'a> { - fontbuf: &'a Vec , + fontbuf: &'a Vec, } impl<'a> font<'a> { @@ -14,10 +14,8 @@ impl<'a> font<'a> { } } -fn font(fontbuf: &Vec ) -> font<'_> { - font { - fontbuf: fontbuf - } +fn font(fontbuf: &Vec) -> font<'_> { + font { fontbuf } } -pub fn main() { } +pub fn main() {} diff --git a/tests/ui/resolve/struct-function-same-name-2550.rs b/tests/ui/resolve/struct-function-same-name-2550.rs index 450db9be627e..c96f58374c6d 100644 --- a/tests/ui/resolve/struct-function-same-name-2550.rs +++ b/tests/ui/resolve/struct-function-same-name-2550.rs @@ -1,20 +1,18 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2550 + //@ run-pass #![allow(dead_code)] #![allow(non_snake_case)] - struct C { x: usize, } fn C(x: usize) -> C { - C { - x: x - } + C { x } } -fn f(_x: T) { -} +fn f(_x: T) {} pub fn main() { f(C(1)); diff --git a/tests/ui/structs/struct-update-syntax-2463.rs b/tests/ui/structs/struct-update-syntax-2463.rs index 8fff9763bd9e..5b2a90a5adf9 100644 --- a/tests/ui/structs/struct-update-syntax-2463.rs +++ b/tests/ui/structs/struct-update-syntax-2463.rs @@ -1,24 +1,17 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2463 + //@ run-pass #![allow(dead_code)] -struct Pair { f: isize, g: isize } +struct Pair { + f: isize, + g: isize, +} pub fn main() { + let x = Pair { f: 0, g: 0 }; - let x = Pair { - f: 0, - g: 0, - }; - - let _y = Pair { - f: 1, - g: 1, - .. x - }; - - let _z = Pair { - f: 1, - .. x - }; + let _y = Pair { f: 1, g: 1, ..x }; + let _z = Pair { f: 1, ..x }; } From 5c8d7dbe8ee508b35c033d25b115f164c7c72307 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 09:32:02 +0530 Subject: [PATCH 460/583] add cancel variant to SubResponse --- .../crates/proc-macro-api/src/bidirectional_protocol/msg.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index 1df0c68379a5..3f0422dc5bc8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -42,6 +42,9 @@ pub enum SubResponse { ByteRangeResult { range: Range, }, + Cancel { + reason: String, + }, } #[derive(Debug, Serialize, Deserialize)] From fb2cb46ea873f90c5a0a237772d4a42a2b7e4188 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 09:32:43 +0530 Subject: [PATCH 461/583] catch unwind on client side, and accordingly send Cancel subResponse to server --- .../proc-macro-api/src/bidirectional_protocol.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index 8311df23d718..ffdc6fdd4dec 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -2,6 +2,7 @@ use std::{ io::{self, BufRead, Write}, + panic::{AssertUnwindSafe, catch_unwind}, sync::Arc, }; @@ -55,9 +56,17 @@ pub fn run_conversation( return Ok(BidirectionalMessage::Response(response)); } BidirectionalMessage::SubRequest(sr) => { - let resp = callback(sr)?; - let reply = BidirectionalMessage::SubResponse(resp); - let encoded = postcard::encode(&reply).map_err(wrap_encode)?; + let resp = match catch_unwind(AssertUnwindSafe(|| callback(sr))) { + Ok(Ok(resp)) => BidirectionalMessage::SubResponse(resp), + Ok(Err(err)) => BidirectionalMessage::SubResponse(SubResponse::Cancel { + reason: err.to_string(), + }), + Err(_) => BidirectionalMessage::SubResponse(SubResponse::Cancel { + reason: "callback panicked or was cancelled".into(), + }), + }; + + let encoded = postcard::encode(&resp).map_err(wrap_encode)?; postcard::write(writer, &encoded) .map_err(wrap_io("failed to write sub-response"))?; } From d2ac252cf258f7d2d1962e31382e17363c8ff7ee Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 09:33:07 +0530 Subject: [PATCH 462/583] add proc-macro-client error variant --- src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index e04f744ae2b0..a6090253d3af 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -96,6 +96,14 @@ impl<'env> ProcMacroSrv<'env> { } } +#[derive(Debug)] +pub enum ProcMacroClientError { + Cancelled { reason: String }, + Io(std::io::Error), + Protocol(String), + Eof, +} + pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { From 26bc8cbc7797d8a988c5b096b14f121dac22035a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 09:43:19 +0530 Subject: [PATCH 463/583] make sure we panic in callback so the srv panics and stops --- .../proc-macro-srv-cli/src/main_loop.rs | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 758629fd1fd6..869c5f93927c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -10,7 +10,7 @@ use std::{ use legacy::Message; -use proc_macro_srv::{EnvSnapshot, SpanId}; +use proc_macro_srv::{EnvSnapshot, ProcMacroClientError, SpanId}; struct SpanTrans; @@ -172,16 +172,26 @@ impl<'a> ProcMacroClientHandle<'a> { fn roundtrip( &mut self, req: bidirectional::SubRequest, - ) -> Option { + ) -> Result { let msg = bidirectional::BidirectionalMessage::SubRequest(req); - if msg.write(&mut *self.stdout).is_err() { - return None; - } + msg.write(&mut *self.stdout).map_err(ProcMacroClientError::Io)?; - match bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) { - Ok(Some(msg)) => Some(msg), - _ => None, + let msg = + bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) + .map_err(ProcMacroClientError::Io)? + .ok_or(ProcMacroClientError::Eof)?; + + match msg { + bidirectional::BidirectionalMessage::SubResponse(resp) => match resp { + bidirectional::SubResponse::Cancel { reason } => { + Err(ProcMacroClientError::Cancelled { reason }) + } + other => Ok(other), + }, + other => { + Err(ProcMacroClientError::Protocol(format!("expected SubResponse, got {other:?}"))) + } } } } @@ -189,10 +199,16 @@ impl<'a> ProcMacroClientHandle<'a> { impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::FilePathResult { name }, - )) => name, - _ => String::new(), + Ok(bidirectional::SubResponse::FilePathResult { name }) => name, + Err(ProcMacroClientError::Cancelled { reason }) => { + panic!("proc-macro expansion cancelled by client: {reason}"); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse in file(): {other:?}"); + } } } @@ -206,20 +222,32 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { start: range.start().into(), end: range.end().into(), }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::SourceTextResult { text }, - )) => text, - _ => None, + Ok(bidirectional::SubResponse::SourceTextResult { text }) => text, + Err(ProcMacroClientError::Cancelled { reason }) => { + panic!("proc-macro expansion cancelled by client: {reason}"); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse in source_text: {other:?}"); + } } } fn local_file(&mut self, file_id: proc_macro_srv::span::FileId) -> Option { match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id: file_id.index() }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::LocalFilePathResult { name }, - )) => name, - _ => None, + Ok(bidirectional::SubResponse::LocalFilePathResult { name }) => name, + Err(ProcMacroClientError::Cancelled { reason }) => { + panic!("proc-macro expansion cancelled by client: {reason}"); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse in local_file(): {other:?}"); + } } } @@ -230,10 +258,18 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { ast_id: anchor.ast_id.into_raw(), offset: range.start().into(), }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::LineColumnResult { line, column }, - )) => Some((line, column)), - _ => None, + Ok(bidirectional::SubResponse::LineColumnResult { line, column }) => { + Some((line, column)) + } + Err(ProcMacroClientError::Cancelled { reason }) => { + panic!("proc-macro expansion cancelled by client: {reason}"); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse in local_file(): {other:?}"); + } } } From 489245903e30492055a479b7e36b8efa505207e6 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 21:12:39 +0530 Subject: [PATCH 464/583] don't kill server on cancellation --- .../crates/proc-macro-srv-cli/src/main_loop.rs | 11 ++++++----- .../rust-analyzer/crates/proc-macro-srv/src/lib.rs | 13 ++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 869c5f93927c..cae6d31f1a29 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -7,10 +7,11 @@ use std::{ io::{self, BufRead, Write}, ops::Range, }; +use std::panic::panic_any; use legacy::Message; -use proc_macro_srv::{EnvSnapshot, ProcMacroClientError, SpanId}; +use proc_macro_srv::{EnvSnapshot, ProcMacroCancelMarker, ProcMacroClientError, SpanId}; struct SpanTrans; @@ -201,7 +202,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Ok(bidirectional::SubResponse::FilePathResult { name }) => name, Err(ProcMacroClientError::Cancelled { reason }) => { - panic!("proc-macro expansion cancelled by client: {reason}"); + panic_any(ProcMacroCancelMarker { reason }); } Err(err) => { panic!("proc-macro IPC failed: {err:?}"); @@ -224,7 +225,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { }) { Ok(bidirectional::SubResponse::SourceTextResult { text }) => text, Err(ProcMacroClientError::Cancelled { reason }) => { - panic!("proc-macro expansion cancelled by client: {reason}"); + panic_any(ProcMacroCancelMarker { reason }); } Err(err) => { panic!("proc-macro IPC failed: {err:?}"); @@ -240,7 +241,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { { Ok(bidirectional::SubResponse::LocalFilePathResult { name }) => name, Err(ProcMacroClientError::Cancelled { reason }) => { - panic!("proc-macro expansion cancelled by client: {reason}"); + panic_any(ProcMacroCancelMarker { reason }); } Err(err) => { panic!("proc-macro IPC failed: {err:?}"); @@ -262,7 +263,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { Some((line, column)) } Err(ProcMacroClientError::Cancelled { reason }) => { - panic!("proc-macro expansion cancelled by client: {reason}"); + panic_any(ProcMacroCancelMarker { reason }); } Err(err) => { panic!("proc-macro IPC failed: {err:?}"); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index a6090253d3af..99dfa24ca412 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -104,6 +104,11 @@ pub enum ProcMacroClientError { Eof, } +#[derive(Debug)] +pub struct ProcMacroCancelMarker { + pub reason: String, +} + pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { @@ -153,7 +158,13 @@ impl ProcMacroSrv<'_> { }); match thread.unwrap().join() { Ok(res) => res, - Err(e) => std::panic::resume_unwind(e), + + Err(payload) => { + if let Some(cancel) = payload.downcast_ref::() { + return Err(PanicMessage { message: Some(cancel.reason.clone()) }); + } + std::panic::resume_unwind(payload) + } } }); prev_env.rollback(); From 8d2811a7c312135c832432af94953c16cdf97f4c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 10 Jan 2026 03:03:36 +0530 Subject: [PATCH 465/583] remove repititive error block in callbacks --- .../proc-macro-srv-cli/src/main_loop.rs | 56 +++++++------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index cae6d31f1a29..534697c68684 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -197,19 +197,25 @@ impl<'a> ProcMacroClientHandle<'a> { } } -impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { +fn handle_failure(failure: Result) -> ! { + match failure { + Err(ProcMacroClientError::Cancelled { reason }) => { + panic_any(ProcMacroCancelMarker { reason }); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse {other:?}"); + } + } +} + +impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Ok(bidirectional::SubResponse::FilePathResult { name }) => name, - Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); - } - Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); - } - Ok(other) => { - panic!("unexpected SubResponse in file(): {other:?}"); - } + other => handle_failure(other), } } @@ -224,15 +230,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { end: range.end().into(), }) { Ok(bidirectional::SubResponse::SourceTextResult { text }) => text, - Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); - } - Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); - } - Ok(other) => { - panic!("unexpected SubResponse in source_text: {other:?}"); - } + other => handle_failure(other), } } @@ -240,15 +238,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id: file_id.index() }) { Ok(bidirectional::SubResponse::LocalFilePathResult { name }) => name, - Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); - } - Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); - } - Ok(other) => { - panic!("unexpected SubResponse in local_file(): {other:?}"); - } + other => handle_failure(other), } } @@ -262,15 +252,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { Ok(bidirectional::SubResponse::LineColumnResult { line, column }) => { Some((line, column)) } - Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); - } - Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); - } - Ok(other) => { - panic!("unexpected SubResponse in local_file(): {other:?}"); - } + other => handle_failure(other), } } From 724606d3491faff9a4edb1b88ec7c3b1b4af923e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 10 Jan 2026 03:04:02 +0530 Subject: [PATCH 466/583] add error variant for cancelled expansion --- .../proc-macro-srv-cli/src/main_loop.rs | 9 +++---- .../crates/proc-macro-srv/src/lib.rs | 26 +++++++++++++++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 534697c68684..bb94d33ac102 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -3,11 +3,11 @@ use proc_macro_api::{ ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, }; +use std::panic::panic_any; use std::{ io::{self, BufRead, Write}, ops::Range, }; -use std::panic::panic_any; use legacy::Message; @@ -178,10 +178,9 @@ impl<'a> ProcMacroClientHandle<'a> { msg.write(&mut *self.stdout).map_err(ProcMacroClientError::Io)?; - let msg = - bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) - .map_err(ProcMacroClientError::Io)? - .ok_or(ProcMacroClientError::Eof)?; + let msg = bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) + .map_err(ProcMacroClientError::Io)? + .ok_or(ProcMacroClientError::Eof)?; match msg { bidirectional::BidirectionalMessage::SubResponse(resp) => match resp { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 99dfa24ca412..0462aafd00e7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -123,6 +123,20 @@ pub trait ProcMacroClientInterface { const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; +pub enum ExpandError { + Panic(PanicMessage), + Cancelled { reason: Option }, +} + +impl ExpandError { + pub fn into_string(self) -> Option { + match self { + ExpandError::Panic(panic_message) => panic_message.into_string(), + ExpandError::Cancelled { reason } => reason, + } + } +} + impl ProcMacroSrv<'_> { pub fn expand( &self, @@ -136,10 +150,12 @@ impl ProcMacroSrv<'_> { call_site: S, mixed_site: S, callback: Option>, - ) -> Result, PanicMessage> { + ) -> Result, ExpandError> { let snapped_env = self.env; - let expander = self.expander(lib.as_ref()).map_err(|err| PanicMessage { - message: Some(format!("failed to load macro: {err}")), + let expander = self.expander(lib.as_ref()).map_err(|err| { + ExpandError::Panic(PanicMessage { + message: Some(format!("failed to load macro: {err}")), + }) })?; let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref)); @@ -157,11 +173,11 @@ impl ProcMacroSrv<'_> { ) }); match thread.unwrap().join() { - Ok(res) => res, + Ok(res) => res.map_err(ExpandError::Panic), Err(payload) => { if let Some(cancel) = payload.downcast_ref::() { - return Err(PanicMessage { message: Some(cancel.reason.clone()) }); + return Err(ExpandError::Cancelled { reason: Some(cancel.reason.clone()) }); } std::panic::resume_unwind(payload) } From a37aa50bca6a7f424907e9f3f638a15935963ef8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 16:49:22 +0530 Subject: [PATCH 467/583] adapt ByteRange to new roundtrip --- .../crates/proc-macro-srv-cli/src/main_loop.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index bb94d33ac102..af8c3cb39ba2 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -265,10 +265,8 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl start: range.start().into(), end: range.end().into(), }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::ByteRangeResult { range }, - )) => range, - _ => Range { start: range.start().into(), end: range.end().into() }, + Ok(bidirectional::SubResponse::ByteRangeResult { range }) => range, + other => handle_failure(other), } } } From 50e2330dce8a9d7a9adfeee2c219fc00487b4dbd Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 18:27:30 +0530 Subject: [PATCH 468/583] add suggested changes: have a internal error variant, comment on unwindsafe --- .../src/bidirectional_protocol.rs | 2 ++ .../proc-macro-srv-cli/src/main_loop.rs | 12 ++++++--- .../crates/proc-macro-srv/src/lib.rs | 25 +++++++++++++------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index ffdc6fdd4dec..ba59cb219b9a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -56,6 +56,8 @@ pub fn run_conversation( return Ok(BidirectionalMessage::Response(response)); } BidirectionalMessage::SubRequest(sr) => { + // TODO: Avoid `AssertUnwindSafe` by making the callback `UnwindSafe` once `ExpandDatabase` + // becomes unwind-safe (currently blocked by `parking_lot::RwLock` in the VFS). let resp = match catch_unwind(AssertUnwindSafe(|| callback(sr))) { Ok(Ok(resp)) => BidirectionalMessage::SubResponse(resp), Ok(Err(err)) => BidirectionalMessage::SubResponse(SubResponse::Cancel { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index af8c3cb39ba2..c19847b4b53d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -11,7 +11,7 @@ use std::{ use legacy::Message; -use proc_macro_srv::{EnvSnapshot, ProcMacroCancelMarker, ProcMacroClientError, SpanId}; +use proc_macro_srv::{EnvSnapshot, ProcMacroClientError, ProcMacroPanicMarker, SpanId}; struct SpanTrans; @@ -199,13 +199,17 @@ impl<'a> ProcMacroClientHandle<'a> { fn handle_failure(failure: Result) -> ! { match failure { Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); + panic_any(ProcMacroPanicMarker::Cancelled { reason }); } Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); + panic_any(ProcMacroPanicMarker::Internal { + reason: format!("proc-macro IPC error: {err:?}"), + }); } Ok(other) => { - panic!("unexpected SubResponse {other:?}"); + panic_any(ProcMacroPanicMarker::Internal { + reason: format!("unexpected SubResponse {other:?}"), + }); } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 0462aafd00e7..c548dc620ad1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -105,8 +105,9 @@ pub enum ProcMacroClientError { } #[derive(Debug)] -pub struct ProcMacroCancelMarker { - pub reason: String, +pub enum ProcMacroPanicMarker { + Cancelled { reason: String }, + Internal { reason: String }, } pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); @@ -126,6 +127,7 @@ const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; pub enum ExpandError { Panic(PanicMessage), Cancelled { reason: Option }, + Internal { reason: Option }, } impl ExpandError { @@ -133,6 +135,7 @@ impl ExpandError { match self { ExpandError::Panic(panic_message) => panic_message.into_string(), ExpandError::Cancelled { reason } => reason, + ExpandError::Internal { reason } => reason, } } } @@ -152,10 +155,8 @@ impl ProcMacroSrv<'_> { callback: Option>, ) -> Result, ExpandError> { let snapped_env = self.env; - let expander = self.expander(lib.as_ref()).map_err(|err| { - ExpandError::Panic(PanicMessage { - message: Some(format!("failed to load macro: {err}")), - }) + let expander = self.expander(lib.as_ref()).map_err(|err| ExpandError::Internal { + reason: Some(format!("failed to load macro: {err}")), })?; let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref)); @@ -176,9 +177,17 @@ impl ProcMacroSrv<'_> { Ok(res) => res.map_err(ExpandError::Panic), Err(payload) => { - if let Some(cancel) = payload.downcast_ref::() { - return Err(ExpandError::Cancelled { reason: Some(cancel.reason.clone()) }); + if let Some(marker) = payload.downcast_ref::() { + return match marker { + ProcMacroPanicMarker::Cancelled { reason } => { + Err(ExpandError::Cancelled { reason: Some(reason.clone()) }) + } + ProcMacroPanicMarker::Internal { reason } => { + Err(ExpandError::Internal { reason: Some(reason.clone()) }) + } + }; } + std::panic::resume_unwind(payload) } } From 98380734814667ba50ad748d8130a77b06ebaa3f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 18 Jan 2026 21:10:32 +0530 Subject: [PATCH 469/583] replace panic_any with resume_unwind on Client panic cancelled message --- .../rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index c19847b4b53d..1e956f91e6b9 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -3,7 +3,7 @@ use proc_macro_api::{ ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, }; -use std::panic::panic_any; +use std::panic::{panic_any, resume_unwind}; use std::{ io::{self, BufRead, Write}, ops::Range, @@ -199,7 +199,7 @@ impl<'a> ProcMacroClientHandle<'a> { fn handle_failure(failure: Result) -> ! { match failure { Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroPanicMarker::Cancelled { reason }); + resume_unwind(Box::new(ProcMacroPanicMarker::Cancelled { reason })); } Err(err) => { panic_any(ProcMacroPanicMarker::Internal { From fcb55722d4ef6623f36a9787c7724bd364adb413 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 1 Feb 2026 15:19:03 +0530 Subject: [PATCH 470/583] correct handler generic input --- .../rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 1e956f91e6b9..9be3199a3836 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -214,7 +214,7 @@ fn handle_failure(failure: Result proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { +impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Ok(bidirectional::SubResponse::FilePathResult { name }) => name, From 8927aa57385144c22a2cf9060a2f7a74071d2e0e Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Jan 2026 17:46:36 +0100 Subject: [PATCH 471/583] Add `inline` syntax for diagnostic messages --- compiler/rustc_error_messages/src/lib.rs | 11 ++- compiler/rustc_errors/src/translation.rs | 26 ++++++- .../src/diagnostics/diagnostic.rs | 15 ++-- .../src/diagnostics/diagnostic_builder.rs | 76 ++++++++++++------- .../rustc_macros/src/diagnostics/message.rs | 28 +++++++ compiler/rustc_macros/src/diagnostics/mod.rs | 1 + .../src/diagnostics/subdiagnostic.rs | 10 ++- .../rustc_macros/src/diagnostics/utils.rs | 26 ++++++- 8 files changed, 151 insertions(+), 42 deletions(-) create mode 100644 compiler/rustc_macros/src/diagnostics/message.rs diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index c0737edd7d65..c9e887061305 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -247,6 +247,9 @@ pub enum SubdiagMessage { /// Identifier of a Fluent message. Instances of this variant are generated by the /// `Subdiagnostic` derive. FluentIdentifier(FluentId), + /// An inline Fluent message. Instances of this variant are generated by the + /// `Subdiagnostic` derive. + Inline(Cow<'static, str>), /// Attribute of a Fluent message. Needs to be combined with a Fluent identifier to produce an /// actual translated message. Instances of this variant are generated by the `fluent_messages` /// macro. @@ -291,6 +294,8 @@ pub enum DiagMessage { /// /// FluentIdentifier(FluentId, Option), + /// An inline Fluent message, containing the to be translated diagnostic message. + Inline(Cow<'static, str>), } impl DiagMessage { @@ -305,21 +310,22 @@ impl DiagMessage { SubdiagMessage::FluentIdentifier(id) => { return DiagMessage::FluentIdentifier(id, None); } + SubdiagMessage::Inline(s) => return DiagMessage::Inline(s), SubdiagMessage::FluentAttr(attr) => attr, }; match self { - DiagMessage::Str(s) => DiagMessage::Str(s.clone()), DiagMessage::FluentIdentifier(id, _) => { DiagMessage::FluentIdentifier(id.clone(), Some(attr)) } + _ => panic!("Tried to add a subdiagnostic to a message without a fluent identifier"), } } pub fn as_str(&self) -> Option<&str> { match self { DiagMessage::Str(s) => Some(s), - DiagMessage::FluentIdentifier(_, _) => None, + DiagMessage::FluentIdentifier(_, _) | DiagMessage::Inline(_) => None, } } } @@ -353,6 +359,7 @@ impl From for SubdiagMessage { // There isn't really a sensible behaviour for this because it loses information but // this is the most sensible of the behaviours. DiagMessage::FluentIdentifier(_, Some(attr)) => SubdiagMessage::FluentAttr(attr), + DiagMessage::Inline(s) => SubdiagMessage::Inline(s), } } } diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 43d5fca301ec..0ee2b7b06090 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -3,11 +3,13 @@ use std::env; use std::error::Report; use std::sync::Arc; +use rustc_error_messages::langid; pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle}; use tracing::{debug, trace}; use crate::error::{TranslateError, TranslateErrorKind}; -use crate::{DiagArg, DiagMessage, FluentBundle, Style}; +use crate::fluent_bundle::FluentResource; +use crate::{DiagArg, DiagMessage, FluentBundle, Style, fluent_bundle}; /// Convert diagnostic arguments (a rustc internal type that exists to implement /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. @@ -79,6 +81,28 @@ impl Translator { return Ok(Cow::Borrowed(msg)); } DiagMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + // This translates an inline fluent diagnostic message + // It does this by creating a new `FluentBundle` with only one message, + // and then translating using this bundle. + DiagMessage::Inline(msg) => { + const GENERATED_MSG_ID: &str = "generated_msg"; + let resource = + FluentResource::try_new(format!("{GENERATED_MSG_ID} = {msg}\n")).unwrap(); + let mut bundle = fluent_bundle::FluentBundle::new(vec![langid!("en-US")]); + bundle.set_use_isolating(false); + bundle.add_resource(resource).unwrap(); + let message = bundle.get_message(GENERATED_MSG_ID).unwrap(); + let value = message.value().unwrap(); + + let mut errs = vec![]; + let translated = bundle.format_pattern(value, Some(args), &mut errs).to_string(); + debug!(?translated, ?errs); + return if errs.is_empty() { + Ok(Cow::Owned(translated)) + } else { + Err(TranslateError::fluent(&Cow::Borrowed(GENERATED_MSG_ID), args, errs)) + }; + } }; let translate_with_bundle = |bundle: &'a FluentBundle| -> Result, TranslateError<'_>> { diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 7e784e3464e9..1ae6393b2860 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -27,15 +27,17 @@ impl<'a> DiagnosticDerive<'a> { let preamble = builder.preamble(variant); let body = builder.body(variant); - let Some(slug) = builder.primary_message() else { + let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().push(slug.clone()); + slugs.borrow_mut().extend(message.slug().cloned()); + let message = message.diag_message(); + let init = quote! { let mut diag = rustc_errors::Diag::new( dcx, level, - crate::fluent_generated::#slug + #message ); }; @@ -91,12 +93,13 @@ impl<'a> LintDiagnosticDerive<'a> { let preamble = builder.preamble(variant); let body = builder.body(variant); - let Some(slug) = builder.primary_message() else { + let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().push(slug.clone()); + slugs.borrow_mut().extend(message.slug().cloned()); + let message = message.diag_message(); let primary_message = quote! { - diag.primary_message(crate::fluent_generated::#slug); + diag.primary_message(#message); }; let formatting_init = &builder.formatting_init; diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 25110fd4f908..14eda017970f 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -4,13 +4,14 @@ 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 syn::{Attribute, LitStr, Meta, Path, Token, Type, parse_quote}; use synstructure::{BindingInfo, Structure, VariantInfo}; use super::utils::SubdiagnosticVariant; use crate::diagnostics::error::{ DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; use crate::diagnostics::utils::{ FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind, build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error, @@ -41,9 +42,9 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { /// derive builder. pub field_map: FieldMap, - /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that + /// Message is a mandatory part of the struct attribute as corresponds to the Fluent message that /// has the actual diagnostic message. - pub slug: Option, + pub message: Option, /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. @@ -90,7 +91,7 @@ impl DiagnosticDeriveKind { span, field_map: build_field_mapping(variant), formatting_init: TokenStream::new(), - slug: None, + message: None, code: None, }; f(builder, variant) @@ -105,8 +106,8 @@ impl DiagnosticDeriveKind { } impl DiagnosticDeriveVariantBuilder { - pub(crate) fn primary_message(&self) -> Option<&Path> { - match self.slug.as_ref() { + pub(crate) fn primary_message(&self) -> Option<&Message> { + match self.message.as_ref() { None => { span_err(self.span, "diagnostic slug not specified") .help( @@ -116,7 +117,7 @@ impl DiagnosticDeriveVariantBuilder { .emit(); None } - Some(slug) + Some(Message::Slug(slug)) if let Some(Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { @@ -126,7 +127,7 @@ impl DiagnosticDeriveVariantBuilder { .emit(); None } - Some(slug) => Some(slug), + Some(msg) => Some(msg), } } @@ -163,7 +164,7 @@ impl DiagnosticDeriveVariantBuilder { fn parse_subdiag_attribute( &self, attr: &Attribute, - ) -> Result, DiagnosticDeriveError> { + ) -> Result, DiagnosticDeriveError> { let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, &self.field_map)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. @@ -175,15 +176,18 @@ impl DiagnosticDeriveVariantBuilder { .help("consider creating a `Subdiagnostic` instead")); } - let slug = subdiag.slug.unwrap_or_else(|| match subdiag.kind { - SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, - SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, - SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, - SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, - SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, - SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, - SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, - SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), + // For subdiagnostics without a message specified, insert a placeholder slug + let slug = subdiag.slug.unwrap_or_else(|| { + Message::Slug(match subdiag.kind { + SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, + SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, + SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, + SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, + SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, + SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, + SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, + SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), + }) }); Ok(Some((subdiag.kind, slug, false))) @@ -210,13 +214,28 @@ impl DiagnosticDeriveVariantBuilder { let mut input = &*input; let slug_recovery_point = input.fork(); - let slug = input.parse::()?; - if input.is_empty() || input.peek(Token![,]) { - self.slug = Some(slug); + if input.peek(LitStr) { + // Parse an inline message + let message = input.parse::()?; + if !message.suffix().is_empty() { + span_err( + message.span().unwrap(), + "Inline message is not allowed to have a suffix", + ) + .emit(); + } + self.message = Some(Message::Inline(message.value())); } else { - input = &slug_recovery_point; + // Parse a slug + let slug = input.parse::()?; + if input.is_empty() || input.peek(Token![,]) { + self.message = Some(Message::Slug(slug)); + } else { + input = &slug_recovery_point; + } } + // Parse arguments while !input.is_empty() { input.parse::()?; // Allow trailing comma @@ -429,6 +448,7 @@ impl DiagnosticDeriveVariantBuilder { applicability.set_once(quote! { #static_applicability }, span); } + let message = slug.diag_message(); let applicability = applicability .value() .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); @@ -438,7 +458,7 @@ impl DiagnosticDeriveVariantBuilder { Ok(quote! { diag.span_suggestions_with_style( #span_field, - crate::fluent_generated::#slug, + #message, #code_field, #applicability, #style @@ -455,22 +475,24 @@ impl DiagnosticDeriveVariantBuilder { &self, field_binding: TokenStream, kind: &Ident, - fluent_attr_identifier: Path, + message: Message, ) -> TokenStream { let fn_name = format_ident!("span_{}", kind); + let message = message.diag_message(); quote! { diag.#fn_name( #field_binding, - crate::fluent_generated::#fluent_attr_identifier + #message ); } } /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug /// and `fluent_attr_identifier`. - fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream { + fn add_subdiagnostic(&self, kind: &Ident, message: Message) -> TokenStream { + let message = message.diag_message(); quote! { - diag.#kind(crate::fluent_generated::#fluent_attr_identifier); + diag.#kind(#message); } } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs new file mode 100644 index 000000000000..cfce252fbdd8 --- /dev/null +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -0,0 +1,28 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::Path; + +pub(crate) enum Message { + Slug(Path), + Inline(String), +} + +impl Message { + pub(crate) fn slug(&self) -> Option<&Path> { + match self { + Message::Slug(slug) => Some(slug), + Message::Inline(_) => None, + } + } + + pub(crate) fn diag_message(&self) -> TokenStream { + match self { + Message::Slug(slug) => { + quote! { crate::fluent_generated::#slug } + } + Message::Inline(message) => { + quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } + } + } + } +} diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 55228248188e..09f05ce972f1 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -1,6 +1,7 @@ mod diagnostic; mod diagnostic_builder; mod error; +mod message; mod subdiagnostic; mod utils; diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index db2a19ab85ba..61a234f96d12 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -11,6 +11,7 @@ use super::utils::SubdiagnosticVariant; use crate::diagnostics::error::{ DiagnosticDeriveError, invalid_attr, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; use crate::diagnostics::utils::{ AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident, @@ -182,7 +183,9 @@ 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 { @@ -532,9 +535,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut calls = TokenStream::new(); for (kind, slug) in kind_slugs { let message = format_ident!("__message"); - calls.extend( - quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); }, - ); + let message_stream = slug.diag_message(); + calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); }); let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let call = match kind { diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index f084ba60ae3f..0718448a0513 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -16,6 +16,7 @@ use super::error::invalid_attr; use crate::diagnostics::error::{ DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; thread_local! { pub(crate) static CODE_IDENT_COUNT: RefCell = RefCell::new(0); @@ -587,7 +588,7 @@ pub(super) enum SubdiagnosticKind { pub(super) struct SubdiagnosticVariant { pub(super) kind: SubdiagnosticKind, - pub(super) slug: Option, + pub(super) slug: Option, } impl SubdiagnosticVariant { @@ -696,11 +697,31 @@ impl SubdiagnosticVariant { list.parse_args_with(|input: ParseStream<'_>| { let mut is_first = true; while !input.is_empty() { + // Try to parse an inline diagnostic message + if input.peek(LitStr) { + let message = input.parse::()?; + if !message.suffix().is_empty() { + span_err( + message.span().unwrap(), + "Inline message is not allowed to have a suffix", + ).emit(); + } + if !input.is_empty() { input.parse::()?; } + if is_first { + slug = Some(Message::Inline(message.value())); + is_first = false; + } else { + span_err(message.span().unwrap(), "a diagnostic message must be the first argument to the attribute").emit(); + } + continue + } + + // Try to parse a slug instead 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); + slug = Some(Message::Slug(arg_name)); is_first = false; } else { span_err(arg_name_span, "a diagnostic slug must be the first argument to the attribute").emit(); @@ -709,6 +730,7 @@ impl SubdiagnosticVariant { } is_first = false; + // Try to parse an argument match (arg_name.require_ident()?.to_string().as_str(), &mut kind) { ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let code_init = build_suggestion_code( From 378c6fc6fe1ac3315364b0864068c7957b432b1a Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Jan 2026 19:26:33 +0100 Subject: [PATCH 472/583] Uitests for `#[diagnostic]` --- .../diagnostic-derive-inline.rs | 764 ++++++++++++++++++ .../diagnostic-derive-inline.stderr | 627 ++++++++++++++ 2 files changed, 1391 insertions(+) create mode 100644 tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs create mode 100644 tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs new file mode 100644 index 000000000000..7d9af0522f63 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs @@ -0,0 +1,764 @@ +//@ check-fail +// Tests error conditions for specifying diagnostics using #[derive(Diagnostic)] +// This test specifically tests diagnostic derives involving the inline fluent syntax. + +//@ normalize-stderr: "the following other types implement trait `IntoDiagArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr" +//@ normalize-stderr: "(COMPILER_DIR/.*\.rs):[0-9]+:[0-9]+" -> "$1:LL:CC" + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Diagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +//@ ignore-stage1 +//@ ignore-beta +//@ ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_span; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +extern crate rustc_fluent_macro; +extern crate rustc_macros; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; + +extern crate rustc_middle; +use rustc_middle::ty::Ty; + +extern crate rustc_errors; +use rustc_errors::{Applicability, DiagMessage, ErrCode, MultiSpan, SubdiagMessage}; + +extern crate rustc_session; + +extern crate core; + +// E0123 and E0456 are no longer used, so we define our own constants here just for this test. +const E0123: ErrCode = ErrCode::from_u32(0123); +const E0456: ErrCode = ErrCode::from_u32(0456); + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct Hello {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +//~^ ERROR unsupported type attribute for diagnostic derive enum +enum DiagnosticOnEnum { + Foo, + //~^ ERROR diagnostic slug not specified + Bar, + //~^ ERROR diagnostic slug not specified +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[diag = "E0123"] +//~^ ERROR expected parentheses: #[diag(...)] +struct WrongStructAttrStyle {} + +#[derive(Diagnostic)] +#[nonsense("this is an example message", code = E0123)] +//~^ ERROR `#[nonsense(...)]` is not a valid attribute +//~^^ ERROR diagnostic slug not specified +//~^^^ ERROR cannot find attribute `nonsense` in this scope +struct InvalidStructAttr {} + +#[derive(Diagnostic)] +#[diag(code = E0123)] +//~^ ERROR diagnostic slug not specified +struct InvalidLitNestedAttr {} + +#[derive(Diagnostic)] +#[diag(nonsense("foo"), code = E0123, slug = "foo")] +//~^ ERROR derive(Diagnostic): diagnostic slug not specified +struct InvalidNestedStructAttr1 {} + +#[derive(Diagnostic)] +#[diag(nonsense = "...", code = E0123, slug = "foo")] +//~^ ERROR diagnostic slug not specified +struct InvalidNestedStructAttr2 {} + +#[derive(Diagnostic)] +#[diag(nonsense = 4, code = E0123, slug = "foo")] +//~^ ERROR diagnostic slug not specified +struct InvalidNestedStructAttr3 {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123, slug = "foo")] +//~^ ERROR unknown argument +struct InvalidNestedStructAttr4 {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WrongPlaceField { + #[suggestion = "bar"] + //~^ ERROR `#[suggestion = ...]` is not a valid attribute + sp: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[diag("this is an example message", code = E0456)] +//~^ ERROR specified multiple times +struct DiagSpecifiedTwice {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123, code = E0456)] +//~^ ERROR specified multiple times +struct CodeSpecifiedTwice {} + +#[derive(Diagnostic)] +#[diag("this is an example message", no_crate::example, code = E0123)] +//~^ ERROR diagnostic slug must be the first argument +struct SlugSpecifiedTwice {} + +#[derive(Diagnostic)] +struct KindNotProvided {} //~ ERROR diagnostic slug not specified + +#[derive(Diagnostic)] +#[diag(code = E0123)] +//~^ ERROR diagnostic slug not specified +struct SlugNotProvided {} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct CodeNotProvided {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MessageWrongType { + #[primary_span] + //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + foo: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidPathFieldAttr { + #[nonsense] + //~^ ERROR `#[nonsense]` is not a valid attribute + //~^^ ERROR cannot find attribute `nonsense` in this scope + foo: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithField { + name: String, + #[label("with a label")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithMessageAppliedToField { + #[label("with a label")] + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + name: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithNonexistentField { + #[suggestion("with a suggestion", code = "{name}")] + //~^ ERROR `name` doesn't refer to a field on this type + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +//~^ ERROR invalid format string: expected `}` +#[diag("this is an example message", code = E0123)] +struct ErrorMissingClosingBrace { + #[suggestion("with a suggestion", code = "{name")] + suggestion: (Span, Applicability), + name: String, + val: usize, +} + +#[derive(Diagnostic)] +//~^ ERROR invalid format string: unmatched `}` +#[diag("this is an example message", code = E0123)] +struct ErrorMissingOpeningBrace { + #[suggestion("with a suggestion", code = "name}")] + suggestion: (Span, Applicability), + name: String, + val: usize, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelOnSpan { + #[label("with a label")] + sp: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelOnNonSpan { + #[label("with a label")] + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + id: u32, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct Suggest { + #[suggestion("with a suggestion", code = "This is the suggested code")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "normal")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "short")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "hidden")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "verbose")] + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithoutCode { + #[suggestion("with a suggestion")] + //~^ ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithBadKey { + #[suggestion("with a suggestion", nonsense = "bar")] + //~^ ERROR invalid nested attribute + //~| ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithShorthandMsg { + #[suggestion("with a suggestion", msg = "bar")] + //~^ ERROR invalid nested attribute + //~| ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithoutMsg { + #[suggestion("with a suggestion", code = "bar")] + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithTypesSwapped { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Applicability, Span), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithWrongTypeApplicabilityOnly { + #[suggestion("with a suggestion", code = "This is suggested code")] + //~^ ERROR wrong field type for suggestion + suggestion: Applicability, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithSpanOnly { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithDuplicateSpanAndApplicability { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Span, Span, Applicability), + //~^ ERROR specified multiple times +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithDuplicateApplicabilityAndSpan { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Applicability, Applicability, Span), + //~^ ERROR specified multiple times +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WrongKindOfAnnotation { + #[label = "bar"] + //~^ ERROR `#[label = ...]` is not a valid attribute + z: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct OptionsInErrors { + #[label("with a label")] + label: Option, + #[suggestion("with a suggestion", code = "...")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MoveOutOfBorrowError<'tcx> { + name: Ident, + ty: Ty<'tcx>, + #[primary_span] + #[label("with a label")] + span: Span, + #[label("with a label")] + other_span: Span, + #[suggestion("with a suggestion", code = "{name}.clone()")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithLifetime<'a> { + #[label("with a label")] + span: Span, + name: &'a str, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ArgFieldWithoutSkip { + #[primary_span] + span: Span, + other: Hello, + //~^ ERROR the trait bound `Hello: IntoDiagArg` is not satisfied +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ArgFieldWithSkip { + #[primary_span] + span: Span, + // `Hello` does not implement `IntoDiagArg` so this would result in an error if + // not for `#[skip_arg]`. + #[skip_arg] + other: Hello, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithSpannedNote { + #[note("with a note")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[note("with a note")] +struct ErrorWithNote { + val: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithSpannedHelpCustom { + #[help("with a help")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[help("with a help")] +struct ErrorWithHelp { + val: String, +} + +#[derive(Diagnostic)] +#[help("with a help")] +#[diag("this is an example message", code = E0123)] +struct ErrorWithHelpWrongOrder { + val: String, +} + +#[derive(Diagnostic)] +#[note("with a note")] +#[diag("this is an example message", code = E0123)] +struct ErrorWithNoteWrongOrder { + val: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ApplicabilityInBoth { + #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + //~^ ERROR specified multiple times + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidApplicability { + #[suggestion("with a suggestion", code = "...", applicability = "batman")] + //~^ ERROR invalid applicability + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ValidApplicability { + #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct NoApplicability { + #[suggestion("with a suggestion", code = "...")] + suggestion: Span, +} + +#[derive(Subdiagnostic)] +#[note("this is an example message")] +struct Note; + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct Subdiagnostic { + #[subdiagnostic] + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct VecField { + #[primary_span] + #[label("with a label")] + spans: Vec, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct UnitField { + #[primary_span] + spans: Span, + #[help("with a help")] + bar: (), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct OptUnitField { + #[primary_span] + spans: Span, + #[help("with a help")] + foo: Option<()>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct BoolField { + #[primary_span] + spans: Span, + #[help("with a help")] + foo: bool, + #[help("with a help")] + //~^ ERROR the `#[help(...)]` attribute can only be applied to fields of type + // only allow plain 'bool' fields + bar: Option, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingPath { + #[label("with a label", foo)] + //~^ ERROR a diagnostic slug must be the first argument to the attribute + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingNameValue { + #[label("with a label", foo = "...")] + //~^ ERROR no nested attribute expected here + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingList { + #[label("with a label", foo("..."))] + //~^ ERROR no nested attribute expected here + span: Span, +} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct LintsGood {} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct PrimarySpanOnLint { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithMultiSpan { + #[primary_span] + span: MultiSpan, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[warning("with a warning")] +struct ErrorWithWarn { + val: String, +} + +#[derive(Diagnostic)] +#[error("this is an example message", code = E0123)] +//~^ ERROR `#[error(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `error` in this scope +struct ErrorAttribute {} + +#[derive(Diagnostic)] +#[warn_("this is an example message", code = E0123)] +//~^ ERROR `#[warn_(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `warn_` in this scope +struct WarnAttribute {} + +#[derive(Diagnostic)] +#[lint("this is an example message", code = E0123)] +//~^ ERROR `#[lint(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `lint` in this scope +struct LintAttributeOnSessionDiag {} + +#[derive(LintDiagnostic)] +#[lint("this is an example message", code = E0123)] +//~^ ERROR `#[lint(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `lint` in this scope +struct LintAttributeOnLintDiag {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct DuplicatedSuggestionCode { + #[suggestion("with a suggestion", code = "...", code = ",,,")] + //~^ ERROR specified multiple times + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidTypeInSuggestionTuple { + #[suggestion("with a suggestion", code = "...")] + suggestion: (Span, usize), + //~^ ERROR wrong types for suggestion +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MissingApplicabilityInSuggestionTuple { + #[suggestion("with a suggestion", code = "...")] + suggestion: (Span,), + //~^ ERROR wrong types for suggestion +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MissingCodeInSuggestion { + #[suggestion("with a suggestion")] + //~^ ERROR suggestion without `code = "..."` + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[multipart_suggestion("with a suggestion")] +//~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute +//~| ERROR cannot find attribute `multipart_suggestion` in this scope +#[multipart_suggestion()] +//~^ ERROR cannot find attribute `multipart_suggestion` in this scope +//~| ERROR `#[multipart_suggestion(...)]` is not a valid attribute +struct MultipartSuggestion { + #[multipart_suggestion("with a suggestion")] + //~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute + //~| ERROR cannot find attribute `multipart_suggestion` in this scope + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[suggestion("with a suggestion", code = "...")] +//~^ ERROR `#[suggestion(...)]` is not a valid attribute +struct SuggestionOnStruct { + #[primary_span] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[label] +//~^ ERROR `#[label]` is not a valid attribute +struct LabelOnStruct { + #[primary_span] + suggestion: Span, +} + +#[derive(Diagnostic)] +enum ExampleEnum { + #[diag("this is an example message")] + Foo { + #[primary_span] + sp: Span, + #[note("with a note")] + note_sp: Span, + }, + #[diag("this is an example message")] + Bar { + #[primary_span] + sp: Span, + }, + #[diag("this is an example message")] + Baz, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct RawIdentDiagnosticArg { + pub r#type: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBad { + #[subdiagnostic(bad)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadStr { + #[subdiagnostic = "bad"] + //~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadTwice { + #[subdiagnostic(bad, bad)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadLitStr { + #[subdiagnostic("bad")] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerLint { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerFormerlyCorrect { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +// Check that formatting of `correct` in suggestion doesn't move the binding for that field, making +// the `arg` call a compile error; and that isn't worked around by moving the `arg` call +// after the `span_suggestion` call - which breaks eager translation. + +#[derive(Subdiagnostic)] +#[suggestion("example message", applicability = "machine-applicable", code = "{correct}")] +pub(crate) struct SubdiagnosticWithSuggestion { + #[primary_span] + span: Span, + invalid: String, + correct: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerSuggestion { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + sub: SubdiagnosticWithSuggestion, +} + +/// with a doc comment on the type.. +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WithDocComment { + /// ..and the field + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsGood { + #[suggestion("with a suggestion", code("foo", "bar"))] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsSingleItem { + #[suggestion("with a suggestion", code("foo"))] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsNoItem { + #[suggestion("with a suggestion", code())] + //~^ ERROR expected at least one string literal for `code(...)` + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsInvalidItem { + #[suggestion("with a suggestion", code(foo))] + //~^ ERROR `code(...)` must contain only string literals + //~| ERROR unexpected token, expected `)` + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsInvalidLiteral { + #[suggestion("with a suggestion", code = 3)] + //~^ ERROR expected string literal + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionStyleGood { + #[suggestion("with a suggestion", code = "", style = "hidden")] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionOnVec { + #[suggestion("with a suggestion", code = "")] + //~^ ERROR `#[suggestion(...)]` is not a valid attribute + sub: Vec, +} diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr new file mode 100644 index 000000000000..bec07a425762 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr @@ -0,0 +1,627 @@ +error: derive(Diagnostic): unsupported type attribute for diagnostic derive enum + --> $DIR/diagnostic-derive-inline.rs:45:1 + | +LL | #[diag("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:48:5 + | +LL | Foo, + | ^^^ + | + = 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-inline.rs:50:5 + | +LL | Bar, + | ^^^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: expected parentheses: #[diag(...)] + --> $DIR/diagnostic-derive-inline.rs:56:8 + | +LL | #[diag = "E0123"] + | ^ + +error: derive(Diagnostic): `#[nonsense(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:61:1 + | +LL | #[nonsense("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:61:1 + | +LL | #[nonsense("this is an example message", 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 not specified + --> $DIR/diagnostic-derive-inline.rs:68:1 + | +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 not specified + --> $DIR/diagnostic-derive-inline.rs:73:1 + | +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): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:78: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): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:83:1 + | +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-inline.rs:88:52 + | +LL | #[diag("this is an example message", 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-inline.rs:95:5 + | +LL | #[suggestion = "bar"] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:102:38 + | +LL | #[diag("this is an example message", code = E0456)] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:101:38 + | +LL | #[diag("this is an example message", code = E0123)] + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:107:52 + | +LL | #[diag("this is an example message", code = E0123, code = E0456)] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:107:38 + | +LL | #[diag("this is an example message", code = E0123, code = E0456)] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be the first argument + --> $DIR/diagnostic-derive-inline.rs:112:38 + | +LL | #[diag("this is an example message", no_crate::example, code = E0123)] + | ^^^^^^^^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:117:1 + | +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-inline.rs:120:1 + | +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-inline.rs:131:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): `#[nonsense]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:139:5 + | +LL | #[nonsense] + | ^ + +error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/diagnostic-derive-inline.rs:156:5 + | +LL | #[label("with a label")] + | ^ + +error: derive(Diagnostic): `name` doesn't refer to a field on this type + --> $DIR/diagnostic-derive-inline.rs:164:46 + | +LL | #[suggestion("with a suggestion", code = "{name}")] + | ^^^^^^^^ + +error: invalid format string: expected `}` but string was terminated + --> $DIR/diagnostic-derive-inline.rs:169:10 + | +LL | #[derive(Diagnostic)] + | ^^^^^^^^^^ expected `}` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + = 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-inline.rs:179:10 + | +LL | #[derive(Diagnostic)] + | ^^^^^^^^^^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + = 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-inline.rs:199:5 + | +LL | #[label("with a label")] + | ^ + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:218:5 + | +LL | #[suggestion("with a suggestion")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/diagnostic-derive-inline.rs:226:39 + | +LL | #[suggestion("with a suggestion", nonsense = "bar")] + | ^^^^^^^^ + | + = help: only `style`, `code` and `applicability` are valid nested attributes + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:226:5 + | +LL | #[suggestion("with a suggestion", nonsense = "bar")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/diagnostic-derive-inline.rs:235:39 + | +LL | #[suggestion("with a suggestion", msg = "bar")] + | ^^^ + | + = help: only `style`, `code` and `applicability` are valid nested attributes + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:235:5 + | +LL | #[suggestion("with a suggestion", msg = "bar")] + | ^ + +error: derive(Diagnostic): wrong field type for suggestion + --> $DIR/diagnostic-derive-inline.rs:258:5 + | +LL | #[suggestion("with a 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-inline.rs:274:24 + | +LL | suggestion: (Span, Span, Applicability), + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:274:18 + | +LL | suggestion: (Span, Span, Applicability), + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:282:33 + | +LL | suggestion: (Applicability, Applicability, Span), + | ^^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:282:18 + | +LL | suggestion: (Applicability, Applicability, Span), + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): `#[label = ...]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:289:5 + | +LL | #[label = "bar"] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:390:5 + | +LL | #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + | ^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:392:24 + | +LL | suggestion: (Span, Applicability), + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): invalid applicability + --> $DIR/diagnostic-derive-inline.rs:398:69 + | +LL | #[suggestion("with a 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-inline.rs:461:5 + | +LL | #[help("with a help")] + | ^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/diagnostic-derive-inline.rs:470:29 + | +LL | #[label("with a label", foo)] + | ^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/diagnostic-derive-inline.rs:478:29 + | +LL | #[label("with a label", foo = "...")] + | ^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/diagnostic-derive-inline.rs:486:29 + | +LL | #[label("with a label", foo("..."))] + | ^^^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:498:5 + | +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-inline.rs:518:1 + | +LL | #[error("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:518:1 + | +LL | #[error("this is an example message", 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-inline.rs:525:1 + | +LL | #[warn_("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:525:1 + | +LL | #[warn_("this is an example message", 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-inline.rs:532:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:532:1 + | +LL | #[lint("this is an example message", 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-inline.rs:539:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:539:1 + | +LL | #[lint("this is an example message", 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-inline.rs:548:53 + | +LL | #[suggestion("with a suggestion", code = "...", code = ",,,")] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:548:39 + | +LL | #[suggestion("with a suggestion", code = "...", code = ",,,")] + | ^^^^ + +error: derive(Diagnostic): wrong types for suggestion + --> $DIR/diagnostic-derive-inline.rs:557:24 + | +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-inline.rs:565:17 + | +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-inline.rs:572:5 + | +LL | #[suggestion("with a suggestion")] + | ^ + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:579:1 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:582:1 + | +LL | #[multipart_suggestion()] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:586:5 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:594:1 + | +LL | #[suggestion("with a suggestion", code = "...")] + | ^ + | + = help: `#[label]` and `#[suggestion]` can only be applied to fields + +error: derive(Diagnostic): `#[label]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:603:1 + | +LL | #[label] + | ^ + | + = help: `#[label]` and `#[suggestion]` can only be applied to fields + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:637:5 + | +LL | #[subdiagnostic(bad)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:645:5 + | +LL | #[subdiagnostic = "bad"] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:653:5 + | +LL | #[subdiagnostic(bad, bad)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:661:5 + | +LL | #[subdiagnostic("bad")] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:669:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:677:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:698:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): expected at least one string literal for `code(...)` + --> $DIR/diagnostic-derive-inline.rs:729:44 + | +LL | #[suggestion("with a suggestion", code())] + | ^ + +error: derive(Diagnostic): `code(...)` must contain only string literals + --> $DIR/diagnostic-derive-inline.rs:737:44 + | +LL | #[suggestion("with a suggestion", code(foo))] + | ^^^ + +error: unexpected token, expected `)` + --> $DIR/diagnostic-derive-inline.rs:737:44 + | +LL | #[suggestion("with a suggestion", code(foo))] + | ^^^ + +error: expected string literal + --> $DIR/diagnostic-derive-inline.rs:746:46 + | +LL | #[suggestion("with a suggestion", code = 3)] + | ^ + +error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:761:5 + | +LL | #[suggestion("with a suggestion", code = "")] + | ^ + | + = note: `#[suggestion(...)]` applied to `Vec` field is ambiguous + = help: to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]` + = help: to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]` + +error: cannot find attribute `nonsense` in this scope + --> $DIR/diagnostic-derive-inline.rs:61:3 + | +LL | #[nonsense("this is an example message", code = E0123)] + | ^^^^^^^^ + +error: cannot find attribute `nonsense` in this scope + --> $DIR/diagnostic-derive-inline.rs:139:7 + | +LL | #[nonsense] + | ^^^^^^^^ + +error: cannot find attribute `error` in this scope + --> $DIR/diagnostic-derive-inline.rs:518:3 + | +LL | #[error("this is an example message", code = E0123)] + | ^^^^^ + | +help: `error` is an attribute that can be used by the derive macro `Error`, you might be missing a `derive` attribute + | +LL + #[derive(Error)] +LL | struct ErrorAttribute {} + | + +error: cannot find attribute `warn_` in this scope + --> $DIR/diagnostic-derive-inline.rs:525:3 + | +LL | #[warn_("this is an example message", code = E0123)] + | ^^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[warn_("this is an example message", code = E0123)] +LL + #[warn("this is an example message", code = E0123)] + | + +error: cannot find attribute `lint` in this scope + --> $DIR/diagnostic-derive-inline.rs:532:3 + | +LL | #[lint("this is an example message", code = E0123)] + | ^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[lint("this is an example message", code = E0123)] +LL + #[link("this is an example message", code = E0123)] + | + +error: cannot find attribute `lint` in this scope + --> $DIR/diagnostic-derive-inline.rs:539:3 + | +LL | #[lint("this is an example message", code = E0123)] + | ^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[lint("this is an example message", code = E0123)] +LL + #[link("this is an example message", code = E0123)] + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:579:3 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + | +LL + #[derive(Subdiagnostic)] +LL | struct MultipartSuggestion { + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:582:3 + | +LL | #[multipart_suggestion()] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + | +LL + #[derive(Subdiagnostic)] +LL | struct MultipartSuggestion { + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:586:7 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + +error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied + --> $DIR/diagnostic-derive-inline.rs:330:12 + | +LL | #[derive(Diagnostic)] + | ---------- required by a bound introduced by this call +... +LL | other: Hello, + | ^^^^^ unsatisfied trait bound + | +help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Hello` + --> $DIR/diagnostic-derive-inline.rs:42:1 + | +LL | struct Hello {} + | ^^^^^^^^^^^^ + = help: normalized in stderr +note: required by a bound in `Diag::<'a, G>::arg` + --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + ::: $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + | + = 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 79 previous errors + +For more information about this error, try `rustc --explain E0277`. From 523d9d920028753e4c170d34366871e14f13a9fc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Jan 2026 19:26:57 +0100 Subject: [PATCH 473/583] Uitests for subdiagnostics --- .../subdiagnostic-derive-inline.rs | 807 ++++++++++++++++++ .../subdiagnostic-derive-inline.stderr | 549 ++++++++++++ .../subdiagnostic-derive.rs | 9 - .../subdiagnostic-derive.stderr | 168 ++-- 4 files changed, 1437 insertions(+), 96 deletions(-) create mode 100644 tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs create mode 100644 tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs new file mode 100644 index 000000000000..eaa681d40be5 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs @@ -0,0 +1,807 @@ +//@ check-fail +// Tests error conditions for specifying inline subdiagnostics using #[derive(Subdiagnostic)] + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Subdiagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +//@ ignore-stage1 +//@ ignore-beta +//@ ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_errors; +extern crate rustc_fluent_macro; +extern crate rustc_macros; +extern crate rustc_session; +extern crate rustc_span; +extern crate core; + +use rustc_errors::{Applicability, DiagMessage, SubdiagMessage}; +use rustc_macros::Subdiagnostic; +use rustc_span::Span; + +#[derive(Subdiagnostic)] +#[label("example message")] +struct A { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +enum B { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, + #[label("example message")] + B { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +//~^ ERROR label without `#[primary_span]` field +struct C { + var: String, +} + +#[derive(Subdiagnostic)] +#[label] +//~^ ERROR diagnostic slug must be first argument +struct D { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[foo] +//~^ ERROR `#[foo]` is not a valid attribute +//~^^ ERROR cannot find attribute `foo` in this scope +struct E { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label = "..."] +//~^ ERROR `#[label = ...]` is not a valid attribute +struct F { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(bug = "...")] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct G { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(slug = 4)] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct J { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(slug("..."))] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct K { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label()] +//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute +struct M { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label("example message", code = "...")] +//~^ ERROR no nested attribute expected here +struct N { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label("example message", applicability = "machine-applicable")] +//~^ ERROR no nested attribute expected here +struct O { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[foo] +//~^ ERROR cannot find attribute `foo` in this scope +//~^^ ERROR unsupported type attribute for subdiagnostic enum +enum P { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum Q { + #[bar] + //~^ ERROR `#[bar]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum R { + #[bar = "..."] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum S { + #[bar = 4] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum T { + #[bar("...")] + //~^ ERROR `#[bar(...)]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum U { + #[label(code = "...")] + //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute + //~| ERROR no nested attribute expected here + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum V { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, + B { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +//~^ ERROR label without `#[primary_span]` field +struct W { + #[primary_span] + //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + span: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct X { + #[primary_span] + span: Span, + #[applicability] + //~^ ERROR `#[applicability]` is only valid on suggestions + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct Y { + #[primary_span] + span: Span, + #[bar] + //~^ ERROR `#[bar]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct Z { + #[primary_span] + span: Span, + #[bar = "..."] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AA { + #[primary_span] + span: Span, + #[bar("...")] + //~^ ERROR `#[bar(...)]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AB { + #[primary_span] + span: Span, + #[skip_arg] + z: Z, +} + +#[derive(Subdiagnostic)] +union AC { + //~^ ERROR unexpected unsupported untagged union + span: u32, + b: u64, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +#[label("example message")] +struct AD { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label("example message", no_crate::example)] +//~^ ERROR a diagnostic slug must be the first argument to the attribute +struct AE { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AF { + #[primary_span] + //~^ NOTE previously specified here + span_a: Span, + #[primary_span] + //~^ ERROR specified multiple times + span_b: Span, +} + +#[derive(Subdiagnostic)] +struct AG { + //~^ ERROR subdiagnostic kind not specified + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AH { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, +} + +#[derive(Subdiagnostic)] +enum AI { + #[suggestion("example message", code = "...")] + A { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, + }, + #[suggestion("example message", code = "...")] + B { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", code = "...")] +//~^ ERROR specified multiple times +//~^^ NOTE previously specified here +struct AJ { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AK { + #[primary_span] + span: Span, + #[applicability] + //~^ NOTE previously specified here + applicability_a: Applicability, + #[applicability] + //~^ ERROR specified multiple times + applicability_b: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AL { + #[primary_span] + span: Span, + #[applicability] + //~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability` + applicability: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AM { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message")] +//~^ ERROR suggestion without `code = "..."` +struct AN { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", applicability = "foo")] +//~^ ERROR invalid applicability +struct AO { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[help("example message")] +struct AP { + var: String, +} + +#[derive(Subdiagnostic)] +#[note("example message")] +struct AQ; + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +//~^ ERROR suggestion without `#[primary_span]` field +struct AR { + var: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", applicability = "machine-applicable")] +struct AS { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label] +//~^ ERROR unsupported type attribute for subdiagnostic enum +enum AT { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "{var}", applicability = "machine-applicable")] +struct AU { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "{var}", applicability = "machine-applicable")] +//~^ ERROR `var` doesn't refer to a field on this type +struct AV { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +enum AW { + #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum AX { + #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + //~^ ERROR `var` doesn't refer to a field on this type + A { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +#[warning("example message")] +struct AY {} + +#[derive(Subdiagnostic)] +#[warning("example message")] +struct AZ { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +//~^ ERROR suggestion without `#[primary_span]` field +struct BA { + #[suggestion_part] + //~^ ERROR `#[suggestion_part]` is not a valid attribute + span: Span, + #[suggestion_part(code = "...")] + //~^ ERROR `#[suggestion_part(...)]` is not a valid attribute + span2: Span, + #[applicability] + applicability: Applicability, + var: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] +//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields +//~| ERROR invalid nested attribute +struct BBa { + var: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BBb { + #[suggestion_part] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BBc { + #[suggestion_part()] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields +struct BC { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BD { + #[suggestion_part] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, + #[suggestion_part()] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span2: Span, + #[suggestion_part(foo = "bar")] + //~^ ERROR `code` is the only valid nested attribute + //~| ERROR expected `,` + span4: Span, + #[suggestion_part(code = "...")] + //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + s1: String, + #[suggestion_part()] + //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + s2: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BE { + #[suggestion_part(code = "...", code = ",,,")] + //~^ ERROR specified multiple times + //~| NOTE previously specified here + span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BF { + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BG { + #[applicability] + appl: Applicability, + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BH { + #[applicability] + //~^ ERROR `#[applicability]` has no effect + appl: Applicability, + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BI { + #[suggestion_part(code = "")] + spans: Vec, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct BJ { + #[primary_span] + span: Span, + r#type: String, +} + +/// with a doc comment on the type.. +#[derive(Subdiagnostic)] +#[label("example message")] +struct BK { + /// ..and the field + #[primary_span] + span: Span, +} + +/// with a doc comment on the type.. +#[derive(Subdiagnostic)] +enum BL { + /// ..and the variant.. + #[label("example message")] + Foo { + /// ..and the field + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BM { + #[suggestion_part(code("foo"))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BN { + #[suggestion_part(code("foo", "bar"))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BO { + #[suggestion_part(code(3))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BP { + #[suggestion_part(code())] + //~^ ERROR expected exactly one string literal for `code = ...` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BQ { + #[suggestion_part(code = 3)] + //~^ ERROR expected string literal + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "")] +struct SuggestionStyleDefault { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "short")] +struct SuggestionStyleShort { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "hidden")] +struct SuggestionStyleHidden { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "verbose")] +struct SuggestionStyleVerbose { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "tool-only")] +struct SuggestionStyleToolOnly { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "hidden", style = "normal")] +//~^ ERROR specified multiple times +//~| NOTE previously specified here +struct SuggestionStyleTwice { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion_hidden("example message", code = "")] +//~^ ERROR #[suggestion_hidden(...)]` is not a valid attribute +struct SuggestionStyleOldSyntax { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion_hidden("example message", code = "", style = "normal")] +//~^ ERROR #[suggestion_hidden(...)]` is not a valid attribute +struct SuggestionStyleOldAndNewSyntax { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "foo")] +//~^ ERROR invalid suggestion style +struct SuggestionStyleInvalid1 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = 42)] +//~^ ERROR expected string literal +struct SuggestionStyleInvalid2 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style)] +//~^ ERROR a diagnostic slug must be the first argument to the attribute +struct SuggestionStyleInvalid3 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style("foo"))] +//~^ ERROR expected `=` +struct SuggestionStyleInvalid4 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "")] +//~^ ERROR suggestion without `#[primary_span]` field +struct PrimarySpanOnVec { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + //~| NOTE there must be exactly one primary span + sub: Vec, +} + +#[derive(Subdiagnostic)] +struct NestedParent { + #[subdiagnostic] + single_sub: A, + #[subdiagnostic] + option_sub: Option, + #[subdiagnostic] + vec_sub: Vec, +} diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr new file mode 100644 index 000000000000..11753b949bc7 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr @@ -0,0 +1,549 @@ +error: derive(Diagnostic): label without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:50:1 + | +LL | #[label("example message")] + | ^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:57:1 + | +LL | #[label] + | ^ + +error: derive(Diagnostic): `#[foo]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:66:1 + | +LL | #[foo] + | ^ + +error: derive(Diagnostic): `#[label = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:76:1 + | +LL | #[label = "..."] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:85:9 + | +LL | #[label(bug = "...")] + | ^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:85:1 + | +LL | #[label(bug = "...")] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:95:9 + | +LL | #[label(slug = 4)] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:95:1 + | +LL | #[label(slug = 4)] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:105:9 + | +LL | #[label(slug("..."))] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:105:1 + | +LL | #[label(slug("..."))] + | ^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:115:1 + | +LL | #[label()] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:124:28 + | +LL | #[label("example message", code = "...")] + | ^^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:133:28 + | +LL | #[label("example message", applicability = "machine-applicable")] + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum + --> $DIR/subdiagnostic-derive-inline.rs:142:1 + | +LL | #[foo] + | ^ + +error: derive(Diagnostic): `#[bar]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:156:5 + | +LL | #[bar] + | ^ + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:168:5 + | +LL | #[bar = "..."] + | ^ + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:180:5 + | +LL | #[bar = 4] + | ^ + +error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:192:5 + | +LL | #[bar("...")] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:204:13 + | +LL | #[label(code = "...")] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:204:5 + | +LL | #[label(code = "...")] + | ^ + +error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:233:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): label without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:230:1 + | +LL | #[label("example message")] + | ^ + +error: derive(Diagnostic): `#[applicability]` is only valid on suggestions + --> $DIR/subdiagnostic-derive-inline.rs:243:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): `#[bar]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:253:5 + | +LL | #[bar] + | ^ + | + = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:264:5 + | +LL | #[bar = "..."] + | ^ + +error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:275:5 + | +LL | #[bar("...")] + | ^ + | + = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes + +error: unexpected unsupported untagged union + --> $DIR/subdiagnostic-derive-inline.rs:291:1 + | +LL | / union AC { +LL | | +LL | | span: u32, +LL | | b: u64, +LL | | } + | |_^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/subdiagnostic-derive-inline.rs:306:28 + | +LL | #[label("example message", no_crate::example)] + | ^^^^^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:319:5 + | +LL | #[primary_span] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:316:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): subdiagnostic kind not specified + --> $DIR/subdiagnostic-derive-inline.rs:325:8 + | +LL | struct AG { + | ^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:362:47 + | +LL | #[suggestion("example message", code = "...", code = "...")] + | ^^^^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:362:33 + | +LL | #[suggestion("example message", code = "...", code = "...")] + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:380:5 + | +LL | #[applicability] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:377:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): the `#[applicability]` attribute can only be applied to fields of type `Applicability` + --> $DIR/subdiagnostic-derive-inline.rs:390:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:403:1 + | +LL | #[suggestion("example message")] + | ^ + +error: derive(Diagnostic): invalid applicability + --> $DIR/subdiagnostic-derive-inline.rs:413:63 + | +LL | #[suggestion("example message", code = "...", applicability = "foo")] + | ^^^^^ + +error: derive(Diagnostic): suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:431:1 + | +LL | #[suggestion("example message", code = "...")] + | ^ + +error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum + --> $DIR/subdiagnostic-derive-inline.rs:445:1 + | +LL | #[label] + | ^ + +error: derive(Diagnostic): `var` doesn't refer to a field on this type + --> $DIR/subdiagnostic-derive-inline.rs:465:40 + | +LL | #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + | ^^^^^^^ + +error: derive(Diagnostic): `var` doesn't refer to a field on this type + --> $DIR/subdiagnostic-derive-inline.rs:484:44 + | +LL | #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + | ^^^^^^^ + +error: derive(Diagnostic): `#[suggestion_part]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:507:5 + | +LL | #[suggestion_part] + | ^ + | + = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead + +error: derive(Diagnostic): `#[suggestion_part(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:510:5 + | +LL | #[suggestion_part(code = "...")] + | ^ + | + = help: `#[suggestion_part(...)]` is only valid in multipart suggestions + +error: derive(Diagnostic): suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:504:1 + | +LL | #[suggestion("example message", code = "...")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/subdiagnostic-derive-inline.rs:519:43 + | +LL | #[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] + | ^^^^ + | + = help: only `style` and `applicability` are valid nested attributes + +error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive-inline.rs:519:1 + | +LL | #[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:529:5 + | +LL | #[suggestion_part] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:537:5 + | +LL | #[suggestion_part()] + | ^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:546:5 + | +LL | #[primary_span] + | ^ + | + = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` + +error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive-inline.rs:543:1 + | +LL | #[multipart_suggestion("example message")] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:554:5 + | +LL | #[suggestion_part] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:557:5 + | +LL | #[suggestion_part()] + | ^ + +error: derive(Diagnostic): `code` is the only valid nested attribute + --> $DIR/subdiagnostic-derive-inline.rs:560:23 + | +LL | #[suggestion_part(foo = "bar")] + | ^^^ + +error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:564:5 + | +LL | #[suggestion_part(code = "...")] + | ^ + +error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:567:5 + | +LL | #[suggestion_part()] + | ^ + +error: expected `,` + --> $DIR/subdiagnostic-derive-inline.rs:560:27 + | +LL | #[suggestion_part(foo = "bar")] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:575:37 + | +LL | #[suggestion_part(code = "...", code = ",,,")] + | ^^^^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:575:23 + | +LL | #[suggestion_part(code = "...", code = ",,,")] + | ^^^^ + +error: derive(Diagnostic): `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_suggestion]` attributes have a static `applicability = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:604:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:652:28 + | +LL | #[suggestion_part(code("foo"))] + | ^^^^^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:652:28 + | +LL | #[suggestion_part(code("foo"))] + | ^^^^^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:662:28 + | +LL | #[suggestion_part(code("foo", "bar"))] + | ^^^^^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:662:28 + | +LL | #[suggestion_part(code("foo", "bar"))] + | ^^^^^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:672:28 + | +LL | #[suggestion_part(code(3))] + | ^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:672:28 + | +LL | #[suggestion_part(code(3))] + | ^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:682:28 + | +LL | #[suggestion_part(code())] + | ^ + +error: expected string literal + --> $DIR/subdiagnostic-derive-inline.rs:691:30 + | +LL | #[suggestion_part(code = 3)] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:733:1 + | +LL | #[suggestion("example message", code = "", style = "hidden", style = "normal")] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:733:1 + | +LL | #[suggestion("example message", code = "", style = "hidden", style = "normal")] + | ^ + +error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:742:1 + | +LL | #[suggestion_hidden("example message", code = "")] + | ^ + | + = help: Use `#[suggestion(..., style = "hidden")]` instead + +error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:750:1 + | +LL | #[suggestion_hidden("example message", code = "", style = "normal")] + | ^ + | + = help: Use `#[suggestion(..., style = "hidden")]` instead + +error: derive(Diagnostic): invalid suggestion style + --> $DIR/subdiagnostic-derive-inline.rs:758:52 + | +LL | #[suggestion("example message", code = "", style = "foo")] + | ^^^^^ + | + = help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only` + +error: expected string literal + --> $DIR/subdiagnostic-derive-inline.rs:766:52 + | +LL | #[suggestion("example message", code = "", style = 42)] + | ^^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/subdiagnostic-derive-inline.rs:774:44 + | +LL | #[suggestion("example message", code = "", style)] + | ^^^^^ + +error: expected `=` + --> $DIR/subdiagnostic-derive-inline.rs:782:49 + | +LL | #[suggestion("example message", code = "", style("foo"))] + | ^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:793:5 + | +LL | #[primary_span] + | ^ + | + = note: there must be exactly one 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-inline.rs:790:1 + | +LL | #[suggestion("example message", code = "")] + | ^ + +error: cannot find attribute `foo` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:66:3 + | +LL | #[foo] + | ^^^ + +error: cannot find attribute `foo` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:142:3 + | +LL | #[foo] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:156:7 + | +LL | #[bar] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:168:7 + | +LL | #[bar = "..."] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:180:7 + | +LL | #[bar = 4] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:192:7 + | +LL | #[bar("...")] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:253:7 + | +LL | #[bar] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:264:7 + | +LL | #[bar = "..."] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:275:7 + | +LL | #[bar("...")] + | ^^^ + +error: aborting due to 82 previous errors + diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index b2e7b4c61daa..c06ea451b9bf 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -93,15 +93,6 @@ struct G { var: String, } -#[derive(Subdiagnostic)] -#[label("...")] -//~^ ERROR expected identifier -struct H { - #[primary_span] - span: Span, - var: String, -} - #[derive(Subdiagnostic)] #[label(slug = 4)] //~^ ERROR no nested attribute expected here diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index 63634741e934..9f18f7ffabcc 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -34,116 +34,110 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label( LL | #[label(bug = "...")] | ^ -error: expected identifier +error: derive(Diagnostic): no nested attribute expected here --> $DIR/subdiagnostic-derive.rs:97:9 | -LL | #[label("...")] - | ^^^^^ - -error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:106:9 - | LL | #[label(slug = 4)] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:106:1 + --> $DIR/subdiagnostic-derive.rs:97:1 | LL | #[label(slug = 4)] | ^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:116:9 + --> $DIR/subdiagnostic-derive.rs:107:9 | LL | #[label(slug("..."))] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:116:1 + --> $DIR/subdiagnostic-derive.rs:107:1 | LL | #[label(slug("..."))] | ^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:126:1 + --> $DIR/subdiagnostic-derive.rs:117:1 | LL | #[label()] | ^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:135:27 + --> $DIR/subdiagnostic-derive.rs:126:27 | LL | #[label(no_crate_example, code = "...")] | ^^^^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:144:27 + --> $DIR/subdiagnostic-derive.rs:135:27 | LL | #[label(no_crate_example, applicability = "machine-applicable")] | ^^^^^^^^^^^^^ error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:153:1 + --> $DIR/subdiagnostic-derive.rs:144:1 | LL | #[foo] | ^ error: derive(Diagnostic): `#[bar]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:167:5 + --> $DIR/subdiagnostic-derive.rs:158:5 | LL | #[bar] | ^ error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:179:5 + --> $DIR/subdiagnostic-derive.rs:170:5 | LL | #[bar = "..."] | ^ error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:191:5 + --> $DIR/subdiagnostic-derive.rs:182:5 | LL | #[bar = 4] | ^ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:203:5 + --> $DIR/subdiagnostic-derive.rs:194:5 | LL | #[bar("...")] | ^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:215:13 + --> $DIR/subdiagnostic-derive.rs:206:13 | LL | #[label(code = "...")] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:215:5 + --> $DIR/subdiagnostic-derive.rs:206:5 | LL | #[label(code = "...")] | ^ error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:244:5 + --> $DIR/subdiagnostic-derive.rs:235:5 | LL | #[primary_span] | ^ error: derive(Diagnostic): label without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:241:1 + --> $DIR/subdiagnostic-derive.rs:232:1 | LL | #[label(no_crate_example)] | ^ error: derive(Diagnostic): `#[applicability]` is only valid on suggestions - --> $DIR/subdiagnostic-derive.rs:254:5 + --> $DIR/subdiagnostic-derive.rs:245:5 | LL | #[applicability] | ^ error: derive(Diagnostic): `#[bar]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:264:5 + --> $DIR/subdiagnostic-derive.rs:255:5 | LL | #[bar] | ^ @@ -151,13 +145,13 @@ LL | #[bar] = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:275:5 + --> $DIR/subdiagnostic-derive.rs:266:5 | LL | #[bar = "..."] | ^ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:286:5 + --> $DIR/subdiagnostic-derive.rs:277:5 | LL | #[bar("...")] | ^ @@ -165,7 +159,7 @@ LL | #[bar("...")] = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: unexpected unsupported untagged union - --> $DIR/subdiagnostic-derive.rs:302:1 + --> $DIR/subdiagnostic-derive.rs:293:1 | LL | / union AC { LL | | @@ -175,97 +169,97 @@ LL | | } | |_^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/subdiagnostic-derive.rs:317:27 + --> $DIR/subdiagnostic-derive.rs:308:27 | LL | #[label(no_crate_example, no_crate::example)] | ^^^^^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:330:5 + --> $DIR/subdiagnostic-derive.rs:321:5 | LL | #[primary_span] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:327:5 + --> $DIR/subdiagnostic-derive.rs:318:5 | LL | #[primary_span] | ^ error: derive(Diagnostic): subdiagnostic kind not specified - --> $DIR/subdiagnostic-derive.rs:336:8 + --> $DIR/subdiagnostic-derive.rs:327:8 | LL | struct AG { | ^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:373:46 + --> $DIR/subdiagnostic-derive.rs:364:46 | LL | #[suggestion(no_crate_example, code = "...", code = "...")] | ^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:373:32 + --> $DIR/subdiagnostic-derive.rs:364:32 | LL | #[suggestion(no_crate_example, code = "...", code = "...")] | ^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:391:5 + --> $DIR/subdiagnostic-derive.rs:382:5 | LL | #[applicability] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:388:5 + --> $DIR/subdiagnostic-derive.rs:379:5 | LL | #[applicability] | ^ error: derive(Diagnostic): the `#[applicability]` attribute can only be applied to fields of type `Applicability` - --> $DIR/subdiagnostic-derive.rs:401:5 + --> $DIR/subdiagnostic-derive.rs:392:5 | LL | #[applicability] | ^ error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:414:1 + --> $DIR/subdiagnostic-derive.rs:405:1 | LL | #[suggestion(no_crate_example)] | ^ error: derive(Diagnostic): invalid applicability - --> $DIR/subdiagnostic-derive.rs:424:62 + --> $DIR/subdiagnostic-derive.rs:415:62 | LL | #[suggestion(no_crate_example, code = "...", applicability = "foo")] | ^^^^^ error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:442:1 + --> $DIR/subdiagnostic-derive.rs:433:1 | LL | #[suggestion(no_crate_example, code = "...")] | ^ error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:456:1 + --> $DIR/subdiagnostic-derive.rs:447:1 | LL | #[label] | ^ error: derive(Diagnostic): `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:476:39 + --> $DIR/subdiagnostic-derive.rs:467:39 | LL | #[suggestion(no_crate_example, code = "{var}", applicability = "machine-applicable")] | ^^^^^^^ error: derive(Diagnostic): `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:495:43 + --> $DIR/subdiagnostic-derive.rs:486:43 | LL | #[suggestion(no_crate_example, code = "{var}", applicability = "machine-applicable")] | ^^^^^^^ error: derive(Diagnostic): `#[suggestion_part]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:518:5 + --> $DIR/subdiagnostic-derive.rs:509:5 | LL | #[suggestion_part] | ^ @@ -273,7 +267,7 @@ LL | #[suggestion_part] = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead error: derive(Diagnostic): `#[suggestion_part(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:521:5 + --> $DIR/subdiagnostic-derive.rs:512:5 | LL | #[suggestion_part(code = "...")] | ^ @@ -281,13 +275,13 @@ LL | #[suggestion_part(code = "...")] = help: `#[suggestion_part(...)]` is only valid in multipart suggestions error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:515:1 + --> $DIR/subdiagnostic-derive.rs:506:1 | LL | #[suggestion(no_crate_example, code = "...")] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/subdiagnostic-derive.rs:530:42 + --> $DIR/subdiagnostic-derive.rs:521:42 | LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")] | ^^^^ @@ -295,25 +289,25 @@ LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "mac = 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 + --> $DIR/subdiagnostic-derive.rs:521:1 | LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:540:5 + --> $DIR/subdiagnostic-derive.rs:531:5 | LL | #[suggestion_part] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:548:5 + --> $DIR/subdiagnostic-derive.rs:539:5 | LL | #[suggestion_part()] | ^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:557:5 + --> $DIR/subdiagnostic-derive.rs:548:5 | LL | #[primary_span] | ^ @@ -321,127 +315,127 @@ LL | #[primary_span] = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:554:1 + --> $DIR/subdiagnostic-derive.rs:545:1 | LL | #[multipart_suggestion(no_crate_example)] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:565:5 + --> $DIR/subdiagnostic-derive.rs:556:5 | LL | #[suggestion_part] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:568:5 + --> $DIR/subdiagnostic-derive.rs:559:5 | LL | #[suggestion_part()] | ^ error: derive(Diagnostic): `code` is the only valid nested attribute - --> $DIR/subdiagnostic-derive.rs:571:23 + --> $DIR/subdiagnostic-derive.rs:562:23 | LL | #[suggestion_part(foo = "bar")] | ^^^ error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:575:5 + --> $DIR/subdiagnostic-derive.rs:566:5 | LL | #[suggestion_part(code = "...")] | ^ error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:578:5 + --> $DIR/subdiagnostic-derive.rs:569:5 | LL | #[suggestion_part()] | ^ error: expected `,` - --> $DIR/subdiagnostic-derive.rs:571:27 + --> $DIR/subdiagnostic-derive.rs:562:27 | LL | #[suggestion_part(foo = "bar")] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:586:37 + --> $DIR/subdiagnostic-derive.rs:577:37 | LL | #[suggestion_part(code = "...", code = ",,,")] | ^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:586:23 + --> $DIR/subdiagnostic-derive.rs:577:23 | LL | #[suggestion_part(code = "...", code = ",,,")] | ^^^^ error: derive(Diagnostic): `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_suggestion]` attributes have a static `applicability = "..."` - --> $DIR/subdiagnostic-derive.rs:615:5 + --> $DIR/subdiagnostic-derive.rs:606:5 | LL | #[applicability] | ^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:663:28 + --> $DIR/subdiagnostic-derive.rs:654:28 | LL | #[suggestion_part(code("foo"))] | ^^^^^ error: unexpected token, expected `)` - --> $DIR/subdiagnostic-derive.rs:663:28 + --> $DIR/subdiagnostic-derive.rs:654:28 | LL | #[suggestion_part(code("foo"))] | ^^^^^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:673:28 + --> $DIR/subdiagnostic-derive.rs:664:28 | LL | #[suggestion_part(code("foo", "bar"))] | ^^^^^ error: unexpected token, expected `)` - --> $DIR/subdiagnostic-derive.rs:673:28 + --> $DIR/subdiagnostic-derive.rs:664:28 | LL | #[suggestion_part(code("foo", "bar"))] | ^^^^^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:683:28 + --> $DIR/subdiagnostic-derive.rs:674:28 | LL | #[suggestion_part(code(3))] | ^ error: unexpected token, expected `)` - --> $DIR/subdiagnostic-derive.rs:683:28 + --> $DIR/subdiagnostic-derive.rs:674:28 | LL | #[suggestion_part(code(3))] | ^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:693:28 + --> $DIR/subdiagnostic-derive.rs:684:28 | LL | #[suggestion_part(code())] | ^ error: expected string literal - --> $DIR/subdiagnostic-derive.rs:702:30 + --> $DIR/subdiagnostic-derive.rs:693:30 | LL | #[suggestion_part(code = 3)] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:744:1 + --> $DIR/subdiagnostic-derive.rs:735:1 | LL | #[suggestion(no_crate_example, code = "", style = "hidden", style = "normal")] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:744:1 + --> $DIR/subdiagnostic-derive.rs:735:1 | LL | #[suggestion(no_crate_example, code = "", style = "hidden", style = "normal")] | ^ error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:753:1 + --> $DIR/subdiagnostic-derive.rs:744:1 | LL | #[suggestion_hidden(no_crate_example, code = "")] | ^ @@ -449,7 +443,7 @@ LL | #[suggestion_hidden(no_crate_example, code = "")] = help: Use `#[suggestion(..., style = "hidden")]` instead error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:761:1 + --> $DIR/subdiagnostic-derive.rs:752:1 | LL | #[suggestion_hidden(no_crate_example, code = "", style = "normal")] | ^ @@ -457,7 +451,7 @@ LL | #[suggestion_hidden(no_crate_example, code = "", style = "normal")] = help: Use `#[suggestion(..., style = "hidden")]` instead error: derive(Diagnostic): invalid suggestion style - --> $DIR/subdiagnostic-derive.rs:769:51 + --> $DIR/subdiagnostic-derive.rs:760:51 | LL | #[suggestion(no_crate_example, code = "", style = "foo")] | ^^^^^ @@ -465,25 +459,25 @@ LL | #[suggestion(no_crate_example, code = "", style = "foo")] = help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only` error: expected string literal - --> $DIR/subdiagnostic-derive.rs:777:51 + --> $DIR/subdiagnostic-derive.rs:768: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:43 + --> $DIR/subdiagnostic-derive.rs:776:43 | LL | #[suggestion(no_crate_example, code = "", style)] | ^^^^^ error: expected `=` - --> $DIR/subdiagnostic-derive.rs:793:48 + --> $DIR/subdiagnostic-derive.rs:784:48 | LL | #[suggestion(no_crate_example, code = "", style("foo"))] | ^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:804:5 + --> $DIR/subdiagnostic-derive.rs:795:5 | LL | #[primary_span] | ^ @@ -492,7 +486,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:801:1 + --> $DIR/subdiagnostic-derive.rs:792:1 | LL | #[suggestion(no_crate_example, code = "")] | ^ @@ -504,52 +498,52 @@ LL | #[foo] | ^^^ error: cannot find attribute `foo` in this scope - --> $DIR/subdiagnostic-derive.rs:153:3 + --> $DIR/subdiagnostic-derive.rs:144:3 | LL | #[foo] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:167:7 + --> $DIR/subdiagnostic-derive.rs:158:7 | LL | #[bar] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:179:7 + --> $DIR/subdiagnostic-derive.rs:170:7 | LL | #[bar = "..."] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:191:7 + --> $DIR/subdiagnostic-derive.rs:182:7 | LL | #[bar = 4] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:203:7 + --> $DIR/subdiagnostic-derive.rs:194:7 | LL | #[bar("...")] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:264:7 + --> $DIR/subdiagnostic-derive.rs:255:7 | LL | #[bar] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:275:7 + --> $DIR/subdiagnostic-derive.rs:266:7 | LL | #[bar = "..."] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:286:7 + --> $DIR/subdiagnostic-derive.rs:277:7 | LL | #[bar("...")] | ^^^ -error: aborting due to 83 previous errors +error: aborting due to 82 previous errors From ca2be71a18487405d72ae87d84e8fc75bb366b82 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Jan 2026 21:23:40 +0100 Subject: [PATCH 474/583] Add verification for inline fluent messages --- Cargo.lock | 2 + compiler/rustc_macros/Cargo.toml | 2 + .../src/diagnostics/diagnostic.rs | 53 ++------ .../src/diagnostics/diagnostic_builder.rs | 38 ++++-- .../rustc_macros/src/diagnostics/message.rs | 127 ++++++++++++++++-- .../src/diagnostics/subdiagnostic.rs | 9 +- .../rustc_macros/src/diagnostics/utils.rs | 2 +- .../diagnostic-derive-inline.rs | 13 ++ .../diagnostic-derive-inline.stderr | 10 +- 9 files changed, 183 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6be4565374af..9acb62b234ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4229,6 +4229,8 @@ dependencies = [ name = "rustc_macros" version = "0.0.0" dependencies = [ + "fluent-bundle", + "fluent-syntax", "proc-macro2", "quote", "syn 2.0.110", diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml index f9d3b7583590..f097aee54abb 100644 --- a/compiler/rustc_macros/Cargo.toml +++ b/compiler/rustc_macros/Cargo.toml @@ -8,6 +8,8 @@ proc-macro = true [dependencies] # tidy-alphabetical-start +fluent-bundle = "0.16" +fluent-syntax = "0.12" proc-macro2 = "1" quote = "1" syn = { version = "2.0.9", features = ["full"] } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 1ae6393b2860..b4270f45422e 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -22,7 +22,7 @@ impl<'a> DiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let DiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::Diagnostic; - let slugs = RefCell::new(Vec::new()); + let messages = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -30,8 +30,8 @@ impl<'a> DiagnosticDerive<'a> { let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().extend(message.slug().cloned()); - let message = message.diag_message(); + messages.borrow_mut().push(message.clone()); + let message = message.diag_message(variant); let init = quote! { let mut diag = rustc_errors::Diag::new( @@ -68,7 +68,7 @@ impl<'a> DiagnosticDerive<'a> { } } }); - for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) { + for test in messages.borrow().iter().map(|s| s.generate_test(&structure)) { imp.extend(test); } imp @@ -88,7 +88,7 @@ impl<'a> LintDiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let LintDiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::LintDiagnostic; - let slugs = RefCell::new(Vec::new()); + let messages = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -96,8 +96,8 @@ impl<'a> LintDiagnosticDerive<'a> { let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().extend(message.slug().cloned()); - let message = message.diag_message(); + messages.borrow_mut().push(message.clone()); + let message = message.diag_message(variant); let primary_message = quote! { diag.primary_message(#message); }; @@ -125,47 +125,10 @@ impl<'a> LintDiagnosticDerive<'a> { } } }); - for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) { + for test in messages.borrow().iter().map(|s| s.generate_test(&structure)) { imp.extend(test); } imp } } - -/// Generates a `#[test]` that verifies that all referenced variables -/// exist on this structure. -fn generate_test(slug: &syn::Path, structure: &Structure<'_>) -> TokenStream { - // FIXME: We can't identify variables in a subdiagnostic - for field in structure.variants().iter().flat_map(|v| v.ast().fields.iter()) { - for attr_name in field.attrs.iter().filter_map(|at| at.path().get_ident()) { - if attr_name == "subdiagnostic" { - return quote!(); - } - } - } - use std::sync::atomic::{AtomicUsize, Ordering}; - // We need to make sure that the same diagnostic slug can be used multiple times without - // causing an error, so just have a global counter here. - static COUNTER: AtomicUsize = AtomicUsize::new(0); - let slug = slug.get_ident().unwrap(); - let ident = quote::format_ident!("verify_{slug}_{}", COUNTER.fetch_add(1, Ordering::Relaxed)); - let ref_slug = quote::format_ident!("{slug}_refs"); - let struct_name = &structure.ast().ident; - let variables: Vec<_> = structure - .variants() - .iter() - .flat_map(|v| v.ast().fields.iter().filter_map(|f| f.ident.as_ref().map(|i| i.to_string()))) - .collect(); - // tidy errors on `#[test]` outside of test files, so we use `#[test ]` to work around this - quote! { - #[cfg(test)] - #[test ] - fn #ident() { - let variables = [#(#variables),*]; - for vref in crate::fluent_generated::#ref_slug { - assert!(variables.contains(vref), "{}: variable `{vref}` not found ({})", stringify!(#struct_name), stringify!(#slug)); - } - } - } -} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 14eda017970f..e6d9409a1fa3 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -137,7 +137,8 @@ impl DiagnosticDeriveVariantBuilder { let ast = variant.ast(); let attrs = &ast.attrs; let preamble = attrs.iter().map(|attr| { - self.generate_structure_code_for_attr(attr).unwrap_or_else(|v| v.to_compile_error()) + self.generate_structure_code_for_attr(attr, variant) + .unwrap_or_else(|v| v.to_compile_error()) }); quote! { @@ -155,7 +156,7 @@ impl DiagnosticDeriveVariantBuilder { } // ..and then subdiagnostic additions. for binding in variant.bindings().iter().filter(|bi| !should_generate_arg(bi.ast())) { - body.extend(self.generate_field_attrs_code(binding)); + body.extend(self.generate_field_attrs_code(binding, variant)); } body } @@ -199,6 +200,7 @@ impl DiagnosticDeriveVariantBuilder { fn generate_structure_code_for_attr( &mut self, attr: &Attribute, + variant: &VariantInfo<'_>, ) -> Result { // Always allow documentation comments. if is_doc_comment(attr) { @@ -224,7 +226,7 @@ impl DiagnosticDeriveVariantBuilder { ) .emit(); } - self.message = Some(Message::Inline(message.value())); + self.message = Some(Message::Inline(message.span(), message.value())); } else { // Parse a slug let slug = input.parse::()?; @@ -285,7 +287,7 @@ impl DiagnosticDeriveVariantBuilder { | SubdiagnosticKind::NoteOnce | SubdiagnosticKind::Help | SubdiagnosticKind::HelpOnce - | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug)), + | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug, variant)), SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { throw_invalid_attr!(attr, |diag| diag .help("`#[label]` and `#[suggestion]` can only be applied to fields")); @@ -313,7 +315,11 @@ impl DiagnosticDeriveVariantBuilder { } } - fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + fn generate_field_attrs_code( + &mut self, + binding_info: &BindingInfo<'_>, + variant: &VariantInfo<'_>, + ) -> TokenStream { let field = binding_info.ast(); let field_binding = &binding_info.binding; @@ -352,6 +358,7 @@ impl DiagnosticDeriveVariantBuilder { attr, FieldInfo { binding: binding_info, ty: inner_ty, span: &field.span() }, binding, + variant ) .unwrap_or_else(|v| v.to_compile_error()); @@ -369,6 +376,7 @@ impl DiagnosticDeriveVariantBuilder { attr: &Attribute, info: FieldInfo<'_>, binding: TokenStream, + variant: &VariantInfo<'_>, ) -> Result { let ident = &attr.path().segments.last().unwrap().ident; let name = ident.to_string(); @@ -407,7 +415,7 @@ impl DiagnosticDeriveVariantBuilder { match subdiag { SubdiagnosticKind::Label => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug, variant)) } SubdiagnosticKind::Note | SubdiagnosticKind::NoteOnce @@ -418,11 +426,11 @@ impl DiagnosticDeriveVariantBuilder { if type_matches_path(inner, &["rustc_span", "Span"]) || type_matches_path(inner, &["rustc_span", "MultiSpan"]) { - Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug, variant)) } else if type_is_unit(inner) || (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner)) { - Ok(self.add_subdiagnostic(&fn_ident, slug)) + Ok(self.add_subdiagnostic(&fn_ident, slug, variant)) } else { report_type_error(attr, "`Span`, `MultiSpan`, `bool` or `()`")? } @@ -448,7 +456,7 @@ impl DiagnosticDeriveVariantBuilder { applicability.set_once(quote! { #static_applicability }, span); } - let message = slug.diag_message(); + let message = slug.diag_message(variant); let applicability = applicability .value() .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); @@ -476,9 +484,10 @@ impl DiagnosticDeriveVariantBuilder { field_binding: TokenStream, kind: &Ident, message: Message, + variant: &VariantInfo<'_>, ) -> TokenStream { let fn_name = format_ident!("span_{}", kind); - let message = message.diag_message(); + let message = message.diag_message(variant); quote! { diag.#fn_name( #field_binding, @@ -489,8 +498,13 @@ impl DiagnosticDeriveVariantBuilder { /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug /// and `fluent_attr_identifier`. - fn add_subdiagnostic(&self, kind: &Ident, message: Message) -> TokenStream { - let message = message.diag_message(); + fn add_subdiagnostic( + &self, + kind: &Ident, + message: Message, + variant: &VariantInfo<'_>, + ) -> TokenStream { + let message = message.diag_message(variant); quote! { diag.#kind(#message); } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index cfce252fbdd8..153abecf8937 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -1,28 +1,133 @@ -use proc_macro2::TokenStream; +use fluent_bundle::FluentResource; +use fluent_syntax::ast::{Expression, InlineExpression, Pattern, PatternElement}; +use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::Path; +use synstructure::{Structure, VariantInfo}; +use crate::diagnostics::error::span_err; + +#[derive(Clone)] pub(crate) enum Message { Slug(Path), - Inline(String), + Inline(Span, String), } impl Message { - pub(crate) fn slug(&self) -> Option<&Path> { - match self { - Message::Slug(slug) => Some(slug), - Message::Inline(_) => None, - } - } - - pub(crate) fn diag_message(&self) -> TokenStream { + pub(crate) fn diag_message(&self, variant: &VariantInfo<'_>) -> TokenStream { match self { Message::Slug(slug) => { quote! { crate::fluent_generated::#slug } } - Message::Inline(message) => { + Message::Inline(message_span, message) => { + verify_fluent_message(*message_span, &message, variant); quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } } } } + + /// Generates a `#[test]` that verifies that all referenced variables + /// exist on this structure. + pub(crate) fn generate_test(&self, structure: &Structure<'_>) -> TokenStream { + match self { + Message::Slug(slug) => { + // FIXME: We can't identify variables in a subdiagnostic + for field in structure.variants().iter().flat_map(|v| v.ast().fields.iter()) { + for attr_name in field.attrs.iter().filter_map(|at| at.path().get_ident()) { + if attr_name == "subdiagnostic" { + return quote!(); + } + } + } + use std::sync::atomic::{AtomicUsize, Ordering}; + // We need to make sure that the same diagnostic slug can be used multiple times without + // causing an error, so just have a global counter here. + static COUNTER: AtomicUsize = AtomicUsize::new(0); + let slug = slug.get_ident().unwrap(); + let ident = quote::format_ident!( + "verify_{slug}_{}", + COUNTER.fetch_add(1, Ordering::Relaxed) + ); + let ref_slug = quote::format_ident!("{slug}_refs"); + let struct_name = &structure.ast().ident; + let variables: Vec<_> = structure + .variants() + .iter() + .flat_map(|v| { + v.ast() + .fields + .iter() + .filter_map(|f| f.ident.as_ref().map(|i| i.to_string())) + }) + .collect(); + // tidy errors on `#[test]` outside of test files, so we use `#[test ]` to work around this + quote! { + #[cfg(test)] + #[test ] + fn #ident() { + let variables = [#(#variables),*]; + for vref in crate::fluent_generated::#ref_slug { + assert!(variables.contains(vref), "{}: variable `{vref}` not found ({})", stringify!(#struct_name), stringify!(#slug)); + } + } + } + } + Message::Inline(..) => { + // We don't generate a test for inline diagnostics, we can verify these at compile-time! + // This verification is done in the `diag_message` function above + quote! {} + } + } + } +} + +fn verify_fluent_message(msg_span: Span, message: &str, variant: &VariantInfo<'_>) { + // Parse the fluent message + const GENERATED_MSG_ID: &str = "generated_msg"; + let resource = FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message}\n")).unwrap(); + assert_eq!(resource.entries().count(), 1); + let Some(fluent_syntax::ast::Entry::Message(message)) = resource.get_entry(0) else { + panic!("Did not parse into a message") + }; + + // Check if all variables are used + let fields: Vec = variant + .bindings() + .iter() + .flat_map(|b| b.ast().ident.as_ref()) + .map(|id| id.to_string()) + .collect(); + for variable in variable_references(&message) { + if !fields.iter().any(|f| f == variable) { + span_err(msg_span.unwrap(), format!("Variable `{variable}` not found in diagnostic ")) + .help(format!("Available fields: {:?}", fields.join(", "))) + .emit(); + } + // assert!(, ); + } +} + +fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&'a str> { + let mut refs = vec![]; + if let Some(Pattern { elements }) = &msg.value { + for elt in elements { + if let PatternElement::Placeable { + expression: Expression::Inline(InlineExpression::VariableReference { id }), + } = elt + { + refs.push(id.name); + } + } + } + for attr in &msg.attributes { + for elt in &attr.value.elements { + if let PatternElement::Placeable { + expression: Expression::Inline(InlineExpression::VariableReference { id }), + } = elt + { + refs.push(id.name); + } + } + } + refs } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 61a234f96d12..adc968dacd5c 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -75,7 +75,7 @@ impl SubdiagnosticDerive { has_subdiagnostic: false, is_enum, }; - builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) + builder.into_tokens(variant).unwrap_or_else(|v| v.to_compile_error()) }); quote! { @@ -497,7 +497,10 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } - pub(crate) fn into_tokens(&mut self) -> Result { + pub(crate) fn into_tokens( + &mut self, + variant: &VariantInfo<'_>, + ) -> Result { let kind_slugs = self.identify_kind()?; let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect(); @@ -535,7 +538,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut calls = TokenStream::new(); for (kind, slug) in kind_slugs { let message = format_ident!("__message"); - let message_stream = slug.diag_message(); + let message_stream = slug.diag_message(variant); calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); }); let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 0718448a0513..a5265a847a9c 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -708,7 +708,7 @@ impl SubdiagnosticVariant { } if !input.is_empty() { input.parse::()?; } if is_first { - slug = Some(Message::Inline(message.value())); + slug = Some(Message::Inline(message.span(), message.value())); is_first = false; } else { span_err(message.span().unwrap(), "a diagnostic message must be the first argument to the attribute").emit(); diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs index 7d9af0522f63..babe3813e40b 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs @@ -762,3 +762,16 @@ struct SuggestionOnVec { //~^ ERROR `#[suggestion(...)]` is not a valid attribute sub: Vec, } + +#[derive(Diagnostic)] +#[diag("exists: {$sub}")] +struct VariableExists { + sub: String, +} + +#[derive(Diagnostic)] +#[diag("does not exist: {$nosub}")] +//~^ ERROR Variable `nosub` not found in diagnostic +struct VariableDoesNotExist { + sub: String, +} diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr index bec07a425762..2ba307940280 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr @@ -508,6 +508,14 @@ LL | #[suggestion("with a suggestion", code = "")] = help: to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]` = help: to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]` +error: derive(Diagnostic): Variable `nosub` not found in diagnostic + --> $DIR/diagnostic-derive-inline.rs:773:8 + | +LL | #[diag("does not exist: {$nosub}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Available fields: "sub" + error: cannot find attribute `nonsense` in this scope --> $DIR/diagnostic-derive-inline.rs:61:3 | @@ -622,6 +630,6 @@ 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 79 previous errors +error: aborting due to 80 previous errors For more information about this error, try `rustc --explain E0277`. From 16b79b3310619ea5a31cc73e6b501102e6e55f3b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 1 Feb 2026 13:58:01 +0100 Subject: [PATCH 475/583] fix: Fix upvar analysis of nested closures --- .../crates/hir-def/src/expr_store.rs | 4 +- .../hir-ty/src/tests/closure_captures.rs | 82 ++++++++++++++----- .../src/handlers/mutability_errors.rs | 30 +++---- .../src/handlers/unused_variables.rs | 42 +++++----- 4 files changed, 92 insertions(+), 66 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index edbfd42d1314..1ce4c881e7ea 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -474,8 +474,8 @@ impl ExpressionStore { match expr_only.binding_owners.get(&binding) { Some(it) => { // We assign expression ids in a way that outer closures will receive - // a lower id - it.into_raw() < relative_to.into_raw() + // a higher id (allocated after their body is collected) + it.into_raw() > relative_to.into_raw() } None => true, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 8408c0a7bfcd..f089120cd7b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -135,7 +135,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec fn deref_in_let() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let b = *a; }; @@ -149,7 +149,7 @@ fn main() { fn deref_then_ref_pattern() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let &mut ref b = a; }; @@ -159,7 +159,7 @@ fn main() { ); check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let &mut ref mut b = a; }; @@ -173,7 +173,7 @@ fn main() { fn unique_borrow() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { *a = false; }; @@ -187,7 +187,7 @@ fn main() { fn deref_ref_mut() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let ref mut b = *a; }; @@ -201,7 +201,7 @@ fn main() { fn let_else_not_consuming() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let _ = *a else { return; }; }; @@ -215,7 +215,7 @@ fn main() { fn consume() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let a = NonCopy; @@ -230,7 +230,7 @@ fn main() { fn ref_to_upvar() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = NonCopy; @@ -248,7 +248,7 @@ fn main() { fn field() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct Foo { a: i32, b: i32 } fn main() { let a = Foo { a: 0, b: 0 }; @@ -263,7 +263,7 @@ fn main() { fn fields_different_mode() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; struct Foo { a: i32, b: i32, c: NonCopy, d: bool } fn main() { @@ -286,7 +286,7 @@ fn main() { fn autoref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct Foo; impl Foo { fn imm(&self) {} @@ -308,7 +308,7 @@ fn main() { fn captures_priority() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = &mut true; @@ -336,7 +336,7 @@ fn main() { fn let_underscore() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = true; let closure = || { let _ = a; }; @@ -350,7 +350,7 @@ fn main() { fn match_wildcard() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = NonCopy; @@ -375,7 +375,7 @@ fn main() { fn multiple_bindings() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { let (b | b) = a; }; @@ -389,7 +389,7 @@ fn main() { fn multiple_usages() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { @@ -410,7 +410,7 @@ fn main() { fn ref_then_deref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { let b = *&mut a; }; @@ -424,7 +424,7 @@ fn main() { fn ref_of_ref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = &false; let closure = || { let b = &a; }; @@ -446,7 +446,7 @@ fn main() { fn multiple_capture_usages() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct A { a: i32, b: bool } fn main() { let mut a = A { a: 123, b: false }; @@ -465,7 +465,7 @@ fn main() { fn let_binding_is_a_ref_capture_in_ref_binding() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct S; fn main() { let mut s = S; @@ -489,7 +489,7 @@ fn main() { fn let_binding_is_a_value_capture_in_binding() { check_closure_captures( r#" -//- minicore:copy, option +//- minicore:copy, fn, option struct Box(i32); fn main() { let b = Some(Box(0)); @@ -508,7 +508,7 @@ fn main() { fn alias_needs_to_be_normalized() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn trait Trait { type Associated; } @@ -528,3 +528,41 @@ fn main() { expect!["220..257;174..175;245..250 ByRef(Shared) c.b.x &'? i32"], ); } + +#[test] +fn nested_ref_captures_from_outer() { + check_closure_captures( + r#" +//- minicore:copy, fn +fn f() { + let a = 1; + let a_closure = || { + let b_closure = || { + { a }; + }; + }; +} +"#, + expect![[r#" + 44..113;17..18;92..93 ByRef(Shared) a &'? i32 + 73..106;17..18;92..93 ByRef(Shared) a &'? i32"#]], + ); +} + +#[test] +fn nested_ref_captures() { + check_closure_captures( + r#" +//- minicore:copy, fn +fn f() { + let a_closure = || { + let b = 2; + let b_closure = || { + { b }; + }; + }; +} +"#, + expect!["77..110;46..47;96..97 ByRef(Shared) b &'? i32"], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index e3cfbdfb515f..18280a4addec 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1,5 +1,3 @@ -#![expect(unused, reason = "diagnostics is temporarily disabled due to too many false positives")] - use hir::db::ExpandDatabase; use ide_db::source_change::SourceChange; use ide_db::text_edit::TextEdit; @@ -90,17 +88,16 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - // Some( - // Diagnostic::new_with_syntax_node_ptr( - // ctx, - // DiagnosticCode::RustcLint("unused_mut"), - // "variable does not need to be mutable", - // ast, - // ) - // // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. - // .with_fixes(fixes), - // ) - None + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_mut"), + "variable does not need to be mutable", + ast, + ) + // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. + .with_fixes(fixes), + ) } pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { @@ -108,7 +105,6 @@ pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option u8) -> u8 { } "#, ); - // FIXME: There should be no "unused variable" here, and there should be a mutability error, - // but our MIR infra is horribly broken and due to the order in which expressions are lowered - // there is no `StorageLive` for `x` in the closure (in fact, `x` should not even be a variable - // of the closure, the environment should be, but as I said, our MIR infra is horribly broken). check_diagnostics( r#" //- minicore: copy, fn @@ -1011,8 +1003,8 @@ fn f() { || { || { let x = 2; - // ^ 💡 warn: unused variable || { || { x = 5; } } + //^^^^^ 💡 error: cannot mutate immutable variable `x` } } }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index ff558d067061..52a2f44fd0f8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -1,5 +1,3 @@ -#![expect(unused, reason = "diagnostics is temporarily disabled due to too many false positives")] - use hir::Name; use ide_db::text_edit::TextEdit; use ide_db::{ @@ -42,26 +40,25 @@ pub(crate) fn unused_variables( .and_then(syntax::ast::RecordPatField::cast) .is_some_and(|field| field.colon_token().is_none()); let var_name = d.local.name(ctx.sema.db); - // Some( - // Diagnostic::new_with_syntax_node_ptr( - // ctx, - // DiagnosticCode::RustcLint("unused_variables"), - // "unused variable", - // ast, - // ) - // .with_fixes(name_range.and_then(|it| { - // fixes( - // ctx.sema.db, - // var_name, - // it.range, - // diagnostic_range, - // ast.file_id.is_macro(), - // is_shorthand_field, - // ctx.edition, - // ) - // })), - // ) - None + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_variables"), + "unused variable", + ast, + ) + .with_fixes(name_range.and_then(|it| { + fixes( + ctx.sema.db, + var_name, + it.range, + diagnostic_range, + ast.file_id.is_macro(), + is_shorthand_field, + ctx.edition, + ) + })), + ) } fn fixes( @@ -94,7 +91,6 @@ fn fixes( } #[cfg(test)] -#[cfg(false)] // Diagnostic temporarily disabled mod tests { use crate::tests::{check_diagnostics, check_fix}; From 2bab7a02f2caf77f53637838ef3cbf67bb718f6f Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 31 Jan 2026 02:06:47 +0000 Subject: [PATCH 476/583] Handle unbalanced delimiters gracefully in make_attr_token_stream --- compiler/rustc_ast/src/tokenstream.rs | 8 +- .../ui/macros/tokenstream-ice-issue-149954.rs | 22 ++++ .../tokenstream-ice-issue-149954.stderr | 110 ++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 tests/ui/macros/tokenstream-ice-issue-149954.rs create mode 100644 tests/ui/macros/tokenstream-ice-issue-149954.stderr diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index e346a56bcf40..8d8e8ffc562f 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -354,7 +354,13 @@ fn make_attr_token_stream( FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] }, )); } else if let Some(delim) = kind.close_delim() { - let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); + // If there's no matching opening delimiter, the token stream is malformed, + // likely due to a improper delimiter positions in the source code. + // It's not delimiter mismatch, and lexer can not detect it, so we just ignore it here. + let Some(frame) = stack_rest.pop() else { + return AttrTokenStream::new(stack_top.inner); + }; + let frame_data = mem::replace(&mut stack_top, frame); let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); assert!( open_delim.eq_ignoring_invisible_origin(&delim), diff --git a/tests/ui/macros/tokenstream-ice-issue-149954.rs b/tests/ui/macros/tokenstream-ice-issue-149954.rs new file mode 100644 index 000000000000..958a86cbde8b --- /dev/null +++ b/tests/ui/macros/tokenstream-ice-issue-149954.rs @@ -0,0 +1,22 @@ +// Regression test for ICE https://github.com/rust-lang/rust/issues/149954 +//@ edition: 2024 + +enum A { + A + const A: A = { //~ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found keyword `const` + #[derive(Debug)] + struct A + where + A: A<{ struct A> ; enum A } + //~^ ERROR malformed `cfg` attribute input + //~| ERROR malformed `cfg` attribute input + //~| ERROR expected trait, found struct `A` + //~| ERROR expected trait, found type parameter `A` + //~| ERROR expected trait, found struct `A` + //~| ERROR expected trait, found type parameter `A` + //~| ERROR expected one of `<`, `where`, or `{`, found `}` + //~| ERROR expected one of `<`, `where`, or `{`, found `}` + //~| ERROR expected one of `,`, `>`, or `}`, found `` + } + >; +}; //~ ERROR `main` function not found in crate diff --git a/tests/ui/macros/tokenstream-ice-issue-149954.stderr b/tests/ui/macros/tokenstream-ice-issue-149954.stderr new file mode 100644 index 000000000000..750f3efcc612 --- /dev/null +++ b/tests/ui/macros/tokenstream-ice-issue-149954.stderr @@ -0,0 +1,110 @@ +error: expected one of `(`, `,`, `=`, `{`, or `}`, found keyword `const` + --> $DIR/tokenstream-ice-issue-149954.rs:6:5 + | +LL | A + | - expected one of `(`, `,`, `=`, `{`, or `}` +LL | const A: A = { + | ^^^^^ unexpected token + | + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + +error: expected one of `<`, `where`, or `{`, found `}` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | - ^ expected one of `<`, `where`, or `{` + | | + | while parsing this enum + +error: expected one of `<`, `where`, or `{`, found `}` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | - ^ expected one of `<`, `where`, or `{` + | | + | while parsing this enum + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected one of `,`, `>`, or `}`, found `` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | ^ expected one of `,`, `>`, or `}` + | +help: you might have meant to end the type parameters here + | +LL | A: A<{ struct A> ; enum A }> + | + + +error[E0539]: malformed `cfg` attribute input + --> $DIR/tokenstream-ice-issue-149954.rs:10:36 + | +LL | A: A<{ struct A> ; enum A } + | ^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/tokenstream-ice-issue-149954.rs:10:36 + | +LL | A: A<{ struct A> ; enum A } + | ^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0404]: expected trait, found struct `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:16 + | +LL | A: A<{ struct A> ; enum A } + | ________________^ +... | +LL | | >; + | |_________^ not a trait + +error[E0404]: expected trait, found type parameter `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:32 + | +LL | A: A<{ struct A> ; enum A } + | - ^^^^^^^^^^^^^^^^ not a trait + | | + | found this type parameter + +error[E0404]: expected trait, found struct `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:16 + | +LL | A: A<{ struct A> ; enum A } + | ________________^ +... | +LL | | >; + | |_________^ not a trait + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0404]: expected trait, found type parameter `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:32 + | +LL | A: A<{ struct A> ; enum A } + | - ^^^^^^^^^^^^^^^^ not a trait + | | + | found this type parameter + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0601]: `main` function not found in crate `tokenstream_ice_issue_149954` + --> $DIR/tokenstream-ice-issue-149954.rs:22:3 + | +LL | }; + | ^ consider adding a `main` function to `$DIR/tokenstream-ice-issue-149954.rs` + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0404, E0539, E0601. +For more information about an error, try `rustc --explain E0404`. From 55e3d2206a21aeb291cbaf6535579e2994d98d4a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 1 Feb 2026 15:05:57 +0100 Subject: [PATCH 477/583] Improve new proc-macro methods name --- library/proc_macro/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 2a75c4489095..18ab470f89d6 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1445,9 +1445,9 @@ impl Literal { }) } - /// Returns the unescaped char value if the current literal is a char. + /// Returns the unescaped character value if the current literal is a byte character literal. #[unstable(feature = "proc_macro_value", issue = "136652")] - pub fn byte_value(&self) -> Result { + pub fn byte_character_value(&self) -> Result { self.0.symbol.with(|symbol| match self.0.kind { bridge::LitKind::Char => { unescape_byte(symbol).map_err(ConversionErrorKind::FailedToUnescape) @@ -1456,9 +1456,9 @@ impl Literal { }) } - /// Returns the unescaped char value if the current literal is a char. + /// Returns the unescaped character value if the current literal is a character literal. #[unstable(feature = "proc_macro_value", issue = "136652")] - pub fn char_value(&self) -> Result { + pub fn character_value(&self) -> Result { self.0.symbol.with(|symbol| match self.0.kind { bridge::LitKind::Char => { unescape_char(symbol).map_err(ConversionErrorKind::FailedToUnescape) From 990c55801ec129a1781c1b3915ff74bb85f9cdf1 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sun, 1 Feb 2026 19:17:28 +0800 Subject: [PATCH 478/583] Include assoc const projections in CFI trait object --- .../cfi/typeid/itanium_cxx_abi/transform.rs | 24 +++++++++---------- .../assoc-const-projection-issue-151878.rs | 20 ++++++++++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 9cea681fcb57..971ac9348fc4 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -243,24 +243,24 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc .flat_map(|super_poly_trait_ref| { tcx.associated_items(super_poly_trait_ref.def_id()) .in_definition_order() - .filter(|item| item.is_type()) + .filter(|item| item.is_type() || item.is_const()) .filter(|item| !tcx.generics_require_sized_self(item.def_id)) - .map(move |assoc_ty| { + .map(move |assoc_item| { super_poly_trait_ref.map_bound(|super_trait_ref| { - let alias_ty = - ty::AliasTy::new_from_args(tcx, assoc_ty.def_id, super_trait_ref.args); - let resolved = tcx.normalize_erasing_regions( - ty::TypingEnv::fully_monomorphized(), - alias_ty.to_ty(tcx), + let projection_term = ty::AliasTerm::new_from_args( + tcx, + assoc_item.def_id, + super_trait_ref.args, ); - debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); + let term = tcx.normalize_erasing_regions( + ty::TypingEnv::fully_monomorphized(), + projection_term.to_term(tcx), + ); + debug!("Projection {:?} -> {term}", projection_term.to_term(tcx),); ty::ExistentialPredicate::Projection( ty::ExistentialProjection::erase_self_ty( tcx, - ty::ProjectionPredicate { - projection_term: alias_ty.into(), - term: resolved.into(), - }, + ty::ProjectionPredicate { projection_term, term }, ), ) }) diff --git a/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs b/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs new file mode 100644 index 000000000000..50530e4f0db0 --- /dev/null +++ b/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Zsanitizer=cfi -Cunsafe-allow-abi-mismatch=sanitizer -Ccodegen-units=1 -Clto +//@ needs-rustc-debug-assertions +//@ needs-sanitizer-cfi +//@ build-pass +//@ no-prefer-dynamic + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const N: usize = 0; + fn process(&self, _: [u8; Self::N]) {} +} + +impl Trait for () {} + +fn main() { + let _x: &dyn Trait = &(); +} From 2292d53b7b5dc3989167e753d3ac4422b321b426 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Sun, 1 Feb 2026 21:41:43 +0530 Subject: [PATCH 479/583] Add codegen test for SLP vectorization --- tests/codegen-llvm/slp-vectorization-mul3.rs | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/codegen-llvm/slp-vectorization-mul3.rs diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs new file mode 100644 index 000000000000..a414192e6a1a --- /dev/null +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -0,0 +1,23 @@ +// compile-flags: -O +#![crate_type = "lib"] + +// CHECK-LABEL: mul3 +// CHECK: %[[C_BPP:.*]] = phi <4 x i8> +// CHECK: %[[V:.*]] = load <4 x i8>, ptr +// CHECK: %[[ADD:.*]] = add <4 x i8> %[[V]], %[[C_BPP]] +// CHECK: store {{<4 x i8>|i32}} {{.*}}, ptr + +pub fn mul3(previous: &[u8], current: &mut [u8]) { + let mut c_bpp = [0u8; 4]; + + for (chunk, b_bpp) in current.chunks_exact_mut(4).zip(previous.chunks_exact(4)) { + let new_chunk = [ + chunk[0].wrapping_add(c_bpp[0]), + chunk[1].wrapping_add(c_bpp[1]), + chunk[2].wrapping_add(c_bpp[2]), + chunk[3].wrapping_add(c_bpp[3]), + ]; + chunk.copy_from_slice(&new_chunk); + c_bpp.copy_from_slice(b_bpp); + } +} \ No newline at end of file From 0a60bd653d3bd42752e4e454567ae5fcb96a2b49 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Sun, 1 Feb 2026 22:09:05 +0530 Subject: [PATCH 480/583] fix: remove trailing newline for tidy --- tests/codegen-llvm/slp-vectorization-mul3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index a414192e6a1a..7a69be5cafb7 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -20,4 +20,4 @@ pub fn mul3(previous: &[u8], current: &mut [u8]) { chunk.copy_from_slice(&new_chunk); c_bpp.copy_from_slice(b_bpp); } -} \ No newline at end of file +} From 1c396d24ddf97d556d41487234f6216b67461b7c Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Sun, 1 Feb 2026 22:14:13 +0530 Subject: [PATCH 481/583] Restrict test to x86_64 per reviewer feedback --- tests/codegen-llvm/slp-vectorization-mul3.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index 7a69be5cafb7..bb4965c46bc0 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -1,4 +1,6 @@ -// compile-flags: -O +//@ only-x86_64 +//@ compile-flags: -O + #![crate_type = "lib"] // CHECK-LABEL: mul3 From caaee92855a64ae1f1e6cf93ce7ad29f1bd12241 Mon Sep 17 00:00:00 2001 From: Joshua Rayton Date: Tue, 23 Sep 2025 15:31:58 +0100 Subject: [PATCH 482/583] more float constants: sqrt(5), 1/sqrt(5) --- library/core/src/num/f128.rs | 11 +++++++++++ library/core/src/num/f16.rs | 10 ++++++++++ library/core/src/num/f32.rs | 8 ++++++++ library/core/src/num/f64.rs | 8 ++++++++ 4 files changed, 37 insertions(+) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index cc142fab8e82..4630fd729c6a 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -108,6 +108,17 @@ pub mod consts { pub const FRAC_1_SQRT_3: f128 = 0.577350269189625764509148780501957455647601751270126876018602_f128; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f128", issue = "116909")] + pub const SQRT_5: f128 = 2.23606797749978969640917366873127623544061835961152572427089_f128; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f128", issue = "116909")] + pub const FRAC_1_SQRT_5: f128 = + 0.447213595499957939281834733746255247088123671922305144854179_f128; + /// Euler's number (e) #[unstable(feature = "f128", issue = "116909")] pub const E: f128 = 2.71828182845904523536028747135266249775724709369995957496697_f128; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e97a44e991f6..21ba8f1f2828 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -103,6 +103,16 @@ pub mod consts { // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f16 = 0.577350269189625764509148780501957456_f16; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f16", issue = "116909")] + pub const SQRT_5: f16 = 2.23606797749978969640917366873127623_f16; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f16", issue = "116909")] + pub const FRAC_1_SQRT_5: f16 = 0.44721359549995793928183473374625524_f16; + /// Euler's number (e) #[unstable(feature = "f16", issue = "116909")] pub const E: f16 = 2.71828182845904523536028747135266250_f16; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 3d8249631037..c4ec774f5956 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -356,6 +356,14 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const SQRT_5: f32 = 2.23606797749978969640917366873127623_f32; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const FRAC_1_SQRT_5: f32 = 0.44721359549995793928183473374625524_f32; + /// Euler's number (e) #[stable(feature = "rust1", since = "1.0.0")] pub const E: f32 = 2.71828182845904523536028747135266250_f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 566a6a7ec947..0bb881323577 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -356,6 +356,14 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f64 = 0.577350269189625764509148780501957456_f64; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const SQRT_5: f64 = 2.23606797749978969640917366873127623_f64; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const FRAC_1_SQRT_5: f64 = 0.44721359549995793928183473374625524_f64; + /// Euler's number (e) #[stable(feature = "rust1", since = "1.0.0")] pub const E: f64 = 2.71828182845904523536028747135266250_f64; From f490f6420bbc7055e212e96c1745b44436a017e3 Mon Sep 17 00:00:00 2001 From: Maarten Craeynest Date: Sun, 1 Feb 2026 21:51:27 +0100 Subject: [PATCH 483/583] Add regression test for issue-136514: Negative literal in unsigned range --- .../range-negative-literal-unsigned-type.rs | 21 +++ ...ange-negative-literal-unsigned-type.stderr | 122 ++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 tests/ui/range/range-negative-literal-unsigned-type.rs create mode 100644 tests/ui/range/range-negative-literal-unsigned-type.stderr diff --git a/tests/ui/range/range-negative-literal-unsigned-type.rs b/tests/ui/range/range-negative-literal-unsigned-type.rs new file mode 100644 index 000000000000..b6152abb340e --- /dev/null +++ b/tests/ui/range/range-negative-literal-unsigned-type.rs @@ -0,0 +1,21 @@ +// Regression tests for: https://github.com/rust-lang/rust/issues/136514 + +#![allow(unreachable_patterns)] +fn main() { + match 0u8 { + -1..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -0..=0 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -256..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -255..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + 0..=-1 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -2..=-1 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + //~| ERROR the trait bound `u8: Neg` is not satisfied + _ => {} + } +} diff --git a/tests/ui/range/range-negative-literal-unsigned-type.stderr b/tests/ui/range/range-negative-literal-unsigned-type.stderr new file mode 100644 index 000000000000..7ca14c3d7790 --- /dev/null +++ b/tests/ui/range/range-negative-literal-unsigned-type.stderr @@ -0,0 +1,122 @@ +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:6:9 + | +LL | -1..=2 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:8:9 + | +LL | -0..=0 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:10:9 + | +LL | -256..=2 => {} + | ^^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:12:9 + | +LL | -255..=2 => {} + | ^^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:14:13 + | +LL | 0..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:16:9 + | +LL | -2..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:16:14 + | +LL | -2..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0277`. From cd1c7736614dcde9fe19961c83d935adedf98662 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Jan 2026 14:45:35 +1100 Subject: [PATCH 484/583] Remove `lift_query_info`. It doesn't use `self`, which means it doesn't need to be part of the `QueryContext` trait; we can just call `extract` directly where necessary. --- compiler/rustc_query_impl/src/plumbing.rs | 12 +----------- compiler/rustc_query_system/src/query/job.rs | 9 +++------ compiler/rustc_query_system/src/query/mod.rs | 6 ++---- compiler/rustc_query_system/src/query/plumbing.rs | 10 +++++----- 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 2d4e10a0380c..da11623d77b7 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -104,13 +104,6 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { if complete { Ok(jobs) } else { Err(jobs) } } - fn lift_query_info( - self, - info: &QueryStackDeferred<'tcx>, - ) -> rustc_query_system::query::QueryStackFrameExtra { - info.extract() - } - // Interactions with on_disk_cache fn load_side_effect( self, @@ -174,10 +167,7 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { self.tcx.sess.dcx().emit_fatal(QueryOverflow { span: info.job.span, - note: QueryOverflowNote { - desc: self.lift_query_info(&info.query.info).description, - depth, - }, + note: QueryOverflowNote { desc: info.query.info.extract().description, depth }, suggested_limit, crate_name: self.tcx.crate_name(LOCAL_CRATE), }); diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 177bcd63cbc6..3affb23d3022 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -27,11 +27,8 @@ pub struct QueryInfo { } impl<'tcx> QueryInfo> { - pub(crate) fn lift>( - &self, - qcx: Qcx, - ) -> QueryInfo { - QueryInfo { span: self.span, query: self.query.lift(qcx) } + pub(crate) fn lift(&self) -> QueryInfo { + QueryInfo { span: self.span, query: self.query.lift() } } } @@ -628,7 +625,7 @@ pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( let Some(query_info) = query_map.get(&query) else { break; }; - let query_extra = qcx.lift_query_info(&query_info.query.info); + let query_extra = query_info.query.info.extract(); if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. dcx.struct_failure_note(format!( diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 63202429679d..e40dcf2daf35 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -70,9 +70,9 @@ impl<'tcx> QueryStackFrame> { Self { info, def_id, dep_kind, hash, def_id_for_ty_in_cycle } } - fn lift>(&self, qcx: Qcx) -> QueryStackFrame { + fn lift(&self) -> QueryStackFrame { QueryStackFrame { - info: qcx.lift_query_info(&self.info), + info: self.info.extract(), dep_kind: self.dep_kind, hash: self.hash, def_id: self.def_id, @@ -168,8 +168,6 @@ pub trait QueryContext<'tcx>: HasDepContext { fn collect_active_jobs(self, require_complete: bool) -> Result, QueryMap<'tcx>>; - fn lift_query_info(self, info: &QueryStackDeferred<'tcx>) -> QueryStackFrameExtra; - /// Load a side effect associated to the node in the previous session. fn load_side_effect( self, diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 98bbd3ebc4a0..04b62daa86b4 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -253,10 +253,10 @@ pub struct CycleError { } impl<'tcx> CycleError> { - fn lift>(&self, qcx: Qcx) -> CycleError { + fn lift(&self) -> CycleError { CycleError { - usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift(qcx))), - cycle: self.cycle.iter().map(|info| info.lift(qcx)).collect(), + usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift())), + cycle: self.cycle.iter().map(|info| info.lift()).collect(), } } } @@ -297,7 +297,7 @@ where let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries"); let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); - (mk_cycle(query, qcx, error.lift(qcx)), None) + (mk_cycle(query, qcx, error.lift()), None) } #[inline(always)] @@ -345,7 +345,7 @@ where (v, Some(index)) } - Err(cycle) => (mk_cycle(query, qcx, cycle.lift(qcx)), None), + Err(cycle) => (mk_cycle(query, qcx, cycle.lift()), None), } } From 4ff360e997eaf57005454eaa8dc6eff2b820d425 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Jan 2026 14:50:45 +1100 Subject: [PATCH 485/583] Rename some query-related things. Various `QueryStackFrame` variables are called `query`; `frame` is a better name. And various `QueryInfo` variables are called `frame`; `info` is a better name. This eliminates some confusing `query.query()` occurrences, which is a good sign, and some `frame.query` occurrences become `info.frame`. --- compiler/rustc_middle/src/values.rs | 34 +++++++------- compiler/rustc_query_impl/src/plumbing.rs | 6 +-- compiler/rustc_query_system/src/query/job.rs | 44 +++++++++---------- .../rustc_query_system/src/query/plumbing.rs | 10 ++--- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index bc73d36216ef..fd08b5a972c6 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -50,9 +50,9 @@ impl<'tcx> Value> for ty::Binder<'_, ty::FnSig<'_>> { ) -> Self { let err = Ty::new_error(tcx, guar); - let arity = if let Some(frame) = cycle_error.cycle.get(0) - && frame.query.dep_kind == dep_kinds::fn_sig - && let Some(def_id) = frame.query.def_id + let arity = if let Some(info) = cycle_error.cycle.get(0) + && info.frame.dep_kind == dep_kinds::fn_sig + && let Some(def_id) = info.frame.def_id && let Some(node) = tcx.hir_get_if_local(def_id) && let Some(sig) = node.fn_sig() { @@ -85,10 +85,10 @@ impl<'tcx> Value> for Representability { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for info in &cycle_error.cycle { - if info.query.dep_kind == dep_kinds::representability - && let Some(field_id) = info.query.def_id + if info.frame.dep_kind == dep_kinds::representability + && let Some(field_id) = info.frame.def_id && let Some(field_id) = field_id.as_local() - && let Some(DefKind::Field) = info.query.info.def_kind + && let Some(DefKind::Field) = info.frame.info.def_kind { let parent_id = tcx.parent(field_id.to_def_id()); let item_id = match tcx.def_kind(parent_id) { @@ -99,8 +99,8 @@ impl<'tcx> Value> for Representability { } } for info in &cycle_error.cycle { - if info.query.dep_kind == dep_kinds::representability_adt_ty - && let Some(def_id) = info.query.def_id_for_ty_in_cycle + if info.frame.dep_kind == dep_kinds::representability_adt_ty + && let Some(def_id) = info.frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) { @@ -141,9 +141,9 @@ impl<'tcx> Value> for &[ty::Variance] { search_for_cycle_permutation( &cycle_error.cycle, |cycle| { - if let Some(frame) = cycle.get(0) - && frame.query.dep_kind == dep_kinds::variances_of - && let Some(def_id) = frame.query.def_id + if let Some(info) = cycle.get(0) + && info.frame.dep_kind == dep_kinds::variances_of + && let Some(def_id) = info.frame.def_id { let n = tcx.generics_of(def_id).own_params.len(); ControlFlow::Break(vec![ty::Bivariant; n].leak()) @@ -189,8 +189,8 @@ impl<'tcx, T> Value> for Result> let diag = search_for_cycle_permutation( &cycle_error.cycle, |cycle| { - if cycle[0].query.dep_kind == dep_kinds::layout_of - && let Some(def_id) = cycle[0].query.def_id_for_ty_in_cycle + if cycle[0].frame.dep_kind == dep_kinds::layout_of + && let Some(def_id) = cycle[0].frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && let def_kind = tcx.def_kind(def_id) && matches!(def_kind, DefKind::Closure) @@ -213,18 +213,18 @@ impl<'tcx, T> Value> for Result> tcx.def_kind_descr_article(def_kind, def_id.to_def_id()), tcx.def_kind_descr(def_kind, def_id.to_def_id()), ); - for (i, frame) in cycle.iter().enumerate() { - if frame.query.dep_kind != dep_kinds::layout_of { + for (i, info) in cycle.iter().enumerate() { + if info.frame.dep_kind != dep_kinds::layout_of { continue; } - let Some(frame_def_id) = frame.query.def_id_for_ty_in_cycle else { + let Some(frame_def_id) = info.frame.def_id_for_ty_in_cycle else { continue; }; let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { continue; }; let frame_span = - frame.query.info.default_span(cycle[(i + 1) % cycle.len()].span); + info.frame.info.default_span(cycle[(i + 1) % cycle.len()].span); if frame_span.is_dummy() { continue; } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index da11623d77b7..b7ce8587695b 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -167,7 +167,7 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { self.tcx.sess.dcx().emit_fatal(QueryOverflow { span: info.job.span, - note: QueryOverflowNote { desc: info.query.info.extract().description, depth }, + note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, suggested_limit, crate_name: self.tcx.crate_name(LOCAL_CRATE), }); @@ -728,14 +728,14 @@ macro_rules! define_queries { qmap: &mut QueryMap<'tcx>, require_complete: bool, ) -> Option<()> { - let make_query = |tcx, key| { + let make_frame = |tcx, key| { let kind = rustc_middle::dep_graph::dep_kinds::$name; let name = stringify!($name); $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) }; let res = tcx.query_system.states.$name.collect_active_jobs( tcx, - make_query, + make_frame, qmap, require_complete, ); diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 3affb23d3022..82b23b022c37 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -23,12 +23,12 @@ use crate::query::{QueryContext, QueryStackFrame}; pub struct QueryInfo { /// The span corresponding to the reason for which this query was required. pub span: Span, - pub query: QueryStackFrame, + pub frame: QueryStackFrame, } impl<'tcx> QueryInfo> { pub(crate) fn lift(&self) -> QueryInfo { - QueryInfo { span: self.span, query: self.query.lift() } + QueryInfo { span: self.span, frame: self.frame.lift() } } } @@ -39,8 +39,8 @@ pub type QueryMap<'tcx> = FxHashMap>; pub struct QueryJobId(pub NonZero); impl QueryJobId { - fn query<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame> { - map.get(&self).unwrap().query.clone() + fn frame<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame> { + map.get(&self).unwrap().frame.clone() } fn span<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Span { @@ -58,7 +58,7 @@ impl QueryJobId { #[derive(Clone, Debug)] pub struct QueryJobInfo<'tcx> { - pub query: QueryStackFrame>, + pub frame: QueryStackFrame>, pub job: QueryJob<'tcx>, } @@ -122,7 +122,7 @@ impl QueryJobId { while let Some(job) = current_job { let info = query_map.get(&job).unwrap(); - cycle.push(QueryInfo { span: info.job.span, query: info.query.clone() }); + cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() }); if job == *self { cycle.reverse(); @@ -137,7 +137,7 @@ impl QueryJobId { .job .parent .as_ref() - .map(|parent| (info.job.span, parent.query(&query_map))); + .map(|parent| (info.job.span, parent.frame(&query_map))); return CycleError { usage, cycle }; } @@ -155,13 +155,13 @@ impl QueryJobId { ) -> (QueryJobInfo<'tcx>, usize) { let mut depth = 1; let info = query_map.get(&self).unwrap(); - let dep_kind = info.query.dep_kind; + let dep_kind = info.frame.dep_kind; let mut current_id = info.job.parent; let mut last_layout = (info.clone(), depth); while let Some(id) = current_id { let info = query_map.get(&id).unwrap(); - if info.query.dep_kind == dep_kind { + if info.frame.dep_kind == dep_kind { depth += 1; last_layout = (info.clone(), depth); } @@ -386,7 +386,7 @@ where .iter() .min_by_key(|v| { let (span, query) = f(v); - let hash = query.query(query_map).hash; + let hash = query.frame(query_map).hash; // Prefer entry points which have valid spans for nicer error messages // We add an integer to the tuple ensuring that entry points // with valid spans are picked first @@ -470,14 +470,14 @@ fn remove_cycle<'tcx>( stack.rotate_left(pos); } - let usage = usage.as_ref().map(|(span, query)| (*span, query.query(query_map))); + let usage = usage.as_ref().map(|(span, query)| (*span, query.frame(query_map))); // Create the cycle error let error = CycleError { usage, cycle: stack .iter() - .map(|&(s, ref q)| QueryInfo { span: s, query: q.query(query_map) }) + .map(|&(s, ref q)| QueryInfo { span: s, frame: q.frame(query_map) }) .collect(), }; @@ -556,7 +556,7 @@ pub fn report_cycle<'a>( ) -> Diag<'a> { assert!(!stack.is_empty()); - let span = stack[0].query.info.default_span(stack[1 % stack.len()].span); + let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span); let mut cycle_stack = Vec::new(); @@ -564,9 +564,9 @@ pub fn report_cycle<'a>( let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; for i in 1..stack.len() { - let query = &stack[i].query; - let span = query.info.default_span(stack[(i + 1) % stack.len()].span); - cycle_stack.push(CycleStack { span, desc: query.info.description.to_owned() }); + let frame = &stack[i].frame; + let span = frame.info.default_span(stack[(i + 1) % stack.len()].span); + cycle_stack.push(CycleStack { span, desc: frame.info.description.to_owned() }); } let mut cycle_usage = None; @@ -578,9 +578,9 @@ pub fn report_cycle<'a>( } let alias = - if stack.iter().all(|entry| matches!(entry.query.info.def_kind, Some(DefKind::TyAlias))) { + if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) { Some(crate::error::Alias::Ty) - } else if stack.iter().all(|entry| entry.query.info.def_kind == Some(DefKind::TraitAlias)) { + } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) { Some(crate::error::Alias::Trait) } else { None @@ -589,7 +589,7 @@ pub fn report_cycle<'a>( let cycle_diag = crate::error::Cycle { span, cycle_stack, - stack_bottom: stack[0].query.info.description.to_owned(), + stack_bottom: stack[0].frame.info.description.to_owned(), alias, cycle_usage, stack_count, @@ -625,12 +625,12 @@ pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( let Some(query_info) = query_map.get(&query) else { break; }; - let query_extra = query_info.query.info.extract(); + let query_extra = query_info.frame.info.extract(); if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. dcx.struct_failure_note(format!( "#{} [{:?}] {}", - count_printed, query_info.query.dep_kind, query_extra.description + count_printed, query_info.frame.dep_kind, query_extra.description )) .with_span(query_info.job.span) .emit(); @@ -642,7 +642,7 @@ pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( file, "#{} [{}] {}", count_total, - qcx.dep_context().dep_kind_vtable(query_info.query.dep_kind).name, + qcx.dep_context().dep_kind_vtable(query_info.frame.dep_kind).name, query_extra.description ); } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 04b62daa86b4..25f96315cd13 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -82,7 +82,7 @@ where pub fn collect_active_jobs( &self, qcx: Qcx, - make_query: fn(Qcx, K) -> QueryStackFrame>, + make_frame: fn(Qcx, K) -> QueryStackFrame>, jobs: &mut QueryMap<'tcx>, require_complete: bool, ) -> Option<()> { @@ -108,11 +108,11 @@ where } } - // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call + // Call `make_frame` while we're not holding a `self.active` lock as `make_frame` may call // queries leading to a deadlock. for (key, job) in active { - let query = make_query(qcx, key); - jobs.insert(job.id, QueryJobInfo { query, job }); + let frame = make_frame(qcx, key); + jobs.insert(job.id, QueryJobInfo { frame, job }); } Some(()) @@ -170,7 +170,7 @@ where } CycleErrorHandling::Stash => { let guar = if let Some(root) = cycle_error.cycle.first() - && let Some(span) = root.query.info.span + && let Some(span) = root.frame.info.span { error.stash(span, StashKey::Cycle).unwrap() } else { From 8e2c9c69c6279c0a63833163d622e4a55df07b3f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Jan 2026 15:14:41 +1100 Subject: [PATCH 486/583] Eliminate some `'a` lifetimes. Putting `+ 'tcx` on the `QueryDispatcher` trait lets a few other places be simplified. --- compiler/rustc_query_impl/src/lib.rs | 11 ++++------- compiler/rustc_query_system/src/query/dispatcher.rs | 6 +++--- compiler/rustc_query_system/src/query/plumbing.rs | 10 +++++----- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 4e79d0842da2..6e119bf92e2d 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -86,10 +86,7 @@ where } #[inline(always)] - fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState<'tcx, Self::Key> - where - QueryCtxt<'tcx>: 'a, - { + fn query_state(self, qcx: QueryCtxt<'tcx>) -> &'tcx QueryState<'tcx, Self::Key> { // Safety: // This is just manually doing the subfield referencing through pointer math. unsafe { @@ -100,7 +97,7 @@ where } #[inline(always)] - fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache { + fn query_cache(self, qcx: QueryCtxt<'tcx>) -> &'tcx Self::Cache { // Safety: // This is just manually doing the subfield referencing through pointer math. unsafe { @@ -216,12 +213,12 @@ trait QueryDispatcherUnerased<'tcx> { ) -> Self::UnerasedValue; } -pub fn query_system<'a>( +pub fn query_system<'tcx>( local_providers: Providers, extern_providers: ExternProviders, on_disk_cache: Option, incremental: bool, -) -> QuerySystem<'a> { +) -> QuerySystem<'tcx> { QuerySystem { states: Default::default(), arenas: Default::default(), diff --git a/compiler/rustc_query_system/src/query/dispatcher.rs b/compiler/rustc_query_system/src/query/dispatcher.rs index ac6c38dd7db5..bcd3d0322d07 100644 --- a/compiler/rustc_query_system/src/query/dispatcher.rs +++ b/compiler/rustc_query_system/src/query/dispatcher.rs @@ -25,7 +25,7 @@ type DepContextOf<'tcx, This: QueryDispatcher<'tcx>> = /// Those types are not visible from this `rustc_query_system` crate. /// /// "Dispatcher" should be understood as a near-synonym of "vtable". -pub trait QueryDispatcher<'tcx>: Copy { +pub trait QueryDispatcher<'tcx>: Copy + 'tcx { fn name(self) -> &'static str; /// Query context used by this dispatcher, i.e. `rustc_query_impl::QueryCtxt`. @@ -41,10 +41,10 @@ pub trait QueryDispatcher<'tcx>: Copy { fn format_value(self) -> fn(&Self::Value) -> String; // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_state<'a>(self, tcx: Self::Qcx) -> &'a QueryState<'tcx, Self::Key>; + fn query_state(self, tcx: Self::Qcx) -> &'tcx QueryState<'tcx, Self::Key>; // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_cache<'a>(self, tcx: Self::Qcx) -> &'a Self::Cache; + fn query_cache(self, tcx: Self::Qcx) -> &'tcx Self::Cache; fn will_cache_on_disk_for_key(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> bool; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 25f96315cd13..472839116107 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -127,11 +127,11 @@ impl<'tcx, K> Default for QueryState<'tcx, K> { /// A type representing the responsibility to execute the job in the `job` field. /// This will poison the relevant query if dropped. -struct JobOwner<'a, 'tcx, K> +struct JobOwner<'tcx, K> where K: Eq + Hash + Copy, { - state: &'a QueryState<'tcx, K>, + state: &'tcx QueryState<'tcx, K>, key: K, } @@ -181,7 +181,7 @@ where } } -impl<'a, 'tcx, K> JobOwner<'a, 'tcx, K> +impl<'tcx, K> JobOwner<'tcx, K> where K: Eq + Hash + Copy, { @@ -218,7 +218,7 @@ where } } -impl<'a, 'tcx, K> Drop for JobOwner<'a, 'tcx, K> +impl<'tcx, K> Drop for JobOwner<'tcx, K> where K: Eq + Hash + Copy, { @@ -422,7 +422,7 @@ where fn execute_job<'tcx, Q, const INCR: bool>( query: Q, qcx: Q::Qcx, - state: &QueryState<'tcx, Q::Key>, + state: &'tcx QueryState<'tcx, Q::Key>, key: Q::Key, key_hash: u64, id: QueryJobId, From c6afd45ac16e0a665ed6feb2d45cf2a4e2769643 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 2 Feb 2026 10:41:03 +1100 Subject: [PATCH 487/583] Move `depth_limit_error` out of `QueryContext` trait. It's defined and used in `rustc_query_impl`; `rustc_query_system` doesn't need it. So it can just be an inherent method on `QueryCtxt`. --- compiler/rustc_query_impl/src/plumbing.rs | 34 ++++++++++---------- compiler/rustc_query_system/src/query/mod.rs | 2 -- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index b7ce8587695b..0002e5950a33 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -48,6 +48,23 @@ impl<'tcx> QueryCtxt<'tcx> { pub fn new(tcx: TyCtxt<'tcx>) -> Self { QueryCtxt { tcx } } + + fn depth_limit_error(self, job: QueryJobId) { + let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); + let (info, depth) = job.find_dep_kind_root(query_map); + + let suggested_limit = match self.tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + + self.tcx.sess.dcx().emit_fatal(QueryOverflow { + span: info.job.span, + note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, + suggested_limit, + crate_name: self.tcx.crate_name(LOCAL_CRATE), + }); + } } impl<'tcx> HasDepContext for QueryCtxt<'tcx> { @@ -155,23 +172,6 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { tls::enter_context(&new_icx, compute) }) } - - fn depth_limit_error(self, job: QueryJobId) { - let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); - let (info, depth) = job.find_dep_kind_root(query_map); - - let suggested_limit = match self.tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - - self.tcx.sess.dcx().emit_fatal(QueryOverflow { - span: info.job.span, - note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, - suggested_limit, - crate_name: self.tcx.crate_name(LOCAL_CRATE), - }); - } } pub(super) fn try_mark_green<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool { diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index e40dcf2daf35..54e5fa4d7229 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -181,6 +181,4 @@ pub trait QueryContext<'tcx>: HasDepContext { /// new query job while it executes. fn start_query(self, token: QueryJobId, depth_limit: bool, compute: impl FnOnce() -> R) -> R; - - fn depth_limit_error(self, job: QueryJobId); } From f3b7a1a9d8e0beb8998f5b621b26d4942c423123 Mon Sep 17 00:00:00 2001 From: Carl Lundin Date: Sun, 1 Feb 2026 17:14:41 -0800 Subject: [PATCH 488/583] Update documentation for `Result::ok()` The term of "discard" is misleading. An error is not discarded but converted to an `Option::None`. --- library/core/src/result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 52f3d43dfd6d..5f438d72ac13 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -690,7 +690,7 @@ impl Result { /// Converts from `Result` to [`Option`]. /// /// Converts `self` into an [`Option`], consuming `self`, - /// and discarding the error, if any. + /// and converting the error to `None`, if any. /// /// # Examples /// From c64f9a0fc46fecb8ccb15d1ab1fcfd83ce94b5e5 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Mon, 2 Feb 2026 07:38:14 +0530 Subject: [PATCH 489/583] Add backlink to issue --- tests/codegen-llvm/slp-vectorization-mul3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index bb4965c46bc0..b6edc212c82d 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -1,3 +1,4 @@ +//! Regression test for #142519 //@ only-x86_64 //@ compile-flags: -O From 0cbbe56d84ef44715e4e12496d904848c0863968 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 2 Feb 2026 13:03:10 +1100 Subject: [PATCH 490/583] Work around rustfmt giving up on a large expression --- compiler/rustc_interface/src/util.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index b3889849430a..249368fd1194 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -231,7 +231,12 @@ pub(crate) fn run_in_thread_pool_with_globals< .name("rustc query cycle handler".to_string()) .spawn(move || { let on_panic = defer(|| { - eprintln!("internal compiler error: query cycle handler thread panicked, aborting process"); + // Split this long string so that it doesn't cause rustfmt to + // give up on the entire builder expression. + // + const MESSAGE: &str = "\ +internal compiler error: query cycle handler thread panicked, aborting process"; + eprintln!("{MESSAGE}"); // We need to abort here as we failed to resolve the deadlock, // otherwise the compiler could just hang, process::abort(); @@ -244,11 +249,16 @@ pub(crate) fn run_in_thread_pool_with_globals< tls::with(|tcx| { // Accessing session globals is sound as they outlive `GlobalCtxt`. // They are needed to hash query keys containing spans or symbols. - let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { - // Ensure there was no errors collecting all active jobs. - // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs(false).expect("failed to collect active queries in deadlock handler") - }); + let query_map = rustc_span::set_session_globals_then( + unsafe { &*(session_globals as *const SessionGlobals) }, + || { + // Ensure there were no errors collecting all active jobs. + // We need the complete map to ensure we find a cycle to break. + QueryCtxt::new(tcx).collect_active_jobs(false).expect( + "failed to collect active queries in deadlock handler", + ) + }, + ); break_query_cycles(query_map, ®istry); }) }) From 59868c13947753220a6727e50c2c596d2f513b29 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Fri, 23 Jan 2026 13:43:20 -0500 Subject: [PATCH 491/583] Fix uninitialized UEFI globals in tests Export globals via a `doc(hidden)` module. In test code, use the globals from `realstd` so that they are properly initialized. --- library/std/src/lib.rs | 1 + library/std/src/os/uefi/env.rs | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b213fa749177..dcde208fac77 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -260,6 +260,7 @@ all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] +#![cfg_attr(all(test, target_os = "uefi"), feature(uefi_std))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] // diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index ab5406e605c6..82e3fc9775cb 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -4,13 +4,25 @@ use crate::ffi::c_void; use crate::ptr::NonNull; -use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, Ordering}; +use crate::sync::atomic::Ordering; -static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); -static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); -// Flag to check if BootServices are still valid. -// Start with assuming that they are not available -static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); +#[doc(hidden)] +#[cfg(not(test))] +pub mod globals { + use crate::ffi::c_void; + use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; + + pub static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); + pub static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); + // Flag to check if BootServices are still valid. + // Start with assuming that they are not available + pub static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); +} + +#[cfg(not(test))] +use globals::*; +#[cfg(test)] +use realstd::os::uefi::env::globals::*; /// Initializes the global System Table and Image Handle pointers. /// From 09de0fd848251aed26149bd5938b1e05ef35dc82 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 30 Jan 2026 12:41:04 +1100 Subject: [PATCH 492/583] Use `#![feature(adt_const_params)]` for static query flags --- compiler/rustc_query_impl/src/lib.rs | 40 ++++++++++++++--------- compiler/rustc_query_impl/src/plumbing.rs | 10 ++++-- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 4e79d0842da2..8dd220e1ae0f 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -2,10 +2,13 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![feature(adt_const_params)] #![feature(min_specialization)] #![feature(rustc_attrs)] // tidy-alphabetical-end +use std::marker::ConstParamTy; + use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; @@ -35,29 +38,34 @@ pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; mod profiling_support; pub use self::profiling_support::alloc_self_profile_query_strings; +#[derive(ConstParamTy)] // Allow this struct to be used for const-generic values. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct QueryFlags { + /// True if this query has the `anon` modifier. + is_anon: bool, + /// True if this query has the `depth_limit` modifier. + is_depth_limit: bool, + /// True if this query has the `feedable` modifier. + is_feedable: bool, +} + /// Combines a [`QueryVTable`] with some additional compile-time booleans /// to implement [`QueryDispatcher`], for use by code in [`rustc_query_system`]. /// /// Baking these boolean flags into the type gives a modest but measurable /// improvement to compiler perf and compiler code size; see /// . -struct SemiDynamicQueryDispatcher< - 'tcx, - C: QueryCache, - const ANON: bool, - const DEPTH_LIMIT: bool, - const FEEDABLE: bool, -> { +struct SemiDynamicQueryDispatcher<'tcx, C: QueryCache, const FLAGS: QueryFlags> { vtable: &'tcx QueryVTable<'tcx, C>, } // Manually implement Copy/Clone, because deriving would put trait bounds on the cache type. -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Copy - for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Copy + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { } -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Clone - for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Clone + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { fn clone(&self) -> Self { *self @@ -65,8 +73,8 @@ impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDA } // This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`. -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> - QueryDispatcher<'tcx> for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> QueryDispatcher<'tcx> + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> where for<'a> C::Key: HashStable>, { @@ -158,7 +166,7 @@ where #[inline(always)] fn anon(self) -> bool { - ANON + FLAGS.is_anon } #[inline(always)] @@ -168,12 +176,12 @@ where #[inline(always)] fn depth_limit(self) -> bool { - DEPTH_LIMIT + FLAGS.is_depth_limit } #[inline(always)] fn feedable(self) -> bool { - FEEDABLE + FLAGS.is_feedable } #[inline(always)] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 2d4e10a0380c..48aed0bad4b8 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -708,14 +708,18 @@ macro_rules! define_queries { data: PhantomData<&'tcx ()> } + const FLAGS: QueryFlags = QueryFlags { + is_anon: is_anon!([$($modifiers)*]), + is_depth_limit: depth_limit!([$($modifiers)*]), + is_feedable: feedable!([$($modifiers)*]), + }; + impl<'tcx> QueryDispatcherUnerased<'tcx> for QueryType<'tcx> { type UnerasedValue = queries::$name::Value<'tcx>; type Dispatcher = SemiDynamicQueryDispatcher< 'tcx, queries::$name::Storage<'tcx>, - { is_anon!([$($modifiers)*]) }, - { depth_limit!([$($modifiers)*]) }, - { feedable!([$($modifiers)*]) }, + FLAGS, >; const NAME: &'static &'static str = &stringify!($name); From 95ac5673cedf8b0ba543aac2ccb67b47f60662e1 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Mon, 2 Feb 2026 15:38:26 +0530 Subject: [PATCH 493/583] Fix SLP vectorization test CHECK patterns --- tests/codegen-llvm/slp-vectorization-mul3.rs | 29 ++++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index b6edc212c82d..1c40c6b28f3f 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -4,23 +4,22 @@ #![crate_type = "lib"] -// CHECK-LABEL: mul3 -// CHECK: %[[C_BPP:.*]] = phi <4 x i8> -// CHECK: %[[V:.*]] = load <4 x i8>, ptr -// CHECK: %[[ADD:.*]] = add <4 x i8> %[[V]], %[[C_BPP]] -// CHECK: store {{<4 x i8>|i32}} {{.*}}, ptr +// CHECK-LABEL: @mul3 +// CHECK: phi <4 x i8> +// CHECK: load <4 x i8> +// CHECK: add <4 x i8> +// CHECK: store <4 x i8> -pub fn mul3(previous: &[u8], current: &mut [u8]) { +#[no_mangle] +pub fn mul3(previous: &[[u8; 4]], current: &mut [[u8; 4]]) { let mut c_bpp = [0u8; 4]; - for (chunk, b_bpp) in current.chunks_exact_mut(4).zip(previous.chunks_exact(4)) { - let new_chunk = [ - chunk[0].wrapping_add(c_bpp[0]), - chunk[1].wrapping_add(c_bpp[1]), - chunk[2].wrapping_add(c_bpp[2]), - chunk[3].wrapping_add(c_bpp[3]), - ]; - chunk.copy_from_slice(&new_chunk); - c_bpp.copy_from_slice(b_bpp); + for i in 0..previous.len() { + current[i][0] = current[i][0].wrapping_add(c_bpp[0]); + current[i][1] = current[i][1].wrapping_add(c_bpp[1]); + current[i][2] = current[i][2].wrapping_add(c_bpp[2]); + current[i][3] = current[i][3].wrapping_add(c_bpp[3]); + + c_bpp = previous[i]; } } From 0830a5a9286e351a009e8eaa1fa86691e3a3b62e Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Mon, 2 Feb 2026 15:44:50 +0530 Subject: [PATCH 494/583] fix: add min-llvm-version --- tests/codegen-llvm/slp-vectorization-mul3.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index 1c40c6b28f3f..2987493e0371 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -1,6 +1,8 @@ //! Regression test for #142519 //@ only-x86_64 //@ compile-flags: -O +//@ min-llvm-version: 18.0 + #![crate_type = "lib"] From c725637dc24fbe47704e92b7e8a8875f60ccd2be Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 2 Feb 2026 11:22:33 +0100 Subject: [PATCH 495/583] explain why we dont skip some of this work when there are field projections --- compiler/rustc_middle/src/mir/statement.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 393e9c59c355..973ceccc67f9 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -441,6 +441,8 @@ impl<'tcx> Place<'tcx> { where D: ?Sized + HasLocalDecls<'tcx>, { + // If there's a field projection element in `projection`, we *could* skip everything + // before that, but on 2026-01-31 a perf experiment showed no benefit from doing so. PlaceTy::from_ty(local_decls.local_decls()[local].ty).multi_projection_ty(tcx, projection) } From 4956604c7db5fb272214331c9db82608fbbbe185 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 31 Jan 2026 21:34:36 +0200 Subject: [PATCH 496/583] external-rustc-drivers.md: some improvements --- .../rustc-driver/external-rustc-drivers.md | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md b/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md index 1049d7a82ddd..2d409ad201b1 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md @@ -42,29 +42,28 @@ For custom-built toolchains or environments not using rustup, additional configu #### Troubleshooting Steps -1. **Check LLVM installation**: Verify LLVM is installed and accessible -2. **Configure library paths**: You may need to set environment variables: - ```text +1. Verify LLVM is installed and accessible +2. Ensure that library paths are set: + ```sh export LD_LIBRARY_PATH=/path/to/llvm/lib:$LD_LIBRARY_PATH ``` -3. **Check version compatibility**: Ensure your LLVM version is compatible with your Rust toolchain +3. Ensure your LLVM version is compatible with your Rust toolchain -### Configuring `rust-analyzer` for Out-of-Tree Projects +### Configuring `rust-analyzer` for out-of-tree projects When developing out-of-tree projects that use `rustc_private` crates, you can configure `rust-analyzer` to recognize these crates. #### Configuration Steps -1. **Set rust-analyzer configuration** - Configure `rust-analyzer.rustc.source` to `"discover"` in your editor settings. +1. Configure `rust-analyzer.rustc.source` to `"discover"` in your editor settings. For VS Code, add to `rust_analyzer_settings.json`: ```json { "rust-analyzer.rustc.source": "discover" } ``` -2. **Add metadata to Cargo.toml** - Add the following to the `Cargo.toml` of every crate that uses `rustc_private`: + +2. Add the following to the `Cargo.toml` of every crate that uses `rustc_private`: ```toml [package.metadata.rust-analyzer] rustc_private = true @@ -74,4 +73,6 @@ This configuration allows `rust-analyzer` to properly recognize and provide IDE ### Additional Resources -- [GitHub Issue #137421](https://github.com/rust-lang/rust/issues/137421): Explains that `rustc_private` linker failures often occur because `llvm-tools` is not installed +- [GitHub Issue #137421] explains that `rustc_private` linker failures often occur because `llvm-tools` is not installed + +[GitHub Issue #137421]: https://github.com/rust-lang/rust/issues/137421 From 61769452234deaf0e035789009ab807dd1ca93d4 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Mon, 2 Feb 2026 16:05:08 +0530 Subject: [PATCH 497/583] fix: remove space for tidy and only for x86_64 --- tests/codegen-llvm/slp-vectorization-mul3.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index 2987493e0371..d7c44c8db71a 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -3,7 +3,6 @@ //@ compile-flags: -O //@ min-llvm-version: 18.0 - #![crate_type = "lib"] // CHECK-LABEL: @mul3 From 5aba6b1635935237a146d759e67bc7d3e8bdc73b Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Mon, 2 Feb 2026 09:38:07 +0000 Subject: [PATCH 498/583] Fix missing unused_variables lint when using a match guard Within a binding pattern match guard, only real reads of a bound local impact its liveness analysis - not the fake read that is injected. --- compiler/rustc_mir_transform/src/liveness.rs | 9 ++++++--- .../clippy/clippy_lints/src/time_subtraction.rs | 2 +- tests/ui/lint/unused/match_with_guard.rs | 10 ++++++++++ tests/ui/lint/unused/match_with_guard.stderr | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/ui/lint/unused/match_with_guard.rs create mode 100644 tests/ui/lint/unused/match_with_guard.stderr diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index cf977be4c3df..9bafee9f31c3 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1243,9 +1243,12 @@ struct TransferFunction<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for TransferFunction<'_, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match statement.kind { - // `ForLet(None)` fake read erroneously marks the just-assigned local as live. - // This defeats the purpose of the analysis for `let` bindings. - StatementKind::FakeRead(box (FakeReadCause::ForLet(None), _)) => return, + // `ForLet(None)` and `ForGuardBinding` fake reads erroneously mark the just-assigned + // locals as live. This defeats the purpose of the analysis for such bindings. + StatementKind::FakeRead(box ( + FakeReadCause::ForLet(None) | FakeReadCause::ForGuardBinding, + _, + )) => return, // Handle self-assignment by restricting the read/write they do. StatementKind::Assign(box (ref dest, ref rvalue)) if self.self_assignment.contains(&location) => diff --git a/src/tools/clippy/clippy_lints/src/time_subtraction.rs b/src/tools/clippy/clippy_lints/src/time_subtraction.rs index 3ba59aefea06..ca8378ba7c6a 100644 --- a/src/tools/clippy/clippy_lints/src/time_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/time_subtraction.rs @@ -85,7 +85,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { let (lhs, rhs) = match expr.kind { ExprKind::Binary(op, lhs, rhs) if matches!(op.node, BinOpKind::Sub,) => (lhs, rhs), - ExprKind::MethodCall(fn_name, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => { + ExprKind::MethodCall(_, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => { (lhs, rhs) }, _ => return, diff --git a/tests/ui/lint/unused/match_with_guard.rs b/tests/ui/lint/unused/match_with_guard.rs new file mode 100644 index 000000000000..61e4321f6735 --- /dev/null +++ b/tests/ui/lint/unused/match_with_guard.rs @@ -0,0 +1,10 @@ +//! The mere presence of a match guard should not deem bound variables "used". +//! Regression test for https://github.com/rust-lang/rust/issues/151983 +//@ check-pass +#![warn(unused)] +fn main() { + match Some(42) { + Some(unused) if true => (), //~WARN unused variable: `unused` + _ => (), + } +} diff --git a/tests/ui/lint/unused/match_with_guard.stderr b/tests/ui/lint/unused/match_with_guard.stderr new file mode 100644 index 000000000000..6a509e568b68 --- /dev/null +++ b/tests/ui/lint/unused/match_with_guard.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `unused` + --> $DIR/match_with_guard.rs:7:14 + | +LL | Some(unused) if true => (), + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` + | +note: the lint level is defined here + --> $DIR/match_with_guard.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + From 8476e893e72e1e3617fb50a06a01548d6177d192 Mon Sep 17 00:00:00 2001 From: Ritik Chahar <126174435+chahar-ritik@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:47:09 +0530 Subject: [PATCH 499/583] Update min-llvm-version: 22 Co-authored-by: Nikita Popov --- tests/codegen-llvm/slp-vectorization-mul3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index d7c44c8db71a..38c0949b8271 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -1,7 +1,7 @@ //! Regression test for #142519 //@ only-x86_64 //@ compile-flags: -O -//@ min-llvm-version: 18.0 +//@ min-llvm-version: 22 #![crate_type = "lib"] From c0393cf8dbfc91f4719e6be9895b5c8a983ef8ec Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 24 Oct 2025 15:29:33 +0300 Subject: [PATCH 500/583] resolve: Report more early resolution ambiguities for imports The new ambiguities are reported when the import's visibility is ambiguous and may depend on the resolution/expansion order. --- compiler/rustc_lint_defs/src/builtin.rs | 50 +++++++++++++++++++ compiler/rustc_middle/src/ty/mod.rs | 6 +++ compiler/rustc_resolve/src/diagnostics.rs | 17 +++++-- compiler/rustc_resolve/src/errors.rs | 9 +++- compiler/rustc_resolve/src/ident.rs | 34 ++++++++++--- compiler/rustc_resolve/src/imports.rs | 6 ++- compiler/rustc_resolve/src/lib.rs | 4 ++ .../ambiguous-import-visibility-macro.rs | 19 +++++++ .../ambiguous-import-visibility-macro.stderr | 29 +++++++++++ .../ambiguous-import-visibility-module.rs | 24 +++++++++ .../ambiguous-import-visibility-module.stderr | 29 +++++++++++ .../ui/imports/ambiguous-import-visibility.rs | 14 ++++++ .../ambiguous-import-visibility.stderr | 25 ++++++++++ 13 files changed, 254 insertions(+), 12 deletions(-) create mode 100644 tests/ui/imports/ambiguous-import-visibility-macro.rs create mode 100644 tests/ui/imports/ambiguous-import-visibility-macro.stderr create mode 100644 tests/ui/imports/ambiguous-import-visibility-module.rs create mode 100644 tests/ui/imports/ambiguous-import-visibility-module.stderr create mode 100644 tests/ui/imports/ambiguous-import-visibility.rs create mode 100644 tests/ui/imports/ambiguous-import-visibility.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ff108031badc..d1d5d0a56ead 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -20,6 +20,7 @@ declare_lint_pass! { AMBIGUOUS_GLOB_IMPORTED_TRAITS, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_GLOB_REEXPORTS, + AMBIGUOUS_IMPORT_VISIBILITIES, AMBIGUOUS_PANIC_IMPORTS, ARITHMETIC_OVERFLOW, ASM_SUB_REGISTER, @@ -4564,6 +4565,55 @@ declare_lint! { }; } +declare_lint! { + /// The `ambiguous_import_visibilities` lint detects imports that should report ambiguity + /// errors, but previously didn't do that due to rustc bugs. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unknown_lints)] + /// #![deny(ambiguous_import_visibilities)] + /// mod reexport { + /// mod m { + /// pub struct S {} + /// } + /// + /// macro_rules! mac { + /// () => { use m::S; } + /// } + /// + /// pub use m::*; + /// mac!(); + /// + /// pub use S as Z; // ambiguous visibility + /// } + /// + /// fn main() { + /// reexport::Z {}; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of Rust compile it successfully because it + /// fetched the glob import's visibility for `pub use S as Z` import, and ignored the private + /// `use m::S` import that appeared later. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub AMBIGUOUS_IMPORT_VISIBILITIES, + Warn, + "detects certain glob imports that require reporting an ambiguity error", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #149145), + }; +} + declare_lint! { /// The `refining_impl_trait_reachable` lint detects `impl Trait` return /// types in method signatures that are refined by a publically reachable diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 4e33b8ebb6de..0220531b09fa 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -408,6 +408,12 @@ impl> Visibility { } } +impl + Copy> Visibility { + pub fn min(self, vis: Visibility, tcx: TyCtxt<'_>) -> Visibility { + if self.is_at_least(vis, tcx) { vis } else { self } + } +} + impl Visibility { pub fn expect_local(self) -> Visibility { self.map_id(|id| id.expect_local()) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6884ed1891a3..f09b98715799 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -24,8 +24,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_PANIC_IMPORTS, - MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, + AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, }; use rustc_session::utils::was_invoked_from_cargo; use rustc_span::edit_distance::find_best_match_for_name; @@ -145,6 +145,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let lint = match ambiguity_warning { + _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, }; @@ -1995,7 +1996,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn ambiguity_diagnostic(&self, ambiguity_error: &AmbiguityError<'ra>) -> errors::Ambiguity { - let AmbiguityError { kind, ident, b1, b2, scope1, scope2, .. } = *ambiguity_error; + let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = + *ambiguity_error; let extern_prelude_ambiguity = || { // Note: b1 may come from a module scope, as an extern crate item in module. matches!(scope2, Scope::ExternPreludeFlags) @@ -2074,9 +2076,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None }; + let ambig_vis = ambig_vis.map(|(vis1, vis2)| { + format!( + "{} or {}", + vis1.to_string(CRATE_DEF_ID, self.tcx), + vis2.to_string(CRATE_DEF_ID, self.tcx) + ) + }); + errors::Ambiguity { ident, help, + ambig_vis, kind: kind.descr(), b1_note, b1_help_msgs, diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 205f2c6aa539..3e5446403052 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1463,6 +1463,7 @@ pub(crate) struct UnknownDiagnosticAttributeTypoSugg { // FIXME: Make this properly translatable. pub(crate) struct Ambiguity { pub ident: Ident, + pub ambig_vis: Option, pub kind: &'static str, pub help: Option<&'static [&'static str]>, pub b1_note: Spanned, @@ -1473,8 +1474,12 @@ pub(crate) struct Ambiguity { impl Ambiguity { fn decorate<'a>(self, diag: &mut Diag<'a, impl EmissionGuarantee>) { - diag.primary_message(format!("`{}` is ambiguous", self.ident)); - diag.span_label(self.ident.span, "ambiguous name"); + if let Some(ambig_vis) = self.ambig_vis { + diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); + } else { + diag.primary_message(format!("`{}` is ambiguous", self.ident)); + diag.span_label(self.ident.span, "ambiguous name"); + } diag.note(format!("ambiguous because of {}", self.kind)); diag.span_note(self.b1_note.span, self.b1_note.node); if let Some(help) = self.help { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 0e73c349c8cd..d4d373d82064 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -5,6 +5,7 @@ use Namespace::*; use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; +use rustc_middle::ty::Visibility; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; @@ -485,9 +486,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // We do not need to report them if we are either in speculative resolution, // or in late resolution when everything is already imported and expanded // and no ambiguities exist. - if matches!(finalize, None | Some(Finalize { stage: Stage::Late, .. })) { - return ControlFlow::Break(Ok(decl)); - } + let import_vis = match finalize { + None | Some(Finalize { stage: Stage::Late, .. }) => { + return ControlFlow::Break(Ok(decl)); + } + Some(Finalize { import_vis, .. }) => import_vis, + }; if let Some(&(innermost_decl, _)) = innermost_results.first() { // Found another solution, if the first one was "weak", report an error. @@ -500,6 +504,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { decl, scope, &innermost_results, + import_vis, ) { // No need to search for more potential ambiguities, one is enough. return ControlFlow::Break(Ok(innermost_decl)); @@ -779,12 +784,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { decl: Decl<'ra>, scope: Scope<'ra>, innermost_results: &[(Decl<'ra>, Scope<'ra>)], + import_vis: Option, ) -> bool { let (innermost_decl, innermost_scope) = innermost_results[0]; let (res, innermost_res) = (decl.res(), innermost_decl.res()); - if res == innermost_res { + let ambig_vis = if res != innermost_res { + None + } else if let Some(import_vis) = import_vis + && let min = + (|d: Decl<'_>| d.vis().min(import_vis.to_def_id(), self.tcx).expect_local()) + && let (min1, min2) = (min(decl), min(innermost_decl)) + && min1 != min2 + { + Some((min1, min2)) + } else { return false; - } + }; // FIXME: Use `scope` instead of `res` to detect built-in attrs and derive helpers, // it will exclude imports, make slightly more code legal, and will require lang approval. @@ -871,10 +886,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { || (self.is_specific_builtin_macro(res, sym::core_panic) && self.is_specific_builtin_macro(innermost_res, sym::std_panic))); - let warning = is_issue_147319_hack.then_some(AmbiguityWarning::PanicImport); + let warning = if ambig_vis.is_some() { + Some(AmbiguityWarning::GlobImport) + } else if is_issue_147319_hack { + Some(AmbiguityWarning::PanicImport) + } else { + None + }; self.ambiguity_errors.push(AmbiguityError { kind, + ambig_vis, ident: ident.orig(orig_ident_span), b1: innermost_decl, b2: decl, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 057340028085..78ad139cff79 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1189,7 +1189,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, ns, &import.parent_scope, - Some(Finalize { report_private: false, ..finalize }), + Some(Finalize { + report_private: false, + import_vis: Some(import.vis), + ..finalize + }), bindings[ns].get().decl(), Some(import), ); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 75bd2c62f9b4..3a65598584ae 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -969,6 +969,7 @@ enum AmbiguityWarning { struct AmbiguityError<'ra> { kind: AmbiguityKind, + ambig_vis: Option<(Visibility, Visibility)>, ident: Ident, b1: Decl<'ra>, b2: Decl<'ra>, @@ -2087,6 +2088,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(b2) = used_decl.ambiguity.get() { let ambiguity_error = AmbiguityError { kind: AmbiguityKind::GlobVsGlob, + ambig_vis: None, ident, b1: used_decl, b2, @@ -2556,6 +2558,8 @@ struct Finalize { used: Used = Used::Other, /// Finalizing early or late resolution. stage: Stage = Stage::Early, + /// Nominal visibility of the import item, in case we are resolving an import's final segment. + import_vis: Option = None, } impl Finalize { diff --git a/tests/ui/imports/ambiguous-import-visibility-macro.rs b/tests/ui/imports/ambiguous-import-visibility-macro.rs new file mode 100644 index 000000000000..e1861cc5d4e0 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-macro.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ edition:2018 +//@ proc-macro: same-res-ambigious-extern-macro.rs + +macro_rules! globbing{ + () => { + pub use same_res_ambigious_extern_macro::*; + } +} + +#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility +extern crate same_res_ambigious_extern_macro; +globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility + +pub trait RustEmbed {} + +pub use RustEmbed as Embed; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +fn main() {} diff --git a/tests/ui/imports/ambiguous-import-visibility-macro.stderr b/tests/ui/imports/ambiguous-import-visibility-macro.stderr new file mode 100644 index 000000000000..ed6eb6f893af --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-macro.stderr @@ -0,0 +1,29 @@ +warning: ambiguous import visibility: pub(crate) or pub + --> $DIR/ambiguous-import-visibility-macro.rs:17:9 + | +LL | pub use RustEmbed as Embed; + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + = note: ambiguous because of a conflict between a name from a glob import and an outer scope during import or macro resolution +note: `RustEmbed` could refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility-macro.rs:7:17 + | +LL | pub use same_res_ambigious_extern_macro::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility + | ------------ in this macro invocation + = help: consider adding an explicit import of `RustEmbed` to disambiguate + = help: or use `crate::RustEmbed` to refer to this derive macro unambiguously +note: `RustEmbed` could also refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility-macro.rs:11:1 + | +LL | #[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility + | ^^^^^^^^^^^^ + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + = note: this warning originates in the macro `globbing` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + diff --git a/tests/ui/imports/ambiguous-import-visibility-module.rs b/tests/ui/imports/ambiguous-import-visibility-module.rs new file mode 100644 index 000000000000..35c6da8b21a4 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-module.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ edition:2018.. + +mod reexport { + mod m { + pub struct S {} + } + + macro_rules! mac { + () => { + use m::S; + }; + } + + pub use m::*; + mac!(); + + pub use S as Z; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +} + +fn main() { + reexport::Z {}; +} diff --git a/tests/ui/imports/ambiguous-import-visibility-module.stderr b/tests/ui/imports/ambiguous-import-visibility-module.stderr new file mode 100644 index 000000000000..a97070c20a62 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-module.stderr @@ -0,0 +1,29 @@ +warning: ambiguous import visibility: pub or pub(in crate::reexport) + --> $DIR/ambiguous-import-visibility-module.rs:18:13 + | +LL | pub use S as Z; + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-module.rs:11:17 + | +LL | use m::S; + | ^^^^ +... +LL | mac!(); + | ------ in this macro invocation + = help: use `self::S` to refer to this struct unambiguously +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-module.rs:15:13 + | +LL | pub use m::*; + | ^^^^ + = help: use `self::S` to refer to this struct unambiguously + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + = note: this warning originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + diff --git a/tests/ui/imports/ambiguous-import-visibility.rs b/tests/ui/imports/ambiguous-import-visibility.rs new file mode 100644 index 000000000000..4cb8b763fbc9 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ edition:2018 +//@ proc-macro: same-res-ambigious-extern-macro.rs + +#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility +extern crate same_res_ambigious_extern_macro; +// this imports the same `RustEmbed` macro with `pub` visibility +pub use same_res_ambigious_extern_macro::*; + +pub trait RustEmbed {} + +pub use RustEmbed as Embed; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +fn main() {} diff --git a/tests/ui/imports/ambiguous-import-visibility.stderr b/tests/ui/imports/ambiguous-import-visibility.stderr new file mode 100644 index 000000000000..30cddca4697d --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility.stderr @@ -0,0 +1,25 @@ +warning: ambiguous import visibility: pub(crate) or pub + --> $DIR/ambiguous-import-visibility.rs:12:9 + | +LL | pub use RustEmbed as Embed; + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + = note: ambiguous because of a conflict between a name from a glob import and an outer scope during import or macro resolution +note: `RustEmbed` could refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility.rs:8:9 + | +LL | pub use same_res_ambigious_extern_macro::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `RustEmbed` to disambiguate + = help: or use `crate::RustEmbed` to refer to this derive macro unambiguously +note: `RustEmbed` could also refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility.rs:5:1 + | +LL | #[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility + | ^^^^^^^^^^^^ + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: 1 warning emitted + From 629ff9b4aa1f0216b26b44958d114332a793cf19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Feb 2026 12:48:42 +0100 Subject: [PATCH 501/583] Update tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs Co-authored-by: lcnr --- tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs index 0a948989f981..a81e18559a39 100644 --- a/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs +++ b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs @@ -7,7 +7,7 @@ // The change we tried to make there caused relating a type variable with an alias inside lub, // In 5bd20bbd0ba6c0285664e55a1ffc677d7487c98b, we moved around code // that adds an alias-relate predicate to be earlier, from one shared codepath into several -// distinct code paths. However, we forgot one codepath, through lub, causing an ICE in serde. +// distinct code paths. However, we forgot the codepath in `LatticeOp`, causing an ICE in serde. // In the end we dropped said commit, but the reproducer is still a useful as test. use std::marker::PhantomData; From 82a530c61c56df0fb89e1d3d70565ec3b69fbf32 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 2 Feb 2026 11:53:30 +0000 Subject: [PATCH 502/583] Port --- .../src/attributes/rustc_internal.rs | 8 ++++++++ compiler/rustc_attr_parsing/src/context.rs | 16 +++++++++------- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_analysis/src/collect/dump.rs | 3 +-- compiler/rustc_passes/src/check_attr.rs | 2 +- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 38f728fa9f55..250bceecbd65 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -307,6 +307,14 @@ impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParse const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls; } +pub(crate) struct RustcHiddenTypeOfOpaquesParser; + +impl NoArgsAttributeParser for RustcHiddenTypeOfOpaquesParser { + const PATH: &[Symbol] = &[sym::rustc_hidden_type_of_opaques]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHiddenTypeOfOpaques; +} pub(crate) struct RustcNounwindParser; impl NoArgsAttributeParser for RustcNounwindParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0cabc0895053..51da6def086c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -75,13 +75,14 @@ use crate::attributes::rustc_dump::{ RustcDumpVtable, }; use crate::attributes::rustc_internal::{ - RustcHasIncoherentInherentImplsParser, RustcLayoutParser, RustcLayoutScalarValidRangeEndParser, - RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, - RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, RustcLintQueryInstabilityParser, - RustcLintUntrackedQueryInformationParser, RustcMainParser, RustcMustImplementOneOfParser, - RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, - RustcNonConstTraitMethodParser, RustcNounwindParser, RustcObjectLifetimeDefaultParser, - RustcOffloadKernelParser, RustcScalableVectorParser, RustcSimdMonomorphizeLaneLimitParser, + RustcHasIncoherentInherentImplsParser, RustcHiddenTypeOfOpaquesParser, RustcLayoutParser, + RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, + RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, + RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, + RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, + RustcNoImplicitAutorefsParser, RustcNonConstTraitMethodParser, RustcNounwindParser, + RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, + RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -299,6 +300,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 92dda79b0920..3104a08d7547 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1057,6 +1057,9 @@ pub enum AttributeKind { /// Represents `#[rustc_has_incoherent_inherent_impls]` RustcHasIncoherentInherentImpls, + /// Represents `#[rustc_hidden_type_of_opaques]` + RustcHiddenTypeOfOpaques, + /// Represents `#[rustc_layout]` RustcLayout(ThinVec), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 816ed07c1dc4..f03f55f85b39 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -111,6 +111,7 @@ impl AttributeKind { RustcDumpVtable(..) => No, RustcDynIncompatibleTrait(..) => No, RustcHasIncoherentInherentImpls => Yes, + RustcHiddenTypeOfOpaques => No, RustcLayout(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 2dfd4ab6111f..bbf912cd4bde 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -7,10 +7,9 @@ use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::sym; pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { - if !tcx.has_attr(CRATE_DEF_ID, sym::rustc_hidden_type_of_opaques) { + if !find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcHiddenTypeOfOpaques) { return; } - for id in tcx.hir_crate_items(()).opaques() { if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8cf68b280850..e413780d6c4f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -299,6 +299,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcDumpVtable(..) | AttributeKind::RustcDynIncompatibleTrait(..) | AttributeKind::RustcHasIncoherentInherentImpls + | AttributeKind::RustcHiddenTypeOfOpaques | AttributeKind::RustcLayout(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) | AttributeKind::RustcLayoutScalarValidRangeStart(..) @@ -390,7 +391,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_capture_analysis | sym::rustc_regions | sym::rustc_strict_coherence - | sym::rustc_hidden_type_of_opaques | sym::rustc_mir | sym::rustc_effective_visibility | sym::rustc_outlives From fd023cb122ffacff4bd14f27d9751ffce1bd7878 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 2 Feb 2026 12:57:35 +0100 Subject: [PATCH 503/583] Add uv to the list of possible python runners I'm one of the rare cases that does have uv, but doesn't have python installed globally --- x | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/x b/x index 4fce0be219e7..a8acbd4d5ac6 100755 --- a/x +++ b/x @@ -29,16 +29,19 @@ xpy=$(dirname "$(realpath "$0")")/x.py # On MacOS, `py` tries to install "Developer command line tools". Try `python3` first. # NOTE: running `bash -c ./x` from Windows doesn't set OSTYPE. case ${OSTYPE:-} in - cygwin*|msys*) SEARCH="py python3 python python2";; - *) SEARCH="python3 python py python2";; + cygwin*|msys*) SEARCH="py python3 python python2 uv";; + *) SEARCH="python3 python py python2 uv";; esac for SEARCH_PYTHON in $SEARCH; do if python=$(command -v $SEARCH_PYTHON) && [ -x "$python" ]; then - if [ $SEARCH_PYTHON = py ]; then - extra_arg="-3" - else - extra_arg="" - fi + case $SEARCH_PYTHON in + py) + extra_arg="-3";; + uv) + extra_arg="run";; + *) + extra_arg="";; + esac exec "$python" $extra_arg "$xpy" "$@" fi done From 30ae46fab148f8f924d445feddc68b9103204423 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 2 Feb 2026 14:43:33 +0100 Subject: [PATCH 504/583] Use rustc_parse's Recovery instead of a boolean --- compiler/rustc_attr_parsing/src/attributes/cfg.rs | 10 ++++++---- .../src/attributes/cfg_select.rs | 12 +++++++----- compiler/rustc_attr_parsing/src/context.rs | 11 ++++++----- compiler/rustc_attr_parsing/src/parser.rs | 14 ++++++++++---- compiler/rustc_builtin_macros/src/cfg.rs | 5 +++-- compiler/rustc_expand/src/config.rs | 5 ++++- compiler/rustc_expand/src/expand.rs | 6 +++--- 7 files changed, 39 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 3a540d80998d..157a91c249d4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -10,7 +10,7 @@ use rustc_feature::{ use rustc_hir::attrs::CfgEntry; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, RustcVersion, Target}; -use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::{exp, parse_in}; use rustc_session::Session; use rustc_session::config::ExpectedValues; @@ -360,8 +360,10 @@ fn parse_cfg_attr_internal<'a>( ) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> { // Parse cfg predicate let pred_start = parser.token.span; - let meta = - MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints { recover: true })?; + let meta = MetaItemOrLitParser::parse_single( + parser, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); let cfg_predicate = AttributeParser::parse_single_args( @@ -376,7 +378,7 @@ fn parse_cfg_attr_internal<'a>( CRATE_NODE_ID, Target::Crate, features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, &meta, parse_cfg_entry, &CFG_ATTR_TEMPLATE, diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index ca844758daaa..4005ad2cba11 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -5,7 +5,7 @@ use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::CfgEntry; use rustc_hir::{AttrPath, Target}; use rustc_parse::exp; -use rustc_parse::parser::Parser; +use rustc_parse::parser::{Parser, Recovery}; use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Span, sym}; @@ -78,9 +78,11 @@ pub fn parse_cfg_select( } } } else { - let meta = - MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints { recover: true }) - .map_err(|diag| diag.emit())?; + let meta = MetaItemOrLitParser::parse_single( + p, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + ) + .map_err(|diag| diag.emit())?; let cfg_span = meta.span(); let cfg = AttributeParser::parse_single_args( sess, @@ -95,7 +97,7 @@ pub fn parse_cfg_select( // Doesn't matter what the target actually is here. Target::Crate, features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, &meta, parse_cfg_entry, &AttributeTemplate::default(), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0cabc0895053..fa6c9513c59c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -10,6 +10,7 @@ use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, HirId}; +use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; @@ -383,7 +384,7 @@ impl Stage for Late { } fn should_emit(&self) -> ShouldEmit { - ShouldEmit::ErrorsAndLints { recover: true } + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed } } } @@ -770,10 +771,10 @@ pub enum ShouldEmit { ErrorsAndLints { /// Whether [`ArgParser`] will attempt to recover from errors. /// - /// If true, it will attempt to recover from bad input (like an invalid literal). Setting - /// this to false will instead return early, and not raise errors except at the top level - /// (in [`ArgParser::from_attr_args`]). - recover: bool, + /// Whether it is allowed to recover from bad input (like an invalid literal). Setting + /// this to `Forbidden` will instead return early, and not raise errors except at the top + /// level (in [`ArgParser::from_attr_args`]). + recovery: Recovery, }, /// The operation will *not* emit errors and lints. /// diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 7f3c6d28005f..80dab0b85edd 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -15,7 +15,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::{Diag, PResult}; use rustc_hir::{self as hir, AttrPath}; use rustc_parse::exp; -use rustc_parse::parser::{ForceCollect, Parser, PathStyle, token_descr}; +use rustc_parse::parser::{ForceCollect, Parser, PathStyle, Recovery, token_descr}; use rustc_session::errors::create_lit_error; use rustc_session::parse::ParseSess; use rustc_span::{Ident, Span, Symbol, sym}; @@ -121,7 +121,7 @@ impl ArgParser { &args.tokens, args.dspan.entire(), psess, - ShouldEmit::ErrorsAndLints { recover: false }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }, ) { Ok(p) => return Some(ArgParser::List(p)), Err(e) => { @@ -373,7 +373,10 @@ fn expr_to_lit<'sess>( } Err(err) => { let err = create_lit_error(psess, err, token_lit, expr.span); - if matches!(should_emit, ShouldEmit::ErrorsAndLints { recover: false }) { + if matches!( + should_emit, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } + ) { Err(err) } else { let lit = MetaItemLit { @@ -431,7 +434,10 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there let err = self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }); - if matches!(self.should_emit, ShouldEmit::ErrorsAndLints { recover: false }) { + if matches!( + self.should_emit, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } + ) { return Err(err); } else { self.should_emit.emit_err(err) diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 8e925cfe09a2..3ebde949b99b 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -13,6 +13,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_hir::attrs::CfgEntry; use rustc_hir::{AttrPath, Target}; use rustc_parse::exp; +use rustc_parse::parser::Recovery; use rustc_span::{ErrorGuaranteed, Span, sym}; use crate::errors; @@ -42,7 +43,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result, span: Span, tts: TokenStream) -> Result StripUnconfigured<'a> { fn in_cfg(&self, attrs: &[Attribute]) -> bool { attrs.iter().all(|attr| { !is_cfg(attr) - || self.cfg_true(attr, ShouldEmit::ErrorsAndLints { recover: true }).as_bool() + || self + .cfg_true(attr, ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }) + .as_bool() }) } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cfa7725c7400..48e2ee8e9abd 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -26,7 +26,7 @@ use rustc_hir::def::MacroKinds; use rustc_hir::limit::Limit; use rustc_parse::parser::{ AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, - RecoverColon, RecoverComma, token_descr, + RecoverColon, RecoverComma, Recovery, token_descr, }; use rustc_session::Session; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; @@ -2170,7 +2170,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { call.span(), self.cx.current_expansion.lint_node_id, Some(self.cx.ecfg.features), - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, ); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; @@ -2220,7 +2220,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // Target doesn't matter for `cfg` parsing. Target::Crate, self.cfg().features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, parse_cfg, &CFG_TEMPLATE, ) else { From aef821794367e03e654acbb2fa8f098102b9ef64 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 2 Feb 2026 14:55:21 +0100 Subject: [PATCH 505/583] Pass `Recovery` down to rustc_parse's Parser --- compiler/rustc_attr_parsing/src/parser.rs | 4 ++++ .../do_not_recommend/does_not_acccept_args.current.stderr | 4 ++-- .../do_not_recommend/does_not_acccept_args.next.stderr | 4 ++-- .../do_not_recommend/does_not_acccept_args.rs | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 80dab0b85edd..973635f432e8 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -575,6 +575,10 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { should_emit: ShouldEmit, ) -> PResult<'sess, MetaItemListParser> { let mut parser = Parser::new(psess, tokens, None); + if let ShouldEmit::ErrorsAndLints { recovery } = should_emit { + parser = parser.recovery(recovery); + } + let mut this = MetaItemListParserContext { parser: &mut parser, should_emit }; // Presumably, the majority of the time there will only be one attr. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr index 43205bd395fc..075e4bf0384d 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr @@ -1,8 +1,8 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments --> $DIR/does_not_acccept_args.rs:12:1 | -LL | #[diagnostic::do_not_recommend(not_accepted)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr index 43205bd395fc..075e4bf0384d 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr @@ -1,8 +1,8 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments --> $DIR/does_not_acccept_args.rs:12:1 | -LL | #[diagnostic::do_not_recommend(not_accepted)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs index 918bf5a0113e..943b5a37f938 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs @@ -9,7 +9,7 @@ trait Bar {} trait Baz {} trait Boo {} -#[diagnostic::do_not_recommend(not_accepted)] +#[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] //~^ WARNING `#[diagnostic::do_not_recommend]` does not expect any arguments impl Foo for T where T: Send {} From bdd19d0db39266bd5c22348d15b8215e3a804e59 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 2 Feb 2026 15:13:38 +0100 Subject: [PATCH 506/583] clarity that the neon extension is present --- tests/ui/asm/cortex-r82.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/ui/asm/cortex-r82.rs b/tests/ui/asm/cortex-r82.rs index a64349b37af7..74313e5cb48c 100644 --- a/tests/ui/asm/cortex-r82.rs +++ b/tests/ui/asm/cortex-r82.rs @@ -22,6 +22,10 @@ use minicore::*; * A `/* */` comment indicates that the extension is being tested in the ISA level codegen test * (`tests/ui/asm/aarch64v8r.rs`) * + * Note that as we use the hard-float `aarch64v8r-unknown-none` target as the base, the neon + * extension is present (`NEON_FPm=1`). This affects which R82-specific extensions are enabled + * (see "when `NEON_FPm == 1`" note in Cortex-R82 Processor Technical Reference Manual) + * * ## References: * * - Arm Cortex-R82 Processor Technical Reference Manual Revision r3p1 (102670_0301_06_en Issue 6) @@ -55,7 +59,7 @@ pub fn mandatory_extensions() { // FEAT_nTLBPA /* FEAT_CRC32 */ /* FEAT_LSE */ - feat_rdm(); + feat_rdm(); // mandatory given that NEON_FPm=1 /* FEAT_HPDS */ /* FEAT_PAN */ // FEAT_HAFDBS @@ -68,10 +72,10 @@ pub fn mandatory_extensions() { /* FEAT_Debugv8p2 */ /* FEAT_ASMv8p2 */ // FEAT_IESB - feat_fp16(); + feat_fp16(); // mandatory given that NEON_FPm=1 // FEAT_PCSRv8p2 - feat_dotprod(); - feat_fhm(); + feat_dotprod(); // mandatory given that NEON_FPm=1 + feat_fhm(); // mandatory given that NEON_FPm=1 feat_dpb2(); /* FEAT_PAuth */ // FEAT_PACQARMA3 @@ -79,9 +83,9 @@ pub fn mandatory_extensions() { // FEAT_FPAC // FEAT_FPACCOMBINE // FEAT_CONSTPACFIELD - feat_jscvt(); + feat_jscvt(); // mandatory given that NEON_FPm=1 /* FEAT_LRCPC */ - feat_fcma(); + feat_fcma(); // mandatory given that NEON_FPm=1 // FEAT_DoPD // FEAT_SEL2 /* FEAT_S2FWB */ From 6490e0ecb5e78932f2e7f5bf5c11726f972eec8c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 2 Feb 2026 13:57:40 +0100 Subject: [PATCH 507/583] stabilize ptr_as_ref_unchecked --- compiler/rustc_resolve/src/lib.rs | 2 +- library/core/src/ptr/const_ptr.rs | 8 +++--- library/core/src/ptr/docs/as_ref.md | 8 +++--- library/core/src/ptr/mut_ptr.rs | 26 +++++++++---------- .../data_race/mixed_size_read_write_read.rs | 1 - 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 5fe1be039a88..cc3c7bc49446 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -8,6 +8,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(ptr_as_ref_unchecked))] #![feature(arbitrary_self_types)] #![feature(assert_matches)] #![feature(box_patterns)] @@ -18,7 +19,6 @@ #![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(ptr_as_ref_unchecked)] #![feature(rustc_attrs)] #![feature(trim_prefix_suffix)] #![recursion_limit = "256"] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 2566d1471ab7..75777144dfb8 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -239,7 +239,7 @@ impl *const T { /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { - /// let val_back = &*ptr; + /// let val_back = ptr.as_ref_unchecked(); /// assert_eq!(val_back, &10); /// } /// ``` @@ -259,6 +259,7 @@ impl *const T { /// /// [`is_null`]: #method.is_null /// [`as_uninit_ref`]: #method.as_uninit_ref + /// [`as_ref_unchecked`]: #method.as_ref_unchecked #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] @@ -283,15 +284,14 @@ impl *const T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { /// assert_eq!(ptr.as_ref_unchecked(), &10); /// } /// ``` - // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { diff --git a/library/core/src/ptr/docs/as_ref.md b/library/core/src/ptr/docs/as_ref.md index 0c0d2768c748..2c7d6e149b76 100644 --- a/library/core/src/ptr/docs/as_ref.md +++ b/library/core/src/ptr/docs/as_ref.md @@ -1,6 +1,7 @@ Returns `None` if the pointer is null, or else returns a shared reference to the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] -must be used instead. +must be used instead. If the value is known to be non-null, [`as_ref_unchecked`] +can be used instead. # Safety @@ -14,6 +15,5 @@ determined to be null or not. See [`is_null`] for more information. # Null-unchecked version -If you are sure the pointer can never be null and are looking for some kind of -`as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can -dereference the pointer directly. +If you are sure the pointer can never be null, you can use `as_ref_unchecked` which returns +`&mut T` instead of `Option<&mut T>`. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 20e71bc2a1a5..02e12d56fa65 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -230,7 +230,7 @@ impl *mut T { /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// /// unsafe { - /// let val_back = &*ptr; + /// let val_back = ptr.as_ref_unchecked(); /// println!("We got back the value: {val_back}!"); /// } /// ``` @@ -252,7 +252,8 @@ impl *mut T { /// For the mutable counterpart see [`as_mut`]. /// /// [`is_null`]: #method.is_null-1 - /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 + /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_ref_unchecked`]: #method.as_ref_unchecked-1 /// [`as_mut`]: #method.as_mut #[stable(feature = "ptr_as_ref", since = "1.9.0")] @@ -281,15 +282,14 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// /// unsafe { /// println!("We got back the value: {}!", ptr.as_ref_unchecked()); /// } /// ``` - // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { @@ -531,11 +531,13 @@ impl *mut T { /// Returns `None` if the pointer is null, or else returns a unique reference to /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_mut`] - /// must be used instead. + /// must be used instead. If the value is known to be non-null, [`as_mut_unchecked`] + /// can be used instead. /// /// For the shared counterpart see [`as_ref`]. /// /// [`as_uninit_mut`]: #method.as_uninit_mut + /// [`as_mut_unchecked`]: #method.as_mut_unchecked /// [`as_ref`]: pointer#method.as_ref-1 /// /// # Safety @@ -564,14 +566,13 @@ impl *mut T { /// /// # Null-unchecked version /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_mut_unchecked` that returns the `&mut T` instead of `Option<&mut T>`, know that - /// you can dereference the pointer directly. + /// If you are sure the pointer can never be null, you can use `as_mut_unchecked` which returns + /// `&mut T` instead of `Option<&mut T>`. /// /// ``` /// let mut s = [1, 2, 3]; /// let ptr: *mut u32 = s.as_mut_ptr(); - /// let first_value = unsafe { &mut *ptr }; + /// let first_value = unsafe { ptr.as_mut_unchecked() }; /// *first_value = 4; /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". @@ -603,7 +604,6 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let mut s = [1, 2, 3]; /// let ptr: *mut u32 = s.as_mut_ptr(); /// let first_value = unsafe { ptr.as_mut_unchecked() }; @@ -611,8 +611,8 @@ impl *mut T { /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". /// ``` - // FIXME: mention it in the docs for `as_mut` and `as_uninit_mut` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_mut_unchecked<'a>(self) -> &'a mut T { diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs b/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs index c84895799b69..2d3d36c0e64a 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs @@ -1,6 +1,5 @@ //@compile-flags: -Zmiri-deterministic-concurrency // A case that is not covered by `mixed_size_read_write`. -#![feature(ptr_as_ref_unchecked)] use std::sync::atomic::*; use std::thread; From 8fa27e03c0e3a080e3b030459fdb69c2ecf4bb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 2 Feb 2026 21:12:44 +0200 Subject: [PATCH 508/583] Also exclude proc-macro-srv-cli from stage 0 tests --- src/bootstrap/src/core/build_steps/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 844251f2c64d..217fa344699e 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -513,6 +513,7 @@ impl Step for RustAnalyzer { // This builds a proc macro against the bootstrap libproc_macro, which is not ABI // compatible with the ABI proc-macro-srv expects to load. cargo.arg("--exclude=proc-macro-srv"); + cargo.arg("--exclude=proc-macro-srv-cli"); } let mut skip_tests = vec![]; From c7c3266b8a073ad51e6cc1bc65ff344ad33860a6 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:48:14 +0100 Subject: [PATCH 509/583] error on unsized AnonConsts --- compiler/rustc_hir_typeck/src/lib.rs | 8 +++ tests/crashes/137582.rs | 16 ----- .../unsized-anon-const-err-1.rs | 22 +++++++ .../unsized-anon-const-err-1.stderr | 44 +++++++++++++ .../unsized-anon-const-err-2.rs | 21 ++++++ .../unsized-anon-const-err-2.stderr | 66 +++++++++++++++++++ .../unsized-anon-const-func-err.rs | 16 +++++ .../unsized-anon-const-func-err.stderr | 38 +++++++++++ .../unsized-anon-const-struct-err.rs | 16 +++++ .../unsized-anon-const-struct-err.stderr | 38 +++++++++++ 10 files changed, 269 insertions(+), 16 deletions(-) delete mode 100644 tests/crashes/137582.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 39c28c4f4e99..6e126b3013c9 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -200,6 +200,14 @@ fn typeck_with_inspect<'tcx>( let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id))); fcx.register_wf_obligation(expected_type.into(), body.value.span, wf_code); + if let hir::Node::AnonConst(_) = node { + fcx.require_type_is_sized( + expected_type, + body.value.span, + ObligationCauseCode::SizedConstOrStatic, + ); + } + fcx.check_expr_coercible_to_type(body.value, expected_type, None); fcx.write_ty(id, expected_type); diff --git a/tests/crashes/137582.rs b/tests/crashes/137582.rs deleted file mode 100644 index e21b6c9578b7..000000000000 --- a/tests/crashes/137582.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #137582 -#![feature(adt_const_params)] - -mod lib { - pub type Matrix = [&'static u32]; - - const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; - - pub struct Walk { - _p: (), - } - - impl Walk {} -} - -fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs new file mode 100644 index 000000000000..17910d52d3d7 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs @@ -0,0 +1,22 @@ +// regression test for issue #137582, where constant evaluating an unsized AnonConst would ICE + +#![feature(adt_const_params)] + +mod lib { + pub type Matrix = [&'static u32]; + + const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + //~^ ERROR the size for values of type `[&'static u32]` cannot be known at compilation time + //~| ERROR mismatched types + //~| ERROR mismatched types + + pub struct Walk { + //~^ ERROR use of unstable library feature `unsized_const_params` + _p: (), + } + + impl Walk {} + //~^ ERROR the size for values of type `[&'static u32]` cannot be known at compilation time +} + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr new file mode 100644 index 000000000000..daea55efbbc7 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr @@ -0,0 +1,44 @@ +error[E0277]: the size for values of type `[&'static u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-1.rs:8:25 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[&'static u32]` + = note: statics and constants must have a statically known size + +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-err-1.rs:13:43 + | +LL | pub struct Walk { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[&'static u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[&'static u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-1.rs:18:46 + | +LL | impl Walk {} + | ^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[&'static u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-err-1.rs:8:35 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^ expected `&u32`, found `[{integer}; 4]` + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-err-1.rs:8:34 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^^^^^^ expected `[&u32]`, found `[&u32; 4]` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs new file mode 100644 index 000000000000..81b1ec6a7719 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs @@ -0,0 +1,21 @@ +// regression test for issue #151591, where constant evaluating an unsized AnonConst would ICE + +#![feature(adt_const_params)] +#![feature(unsized_const_params)] +//~^ WARN the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + +#[derive(Clone)] +struct S; + +const A: [u8]; +//~^ ERROR free constant item without body +//~| ERROR the size for values of type `[u8]` cannot be known at compilation time + +impl Copy for S {} +//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +//~| ERROR the const parameter `N` is not constrained by the impl trait, self type, or predicates +impl Copy for S {} +//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +//~| ERROR the const parameter `M` is not constrained by the impl trait, self type, or predicates + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr new file mode 100644 index 000000000000..d538bb0af09a --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr @@ -0,0 +1,66 @@ +error: free constant item without body + --> $DIR/unsized-anon-const-err-2.rs:10:1 + | +LL | const A: [u8]; + | ^^^^^^^^^^^^^- + | | + | help: provide a definition for the constant: `= ;` + +warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unsized-anon-const-err-2.rs:4:12 + | +LL | #![feature(unsized_const_params)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:10:10 + | +LL | const A: [u8]; + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:14:31 + | +LL | impl Copy for S {} + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:17:33 + | +LL | impl Copy for S {} + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/unsized-anon-const-err-2.rs:14:6 + | +LL | impl Copy for S {} + | ^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0207]: the const parameter `M` is not constrained by the impl trait, self type, or predicates + --> $DIR/unsized-anon-const-err-2.rs:17:6 + | +LL | impl Copy for S {} + | ^^^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 6 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0207, E0277. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs new file mode 100644 index 000000000000..a299cc29fcc5 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs @@ -0,0 +1,16 @@ +// manually reduced reproduction of issue #137582, where constant evaluating an unsized AnonConst +// would ICE + +#![feature(adt_const_params)] + +fn func() {} +//~^ ERROR use of unstable library feature `unsized_const_params` + +const VALUE: [u32] = [0; 4]; +//~^ ERROR mismatched types +//~| ERROR the size for values of type `[u32]` cannot be known at compilation time + +fn main() { + func::(); + //~^ ERROR the size for values of type `[u32]` cannot be known at compilation time +} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr new file mode 100644 index 000000000000..14a41e87e4aa --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr @@ -0,0 +1,38 @@ +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-func-err.rs:6:9 + | +LL | fn func() {} + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-func-err.rs:9:14 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-func-err.rs:9:22 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^^ expected `[u32]`, found `[u32; 4]` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-func-err.rs:14:12 + | +LL | func::(); + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs new file mode 100644 index 000000000000..35407d02f6ef --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs @@ -0,0 +1,16 @@ +// manually reduced reproduction of issue #137582, where constant evaluating an unsized AnonConst +// would ICE + +#![feature(adt_const_params)] + +const VALUE: [u32] = [0; 4]; +//~^ ERROR the size for values of type `[u32]` cannot be known at compilation time +//~| ERROR mismatched types + +struct SomeStruct {} +//~^ ERROR use of unstable library feature `unsized_const_params` + +impl SomeStruct {} +//~^ ERROR the size for values of type `[u32]` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr new file mode 100644 index 000000000000..d9a13976d68a --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr @@ -0,0 +1,38 @@ +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-struct-err.rs:6:14 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-struct-err.rs:10:19 + | +LL | struct SomeStruct {} + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-struct-err.rs:13:17 + | +LL | impl SomeStruct {} + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-struct-err.rs:6:22 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^^ expected `[u32]`, found `[u32; 4]` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. From 06fe81964e924a2cd0f94fbe3859615a5cf66329 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 22:26:46 +0200 Subject: [PATCH 510/583] use sentence case for titles This makes things consistent --- src/doc/rustc-dev-guide/src/external-repos.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 2e32fcfe78c1..7ae1c881be8f 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -1,4 +1,4 @@ -# Using External Repositories +# Using external repositories The `rust-lang/rust` git repository depends on several other repos in the `rust-lang` organization. There are three main ways we use dependencies: @@ -12,7 +12,7 @@ As a general rule: changes - Use submodules for tools that are independent of the compiler -## External Dependencies (subtrees) +## External dependencies (subtrees) The following external projects are managed using some form of a `subtree`: @@ -153,7 +153,7 @@ Now you're done, the `src/tools/clippy` directory behaves as if Clippy were part of the rustc monorepo, so no one but you (or others that synchronize subtrees) actually needs to use `git subtree`. -## External Dependencies (submodules) +## External dependencies (submodules) Building Rust will also use external git repositories tracked using [git submodules]. The complete list may be found in the [`.gitmodules`] file. From 6b1d4059cbd650617c298079b6c488cb6b68086f Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Mon, 2 Feb 2026 21:14:30 +0000 Subject: [PATCH 511/583] Port rustc_preserve_ub_checks to attr parser. --- .../rustc_attr_parsing/src/attributes/crate_level.rs | 9 +++++++++ compiler/rustc_attr_parsing/src/context.rs | 4 +++- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_mir_transform/src/instsimplify.rs | 6 +++--- compiler/rustc_passes/src/check_attr.rs | 4 ++-- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index a367e699fcb9..557dfe09853b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -274,3 +274,12 @@ impl NoArgsAttributeParser for NoBuiltinsParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins; } + +pub(crate) struct RustcPreserveUbChecksParser; + +impl NoArgsAttributeParser for RustcPreserveUbChecksParser { + const PATH: &[Symbol] = &[sym::rustc_preserve_ub_checks]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0cabc0895053..736eb704b737 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -31,7 +31,8 @@ use crate::attributes::crate_level::{ CrateNameParser, CrateTypeParser, MoveSizeLimitParser, NeedsPanicRuntimeParser, NoBuiltinsParser, NoCoreParser, NoMainParser, NoStdParser, PanicRuntimeParser, PatternComplexityLimitParser, ProfilerRuntimeParser, RecursionLimitParser, - RustcCoherenceIsCoreParser, TypeLengthLimitParser, WindowsSubsystemParser, + RustcCoherenceIsCoreParser, RustcPreserveUbChecksParser, TypeLengthLimitParser, + WindowsSubsystemParser, }; use crate::attributes::debugger::DebuggerViualizerParser; use crate::attributes::deprecation::DeprecationParser; @@ -309,6 +310,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 92dda79b0920..13e36a0ed8e6 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1123,6 +1123,9 @@ pub enum AttributeKind { /// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]` RustcPassIndirectlyInNonRusticAbis(Span), + /// Represents `#[rustc_preserve_ub_checks]` + RustcPreserveUbChecks, + /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). RustcPubTransparent(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 816ed07c1dc4..4ffabf82dceb 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -133,6 +133,7 @@ impl AttributeKind { RustcParenSugar(..) => No, RustcPassByValue(..) => Yes, RustcPassIndirectlyInNonRusticAbis(..) => No, + RustcPreserveUbChecks => No, RustcPubTransparent(..) => Yes, RustcReallocator => No, RustcScalableVector { .. } => Yes, diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index fa9ceb018dd5..84d642ea4ebc 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -1,8 +1,8 @@ //! Performs various peephole optimizations. use rustc_abi::ExternAbi; -use rustc_ast::attr; -use rustc_hir::LangItem; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{LangItem, find_attr}; use rustc_middle::bug; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; @@ -31,7 +31,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let preserve_ub_checks = - attr::contains_name(tcx.hir_krate_attrs(), sym::rustc_preserve_ub_checks); + find_attr!(tcx.hir_krate_attrs(), AttributeKind::RustcPreserveUbChecks); if !preserve_ub_checks { SimplifyUbCheck { tcx }.visit_body(body); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8cf68b280850..f93d420fb06d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -318,6 +318,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcParenSugar(..) | AttributeKind::RustcPassByValue (..) | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) + | AttributeKind::RustcPreserveUbChecks | AttributeKind::RustcReallocator | AttributeKind::RustcScalableVector { .. } | AttributeKind::RustcShouldNotBeCalledOnConstItems(..) @@ -406,8 +407,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::register_tool | sym::rustc_no_implicit_bounds | sym::test_runner - | sym::reexport_test_harness_main - | sym::rustc_preserve_ub_checks, + | sym::reexport_test_harness_main, .. ] => {} [name, rest@..] => { From adb3861f98f5991703160e7d8a5e67de3bcef903 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:18:00 +0200 Subject: [PATCH 512/583] sembr src/profiling/wpa-profiling.md --- .../src/profiling/wpa-profiling.md | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md index 2267f9b490f7..eb829242e02f 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md @@ -3,13 +3,13 @@ ## Introducing WPR and WPA High-level performance analysis (including memory usage) can be performed with the Windows -Performance Recorder (WPR) and Windows Performance Analyzer (WPA). As the names suggest, WPR is for -recording system statistics (in the form of event trace log a.k.a. ETL files), while WPA is for -analyzing these ETL files. +Performance Recorder (WPR) and Windows Performance Analyzer (WPA). +As the names suggest, WPR is for recording system statistics (in the form of event trace log a.k.a. +ETL files), while WPA is for analyzing these ETL files. WPR collects system wide statistics, so it won't just record things relevant to rustc but also -everything else that's running on the machine. During analysis, we can filter to just the things we -find interesting. +everything else that's running on the machine. +During analysis, we can filter to just the things we find interesting. These tools are quite powerful but also require a bit of learning before we can successfully profile the Rust compiler. @@ -21,36 +21,42 @@ specifically designed to make analyzing rustc easier. ### Installing WPR and WPA You can install WPR and WPA as part of the Windows Performance Toolkit which itself is an option as -part of downloading the Windows Assessment and Deployment Kit (ADK). You can download the ADK -installer [here](https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install). Make -sure to select the Windows Performance Toolkit (you don't need to select anything else). +part of downloading the Windows Assessment and Deployment Kit (ADK). +You can download the ADK +installer [here](https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install). +Make sure to select the Windows Performance Toolkit (you don't need to select anything else). ## Recording -In order to perform system analysis, you'll first need to record your system with WPR. Open WPR and -at the bottom of the window select the "profiles" of the things you want to record. For looking +In order to perform system analysis, you'll first need to record your system with WPR. +Open WPR and at the bottom of the window select the "profiles" of the things you want to record. +For looking into memory usage of the rustc bootstrap process, we'll want to select the following items: * CPU usage * VirtualAlloc usage You might be tempted to record "Heap usage" as well, but this records every single heap allocation -and can be very, very expensive. For high-level analysis, it might be best to leave that turned -off. +and can be very, very expensive. +For high-level analysis, it might be best to leave that turned off. -Now we need to get our setup ready to record. For memory usage analysis, it is best to record the -stage 2 compiler build with a stage 1 compiler build with debug symbols. Having symbols in the +Now we need to get our setup ready to record. +For memory usage analysis, it is best to record the +stage 2 compiler build with a stage 1 compiler build with debug symbols. +Having symbols in the compiler we're using to build rustc will aid our analysis greatly by allowing WPA to resolve Rust -symbols correctly. Unfortunately, the stage 0 compiler does not have symbols turned on which is why +symbols correctly. +Unfortunately, the stage 0 compiler does not have symbols turned on which is why we'll need to build a stage 1 compiler and then a stage 2 compiler ourselves. -To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. This tells -rustc to generate debug information which includes stack frames when bootstrapping. +To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. +This tells rustc to generate debug information which includes stack frames when bootstrapping. Now you can build the stage 1 compiler: `x build --stage 1 -i library` or however else you want to build the stage 1 compiler. -Now that the stage 1 compiler is built, we can record the stage 2 build. Go back to WPR, click the +Now that the stage 1 compiler is built, we can record the stage 2 build. +Go back to WPR, click the "start" button and build the stage 2 compiler (e.g., `x build --stage=2 -i library`). When this process finishes, stop the recording. @@ -61,8 +67,10 @@ appears. ## Analysis -Now that our ETL file is open in WPA, we can analyze the results. First, we'll want to apply the -pre-made "profile" which will put WPA into a state conducive to analyzing rustc bootstrap. Download +Now that our ETL file is open in WPA, we can analyze the results. +First, we'll want to apply the +pre-made "profile" which will put WPA into a state conducive to analyzing rustc bootstrap. +Download the profile [here](https://github.com/wesleywiser/rustc-bootstrap-wpa-analysis/releases/download/1/rustc.generic.wpaProfile). Select the "Profiles" menu at the top, then "apply" and then choose the downloaded profile. @@ -71,8 +79,9 @@ You should see something resembling the following: ![WPA with profile applied](../img/wpa-initial-memory.png) Next, we will need to tell WPA to load and process debug symbols so that it can properly demangle -the Rust stack traces. To do this, click "Trace" and then choose "Load Symbols". This step can take -a while. +the Rust stack traces. +To do this, click "Trace" and then choose "Load Symbols". +This step can take a while. Once WPA has loaded symbols for rustc, we can expand the rustc.exe node and begin drilling down into the stack with the largest allocations. @@ -81,8 +90,8 @@ To do that, we'll expand the `[Root]` node in the "Commit Stack" column and cont until we find interesting stack frames. > Tip: After selecting the node you want to expand, press the right arrow key. This will expand the -node and put the selection on the next largest node in the expanded set. You can continue pressing -the right arrow key until you reach an interesting frame. +node and put the selection on the next largest node in the expanded set. +You can continue pressing the right arrow key until you reach an interesting frame. ![WPA with expanded stack](../img/wpa-stack.png) From f59931384687c4a9bf14fca54f5af4f0e5083150 Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Mon, 2 Feb 2026 21:19:50 +0000 Subject: [PATCH 513/583] Make parser naming consistent for dump parsers. --- .../src/attributes/rustc_dump.rs | 20 +++++++++---------- compiler/rustc_attr_parsing/src/context.rs | 14 ++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 53120dece916..71a8fb0dd47d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -7,36 +7,36 @@ use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; use crate::context::Stage; use crate::target_checking::AllowedTargets; -pub(crate) struct RustcDumpUserArgs; +pub(crate) struct RustcDumpUserArgsParser; -impl NoArgsAttributeParser for RustcDumpUserArgs { +impl NoArgsAttributeParser for RustcDumpUserArgsParser { const PATH: &[Symbol] = &[sym::rustc_dump_user_args]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpUserArgs; } -pub(crate) struct RustcDumpDefParents; +pub(crate) struct RustcDumpDefParentsParser; -impl NoArgsAttributeParser for RustcDumpDefParents { +impl NoArgsAttributeParser for RustcDumpDefParentsParser { const PATH: &[Symbol] = &[sym::rustc_dump_def_parents]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents; } -pub(crate) struct RustcDumpItemBounds; +pub(crate) struct RustcDumpItemBoundsParser; -impl NoArgsAttributeParser for RustcDumpItemBounds { +impl NoArgsAttributeParser for RustcDumpItemBoundsParser { const PATH: &[Symbol] = &[sym::rustc_dump_item_bounds]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocTy)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds; } -pub(crate) struct RustcDumpPredicates; +pub(crate) struct RustcDumpPredicatesParser; -impl NoArgsAttributeParser for RustcDumpPredicates { +impl NoArgsAttributeParser for RustcDumpPredicatesParser { const PATH: &[Symbol] = &[sym::rustc_dump_predicates]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -49,9 +49,9 @@ impl NoArgsAttributeParser for RustcDumpPredicates { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpPredicates; } -pub(crate) struct RustcDumpVtable; +pub(crate) struct RustcDumpVtableParser; -impl NoArgsAttributeParser for RustcDumpVtable { +impl NoArgsAttributeParser for RustcDumpVtableParser { const PATH: &[Symbol] = &[sym::rustc_dump_vtable]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 736eb704b737..451638725c52 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -72,8 +72,8 @@ use crate::attributes::rustc_allocator::{ RustcDeallocatorParser, RustcReallocatorParser, }; use crate::attributes::rustc_dump::{ - RustcDumpDefParents, RustcDumpItemBounds, RustcDumpPredicates, RustcDumpUserArgs, - RustcDumpVtable, + RustcDumpDefParentsParser, RustcDumpItemBoundsParser, RustcDumpPredicatesParser, + RustcDumpUserArgsParser, RustcDumpVtableParser, }; use crate::attributes::rustc_internal::{ RustcHasIncoherentInherentImplsParser, RustcLayoutParser, RustcLayoutScalarValidRangeEndParser, @@ -294,11 +294,11 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, - Single>, - Single>, - Single>, - Single>, + Single>, + Single>, + Single>, + Single>, + Single>, Single>, Single>, Single>, From 957f28c9b8c8fc1513c61657be836ceafc2eafe7 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:25:33 +0200 Subject: [PATCH 514/583] add some pauses --- src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md index eb829242e02f..6d885d303629 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md @@ -46,8 +46,9 @@ stage 2 compiler build with a stage 1 compiler build with debug symbols. Having symbols in the compiler we're using to build rustc will aid our analysis greatly by allowing WPA to resolve Rust symbols correctly. -Unfortunately, the stage 0 compiler does not have symbols turned on which is why -we'll need to build a stage 1 compiler and then a stage 2 compiler ourselves. +Unfortunately, the stage 0 compiler does not have symbols turned on, +which is why we'll need to build a stage 1 compiler, +and then a stage 2 compiler ourselves. To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. This tells rustc to generate debug information which includes stack frames when bootstrapping. From f285d5d0601d0e08db06b6beda18925337829ada Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:27:22 +0200 Subject: [PATCH 515/583] provide the full path --- src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md index 6d885d303629..a0a5bee63e9e 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md @@ -50,7 +50,7 @@ Unfortunately, the stage 0 compiler does not have symbols turned on, which is why we'll need to build a stage 1 compiler, and then a stage 2 compiler ourselves. -To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. +To do this, make sure you have set `rust.debuginfo-level = 1` in your `bootstrap.toml` file. This tells rustc to generate debug information which includes stack frames when bootstrapping. Now you can build the stage 1 compiler: `x build --stage 1 -i library` or however From 6169f2e4849f35165793ab3f810b1f9fdea6a33e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:27:41 +0200 Subject: [PATCH 516/583] sembr src/effects.md --- src/doc/rustc-dev-guide/src/effects.md | 78 +++++++++++++------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index 732ba7153116..a78428cf09eb 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -2,16 +2,17 @@ ## The `HostEffect` predicate -[`HostEffectPredicate`]s are a kind of predicate from `~const Tr` or `const Tr` -bounds. It has a trait reference, and a `constness` which could be `Maybe` or -`Const` depending on the bound. Because `~const Tr`, or rather `Maybe` bounds +[`HostEffectPredicate`]s are a kind of predicate from `~const Tr` or `const Tr` bounds. +It has a trait reference, and a `constness` which could be `Maybe` or +`Const` depending on the bound. +Because `~const Tr`, or rather `Maybe` bounds apply differently based on whichever contexts they are in, they have different -behavior than normal bounds. Where normal trait bounds on a function such as +behavior than normal bounds. +Where normal trait bounds on a function such as `T: Tr` are collected within the [`predicates_of`] query to be proven when a function is called and to be assumed within the function, bounds such as `T: ~const Tr` will behave as a normal trait bound and add `T: Tr` to the result -from `predicates_of`, but also adds a `HostEffectPredicate` to the -[`const_conditions`] query. +from `predicates_of`, but also adds a `HostEffectPredicate` to the [`const_conditions`] query. On the other hand, `T: const Tr` bounds do not change meaning across contexts, therefore they will result in `HostEffect(T: Tr, const)` being added to @@ -23,17 +24,17 @@ therefore they will result in `HostEffect(T: Tr, const)` being added to ## The `const_conditions` query -`predicates_of` represents a set of predicates that need to be proven to use an -item. For example, to use `foo` in the example below: +`predicates_of` represents a set of predicates that need to be proven to use an item. +For example, to use `foo` in the example below: ```rust fn foo() where T: Default {} ``` -We must be able to prove that `T` implements `Default`. In a similar vein, +We must be able to prove that `T` implements `Default`. +In a similar vein, `const_conditions` represents a set of predicates that need to be proven to use -an item *in const contexts*. If we adjust the example above to use `const` trait -bounds: +an item *in const contexts*. If we adjust the example above to use `const` trait bounds: ```rust const fn foo() where T: ~const Default {} @@ -45,13 +46,13 @@ prove that `T` has a const implementation of `Default`. ## Enforcement of `const_conditions` -`const_conditions` are currently checked in various places. +`const_conditions` are currently checked in various places. Every call in HIR from a const context (which includes `const fn` and `const` items) will check that `const_conditions` of the function we are calling hold. -This is done in [`FnCtxt::enforce_context_effects`]. Note that we don't check -if the function is only referred to but not called, as the following code needs -to compile: +This is done in [`FnCtxt::enforce_context_effects`]. +Note that we don't check +if the function is only referred to but not called, as the following code needs to compile: ```rust const fn hi() -> T { @@ -61,8 +62,8 @@ const X: fn() -> u32 = hi::; ``` For a trait `impl` to be well-formed, we must be able to prove the -`const_conditions` of the trait from the `impl`'s environment. This is checked -in [`wfcheck::check_impl`]. +`const_conditions` of the trait from the `impl`'s environment. +This is checked in [`wfcheck::check_impl`]. Here's an example: @@ -77,10 +78,11 @@ impl const Foo for () {} ``` Methods of trait impls must not have stricter bounds than the method of the -trait that they are implementing. To check that the methods are compatible, a +trait that they are implementing. +To check that the methods are compatible, a hybrid environment is constructed with the predicates of the `impl` plus the -predicates of the trait method, and we attempt to prove the predicates of the -impl method. We do the same for `const_conditions`: +predicates of the trait method, and we attempt to prove the predicates of the impl method. +We do the same for `const_conditions`: ```rust const trait Foo { @@ -95,10 +97,10 @@ impl Foo for Vec { } ``` -These checks are done in [`compare_method_predicate_entailment`]. A similar -function that does the same check for associated types is called -[`compare_type_predicate_entailment`]. Both of these need to consider -`const_conditions` when in const contexts. +These checks are done in [`compare_method_predicate_entailment`]. +A similar function that does the same check for associated types is called +[`compare_type_predicate_entailment`]. +Both of these need to consider `const_conditions` when in const contexts. In MIR, as part of const checking, `const_conditions` of items that are called are revalidated again in [`Checker::revalidate_conditional_constness`]. @@ -122,11 +124,12 @@ fn foo() -> impl ~const PartialEq { } ``` -Have their bounds represented differently. Unlike `const_conditions` which need +Have their bounds represented differently. +Unlike `const_conditions` which need to be proved for callers, and can be assumed inside the definition (e.g. trait bounds on functions), these bounds need to be proved at definition (at the impl, -or when returning the opaque) but can be assumed for callers. The non-const -equivalent of these bounds are called [`explicit_item_bounds`]. +or when returning the opaque) but can be assumed for callers. +The non-const equivalent of these bounds are called [`explicit_item_bounds`]. These bounds are checked in [`compare_impl_item::check_type_bounds`] for HIR typeck, [`evaluate_host_effect_from_item_bounds`] in the old solver and @@ -139,16 +142,14 @@ typeck, [`evaluate_host_effect_from_item_bounds`] in the old solver and ## Proving `HostEffectPredicate`s -`HostEffectPredicate`s are implemented both in the [old solver] and the [new -trait solver]. In general, we can prove a `HostEffect` predicate when either of -these conditions are met: +`HostEffectPredicate`s are implemented both in the [old solver] and the [new trait solver]. +In general, we can prove a `HostEffect` predicate when either of these conditions are met: * The predicate can be assumed from caller bounds; * The type has a `const` `impl` for the trait, *and* that const conditions on -the impl holds, *and* that the `explicit_implied_const_bounds` on the trait -holds; or -* The type has a built-in implementation for the trait in const contexts. For -example, `Fn` may be implemented by function items if their const conditions +the impl holds, *and* that the `explicit_implied_const_bounds` on the trait holds; or +* The type has a built-in implementation for the trait in const contexts. + For example, `Fn` may be implemented by function items if their const conditions are satisfied, or `Destruct` is implemented in const contexts if the type can be dropped at compile time. @@ -161,10 +162,11 @@ To be expanded later. ### The `#[rustc_non_const_trait_method]` attribute -This is intended for internal (standard library) usage only. With this attribute -applied to a trait method, the compiler will not check the default body of this -method for ability to run in compile time. Users of the trait will also not be -allowed to use this trait method in const contexts. This attribute is primarily +This is intended for internal (standard library) usage only. +With this attribute applied to a trait method, the compiler will not check the default body of this +method for ability to run in compile time. +Users of the trait will also not be allowed to use this trait method in const contexts. +This attribute is primarily used for constifying large traits such as `Iterator` without having to make all its methods `const` at the same time. From 5143080050451dc45f30668b788078bdba2f8fdc Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:38:03 +0200 Subject: [PATCH 517/583] some improvements --- src/doc/rustc-dev-guide/src/effects.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index a78428cf09eb..26530a30d066 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -113,7 +113,9 @@ are revalidated again in [`Checker::revalidate_conditional_constness`]. ## `explicit_implied_const_bounds` on associated types and traits -Bounds on associated types, opaque types, and supertraits such as +Bounds on associated types, opaque types, and supertraits such as the following +have their bounds represented differently: + ```rust trait Foo: ~const PartialEq { type X: ~const PartialEq; @@ -124,9 +126,8 @@ fn foo() -> impl ~const PartialEq { } ``` -Have their bounds represented differently. -Unlike `const_conditions` which need -to be proved for callers, and can be assumed inside the definition (e.g. trait +Unlike `const_conditions`, which need to be proved for callers, +and can be assumed inside the definition (e.g. trait bounds on functions), these bounds need to be proved at definition (at the impl, or when returning the opaque) but can be assumed for callers. The non-const equivalent of these bounds are called [`explicit_item_bounds`]. @@ -147,11 +148,11 @@ In general, we can prove a `HostEffect` predicate when either of these condition * The predicate can be assumed from caller bounds; * The type has a `const` `impl` for the trait, *and* that const conditions on -the impl holds, *and* that the `explicit_implied_const_bounds` on the trait holds; or + the impl holds, *and* that the `explicit_implied_const_bounds` on the trait holds; or * The type has a built-in implementation for the trait in const contexts. For example, `Fn` may be implemented by function items if their const conditions -are satisfied, or `Destruct` is implemented in const contexts if the type can -be dropped at compile time. + are satisfied, or `Destruct` is implemented in const contexts if the type can + be dropped at compile time. [old solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_trait_selection/traits/effects.rs.html [new trait solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_next_trait_solver/solve/effect_goals.rs.html From a802e7c161a351fea0a35eca5c145361c9974234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 2 Feb 2026 22:50:29 +0100 Subject: [PATCH 518/583] Remove `with_no_trimmed_paths` use in query macro --- compiler/rustc_macros/src/query.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 0cac699e5b62..0e2b16d72eb1 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -303,9 +303,7 @@ fn add_query_desc_cached_impl( #[allow(unused_variables)] pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::query::queries::#name::Key<'tcx>) -> String { let (#tcx, #key) = (tcx, key); - ::rustc_middle::ty::print::with_no_trimmed_paths!( - format!(#desc) - ) + format!(#desc) } }; From 06d17c0d3bd16286090f1db445e757875c7821c5 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:10:22 +0200 Subject: [PATCH 519/583] use convenient notation, as seen in bootstrap.example.toml --- .../rustc-dev-guide/src/backend/debugging.md | 7 +++---- .../src/backend/updating-llvm.md | 3 +-- .../src/building/compiler-documenting.md | 3 +-- .../src/building/how-to-build-and-run.md | 13 ++++++------ .../src/building/new-target.md | 12 +++-------- .../src/building/optimized-build.md | 21 +++++++------------ .../rustc-dev-guide/src/building/suggested.md | 2 +- .../rustc-dev-guide/src/compiler-debugging.md | 14 ++++++------- .../src/llvm-coverage-instrumentation.md | 6 ++---- src/doc/rustc-dev-guide/src/profiling.md | 11 +++++----- src/doc/rustc-dev-guide/src/sanitizers.md | 5 ++--- .../rustc-dev-guide/src/tests/compiletest.md | 4 +--- .../rustc-dev-guide/src/tests/directives.md | 2 +- src/doc/rustc-dev-guide/src/tests/docker.md | 2 +- src/doc/rustc-dev-guide/src/tracing.md | 2 +- 15 files changed, 42 insertions(+), 65 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 84efa72f4c46..319154f023cf 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -38,7 +38,7 @@ which means that LLVM assertion failures can show up as compiler crashes (not ICEs but "real" crashes) and other sorts of weird behavior. If you are encountering these, it is a good idea to try using a compiler with LLVM assertions enabled - either an "alt" nightly or a compiler you build yourself -by setting `[llvm] assertions=true` in your bootstrap.toml - and see whether +by setting `llvm.assertions = true` in your bootstrap.toml - and see whether anything turns up. The rustc build process builds the LLVM tools into @@ -162,12 +162,11 @@ aware of its [internal debug infrastructure][llvm-debug]. This is provided in LLVM Debug builds, which you enable for rustc LLVM builds by changing this setting in the bootstrap.toml: ``` -[llvm] # Indicates whether the LLVM assertions are enabled or not -assertions = true +llvm.assertions = true # Indicates whether the LLVM build is a Release or Debug build -optimize = false +llvm.optimize = false ``` The quick summary is: * Setting `assertions=true` enables coarse-grain debug messaging. diff --git a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md index 70deeebf8bec..5962791f64b1 100644 --- a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md +++ b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md @@ -145,8 +145,7 @@ so let's go through each in detail. This is done by having the following setting in `bootstrap.toml`: ```toml - [llvm] - download-ci-llvm = false + llvm.download-ci-llvm = false ``` 1. Test for regressions across other platforms. LLVM often has at least one bug diff --git a/src/doc/rustc-dev-guide/src/building/compiler-documenting.md b/src/doc/rustc-dev-guide/src/building/compiler-documenting.md index b031462ea15f..026a42907493 100644 --- a/src/doc/rustc-dev-guide/src/building/compiler-documenting.md +++ b/src/doc/rustc-dev-guide/src/building/compiler-documenting.md @@ -39,8 +39,7 @@ like the standard library (std) or the compiler (rustc). To create it by default with `x doc`, modify `bootstrap.toml`: ```toml - [build] - compiler-docs = true + build.compiler-docs = true ``` Note that when enabled, diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index d0a38b12c550..3e44a75ad732 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -10,7 +10,7 @@ case, `./x {check,build} library/std` should still work. In the short-term, you may need to disable `download-rustc` for `./x test library/std`. This can be done either by: 1. `./x test library/std --set rust.download-rustc=false` -2. Or set `rust.download-rustc=false` in `bootstrap.toml`. +2. Or set `rust.download-rustc = false` in `bootstrap.toml`. Unfortunately that will require building the stage 1 compiler. The bootstrap team is working on this, but implementing a maintainable fix is taking some time. @@ -278,9 +278,9 @@ Once you have successfully built `rustc`, you will have created a bunch of files in your `build` directory. In order to actually run the resulting `rustc`, we recommend creating rustup toolchains. The first command listed below creates the stage1 toolchain, which was built in the -steps above, with the name `stage1`. The second command creates the stage2 -toolchain using the stage1 compiler. This will be needed in the future -if running the entire test suite, but will not be built in this page. +steps above, with the name `stage1`. The second command creates the stage2 +toolchain using the stage1 compiler. This will be needed in the future +if running the entire test suite, but will not be built in this page. Building stage2 is done with the same `./x build` command as for stage1, specifying that the stage is 2 instead. @@ -289,7 +289,7 @@ rustup toolchain link stage1 build/host/stage1 rustup toolchain link stage2 build/host/stage2 ``` -Now you can run the `rustc` you built with via the toolchain. If you run with +Now you can run the `rustc` you built with via the toolchain. If you run with `-vV`, you should see a version number ending in `-dev`, indicating a build from your local environment: @@ -342,8 +342,7 @@ If you want to always build for other targets without needing to pass flags to ` you can configure this in the `[build]` section of your `bootstrap.toml` like so: ```toml -[build] -target = ["x86_64-unknown-linux-gnu", "wasm32-wasip1"] +build.target = ["x86_64-unknown-linux-gnu", "wasm32-wasip1"] ``` Note that building for some targets requires having external dependencies installed diff --git a/src/doc/rustc-dev-guide/src/building/new-target.md b/src/doc/rustc-dev-guide/src/building/new-target.md index d0ed787f520a..1be83e4a5a58 100644 --- a/src/doc/rustc-dev-guide/src/building/new-target.md +++ b/src/doc/rustc-dev-guide/src/building/new-target.md @@ -52,7 +52,7 @@ own preinstalled LLVM, you will need to provide `FileCheck` in some other way. On Debian-based systems, you can install the `llvm-N-tools` package (where `N` is the LLVM version number, e.g. `llvm-8-tools`). Alternately, you can specify the path to `FileCheck` with the `llvm-filecheck` config item in `bootstrap.toml` -or you can disable codegen test with the `codegen-tests` item in `bootstrap.toml`. +or you can disable codegen test with the `rust.codegen-tests` item in `bootstrap.toml`. ## Creating a target specification @@ -118,16 +118,10 @@ After this, run `cargo update -p libc` to update the lockfiles. Beware that if you patch to a local `path` dependency, this will enable warnings for that dependency. Some dependencies are not warning-free, and due -to the `deny-warnings` setting in `bootstrap.toml`, the build may suddenly start to fail. +to the `rust.deny-warnings` setting in `bootstrap.toml`, the build may suddenly start to fail. To work around warnings, you may want to: - Modify the dependency to remove the warnings -- Or for local development purposes, suppress the warnings by setting deny-warnings = false in bootstrap.toml. - -```toml -# bootstrap.toml -[rust] -deny-warnings = false -``` +- Or for local development purposes, suppress the warnings by setting `rust.deny-warnings = false` in bootstrap.toml. [`libc`]: https://crates.io/crates/libc [`cc`]: https://crates.io/crates/cc diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 46def66d1782..d11bfaa4ba00 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -14,8 +14,7 @@ enable (Thin-)LTO when building `rustc`, set the `rust.lto` config option to `"t in `bootstrap.toml`: ```toml -[rust] -lto = "thin" +rust.lto = "thin" ``` > Note that LTO for `rustc` is currently supported and tested only for @@ -35,8 +34,7 @@ want to enable the `jemalloc` allocator, you can set the `rust.jemalloc` option in `bootstrap.toml`: ```toml -[rust] -jemalloc = true +rust.jemalloc = true ``` > Note that this option is currently only supported for Linux and macOS targets. @@ -48,9 +46,8 @@ You can modify the number of codegen units for `rustc` and `libstd` in `bootstra following options: ```toml -[rust] -codegen-units = 1 -codegen-units-std = 1 +rust.codegen-units = 1 +rust.codegen-units-std = 1 ``` ## Instruction set @@ -68,9 +65,8 @@ If you also want to compile LLVM for a specific instruction set, you can set `ll in `bootstrap.toml`: ```toml -[llvm] -cxxflags = "-march=x86-64-v3" -cflags = "-march=x86-64-v3" +llvm.cxxflags = "-march=x86-64-v3" +llvm.cflags = "-march=x86-64-v3" ``` ## Profile-guided optimization @@ -108,9 +104,8 @@ like Python or LLVM. Here is an example of how can `opt-dist` be used locally (outside of CI): 1. Enable metrics in your `bootstrap.toml` file, because `opt-dist` expects it to be enabled: - ```toml - [build] - metrics = true + ```toml + build.metrics = true ``` 2. Build the tool with the following command: ```bash diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 5eb214fac904..af4dc0ab699c 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -441,7 +441,7 @@ ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc # Use nix-shell ### Note Note that when using nix on a not-NixOS distribution, it may be necessary to set -**`patch-binaries-for-nix = true` in `bootstrap.toml`**. Bootstrap tries to detect +**`build.patch-binaries-for-nix = true` in `bootstrap.toml`**. Bootstrap tries to detect whether it's running in nix and enable patching automatically, but this detection can have false negatives. diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 1944bf53378d..7b67c262c456 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -9,18 +9,17 @@ chapter](./backend/debugging.md)). ## Configuring the compiler By default, rustc is built without most debug information. To enable debug info, -set `debug = true` in your bootstrap.toml. +set `rust.debug = true` in your bootstrap.toml. -Setting `debug = true` turns on many different debug options (e.g., `debug-assertions`, +Setting `rust.debug = true` turns on many different debug options (e.g., `debug-assertions`, `debug-logging`, etc.) which can be individually tweaked if you want to, but many people -simply set `debug = true`. +simply set `rust.debug = true`. If you want to use GDB to debug rustc, please set `bootstrap.toml` with options: ```toml -[rust] -debug = true -debuginfo-level = 2 +rust.debug = true +rust.debuginfo-level = 2 ``` > NOTE: @@ -36,8 +35,7 @@ This requires at least GDB v10.2, otherwise you need to disable new symbol-mangling-version in `bootstrap.toml`. ```toml -[rust] -new-symbol-mangling = false +rust.new-symbol-mangling = false ``` > See the comments in `bootstrap.example.toml` for more info. diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index d71e51d5f61b..fbfcc4198de6 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -47,15 +47,13 @@ mandatory. # These assertions can detect malformed coverage mappings in some cases. profile = "codegen" -[build] # IMPORTANT: This tells the build system to build the LLVM profiler runtime. # Without it, the compiler can't produce coverage-instrumented binaries, # and many of the coverage tests will be skipped. -profiler = true +build.profiler = true -[rust] # Enable debug assertions in the compiler. -debug-assertions = true +rust.debug-assertions = true ``` ## Rust symbol mangling diff --git a/src/doc/rustc-dev-guide/src/profiling.md b/src/doc/rustc-dev-guide/src/profiling.md index 519d9b5488cb..06ebb8998342 100644 --- a/src/doc/rustc-dev-guide/src/profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling.md @@ -89,22 +89,21 @@ Since this doesn't seem to work with incremental compilation or `./x check`, you will be compiling rustc _a lot_. I recommend changing a few settings in `bootstrap.toml` to make it bearable: ``` -[rust] # A debug build takes _a third_ as long on my machine, # but compiling more than stage0 rustc becomes unbearably slow. -optimize = false +rust.optimize = false # We can't use incremental anyway, so we disable it for a little speed boost. -incremental = false +rust.incremental = false # We won't be running it, so no point in compiling debug checks. -debug = false +rust.debug = false # Using a single codegen unit gives less output, but is slower to compile. -codegen-units = 0 # num_cpus +rust.codegen-units = 0 # num_cpus ``` The llvm-lines output is affected by several options. -`optimize = false` increases it from 2.1GB to 3.5GB and `codegen-units = 0` to 4.1GB. +`rust.optimize = false` increases it from 2.1GB to 3.5GB and `codegen-units = 0` to 4.1GB. MIR optimizations have little impact. Compared to the default `RUSTFLAGS="-Z mir-opt-level=1"`, level 0 adds 0.3GB and level 2 removes 0.2GB. diff --git a/src/doc/rustc-dev-guide/src/sanitizers.md b/src/doc/rustc-dev-guide/src/sanitizers.md index 673d650e6050..65e1c0c70505 100644 --- a/src/doc/rustc-dev-guide/src/sanitizers.md +++ b/src/doc/rustc-dev-guide/src/sanitizers.md @@ -36,8 +36,7 @@ Highlight of the most important aspects of the implementation: when enabled in `bootstrap.toml`: ```toml - [build] - sanitizers = true + build.sanitizers = true ``` The runtimes are [placed into target libdir][sanitizer-copy]. @@ -84,7 +83,7 @@ Sanitizers are validated by code generation tests in [`tests/ui/sanitizer/`][test-ui] directory. Testing sanitizer functionality requires the sanitizer runtimes (built when -`sanitizer = true` in `bootstrap.toml`) and target providing support for particular a sanitizer. +`build.sanitizer = true` in `bootstrap.toml`) and target providing support for particular a sanitizer. When a sanitizer is unsupported on a given target, sanitizer tests will be ignored. This behaviour is controlled by compiletest `needs-sanitizer-*` directives. diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 5c4dfb6e0dd1..d7372de11ff8 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -570,9 +570,7 @@ Instrumented binaries need to be linked against the LLVM profiler runtime, so is enabled in `bootstrap.toml`: ```toml -# bootstrap.toml -[build] -profiler = true +build.profiler = true ``` This also means that they typically don't run in PR CI jobs, though they do run diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 08371a779e11..7034420e9be0 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -166,7 +166,7 @@ The following directives will check rustc build settings and target settings: - `needs-profiler-runtime` — ignores the test if the profiler runtime was not enabled for the target (`build.profiler = true` in rustc's `bootstrap.toml`) - `needs-sanitizer-support` — ignores if the sanitizer support was not enabled - for the target (`sanitizers = true` in rustc's `bootstrap.toml`) + for the target (`build.sanitizers = true` in rustc's `bootstrap.toml`) - `needs-sanitizer-{address,hwaddress,leak,memory,thread}` — ignores if the corresponding sanitizer is not enabled for the target (AddressSanitizer, hardware-assisted AddressSanitizer, LeakSanitizer, MemorySanitizer or diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index af8e0c36d050..436aa13545eb 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -21,7 +21,7 @@ The [`src/ci/docker/run.sh`] script is used to build a specific Docker image, ru build Rust within the image, and either run tests or prepare a set of archives designed for distribution. The script will mount your local Rust source tree in read-only mode, and an `obj` directory in read-write mode. All the compiler artifacts will be stored in the `obj` directory. The shell will start out in the `obj`directory. From there, it will execute `../src/ci/run.sh` which starts the build as defined by the Docker image. You can run `src/ci/docker/run.sh ` directly. A few important notes regarding the `run.sh` script: -- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). +- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). - `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). - If you are executing a "dist" job (job beginning with `dist-`), you should set the `DEPLOY=1` environment variable. - If you are executing an "alternative dist" job (job beginning with `dist-` and ending with `-alt`), you should set the `DEPLOY_ALT=1` environment variable. diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 28c0bcc737ca..323f81dcee04 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -194,7 +194,7 @@ calls to `debug!` and `trace!` are only included in the program if `rust.debug-logging=true` is turned on in bootstrap.toml (it is turned off by default), so if you don't see `DEBUG` logs, especially if you run the compiler with `RUSTC_LOG=rustc rustc some.rs` and only see -`INFO` logs, make sure that `debug-logging=true` is turned on in your bootstrap.toml. +`INFO` logs, make sure that `rust.debug-logging=true` is turned on in your bootstrap.toml. ## Logging etiquette and conventions From ed50cc909ad5e8dc1a8cdf0b16aa805028286ebf Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:10:50 +0200 Subject: [PATCH 520/583] I do not expect there will be any confusion --- src/doc/rustc-dev-guide/src/tests/directives.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 7034420e9be0..ac76a2e15d9a 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -164,9 +164,9 @@ The following directives will check rustc build settings and target settings: via `--target`, use `needs-llvm-components` instead to ensure the appropriate backend is available. - `needs-profiler-runtime` — ignores the test if the profiler runtime was not - enabled for the target (`build.profiler = true` in rustc's `bootstrap.toml`) + enabled for the target (`build.profiler = true` in `bootstrap.toml`) - `needs-sanitizer-support` — ignores if the sanitizer support was not enabled - for the target (`build.sanitizers = true` in rustc's `bootstrap.toml`) + for the target (`build.sanitizers = true` in `bootstrap.toml`) - `needs-sanitizer-{address,hwaddress,leak,memory,thread}` — ignores if the corresponding sanitizer is not enabled for the target (AddressSanitizer, hardware-assisted AddressSanitizer, LeakSanitizer, MemorySanitizer or From ca24637a66fd274087d2393176cf30bd1c1c4943 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:12:31 +0200 Subject: [PATCH 521/583] sembr src/backend/debugging.md --- .../rustc-dev-guide/src/backend/debugging.md | 93 ++++++++++--------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 319154f023cf..2f26181abfca 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -6,16 +6,16 @@ [codegen]: ./codegen.md This section is about debugging compiler bugs in code generation (e.g. why the -compiler generated some piece of code or crashed in LLVM). LLVM is a big -project on its own that probably needs to have its own debugging document (not -that I could find one). But here are some tips that are important in a rustc -context: +compiler generated some piece of code or crashed in LLVM). + LLVM is a big project on its own that probably needs to have its own debugging document (not +that I could find one). +But here are some tips that are important in a rustc context: ### Minimize the example As a general rule, compilers generate lots of information from analyzing code. -Thus, a useful first step is usually to find a minimal example. One way to do -this is to +Thus, a useful first step is usually to find a minimal example. +One way to do this is to 1. create a new crate that reproduces the issue (e.g. adding whatever crate is at fault as a dependency, and using it from there) @@ -35,14 +35,13 @@ For more discussion on methodology for steps 2 and 3 above, there is an The official compilers (including nightlies) have LLVM assertions disabled, which means that LLVM assertion failures can show up as compiler crashes (not -ICEs but "real" crashes) and other sorts of weird behavior. If you are -encountering these, it is a good idea to try using a compiler with LLVM +ICEs but "real" crashes) and other sorts of weird behavior. +If you are encountering these, it is a good idea to try using a compiler with LLVM assertions enabled - either an "alt" nightly or a compiler you build yourself -by setting `llvm.assertions = true` in your bootstrap.toml - and see whether -anything turns up. +by setting `llvm.assertions = true` in your bootstrap.toml - and see whether anything turns up. -The rustc build process builds the LLVM tools into -`./build//llvm/bin`. They can be called directly. +The rustc build process builds the LLVM tools into `./build//llvm/bin`. +They can be called directly. These tools include: * [`llc`], which compiles bitcode (`.bc` files) to executable code; this can be used to replicate LLVM backend bugs. @@ -55,7 +54,8 @@ These tools include: [`bugpoint`]: https://llvm.org/docs/Bugpoint.html By default, the Rust build system does not check for changes to the LLVM source code or -its build configuration settings. So, if you need to rebuild the LLVM that is linked +its build configuration settings. +So, if you need to rebuild the LLVM that is linked into `rustc`, first delete the file `.llvm-stamp`, which should be located in `build//llvm/`. @@ -66,26 +66,28 @@ disappear), passing `-C codegen-units=1` to rustc will make debugging easier. ### Get your hands on raw LLVM input -For rustc to generate LLVM IR, you need to pass the `--emit=llvm-ir` flag. If +For rustc to generate LLVM IR, you need to pass the `--emit=llvm-ir` flag. +If you are building via cargo, use the `RUSTFLAGS` environment variable (e.g. -`RUSTFLAGS='--emit=llvm-ir'`). This causes rustc to spit out LLVM IR into the -target directory. +`RUSTFLAGS='--emit=llvm-ir'`). +This causes rustc to spit out LLVM IR into the target directory. -`cargo llvm-ir [options] path` spits out the LLVM IR for a particular function -at `path`. (`cargo install cargo-asm` installs `cargo asm` and `cargo -llvm-ir`). `--build-type=debug` emits code for debug builds. There are also -other useful options. Also, debug info in LLVM IR can clutter the output a lot: +`cargo llvm-ir [options] path` spits out the LLVM IR for a particular function at `path`. +(`cargo install cargo-asm` installs `cargo asm` and `cargo llvm-ir`). +`--build-type=debug` emits code for debug builds. +There are also other useful options. +Also, debug info in LLVM IR can clutter the output a lot: `RUSTFLAGS="-C debuginfo=0"` is really useful. `RUSTFLAGS="-C save-temps"` outputs LLVM bitcode at -different stages during compilation, which is sometimes useful. The output LLVM -bitcode will be in `.bc` files in the compiler's output directory, set via the +different stages during compilation, which is sometimes useful. +The output LLVM bitcode will be in `.bc` files in the compiler's output directory, set via the `--out-dir DIR` argument to `rustc`. * If you are hitting an assertion failure or segmentation fault from the LLVM backend when invoking `rustc` itself, it is a good idea to try passing each - of these `.bc` files to the `llc` command, and see if you get the same - failure. (LLVM developers often prefer a bug reduced to a `.bc` file over one + of these `.bc` files to the `llc` command, and see if you get the same failure. + (LLVM developers often prefer a bug reduced to a `.bc` file over one that uses a Rust crate for its minimized reproduction.) * To get human readable versions of the LLVM bitcode, one just needs to convert @@ -112,8 +114,8 @@ llvm-args='-filter-print-funcs=EXACT_FUNCTION_NAME` (e.g. `-C llvm-args='-filter-print-funcs=_ZN11collections3str21_$LT$impl$u20$str$GT$\ 7replace17hbe10ea2e7c809b0bE'`). -That produces a lot of output into standard error, so you'll want to pipe that -to some file. Also, if you are using neither `-filter-print-funcs` nor `-C +That produces a lot of output into standard error, so you'll want to pipe that to some file. +Also, if you are using neither `-filter-print-funcs` nor `-C codegen-units=1`, then, because the multiple codegen units run in parallel, the printouts will mix together and you won't be able to read anything. @@ -125,8 +127,8 @@ printouts will mix together and you won't be able to read anything. * Within LLVM itself, calling `F.getParent()->dump()` at the beginning of `SafeStackLegacyPass::runOnFunction` will dump the whole module, which - may provide better basis for reproduction. (However, you - should be able to get that same dump from the `.bc` files dumped by + may provide better basis for reproduction. + (However, you should be able to get that same dump from the `.bc` files dumped by `-C save-temps`.) If you want just the IR for a specific function (say, you want to see why it @@ -145,10 +147,11 @@ $ ./build/$TRIPLE/llvm/bin/llvm-extract \ If you are seeing incorrect behavior due to an optimization pass, a very handy LLVM option is `-opt-bisect-limit`, which takes an integer denoting the index -value of the highest pass to run. Index values for taken passes are stable +value of the highest pass to run. + Index values for taken passes are stable from run to run; by coupling this with software that automates bisecting the -search space based on the resulting program, an errant pass can be quickly -determined. When an `-opt-bisect-limit` is specified, all runs are displayed +search space based on the resulting program, an errant pass can be quickly determined. + When an `-opt-bisect-limit` is specified, all runs are displayed to standard error, along with their index and output indicating if the pass was run or skipped. Setting the limit to an index of -1 (e.g., `RUSTFLAGS="-C llvm-args=-opt-bisect-limit=-1"`) will show all passes and @@ -189,8 +192,8 @@ specifically the `#t-compiler/wg-llvm` channel. ### Compiler options to know and love The `-C help` and `-Z help` compiler switches will list out a variety -of interesting options you may find useful. Here are a few of the most -common that pertain to LLVM development (some of them are employed in the +of interesting options you may find useful. +Here are a few of the most common that pertain to LLVM development (some of them are employed in the tutorial above): - The `--emit llvm-ir` option emits a `.ll` file with LLVM IR in textual format @@ -200,7 +203,8 @@ tutorial above): e.g. `-C llvm-args=-print-before-all` to print IR before every LLVM pass. - The `-C no-prepopulate-passes` will avoid pre-populate the LLVM pass - manager with a list of passes. This will allow you to view the LLVM + manager with a list of passes. + This will allow you to view the LLVM IR that rustc generates, not the LLVM IR after optimizations. - The `-C passes=val` option allows you to supply a space separated list of extra LLVM passes to run - The `-C save-temps` option saves all temporary output files during compilation @@ -210,18 +214,17 @@ tutorial above): - The `-Z no-parallel-backend` will disable parallel compilation of distinct compilation units - The `-Z llvm-time-trace` option will output a Chrome profiler compatible JSON file which contains details and timings for LLVM passes. -- The `-C llvm-args=-opt-bisect-limit=` option allows for bisecting LLVM - optimizations. +- The `-C llvm-args=-opt-bisect-limit=` option allows for bisecting LLVM optimizations. ### Filing LLVM bug reports When filing an LLVM bug report, you will probably want some sort of minimal -working example that demonstrates the problem. The Godbolt compiler explorer is -really helpful for this. +working example that demonstrates the problem. +The Godbolt compiler explorer is really helpful for this. 1. Once you have some LLVM IR for the problematic code (see above), you can -create a minimal working example with Godbolt. Go to -[llvm.godbolt.org](https://llvm.godbolt.org). +create a minimal working example with Godbolt. +Go to [llvm.godbolt.org](https://llvm.godbolt.org). 2. Choose `LLVM-IR` as programming language. @@ -229,8 +232,7 @@ create a minimal working example with Godbolt. Go to - There are some useful flags: `-mattr` enables target features, `-march=` selects the target, `-mcpu=` selects the CPU, etc. - Commands like `llc -march=help` output all architectures available, which - is useful because sometimes the Rust arch names and the LLVM names do not - match. + is useful because sometimes the Rust arch names and the LLVM names do not match. - If you have compiled rustc yourself somewhere, in the target directory you have binaries for `llc`, `opt`, etc. @@ -238,7 +240,8 @@ create a minimal working example with Godbolt. Go to optimizations transform it. 5. Once you have a godbolt link demonstrating the issue, it is pretty easy to - fill in an LLVM bug. Just visit their [github issues page][llvm-issues]. + fill in an LLVM bug. + Just visit their [github issues page][llvm-issues]. [llvm-issues]: https://github.com/llvm/llvm-project/issues @@ -250,8 +253,8 @@ gotten the fix yet (or perhaps you are familiar enough with LLVM to fix it yours In that case, we can sometimes opt to port the fix for the bug directly to our own LLVM fork, so that rustc can use it more easily. -Our fork of LLVM is maintained in [rust-lang/llvm-project]. Once -you've landed the fix there, you'll also need to land a PR modifying +Our fork of LLVM is maintained in [rust-lang/llvm-project]. +Once you've landed the fix there, you'll also need to land a PR modifying our submodule commits -- ask around on Zulip for help. [rust-lang/llvm-project]: https://github.com/rust-lang/llvm-project/ From 4c958e731b9ee0aa3c21c3e0eca745fb04788d31 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:18:37 +0200 Subject: [PATCH 522/583] this is text with multiple authors --- src/doc/rustc-dev-guide/src/backend/debugging.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 2f26181abfca..f18154662e69 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -7,9 +7,8 @@ This section is about debugging compiler bugs in code generation (e.g. why the compiler generated some piece of code or crashed in LLVM). - LLVM is a big project on its own that probably needs to have its own debugging document (not -that I could find one). -But here are some tips that are important in a rustc context: +LLVM is a big project that probably needs to have its own debugging document, +but following are some tips that are important in a rustc context. ### Minimize the example From b658d1521f4fcc4ead01a85c94f23aa9054cee61 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:20:22 +0200 Subject: [PATCH 523/583] a symlink is now helpfully provided --- src/doc/rustc-dev-guide/src/backend/debugging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index f18154662e69..8f0d35f0c9da 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -39,7 +39,7 @@ If you are encountering these, it is a good idea to try using a compiler with LL assertions enabled - either an "alt" nightly or a compiler you build yourself by setting `llvm.assertions = true` in your bootstrap.toml - and see whether anything turns up. -The rustc build process builds the LLVM tools into `./build//llvm/bin`. +The rustc build process builds the LLVM tools into `./build/host/llvm/bin`. They can be called directly. These tools include: * [`llc`], which compiles bitcode (`.bc` files) to executable code; this can be used to @@ -56,7 +56,7 @@ By default, the Rust build system does not check for changes to the LLVM source its build configuration settings. So, if you need to rebuild the LLVM that is linked into `rustc`, first delete the file `.llvm-stamp`, which should be located -in `build//llvm/`. +in `build/host/llvm/`. The default rustc compilation pipeline has multiple codegen units, which is hard to replicate manually and means that LLVM is called multiple times in @@ -157,7 +157,7 @@ pass was run or skipped. Setting the limit to an index of -1 (e.g., their corresponding index values. If you want to play with the optimization pipeline, you can use the [`opt`] tool -from `./build//llvm/bin/` with the LLVM IR emitted by rustc. +from `./build/host/llvm/bin/` with the LLVM IR emitted by rustc. When investigating the implementation of LLVM itself, you should be aware of its [internal debug infrastructure][llvm-debug]. From c305590083a85118afca0c136eefb39b875daeb8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:21:02 +0200 Subject: [PATCH 524/583] more clean --- src/doc/rustc-dev-guide/src/backend/debugging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 8f0d35f0c9da..c9d47181bde8 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -39,7 +39,7 @@ If you are encountering these, it is a good idea to try using a compiler with LL assertions enabled - either an "alt" nightly or a compiler you build yourself by setting `llvm.assertions = true` in your bootstrap.toml - and see whether anything turns up. -The rustc build process builds the LLVM tools into `./build/host/llvm/bin`. +The rustc build process builds the LLVM tools into `build/host/llvm/bin`. They can be called directly. These tools include: * [`llc`], which compiles bitcode (`.bc` files) to executable code; this can be used to @@ -101,7 +101,7 @@ you should: ```bash $ rustc +local my-file.rs --emit=llvm-ir -O -C no-prepopulate-passes \ -C codegen-units=1 -$ OPT=./build/$TRIPLE/llvm/bin/opt +$ OPT=build/$TRIPLE/llvm/bin/opt $ $OPT -S -O2 < my-file.ll > my ``` From b55c621216f62f7e0fb8183d0f19fa2f8ccadd31 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:22:44 +0200 Subject: [PATCH 525/583] reflow --- src/doc/rustc-dev-guide/src/backend/debugging.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index c9d47181bde8..d3e59993a86d 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -66,9 +66,8 @@ disappear), passing `-C codegen-units=1` to rustc will make debugging easier. ### Get your hands on raw LLVM input For rustc to generate LLVM IR, you need to pass the `--emit=llvm-ir` flag. -If -you are building via cargo, use the `RUSTFLAGS` environment variable (e.g. -`RUSTFLAGS='--emit=llvm-ir'`). +If you are building via cargo, +use the `RUSTFLAGS` environment variable (e.g. `RUSTFLAGS='--emit=llvm-ir'`). This causes rustc to spit out LLVM IR into the target directory. `cargo llvm-ir [options] path` spits out the LLVM IR for a particular function at `path`. From 616954b0bd1fbd5368b323317f1218f9ba62c6bd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:25:41 +0200 Subject: [PATCH 526/583] whitespace --- src/doc/rustc-dev-guide/src/backend/debugging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index d3e59993a86d..eaa9e399a052 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -146,10 +146,10 @@ $ ./build/$TRIPLE/llvm/bin/llvm-extract \ If you are seeing incorrect behavior due to an optimization pass, a very handy LLVM option is `-opt-bisect-limit`, which takes an integer denoting the index value of the highest pass to run. - Index values for taken passes are stable +Index values for taken passes are stable from run to run; by coupling this with software that automates bisecting the search space based on the resulting program, an errant pass can be quickly determined. - When an `-opt-bisect-limit` is specified, all runs are displayed +When an `-opt-bisect-limit` is specified, all runs are displayed to standard error, along with their index and output indicating if the pass was run or skipped. Setting the limit to an index of -1 (e.g., `RUSTFLAGS="-C llvm-args=-opt-bisect-limit=-1"`) will show all passes and @@ -202,7 +202,7 @@ tutorial above): pass. - The `-C no-prepopulate-passes` will avoid pre-populate the LLVM pass manager with a list of passes. - This will allow you to view the LLVM + This will allow you to view the LLVM IR that rustc generates, not the LLVM IR after optimizations. - The `-C passes=val` option allows you to supply a space separated list of extra LLVM passes to run - The `-C save-temps` option saves all temporary output files during compilation From 9bafb7744aa82f0298a961fa8fb26aed75dde88f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:27:12 +0200 Subject: [PATCH 527/583] sembr src/backend/updating-llvm.md --- .../src/backend/updating-llvm.md | 77 +++++++++---------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md index 5962791f64b1..3d0e130b6aaa 100644 --- a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md +++ b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md @@ -3,16 +3,16 @@ Rust supports building against multiple LLVM versions: -* Tip-of-tree for the current LLVM development branch is usually supported - within a few days. PRs for such fixes are tagged with `llvm-main`. +* Tip-of-tree for the current LLVM development branch is usually supported within a few days. + PRs for such fixes are tagged with `llvm-main`. * The latest released major version is always supported. * The one or two preceding major versions are usually supported. By default, Rust uses its own fork in the [rust-lang/llvm-project repository]. This fork is based on a `release/$N.x` branch of the upstream project, where `$N` is either the latest released major version, or the current major version -in release candidate phase. The fork is never based on the `main` development -branch. +in release candidate phase. +The fork is never based on the `main` development branch. Our LLVM fork only accepts: @@ -32,16 +32,15 @@ There are three types of LLVM updates, with different procedures: ## Backports (upstream supported) While the current major LLVM version is supported upstream, fixes should be -backported upstream first, and the release branch then merged back into the -Rust fork. +backported upstream first, and the release branch then merged back into the Rust fork. 1. Make sure the bugfix is in upstream LLVM. -2. If this hasn't happened already, request a backport to the upstream release - branch. If you have LLVM commit access, follow the [backport process]. - Otherwise, open an issue requesting the backport. Continue once the - backport has been approved and merged. -3. Identify the branch that rustc is currently using. The `src/llvm-project` - submodule is always pinned to a branch of the +2. If this hasn't happened already, request a backport to the upstream release branch. + If you have LLVM commit access, follow the [backport process]. + Otherwise, open an issue requesting the backport. + Continue once the backport has been approved and merged. +3. Identify the branch that rustc is currently using. + The `src/llvm-project` submodule is always pinned to a branch of the [rust-lang/llvm-project repository]. 4. Fork the rust-lang/llvm-project repository. 5. Check out the appropriate branch (typically named `rustc/a.b-yyyy-mm-dd`). @@ -51,26 +50,23 @@ Rust fork. 7. Merge the `upstream/release/$N.x` branch. 8. Push this branch to your fork. 9. Send a Pull Request to rust-lang/llvm-project to the same branch as before. - Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR - description. + Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR description. 10. Wait for the PR to be merged. -11. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with - your bugfix. This can be done locally with `git submodule update --remote - src/llvm-project` typically. +11. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with your bugfix. + This can be done locally with `git submodule update --remote src/llvm-project` typically. 12. Wait for PR to be merged. -An example PR: -[#59089](https://github.com/rust-lang/rust/pull/59089) +An example PR: [#59089](https://github.com/rust-lang/rust/pull/59089) ## Backports (upstream not supported) -Upstream LLVM releases are only supported for two to three months after the -GA release. Once upstream backports are no longer accepted, changes should be +Upstream LLVM releases are only supported for two to three months after the GA release. +Once upstream backports are no longer accepted, changes should be cherry-picked directly to our fork. 1. Make sure the bugfix is in upstream LLVM. -2. Identify the branch that rustc is currently using. The `src/llvm-project` - submodule is always pinned to a branch of the +2. Identify the branch that rustc is currently using. + The `src/llvm-project` submodule is always pinned to a branch of the [rust-lang/llvm-project repository]. 3. Fork the rust-lang/llvm-project repository. 4. Check out the appropriate branch (typically named `rustc/a.b-yyyy-mm-dd`). @@ -80,16 +76,13 @@ cherry-picked directly to our fork. 6. Cherry-pick the relevant commit(s) using `git cherry-pick -x`. 7. Push this branch to your fork. 8. Send a Pull Request to rust-lang/llvm-project to the same branch as before. - Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR - description. + Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR description. 9. Wait for the PR to be merged. -10. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with - your bugfix. This can be done locally with `git submodule update --remote - src/llvm-project` typically. +10. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with your bugfix. + This can be done locally with `git submodule update --remote src/llvm-project` typically. 11. Wait for PR to be merged. -An example PR: -[#59089](https://github.com/rust-lang/rust/pull/59089) +An example PR: [#59089](https://github.com/rust-lang/rust/pull/59089) ## New LLVM Release Updates @@ -110,14 +103,12 @@ so let's go through each in detail. 1. Create a new branch in the [rust-lang/llvm-project repository] from this `release/$N.x` branch, and name it `rustc/a.b-yyyy-mm-dd`, - where `a.b` is the current version number of LLVM in-tree - at the time of the branch, + where `a.b` is the current version number of LLVM in-tree at the time of the branch, and the remaining part is the current date. 1. Apply Rust-specific patches to the llvm-project repository. All features and bugfixes are upstream, - but there's often some weird build-related patches - that don't make sense to upstream. + but there's often some weird build-related patches that don't make sense to upstream. These patches are typically the latest patches in the rust-lang/llvm-project branch that rustc is currently using. @@ -148,9 +139,11 @@ so let's go through each in detail. llvm.download-ci-llvm = false ``` -1. Test for regressions across other platforms. LLVM often has at least one bug +1. Test for regressions across other platforms. + LLVM often has at least one bug for non-tier-1 architectures, so it's good to do some more testing before - sending this to bors! If you're low on resources you can send the PR as-is + sending this to bors! + If you're low on resources you can send the PR as-is now to bors, though, and it'll get tested anyway. Ideally, build LLVM and test it on a few platforms: @@ -167,9 +160,11 @@ so let's go through each in detail. * `./src/ci/docker/run.sh dist-various-2` * `./src/ci/docker/run.sh armhf-gnu` -1. Prepare a PR to `rust-lang/rust`. Work with maintainers of +1. Prepare a PR to `rust-lang/rust`. + Work with maintainers of `rust-lang/llvm-project` to get your commit in a branch of that repository, - and then you can send a PR to `rust-lang/rust`. You'll change at least + and then you can send a PR to `rust-lang/rust`. + You'll change at least `src/llvm-project` and will likely also change [`llvm-wrapper`] as well. @@ -191,14 +186,12 @@ so let's go through each in detail. We will often want to have those bug fixes as well. The merge process for that is to use `git merge` itself to merge LLVM's `release/a.b` branch with the branch created in step 2. - This is typically - done multiple times when necessary while LLVM's release branch is baking. + This is typically done multiple times when necessary while LLVM's release branch is baking. 1. LLVM then announces the release of version `a.b`. 1. After LLVM's official release, - we follow the process of creating a new branch on the - rust-lang/llvm-project repository again, + we follow the process of creating a new branch on the rust-lang/llvm-project repository again, this time with a new date. It is only then that the PR to update Rust to use that version is merged. From 299b429e966c4e8de65be80bb6b003c80a61348e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:36:10 +0200 Subject: [PATCH 528/583] sembr src/building/how-to-build-and-run.md --- .../src/building/how-to-build-and-run.md | 139 ++++++++++-------- 1 file changed, 76 insertions(+), 63 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 3e44a75ad732..2c127245b810 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -4,22 +4,25 @@ For `profile = "library"` users, or users who use `download-rustc = true | "if-unchanged"`, please be advised that the `./x test library/std` flow where `download-rustc` is active (i.e. no compiler changes) is currently broken. -This is tracked in . Only the `./x test` flow is affected in this +This is tracked in . +Only the `./x test` flow is affected in this case, `./x {check,build} library/std` should still work. -In the short-term, you may need to disable `download-rustc` for `./x test library/std`. This can be done either by: +In the short-term, you may need to disable `download-rustc` for `./x test library/std`. +This can be done either by: 1. `./x test library/std --set rust.download-rustc=false` 2. Or set `rust.download-rustc = false` in `bootstrap.toml`. -Unfortunately that will require building the stage 1 compiler. The bootstrap team is working on this, but +Unfortunately that will require building the stage 1 compiler. +The bootstrap team is working on this, but implementing a maintainable fix is taking some time. -The compiler is built using a tool called `x.py`. You will need to -have Python installed to run it. +The compiler is built using a tool called `x.py`. +You will need to have Python installed to run it. ## Quick Start @@ -28,7 +31,8 @@ For a less in-depth quick-start of getting the compiler running, see [quickstart ## Get the source code -The main repository is [`rust-lang/rust`][repo]. This contains the compiler, +The main repository is [`rust-lang/rust`][repo]. +This contains the compiler, the standard library (including `core`, `alloc`, `test`, `proc_macro`, etc), and a bunch of tools (e.g. `rustdoc`, the bootstrapping infrastructure, etc). @@ -86,8 +90,8 @@ cd rust ## What is `x.py`? -`x.py` is the build tool for the `rust` repository. It can build docs, run tests, and compile the -compiler and standard library. +`x.py` is the build tool for the `rust` repository. +It can build docs, run tests, and compile the compiler and standard library. This chapter focuses on the basics to be productive, but if you want to learn more about `x.py`, [read this chapter][bootstrap]. @@ -103,11 +107,13 @@ Also, using `x` rather than `x.py` is recommended as: (You can find the platform related scripts around the `x.py`, like `x.ps1`) -Notice that this is not absolute. For instance, using Nushell in VSCode on Win10, -typing `x` or `./x` still opens `x.py` in an editor rather than invoking the program. :) +Notice that this is not absolute. +For instance, using Nushell in VSCode on Win10, +typing `x` or `./x` still opens `x.py` in an editor rather than invoking the program. +:) -In the rest of this guide, we use `x` rather than `x.py` directly. The following -command: +In the rest of this guide, we use `x` rather than `x.py` directly. +The following command: ```bash ./x check @@ -164,9 +170,10 @@ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser #### Running `x.py` slightly more conveniently -There is a binary that wraps `x.py` called `x` in `src/tools/x`. All it does is -run `x.py`, but it can be installed system-wide and run from any subdirectory -of a checkout. It also looks up the appropriate version of `python` to use. +There is a binary that wraps `x.py` called `x` in `src/tools/x`. +All it does is run `x.py`, but it can be installed system-wide and run from any subdirectory +of a checkout. +It also looks up the appropriate version of `python` to use. You can install it with `cargo install --path src/tools/x`. @@ -177,18 +184,20 @@ shell to run the platform related scripts. ## Create a `bootstrap.toml` -To start, run `./x setup` and select the `compiler` defaults. This will do some initialization -and create a `bootstrap.toml` for you with reasonable defaults. If you use a different default (which +To start, run `./x setup` and select the `compiler` defaults. +This will do some initialization and create a `bootstrap.toml` for you with reasonable defaults. +If you use a different default (which you'll likely want to do if you want to contribute to an area of rust other than the compiler, such as rustdoc), make sure to read information about that default (located in `src/bootstrap/defaults`) as the build process may be different for other defaults. -Alternatively, you can write `bootstrap.toml` by hand. See `bootstrap.example.toml` for all the available -settings and explanations of them. See `src/bootstrap/defaults` for common settings to change. +Alternatively, you can write `bootstrap.toml` by hand. +See `bootstrap.example.toml` for all the available settings and explanations of them. +See `src/bootstrap/defaults` for common settings to change. If you have already built `rustc` and you change settings related to LLVM, then you may have to -execute `./x clean --all` for subsequent configuration changes to take effect. Note that `./x -clean` will not cause a rebuild of LLVM. +execute `./x clean --all` for subsequent configuration changes to take effect. +Note that `./x clean` will not cause a rebuild of LLVM. ## Common `x` commands @@ -202,28 +211,28 @@ working on `rustc`, `std`, `rustdoc`, and other tools. | `./x test` | Runs all tests | | `./x fmt` | Formats all code | -As written, these commands are reasonable starting points. However, there are -additional options and arguments for each of them that are worth learning for -serious development work. In particular, `./x build` and `./x test` -provide many ways to compile or test a subset of the code, which can save a lot -of time. +As written, these commands are reasonable starting points. +However, there are additional options and arguments for each of them that are worth learning for +serious development work. +In particular, `./x build` and `./x test` +provide many ways to compile or test a subset of the code, which can save a lot of time. Also, note that `x` supports all kinds of path suffixes for `compiler`, `library`, -and `src/tools` directories. So, you can simply run `x test tidy` instead of -`x test src/tools/tidy`. Or, `x build std` instead of `x build library/std`. +and `src/tools` directories. +So, you can simply run `x test tidy` instead of `x test src/tools/tidy`. +Or, `x build std` instead of `x build library/std`. [rust-analyzer]: suggested.html#configuring-rust-analyzer-for-rustc -See the chapters on -[testing](../tests/running.md) and [rustdoc](../rustdoc.md) for more details. +See the chapters on [testing](../tests/running.md) and [rustdoc](../rustdoc.md) for more details. ### Building the compiler Note that building will require a relatively large amount of storage space. You may want to have upwards of 10 or 15 gigabytes available to build the compiler. -Once you've created a `bootstrap.toml`, you are now ready to run -`x`. There are a lot of options here, but let's start with what is +Once you've created a `bootstrap.toml`, you are now ready to run `x`. +There are a lot of options here, but let's start with what is probably the best "go to" command for building a local compiler: ```console @@ -236,8 +245,7 @@ What this command does is: - Assemble a working stage1 sysroot, containing the stage1 compiler and stage1 standard libraries. This final product (stage1 compiler + libs built using that compiler) -is what you need to build other Rust programs (unless you use `#![no_std]` or -`#![no_core]`). +is what you need to build other Rust programs (unless you use `#![no_std]` or `#![no_core]`). You will probably find that building the stage1 `std` is a bottleneck for you, but fear not, there is a (hacky) workaround... @@ -245,12 +253,12 @@ see [the section on avoiding rebuilds for std][keep-stage]. [keep-stage]: ./suggested.md#faster-rebuilds-with---keep-stage-std -Sometimes you don't need a full build. When doing some kind of -"type-based refactoring", like renaming a method, or changing the +Sometimes you don't need a full build. +When doing some kind of "type-based refactoring", like renaming a method, or changing the signature of some function, you can use `./x check` instead for a much faster build. -Note that this whole command just gives you a subset of the full `rustc` -build. The **full** `rustc` build (what you get with `./x build +Note that this whole command just gives you a subset of the full `rustc` build. +The **full** `rustc` build (what you get with `./x build --stage 2 rustc`) has quite a few more steps: - Build `rustc` with the stage1 compiler. @@ -262,8 +270,8 @@ You almost never need to do this. ### Build specific components If you are working on the standard library, you probably don't need to build -every other default component. Instead, you can build a specific component by -providing its name, like this: +every other default component. +Instead, you can build a specific component by providing its name, like this: ```bash ./x build --stage 1 library @@ -275,11 +283,12 @@ default). ## Creating a rustup toolchain Once you have successfully built `rustc`, you will have created a bunch -of files in your `build` directory. In order to actually run the -resulting `rustc`, we recommend creating rustup toolchains. The first -command listed below creates the stage1 toolchain, which was built in the -steps above, with the name `stage1`. The second command creates the stage2 -toolchain using the stage1 compiler. This will be needed in the future +of files in your `build` directory. +In order to actually run the resulting `rustc`, we recommend creating rustup toolchains. +The first command listed below creates the stage1 toolchain, which was built in the +steps above, with the name `stage1`. +The second command creates the stage2 toolchain using the stage1 compiler. +This will be needed in the future if running the entire test suite, but will not be built in this page. Building stage2 is done with the same `./x build` command as for stage1, specifying that the stage is 2 instead. @@ -289,8 +298,8 @@ rustup toolchain link stage1 build/host/stage1 rustup toolchain link stage2 build/host/stage2 ``` -Now you can run the `rustc` you built with via the toolchain. If you run with -`-vV`, you should see a version number ending in `-dev`, indicating a build from +Now you can run the `rustc` you built with via the toolchain. +If you run with `-vV`, you should see a version number ending in `-dev`, indicating a build from your local environment: ```bash @@ -308,14 +317,18 @@ The rustup toolchain points to the specified toolchain compiled in your `build` so the rustup toolchain will be updated whenever `x build` or `x test` are run for that toolchain/stage. -**Note:** the toolchain we've built does not include `cargo`. In this case, `rustup` will +**Note:** the toolchain we've built does not include `cargo`. + In this case, `rustup` will fall back to using `cargo` from the installed `nightly`, `beta`, or `stable` toolchain -(in that order). If you need to use unstable `cargo` flags, be sure to run -`rustup install nightly` if you haven't already. See the +(in that order). + If you need to use unstable `cargo` flags, be sure to run +`rustup install nightly` if you haven't already. + See the [rustup documentation on custom toolchains](https://rust-lang.github.io/rustup/concepts/toolchains.html#custom-toolchains). **Note:** rust-analyzer and IntelliJ Rust plugin use a component called -`rust-analyzer-proc-macro-srv` to work with proc macros. If you intend to use a +`rust-analyzer-proc-macro-srv` to work with proc macros. +If you intend to use a custom toolchain for a project (e.g. via `rustup override set stage1`) you may want to build this component: @@ -368,16 +381,14 @@ cargo +stage1 build --target wasm32-wasip1 ## Other `x` commands -Here are a few other useful `x` commands. We'll cover some of them in detail -in other sections: +Here are a few other useful `x` commands. +We'll cover some of them in detail in other sections: - Building things: - `./x build` – builds everything using the stage 1 compiler, not just up to `std` - - `./x build --stage 2` – builds everything with the stage 2 compiler including - `rustdoc` -- Running tests (see the [section on running tests](../tests/running.html) for - more details): + - `./x build --stage 2` – builds everything with the stage 2 compiler including `rustdoc` +- Running tests (see the [section on running tests](../tests/running.html) for more details): - `./x test library/std` – runs the unit tests and integration tests from `std` - `./x test tests/ui` – runs the `ui` test suite - `./x test tests/ui/const-generics` - runs all the tests in @@ -389,8 +400,8 @@ in other sections: Sometimes you need to start fresh, but this is normally not the case. If you need to run this then bootstrap is most likely not acting right and -you should file a bug as to what is going wrong. If you do need to clean -everything up then you only need to run one command! +you should file a bug as to what is going wrong. +If you do need to clean everything up then you only need to run one command! ```bash ./x clean @@ -402,15 +413,17 @@ a long time even on fast computers. ## Remarks on disk space Building the compiler (especially if beyond stage 1) can require significant amounts of free disk -space, possibly around 100GB. This is compounded if you have a separate build directory for +space, possibly around 100GB. +This is compounded if you have a separate build directory for rust-analyzer (e.g. `build-rust-analyzer`). This is easy to hit with dev-desktops which have a [set disk quota](https://github.com/rust-lang/simpleinfra/blob/8a59e4faeb75a09b072671c74a7cb70160ebef50/ansible/roles/dev-desktop/defaults/main.yml#L7) -for each user, but this also applies to local development as well. Occasionally, you may need to: +for each user, but this also applies to local development as well. +Occasionally, you may need to: - Remove `build/` directory. - Remove `build-rust-analyzer/` directory (if you have a separate rust-analyzer build directory). -- Uninstall unnecessary toolchains if you use `cargo-bisect-rustc`. You can check which toolchains - are installed with `rustup toolchain list`. +- Uninstall unnecessary toolchains if you use `cargo-bisect-rustc`. + You can check which toolchains are installed with `rustup toolchain list`. [^1]: issue[#1707](https://github.com/rust-lang/rustc-dev-guide/issues/1707) From d4ff9c82273c3322c3cc49fb97731d5d3df5c020 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:44:47 +0200 Subject: [PATCH 529/583] less awkward --- src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 2c127245b810..11c4cac024b0 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -91,7 +91,7 @@ cd rust ## What is `x.py`? `x.py` is the build tool for the `rust` repository. -It can build docs, run tests, and compile the compiler and standard library. +It can build docs, run tests, and build the compiler and standard library. This chapter focuses on the basics to be productive, but if you want to learn more about `x.py`, [read this chapter][bootstrap]. @@ -192,7 +192,7 @@ as rustdoc), make sure to read information about that default (located in `src/b as the build process may be different for other defaults. Alternatively, you can write `bootstrap.toml` by hand. -See `bootstrap.example.toml` for all the available settings and explanations of them. +See `bootstrap.example.toml` for all the available settings and what they do. See `src/bootstrap/defaults` for common settings to change. If you have already built `rustc` and you change settings related to LLVM, then you may have to From e83ee8a2be96f6182703c3d994579ded413d141f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:45:01 +0200 Subject: [PATCH 530/583] missing pause --- src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 11c4cac024b0..fc8fe402bfa9 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -329,7 +329,7 @@ fall back to using `cargo` from the installed `nightly`, `beta`, or `stable` too **Note:** rust-analyzer and IntelliJ Rust plugin use a component called `rust-analyzer-proc-macro-srv` to work with proc macros. If you intend to use a -custom toolchain for a project (e.g. via `rustup override set stage1`) you may +custom toolchain for a project (e.g. via `rustup override set stage1`), you may want to build this component: ```bash From 222e5216a0cf4c7b1cdaeb82e72947275fcff8d0 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:46:00 +0200 Subject: [PATCH 531/583] sembr src/building/optimized-build.md --- .../src/building/optimized-build.md | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index d11bfaa4ba00..4953e2004a4c 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -2,15 +2,16 @@ There are multiple additional build configuration options and techniques that can be used to compile a build of `rustc` that is as optimized as possible (for example when building `rustc` for a Linux -distribution). The status of these configuration options for various Rust targets is tracked [here]. +distribution). +The status of these configuration options for various Rust targets is tracked [here]. This page describes how you can use these approaches when building `rustc` yourself. [here]: https://github.com/rust-lang/rust/issues/103595 ## Link-time optimization -Link-time optimization is a powerful compiler technique that can increase program performance. To -enable (Thin-)LTO when building `rustc`, set the `rust.lto` config option to `"thin"` +Link-time optimization is a powerful compiler technique that can increase program performance. +To enable (Thin-)LTO when building `rustc`, set the `rust.lto` config option to `"thin"` in `bootstrap.toml`: ```toml @@ -29,8 +30,8 @@ Enabling LTO on Linux has [produced] speed-ups by up to 10%. ## Memory allocator -Using a different memory allocator for `rustc` can provide significant performance benefits. If you -want to enable the `jemalloc` allocator, you can set the `rust.jemalloc` option to `true` +Using a different memory allocator for `rustc` can provide significant performance benefits. +If you want to enable the `jemalloc` allocator, you can set the `rust.jemalloc` option to `true` in `bootstrap.toml`: ```toml @@ -53,7 +54,8 @@ rust.codegen-units-std = 1 ## Instruction set By default, `rustc` is compiled for a generic (and conservative) instruction set architecture -(depending on the selected target), to make it support as many CPUs as possible. If you want to +(depending on the selected target), to make it support as many CPUs as possible. +If you want to compile `rustc` for a specific instruction set architecture, you can set the `target_cpu` compiler option in `RUSTFLAGS`: @@ -72,14 +74,16 @@ llvm.cflags = "-march=x86-64-v3" ## Profile-guided optimization Applying profile-guided optimizations (or more generally, feedback-directed optimizations) can -produce a large increase to `rustc` performance, by up to 15% ([1], [2]). However, these techniques +produce a large increase to `rustc` performance, by up to 15% ([1], [2]). +However, these techniques are not simply enabled by a configuration option, but rather they require a complex build workflow that compiles `rustc` multiple times and profiles it on selected benchmarks. There is a tool called `opt-dist` that is used to optimize `rustc` with [PGO] (profile-guided -optimizations) and [BOLT] (a post-link binary optimizer) for builds distributed to end users. You -can examine the tool, which is located in `src/tools/opt-dist`, and build a custom PGO build -workflow based on it, or try to use it directly. Note that the tool is currently quite hardcoded to +optimizations) and [BOLT] (a post-link binary optimizer) for builds distributed to end users. +You can examine the tool, which is located in `src/tools/opt-dist`, and build a custom PGO build +workflow based on it, or try to use it directly. +Note that the tool is currently quite hardcoded to the way we use it in Rust's continuous integration workflows, and it might require some custom changes to make it work in a different environment. @@ -93,9 +97,9 @@ changes to make it work in a different environment. To use the tool, you will need to provide some external dependencies: - A Python3 interpreter (for executing `x.py`). -- Compiled LLVM toolchain, with the `llvm-profdata` binary. Optionally, if you want to use BOLT, - the `llvm-bolt` and - `merge-fdata` binaries have to be available in the toolchain. +- Compiled LLVM toolchain, with the `llvm-profdata` binary. + Optionally, if you want to use BOLT, + the `llvm-bolt` and `merge-fdata` binaries have to be available in the toolchain. These dependencies are provided to `opt-dist` by an implementation of the [`Environment`] struct. It specifies directories where will the PGO/BOLT pipeline take place, and also external dependencies From 670fec6bbd14cada71c85b4af9f92c3b48d37751 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:47:52 +0200 Subject: [PATCH 532/583] reflow --- src/doc/rustc-dev-guide/src/building/new-target.md | 3 +-- src/doc/rustc-dev-guide/src/building/optimized-build.md | 5 ++--- src/doc/rustc-dev-guide/src/building/suggested.md | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/new-target.md b/src/doc/rustc-dev-guide/src/building/new-target.md index 1be83e4a5a58..73174aaa0adf 100644 --- a/src/doc/rustc-dev-guide/src/building/new-target.md +++ b/src/doc/rustc-dev-guide/src/building/new-target.md @@ -72,8 +72,7 @@ somewhat successfully, you can copy the specification into the compiler itself. You will need to add a line to the big table inside of the `supported_targets` macro in the `rustc_target::spec` module. -You will then add a corresponding file for your new target containing a -`target` function. +You will then add a corresponding file for your new target containing a `target` function. Look for existing targets to use as examples. diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 4953e2004a4c..0a6b82eba46a 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -55,9 +55,8 @@ rust.codegen-units-std = 1 By default, `rustc` is compiled for a generic (and conservative) instruction set architecture (depending on the selected target), to make it support as many CPUs as possible. -If you want to -compile `rustc` for a specific instruction set architecture, you can set the `target_cpu` compiler -option in `RUSTFLAGS`: +If you want to compile `rustc` for a specific instruction set architecture, +you can set the `target_cpu` compiler option in `RUSTFLAGS`: ```bash RUSTFLAGS="-C target_cpu=x86-64-v3" ./x build ... diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index af4dc0ab699c..3ed4f129d144 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -118,8 +118,7 @@ requires extra disk space. Selecting `vscode` in `./x setup editor` will prompt you to create a `.vscode/settings.json` file which will configure Visual Studio code. -The recommended `rust-analyzer` settings live at -[`src/etc/rust_analyzer_settings.json`]. +The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_settings.json`]. If running `./x check` on save is inconvenient, in VS Code you can use a [Build Task] instead: @@ -253,8 +252,7 @@ It can be configured through `.zed/settings.json`, as described [here](https://zed.dev/docs/configuring-languages). Selecting `zed` in `./x setup editor` will prompt you to create a `.zed/settings.json` file which will configure Zed with the recommended configuration. -The recommended `rust-analyzer` settings live -at [`src/etc/rust_analyzer_zed.json`]. +The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_zed.json`]. ## Check, check, and check again From a2fa6185513bffff922988979d7ea2a7704767f7 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:49:52 +0200 Subject: [PATCH 533/583] sembr src/compiler-debugging.md --- .../rustc-dev-guide/src/compiler-debugging.md | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 7b67c262c456..b8f6689b05b2 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -1,14 +1,16 @@ # Debugging the compiler -This chapter contains a few tips to debug the compiler. These tips aim to be -useful no matter what you are working on. Some of the other chapters have +This chapter contains a few tips to debug the compiler. +These tips aim to be useful no matter what you are working on. + Some of the other chapters have advice about specific parts of the compiler (e.g. the [Queries Debugging and Testing chapter](./incrcomp-debugging.html) or the [LLVM Debugging chapter](./backend/debugging.md)). ## Configuring the compiler -By default, rustc is built without most debug information. To enable debug info, +By default, rustc is built without most debug information. +To enable debug info, set `rust.debug = true` in your bootstrap.toml. Setting `rust.debug = true` turns on many different debug options (e.g., `debug-assertions`, @@ -45,16 +47,17 @@ You will need to rebuild the compiler after changing any configuration option. ## Suppressing the ICE file By default, if rustc encounters an Internal Compiler Error (ICE) it will dump the ICE contents to an -ICE file within the current working directory named `rustc-ice--.txt`. If this is -not desirable, you can prevent the ICE file from being created with `RUSTC_ICE=0`. +ICE file within the current working directory named `rustc-ice--.txt`. +If this is not desirable, you can prevent the ICE file from being created with `RUSTC_ICE=0`. ## Getting a backtrace [getting-a-backtrace]: #getting-a-backtrace When you have an ICE (panic in the compiler), you can set -`RUST_BACKTRACE=1` to get the stack trace of the `panic!` like in -normal Rust programs. IIRC backtraces **don't work** on MinGW, -sorry. If you have trouble or the backtraces are full of `unknown`, +`RUST_BACKTRACE=1` to get the stack trace of the `panic!` like in normal Rust programs. +IIRC backtraces **don't work** on MinGW, +sorry. +If you have trouble or the backtraces are full of `unknown`, you might want to find some way to use Linux, Mac, or MSVC on Windows. In the default configuration (without `debug` set to `true`), you don't have line numbers @@ -98,9 +101,10 @@ stack backtrace: ## `-Z` flags -The compiler has a bunch of `-Z *` flags. These are unstable flags that are only -enabled on nightly. Many of them are useful for debugging. To get a full listing -of `-Z` flags, use `-Z help`. +The compiler has a bunch of `-Z *` flags. +These are unstable flags that are only enabled on nightly. +Many of them are useful for debugging. +To get a full listing of `-Z` flags, use `-Z help`. One useful flag is `-Z verbose-internals`, which generally enables printing more info that could be useful for debugging. @@ -112,7 +116,8 @@ Right below you can find elaborate explainers on a selected few. If you want to get a backtrace to the point where the compiler emits an error message, you can pass the `-Z treat-err-as-bug=n`, which will make -the compiler panic on the `nth` error. If you leave off `=n`, the compiler will +the compiler panic on the `nth` error. +If you leave off `=n`, the compiler will assume `1` for `n` and thus panic on the first error it encounters. For example: @@ -188,13 +193,12 @@ Cool, now I have a backtrace for the error! The `-Z eagerly-emit-delayed-bugs` option makes it easy to debug delayed bugs. It turns them into normal errors, i.e. makes them visible. This can be used in -combination with `-Z treat-err-as-bug` to stop at a particular delayed bug and -get a backtrace. +combination with `-Z treat-err-as-bug` to stop at a particular delayed bug and get a backtrace. ### Getting the error creation location -`-Z track-diagnostics` can help figure out where errors are emitted. It uses `#[track_caller]` -for this and prints its location alongside the error: +`-Z track-diagnostics` can help figure out where errors are emitted. +It uses `#[track_caller]` for this and prints its location alongside the error: ``` $ RUST_BACKTRACE=1 rustc +stage1 error.rs -Z track-diagnostics @@ -236,11 +240,11 @@ For details see [the guide section on tracing](./tracing.md) ## Narrowing (Bisecting) Regressions The [cargo-bisect-rustc][bisect] tool can be used as a quick and easy way to -find exactly which PR caused a change in `rustc` behavior. It automatically -downloads `rustc` PR artifacts and tests them against a project you provide -until it finds the regression. You can then look at the PR to get more context -on *why* it was changed. See [this tutorial][bisect-tutorial] on how to use -it. +find exactly which PR caused a change in `rustc` behavior. +It automatically downloads `rustc` PR artifacts and tests them against a project you provide +until it finds the regression. +You can then look at the PR to get more context on *why* it was changed. + See [this tutorial][bisect-tutorial] on how to use it. [bisect]: https://github.com/rust-lang/cargo-bisect-rustc [bisect-tutorial]: https://rust-lang.github.io/cargo-bisect-rustc/tutorial.html @@ -250,8 +254,9 @@ it. The [rustup-toolchain-install-master][rtim] tool by kennytm can be used to download the artifacts produced by Rust's CI for a specific SHA1 -- this basically corresponds to the successful landing of some PR -- and then sets -them up for your local use. This also works for artifacts produced by `@bors -try`. This is helpful when you want to examine the resulting build of a PR +them up for your local use. +This also works for artifacts produced by `@bors try`. +This is helpful when you want to examine the resulting build of a PR without doing the build yourself. [rtim]: https://github.com/kennytm/rustup-toolchain-install-master @@ -259,10 +264,12 @@ without doing the build yourself. ## `#[rustc_*]` TEST attributes The compiler defines a whole lot of internal (perma-unstable) attributes some of which are useful -for debugging by dumping extra compiler-internal information. These are prefixed with `rustc_` and +for debugging by dumping extra compiler-internal information. +These are prefixed with `rustc_` and are gated behind the internal feature `rustc_attrs` (enabled via e.g. `#![feature(rustc_attrs)]`). -For a complete and up to date list, see [`builtin_attrs`]. More specifically, the ones marked `TEST`. +For a complete and up to date list, see [`builtin_attrs`]. +More specifically, the ones marked `TEST`. Here are some notable ones: | Attribute | Description | @@ -311,7 +318,8 @@ $ firefox maybe_init_suffix.pdf # Or your favorite pdf viewer ### Debugging type layouts The internal attribute `#[rustc_layout]` can be used to dump the [`Layout`] of -the type it is attached to. For example: +the type it is attached to. +For example: ```rust #![feature(rustc_attrs)] From faea3136bba95ceefb7a5eb0c96d001bc85bc307 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:53:58 +0200 Subject: [PATCH 534/583] whitespace --- src/doc/rustc-dev-guide/src/compiler-debugging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index b8f6689b05b2..8efcf07d5237 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -2,7 +2,7 @@ This chapter contains a few tips to debug the compiler. These tips aim to be useful no matter what you are working on. - Some of the other chapters have +Some of the other chapters have advice about specific parts of the compiler (e.g. the [Queries Debugging and Testing chapter](./incrcomp-debugging.html) or the [LLVM Debugging chapter](./backend/debugging.md)). From 8c322fcdb3298e445fabb8c5cea5cae151c3daa3 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:55:09 +0200 Subject: [PATCH 535/583] sembr src/llvm-coverage-instrumentation.md --- .../src/llvm-coverage-instrumentation.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index fbfcc4198de6..0dde8ed6c9ff 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -7,13 +7,13 @@ libraries and binaries with additional instructions and data, at compile time. The coverage instrumentation injects calls to the LLVM intrinsic instruction [`llvm.instrprof.increment`][llvm-instrprof-increment] at code branches (based on a MIR-based control flow analysis), and LLVM converts these to -instructions that increment static counters, when executed. The LLVM coverage -instrumentation also requires a [Coverage Map] that encodes source metadata, +instructions that increment static counters, when executed. +The LLVM coverage instrumentation also requires a [Coverage Map] that encodes source metadata, mapping counter IDs--directly and indirectly--to the file locations (with start and end line and column). -Rust libraries, with or without coverage instrumentation, can be linked into -instrumented binaries. When the program is executed and cleanly terminates, +Rust libraries, with or without coverage instrumentation, can be linked into instrumented binaries. +When the program is executed and cleanly terminates, LLVM libraries write the final counter values to a file (`default.profraw` or a custom file set through environment variable `LLVM_PROFILE_FILE`). @@ -22,8 +22,7 @@ files, with corresponding Coverage Maps (from matching binaries that produced them), and generate various reports for analysis, for example: Screenshot of sample `llvm-cov show` result, for function add_quoted_string -
+ src="img/llvm-cov-show-01.png" class="center"/>
Detailed instructions and examples are documented in the [rustc book][rustc-book-instrument-coverage]. @@ -39,8 +38,7 @@ When working on the coverage instrumentation code, it is usually necessary to This allows the compiler to produce instrumented binaries, and makes it possible to run the full coverage test suite. -Enabling debug assertions in the compiler and in LLVM is recommended, but not -mandatory. +Enabling debug assertions in the compiler and in LLVM is recommended, but not mandatory. ```toml # Similar to the "compiler" profile, but also enables debug assertions in LLVM. @@ -60,8 +58,8 @@ rust.debug-assertions = true `-C instrument-coverage` automatically enables Rust symbol mangling `v0` (as if the user specified `-C symbol-mangling-version=v0` option when invoking -`rustc`) to ensure consistent and reversible name mangling. This has two -important benefits: +`rustc`) to ensure consistent and reversible name mangling. +This has two important benefits: 1. LLVM coverage tools can analyze coverage over multiple runs, including some changes to source code; so mangled names must be consistent across compilations. @@ -71,8 +69,8 @@ important benefits: ## The LLVM profiler runtime -Coverage data is only generated by running the executable Rust program. `rustc` -statically links coverage-instrumented binaries with LLVM runtime code +Coverage data is only generated by running the executable Rust program. +`rustc` statically links coverage-instrumented binaries with LLVM runtime code ([compiler-rt][compiler-rt-profile]) that implements program hooks (such as an `exit` hook) to write the counter values to the `.profraw` file. From 48760618e8ecdd5eab2acdb4f1a9c8f7d9416b99 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 01:02:14 +0200 Subject: [PATCH 536/583] replace html with markdown --- src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index 0dde8ed6c9ff..8e94928c8a1a 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -21,8 +21,7 @@ Developers use existing LLVM coverage analysis tools to decode `.profraw` files, with corresponding Coverage Maps (from matching binaries that produced them), and generate various reports for analysis, for example: -Screenshot of sample `llvm-cov show` result, for function add_quoted_string
+![Screenshot of sample `llvm-cov show` result, for function add_quoted_string](img/llvm-cov-show-01.png) Detailed instructions and examples are documented in the [rustc book][rustc-book-instrument-coverage]. From 0c6e040daddbaef2badd583de2beed043068fa11 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 01:03:06 +0200 Subject: [PATCH 537/583] sembr src/profiling.md --- src/doc/rustc-dev-guide/src/profiling.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling.md b/src/doc/rustc-dev-guide/src/profiling.md index 06ebb8998342..66eabe7d8c93 100644 --- a/src/doc/rustc-dev-guide/src/profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling.md @@ -33,10 +33,11 @@ Since most of the time compiling rustc is spent in LLVM, the idea is that by reducing the amount of code passed to LLVM, compiling rustc gets faster. To use `cargo-llvm-lines` together with somewhat custom rustc build process, you can use -`-C save-temps` to obtain required LLVM IR. The option preserves temporary work products -created during compilation. Among those is LLVM IR that represents an input to the -optimization pipeline; ideal for our purposes. It is stored in files with `*.no-opt.bc` -extension in LLVM bitcode format. +`-C save-temps` to obtain required LLVM IR. +The option preserves temporary work products created during compilation. +Among those is LLVM IR that represents an input to the +optimization pipeline; ideal for our purposes. +It is stored in files with `*.no-opt.bc` extension in LLVM bitcode format. Example usage: ``` @@ -105,7 +106,8 @@ rust.codegen-units = 0 # num_cpus The llvm-lines output is affected by several options. `rust.optimize = false` increases it from 2.1GB to 3.5GB and `codegen-units = 0` to 4.1GB. -MIR optimizations have little impact. Compared to the default `RUSTFLAGS="-Z +MIR optimizations have little impact. +Compared to the default `RUSTFLAGS="-Z mir-opt-level=1"`, level 0 adds 0.3GB and level 2 removes 0.2GB. As of July 2022, inlining happens in LLVM and GCC codegen backends, From 809bf7922fc47eb4bf6ea59fde8db66779ae40fb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 01:04:49 +0200 Subject: [PATCH 538/583] sembr src/tests/docker.md --- src/doc/rustc-dev-guide/src/tests/docker.md | 60 +++++++++++++-------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index 436aa13545eb..94fdf34dc80d 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -1,12 +1,14 @@ # Testing with Docker -The [`src/ci/docker`] directory includes [Docker] image definitions for Linux-based jobs executed on GitHub Actions (non-Linux jobs run outside Docker). You can run these jobs on your local development machine, which can be -helpful to test environments different from your local system. You will -need to install Docker on a Linux, Windows, or macOS system (typically Linux +The [`src/ci/docker`] directory includes [Docker] image definitions for Linux-based jobs executed on GitHub Actions (non-Linux jobs run outside Docker). +You can run these jobs on your local development machine, which can be +helpful to test environments different from your local system. +You will need to install Docker on a Linux, Windows, or macOS system (typically Linux will be much faster than Windows or macOS because the latter use virtual machines to emulate a Linux environment). -Jobs running in CI are configured through a set of bash scripts, and it is not always trivial to reproduce their behavior locally. If you want to run a CI job locally in the simplest way possible, you can use a provided helper `citool` that tries to replicate what happens on CI as closely as possible: +Jobs running in CI are configured through a set of bash scripts, and it is not always trivial to reproduce their behavior locally. +If you want to run a CI job locally in the simplest way possible, you can use a provided helper `citool` that tries to replicate what happens on CI as closely as possible: ```bash cargo run --manifest-path src/ci/citool/Cargo.toml run-local @@ -18,39 +20,51 @@ If the above script does not work for you, you would like to have more control o ## The `run.sh` script The [`src/ci/docker/run.sh`] script is used to build a specific Docker image, run it, -build Rust within the image, and either run tests or prepare a set of archives designed for distribution. The script will mount your local Rust source tree in read-only mode, and an `obj` directory in read-write mode. All the compiler artifacts will be stored in the `obj` directory. The shell will start out in the `obj`directory. From there, it will execute `../src/ci/run.sh` which starts the build as defined by the Docker image. +build Rust within the image, and either run tests or prepare a set of archives designed for distribution. +The script will mount your local Rust source tree in read-only mode, and an `obj` directory in read-write mode. +All the compiler artifacts will be stored in the `obj` directory. +The shell will start out in the `obj`directory. +From there, it will execute `../src/ci/run.sh` which starts the build as defined by the Docker image. -You can run `src/ci/docker/run.sh ` directly. A few important notes regarding the `run.sh` script: -- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). -- `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). +You can run `src/ci/docker/run.sh ` directly. +A few important notes regarding the `run.sh` script: +- When executed on CI, the script expects that all submodules are checked out. + If some submodule that is accessed by the job is not available, the build will result in an error. + You should thus make sure that you have all required submodules checked out locally. + You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). +- `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. + Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). - If you are executing a "dist" job (job beginning with `dist-`), you should set the `DEPLOY=1` environment variable. - If you are executing an "alternative dist" job (job beginning with `dist-` and ending with `-alt`), you should set the `DEPLOY_ALT=1` environment variable. -- Some of the std tests require IPv6 support. Docker on Linux seems to have it - disabled by default. Run the commands in [`enable-docker-ipv6.sh`] to enable - IPv6 before creating the container. This only needs to be done once. +- Some of the std tests require IPv6 support. + Docker on Linux seems to have it disabled by default. + Run the commands in [`enable-docker-ipv6.sh`] to enable IPv6 before creating the container. + This only needs to be done once. ### Interactive mode -Sometimes, it can be useful to build a specific Docker image, and then run custom commands inside it, so that you can experiment with how the given system behaves. You can do that using an interactive mode, which will +Sometimes, it can be useful to build a specific Docker image, and then run custom commands inside it, so that you can experiment with how the given system behaves. +You can do that using an interactive mode, which will start a bash shell in the container, using `src/ci/docker/run.sh --dev `. -When inside the Docker container, you can run individual commands to do specific tasks. For -example, you can run `../x test tests/ui` to just run UI tests. +When inside the Docker container, you can run individual commands to do specific tasks. +For example, you can run `../x test tests/ui` to just run UI tests. Some additional notes about using the interactive mode: - The container will be deleted automatically when you exit the shell, however - the build artifacts persist in the `obj` directory. If you are switching - between different Docker images, the artifacts from previous environments - stored in the `obj` directory may confuse the build system. Sometimes you - will need to delete parts or all of the `obj` directory before building + the build artifacts persist in the `obj` directory. + If you are switching between different Docker images, the artifacts from previous environments + stored in the `obj` directory may confuse the build system. + Sometimes you will need to delete parts or all of the `obj` directory before building inside the container. -- The container is bare-bones, with only a minimal set of packages. You may - want to install some things like `apt install less vim`. -- You can open multiple shells in the container. First you need the container +- The container is bare-bones, with only a minimal set of packages. + You may want to install some things like `apt install less vim`. +- You can open multiple shells in the container. + First you need the container name (a short hash), which is displayed in the shell prompt, or you can run - `docker container ls` outside of the container to list the available - containers. With the container name, run `docker exec -it + `docker container ls` outside of the container to list the available containers. + With the container name, run `docker exec -it /bin/bash` where `` is the container name like `4ba195e95cef`. [Docker]: https://www.docker.com/ From 06af960fba2777f8865351e4cc256f82fee911c9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 01:10:53 +0200 Subject: [PATCH 539/583] sounds better as separate sentences --- src/doc/rustc-dev-guide/src/tests/docker.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index 94fdf34dc80d..51beee8a866c 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -31,9 +31,11 @@ A few important notes regarding the `run.sh` script: - When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. - You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). + You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules + Note that this might not be enough for the given CI job that you are trying to execute though. - `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. - Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). + Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments + This is a part of the complexity that makes it difficult to run CI jobs locally. - If you are executing a "dist" job (job beginning with `dist-`), you should set the `DEPLOY=1` environment variable. - If you are executing an "alternative dist" job (job beginning with `dist-` and ending with `-alt`), you should set the `DEPLOY_ALT=1` environment variable. - Some of the std tests require IPv6 support. From 3ea1571787bbfdbdf0596e4c73a08e1257ac89d4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:54:07 +0200 Subject: [PATCH 540/583] reflow --- src/doc/rustc-dev-guide/src/compiler-debugging.md | 3 +-- src/doc/rustc-dev-guide/src/tests/compiletest.md | 3 +-- src/doc/rustc-dev-guide/src/tracing.md | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 8efcf07d5237..d4c599a6edf7 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -55,8 +55,7 @@ If this is not desirable, you can prevent the ICE file from being created with ` When you have an ICE (panic in the compiler), you can set `RUST_BACKTRACE=1` to get the stack trace of the `panic!` like in normal Rust programs. -IIRC backtraces **don't work** on MinGW, -sorry. +IIRC backtraces **don't work** on MinGW, sorry. If you have trouble or the backtraces are full of `unknown`, you might want to find some way to use Linux, Mac, or MSVC on Windows. diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index d7372de11ff8..959a73a5a9ad 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -277,8 +277,7 @@ the debugger currently being used: gdb is in a range (inclusive) - `min-lldb-version: 310` — ignores the test if the version of lldb is below the given version - `rust-lldb` — ignores the test if lldb is not contain the Rust plugin. - NOTE: The "Rust" version of LLDB doesn't exist anymore, so this will always be - ignored. + NOTE: The "Rust" version of LLDB doesn't exist anymore, so this will always be ignored. This should probably be removed. By passing the `--debugger` option to compiletest, you can specify a single debugger to run tests with. diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 323f81dcee04..2ad035bbafbc 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -74,8 +74,7 @@ RUSTC_LOG=[typeck] The query arguments are included as a tracing field which means that you can filter on the debug display of the arguments. For example, the `typeck` query has an argument `key: LocalDefId` of what is being checked. -You can use a regex to match on that `LocalDefId` to log type checking for a specific -function: +You can use a regex to match on that `LocalDefId` to log type checking for a specific function: ``` RUSTC_LOG=[typeck{key=.*name_of_item.*}] From 28feae0c875f8e95ea667abd5d32b49bcf978164 Mon Sep 17 00:00:00 2001 From: ltdk Date: Mon, 2 Feb 2026 17:34:15 -0500 Subject: [PATCH 541/583] Move bigint helper tracking issues --- library/core/src/lib.rs | 4 ++- library/core/src/num/int_macros.rs | 26 ++++++++++---------- library/core/src/num/uint_macros.rs | 24 +++++++++--------- library/coretests/tests/lib.rs | 4 ++- library/stdarch/crates/core_arch/src/lib.rs | 1 - tests/assembly-llvm/x86_64-bigint-helpers.rs | 1 - tests/codegen-llvm/bigint-helpers.rs | 1 - 7 files changed, 31 insertions(+), 30 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e8c9d26fb3b5..432ca50b3361 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -97,7 +97,6 @@ // tidy-alphabetical-start #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] -#![feature(bigint_helper_methods)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(cfg_select)] @@ -107,6 +106,7 @@ #![feature(const_destruct)] #![feature(const_eval_select)] #![feature(const_select_unpredictable)] +#![feature(const_unsigned_bigint_helpers)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] @@ -120,6 +120,7 @@ #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] +#![feature(signed_bigint_helpers)] #![feature(slice_ptr_get)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] @@ -129,6 +130,7 @@ #![feature(unsafe_pinned)] #![feature(utf16_extra)] #![feature(variant_count)] +#![feature(widening_mul)] // tidy-alphabetical-end // // Language features: diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 9a27f8a0b5e6..b21865a9ae54 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2522,7 +2522,7 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// // Only the most significant word is signed. /// // #[doc = concat!("// 10 MAX (a = 10 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")] @@ -2544,7 +2544,7 @@ macro_rules! int_impl { /// /// assert_eq!((sum1, sum0), (6, 8)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2625,7 +2625,7 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// // Only the most significant word is signed. /// // #[doc = concat!("// 6 8 (a = 6 × 2^", stringify!($BITS), " + 8)")] @@ -2647,7 +2647,7 @@ macro_rules! int_impl { /// #[doc = concat!("assert_eq!((diff1, diff0), (10, ", stringify!($UnsignedT), "::MAX));")] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2717,12 +2717,12 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] /// assert_eq!(5i32.widening_mul(-2), (4294967286, -1)); /// assert_eq!(1_000_000_000i32.widening_mul(-10), (2884901888, -3)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "widening_mul", issue = "152016")] + #[rustc_const_unstable(feature = "widening_mul", issue = "152016")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2747,7 +2747,7 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// assert_eq!(5i32.carrying_mul(-2, 0), (4294967286, -1)); /// assert_eq!(5i32.carrying_mul(-2, 10), (0, 0)); /// assert_eq!(1_000_000_000i32.carrying_mul(-10, 0), (2884901888, -3)); @@ -2757,8 +2757,8 @@ macro_rules! int_impl { "(", stringify!($SelfT), "::MAX.unsigned_abs() + 1, ", stringify!($SelfT), "::MAX / 2));" )] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] + #[rustc_const_unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2784,7 +2784,7 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// assert_eq!(5i32.carrying_mul_add(-2, 0, 0), (4294967286, -1)); /// assert_eq!(5i32.carrying_mul_add(-2, 10, 10), (10, 0)); /// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 0, 0), (2884901888, -3)); @@ -2794,8 +2794,8 @@ macro_rules! int_impl { "(", stringify!($UnsignedT), "::MAX, ", stringify!($SelfT), "::MAX / 2));" )] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] + #[rustc_const_unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c48320c0eab3..5c263ea845cc 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2807,7 +2807,7 @@ macro_rules! uint_impl { /// assert_eq!((sum1, sum0), (9, 6)); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2899,7 +2899,7 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")] /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3011,14 +3011,14 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".widening_mul(7), (35, 0));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.widening_mul(", stringify!($SelfT), "::MAX), (1, ", stringify!($SelfT), "::MAX - 1));")] /// ``` /// /// Compared to other `*_mul` methods: /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::widening_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, 3));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::overflowing_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, true));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::wrapping_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), 0);")] @@ -3028,12 +3028,12 @@ macro_rules! uint_impl { /// Please note that this example is shared among integer types, which is why `u32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] /// assert_eq!(5u32.widening_mul(2), (10, 0)); /// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "widening_mul", issue = "152016")] + #[rustc_const_unstable(feature = "widening_mul", issue = "152016")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3072,7 +3072,7 @@ macro_rules! uint_impl { /// implementing it for wider-than-native types. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// fn scalar_mul_eq(little_endian_digits: &mut Vec, multiplicand: u16) { /// let mut carry = 0; /// for d in little_endian_digits.iter_mut() { @@ -3097,7 +3097,7 @@ macro_rules! uint_impl { /// except that it gives the value of the overflow instead of just whether one happened: /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// let r = u8::carrying_mul(7, 13, 0); /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13)); /// let r = u8::carrying_mul(13, 42, 0); @@ -3109,14 +3109,14 @@ macro_rules! uint_impl { /// [`wrapping_add`](Self::wrapping_add) methods: /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// assert_eq!( /// 789_u16.carrying_mul(456, 123).0, /// 789_u16.wrapping_mul(456).wrapping_add(123), /// ); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3182,7 +3182,7 @@ macro_rules! uint_impl { /// ); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 9054eada12fb..d085e4ad1a8f 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -8,7 +8,6 @@ #![feature(ascii_char_variants)] #![feature(async_iter_from_iter)] #![feature(async_iterator)] -#![feature(bigint_helper_methods)] #![feature(bool_to_result)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] @@ -34,6 +33,7 @@ #![feature(const_result_trait_fn)] #![feature(const_select_unpredictable)] #![feature(const_trait_impl)] +#![feature(const_unsigned_bigint_helpers)] #![feature(control_flow_ok)] #![feature(core_float_math)] #![feature(core_intrinsics)] @@ -98,6 +98,7 @@ #![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(result_option_map_or_default)] +#![feature(signed_bigint_helpers)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] #![feature(slice_internals)] @@ -122,6 +123,7 @@ #![feature(uint_gather_scatter_bits)] #![feature(unsize)] #![feature(unwrap_infallible)] +#![feature(widening_mul)] // tidy-alphabetical-end #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 3992eaee30fd..039a4c4411f2 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -33,7 +33,6 @@ f16, aarch64_unstable_target_feature, target_feature_inline_always, - bigint_helper_methods, funnel_shifts, avx10_target_feature, const_trait_impl, diff --git a/tests/assembly-llvm/x86_64-bigint-helpers.rs b/tests/assembly-llvm/x86_64-bigint-helpers.rs index 9d998a31cf30..d5d1eba99f39 100644 --- a/tests/assembly-llvm/x86_64-bigint-helpers.rs +++ b/tests/assembly-llvm/x86_64-bigint-helpers.rs @@ -4,7 +4,6 @@ //@ compile-flags: -C llvm-args=-x86-asm-syntax=intel #![no_std] -#![feature(bigint_helper_methods)] // This checks that the `carrying_add` and `borrowing_sub` implementation successfully chain, // to catch issues like diff --git a/tests/codegen-llvm/bigint-helpers.rs b/tests/codegen-llvm/bigint-helpers.rs index ec70a3eabedb..404dc901de8c 100644 --- a/tests/codegen-llvm/bigint-helpers.rs +++ b/tests/codegen-llvm/bigint-helpers.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C opt-level=3 #![crate_type = "lib"] -#![feature(bigint_helper_methods)] // Note that there's also an assembly test for this, which is what checks for // the `ADC` (Add with Carry) instruction on x86 now that the IR we emit uses From 1c4940b2bedafe80058fa81d283c6de32307ed4f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 3 Feb 2026 09:29:54 +1100 Subject: [PATCH 542/583] Remove an unneeded `HashStable` derive. This has the nice side-effect of eliminating `rustc_codegen_ssa`'s dependency on `rustc_query_system`. (Indeed, looking through such dependencies was how I found this.) --- Cargo.lock | 1 - compiler/rustc_codegen_ssa/Cargo.toml | 1 - compiler/rustc_codegen_ssa/src/lib.rs | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02459afa9072..d3bd5d7b71b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3684,7 +3684,6 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", - "rustc_query_system", "rustc_serialize", "rustc_session", "rustc_span", diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 9c5a3d839ceb..3d045a02fef1 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -28,7 +28,6 @@ rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } -rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 3ffc16d49ac1..904c74944959 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -24,7 +24,7 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir::CRATE_HIR_ID; use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; -use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_macros::{Decodable, Encodable}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::lint::LevelAndSource; @@ -175,7 +175,7 @@ bitflags::bitflags! { } } -#[derive(Clone, Debug, Encodable, Decodable, HashStable)] +#[derive(Clone, Debug, Encodable, Decodable)] pub struct NativeLib { pub kind: NativeLibKind, pub name: Symbol, From 3a5d7df8418b6d287a9c258fc3bd5e6b2e1746dd Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 3 Feb 2026 15:46:05 +1100 Subject: [PATCH 543/583] Add a useful comment on `rustc_codegen_ssa::NativeLib`. --- compiler/rustc_codegen_ssa/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 904c74944959..5cca916c17b6 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -175,6 +175,11 @@ bitflags::bitflags! { } } +// This is the same as `rustc_session::cstore::NativeLib`, except: +// - (important) the `foreign_module` field is missing, because it contains a `DefId`, which can't +// be encoded with `FileEncoder`. +// - (less important) the `verbatim` field is a `bool` rather than an `Option`, because here +// we can treat `false` and `absent` the same. #[derive(Clone, Debug, Encodable, Decodable)] pub struct NativeLib { pub kind: NativeLibKind, From f14e3ee38ff234ca00cb73b61bf306316b9ab91d Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 15 Jan 2026 06:12:57 +0100 Subject: [PATCH 544/583] compiletest: Don't assume `aux-crate` becomes a `*.so` with `no-prefer-dynamic` Since it does not make sense to do so. If someone prefers no dynamic stuff, the last thing they want to look for is an `.so` file. Also add a regression test. Without the fix, the test fails with: error: test compilation failed although it shouldn't! --- stderr ------------------------------- error: extern location for no_prefer_dynamic_lib does not exist: .../auxiliary/libno_prefer_dynamic_lib.so --> .../no-prefer-dynamic-means-no-so.rs:9:5 | LL | no_prefer_dynamic_lib::return_42(); | ^^^^^^^^^^^^^^^^^^^^^ --- src/tools/compiletest/src/runtest.rs | 2 +- .../auxiliary/no_prefer_dynamic_lib.rs | 10 ++++++++++ .../no-prefer-dynamic-means-no-so.rs | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs create mode 100644 tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index ef90b81fb2a7..1804490a5f12 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1438,7 +1438,7 @@ impl<'test> TestCx<'test> { } else if aux_type.is_some() { panic!("aux_type {aux_type:?} not expected"); } else if aux_props.no_prefer_dynamic { - (AuxType::Dylib, None) + (AuxType::Lib, None) } else if self.config.target.contains("emscripten") || (self.config.target.contains("musl") && !aux_props.force_host diff --git a/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs b/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs new file mode 100644 index 000000000000..6688dadbab24 --- /dev/null +++ b/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs @@ -0,0 +1,10 @@ +//@ no-prefer-dynamic + +//! Since this is `no-prefer-dynamic` we expect compiletest to _not_ look for +//! this create as `libno_prefer_dynamic_lib.so`. + +#![crate_type = "rlib"] + +pub fn return_42() -> i32 { + 42 +} diff --git a/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs b/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs new file mode 100644 index 000000000000..b7e8fae506c3 --- /dev/null +++ b/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs @@ -0,0 +1,10 @@ +//! Since we and our `aux-crate` is `no-prefer-dynamic`, we expect compiletest +//! to _not_ look for `libno_prefer_dynamic_lib.so`. + +//@ check-pass +//@ no-prefer-dynamic +//@ aux-crate: no_prefer_dynamic_lib=no_prefer_dynamic_lib.rs + +fn main() { + no_prefer_dynamic_lib::return_42(); +} From c07f10c2c1296431efba33bb52c64ee72eb7146f Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 1 Feb 2026 14:25:18 +0100 Subject: [PATCH 545/583] Convert to inline diagnostics in `rustc_driver_impl` --- Cargo.lock | 1 - compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/messages.ftl | 29 ------------ compiler/rustc_driver_impl/src/lib.rs | 3 -- .../src/session_diagnostics.rs | 44 +++++++++++-------- 5 files changed, 25 insertions(+), 53 deletions(-) delete mode 100644 compiler/rustc_driver_impl/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..91fce715c8cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3789,7 +3789,6 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", - "rustc_fluent_macro", "rustc_hir_analysis", "rustc_hir_pretty", "rustc_hir_typeck", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..d97c552b412b 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -21,7 +21,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl deleted file mode 100644 index b62cdc35f513..000000000000 --- a/compiler/rustc_driver_impl/messages.ftl +++ /dev/null @@ -1,29 +0,0 @@ -driver_impl_cant_emit_mir = could not emit MIR: {$error} - -driver_impl_ice = the compiler unexpectedly panicked. this is a bug. -driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} -driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly -driver_impl_ice_bug_report_update_note = please make sure that you have updated to the latest nightly -driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden - -driver_impl_ice_flags = compiler flags: {$flags} -driver_impl_ice_path = please attach the file at `{$path}` to your bug report -driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error} -driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}` -driver_impl_ice_version = rustc {$version} running on {$triple} - -driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}` - -driver_impl_rlink_empty_version_number = the input does not contain version number - -driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}` - -driver_impl_rlink_no_a_file = rlink must be a file - -driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}` - -driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}` - -driver_impl_rlink_wrong_file_type = the input does not look like a .rlink file - -driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..a0cb29ef15cf 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -108,15 +108,12 @@ use crate::session_diagnostics::{ RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage, }; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn default_translator() -> Translator { Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false) } pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ // tidy-alphabetical-start - crate::DEFAULT_LOCALE_RESOURCE, rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE, rustc_ast_passes::DEFAULT_LOCALE_RESOURCE, rustc_attr_parsing::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 774221fd396a..565c176645de 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -3,82 +3,88 @@ use std::error::Error; use rustc_macros::{Diagnostic, Subdiagnostic}; #[derive(Diagnostic)] -#[diag(driver_impl_cant_emit_mir)] +#[diag("could not emit MIR: {$error}")] pub struct CantEmitMIR { pub error: std::io::Error, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_unable_to_read)] +#[diag("failed to read rlink file: `{$err}`")] pub(crate) struct RlinkUnableToRead { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_wrong_file_type)] +#[diag("the input does not look like a .rlink file")] pub(crate) struct RLinkWrongFileType; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_empty_version_number)] +#[diag("the input does not contain version number")] pub(crate) struct RLinkEmptyVersionNumber; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_encoding_version_mismatch)] +#[diag( + ".rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`" +)] pub(crate) struct RLinkEncodingVersionMismatch { pub version_array: String, pub rlink_version: u32, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_rustc_version_mismatch)] +#[diag( + ".rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`" +)] pub(crate) struct RLinkRustcVersionMismatch<'a> { pub rustc_version: String, pub current_version: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_no_a_file)] +#[diag("rlink must be a file")] pub(crate) struct RlinkNotAFile; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_corrupt_file)] +#[diag("corrupt metadata encountered in `{$file}`")] pub(crate) struct RlinkCorruptFile<'a> { pub file: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(driver_impl_ice)] +#[diag("the compiler unexpectedly panicked. this is a bug.")] pub(crate) struct Ice; #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report)] +#[diag("we would appreciate a bug report: {$bug_report_url}")] pub(crate) struct IceBugReport<'a> { pub bug_report_url: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_update_note)] +#[diag("please make sure that you have updated to the latest nightly")] pub(crate) struct UpdateNightlyNote; #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_internal_feature)] +#[diag( + "using internal features is not supported and expected to cause internal compiler errors when used incorrectly" +)] pub(crate) struct IceBugReportInternalFeature; #[derive(Diagnostic)] -#[diag(driver_impl_ice_version)] +#[diag("rustc {$version} running on {$triple}")] pub(crate) struct IceVersion<'a> { pub version: &'a str, pub triple: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_path)] +#[diag("please attach the file at `{$path}` to your bug report")] pub(crate) struct IcePath { pub path: std::path::PathBuf, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_path_error)] +#[diag("the ICE couldn't be written to `{$path}`: {$error}")] pub(crate) struct IcePathError { pub path: std::path::PathBuf, pub error: String, @@ -87,23 +93,23 @@ pub(crate) struct IcePathError { } #[derive(Subdiagnostic)] -#[note(driver_impl_ice_path_error_env)] +#[note("the environment variable `RUSTC_ICE` is set to `{$env_var}`")] pub(crate) struct IcePathErrorEnv { pub env_var: std::path::PathBuf, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_flags)] +#[diag("compiler flags: {$flags}")] pub(crate) struct IceFlags { pub flags: String, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_exclude_cargo_defaults)] +#[diag("some of the compiler flags provided by cargo are hidden")] pub(crate) struct IceExcludeCargoDefaults; #[derive(Diagnostic)] -#[diag(driver_impl_unstable_feature_usage)] +#[diag("cannot dump feature usage metrics: {$error}")] pub(crate) struct UnstableFeatureUsage { pub error: Box, } From 079913ec7169bae6f48528d40da03a5da321dbe4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 3 Feb 2026 09:02:27 +0100 Subject: [PATCH 546/583] disable socket tests in Miri --- library/std/tests/windows_unix_socket.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/tests/windows_unix_socket.rs b/library/std/tests/windows_unix_socket.rs index 18f4c52e72c2..1f20cf586ca2 100644 --- a/library/std/tests/windows_unix_socket.rs +++ b/library/std/tests/windows_unix_socket.rs @@ -1,4 +1,5 @@ #![cfg(windows)] +#![cfg(not(miri))] // no socket support in Miri #![feature(windows_unix_domain_sockets)] // Now only test windows_unix_domain_sockets feature // in the future, will test both unix and windows uds From e9ec12a2256471dcf0d6227e7ec13d6ea49fd7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 27 Jan 2026 19:00:26 +0200 Subject: [PATCH 547/583] Fix postcard test too --- .../crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index 33ca1d791de7..ba9657a9bb45 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -1,4 +1,8 @@ #![cfg(feature = "sysroot-abi")] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_driver as _; mod common { pub(crate) mod utils; From a9f81ea43e45eb4d64c93099be6b7d2e47a9290b Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 09:59:11 +0100 Subject: [PATCH 548/583] Convert to inline diagnostics in `rustc_attr_parsing` --- Cargo.lock | 2 - compiler/rustc_attr_parsing/Cargo.toml | 1 - compiler/rustc_attr_parsing/messages.ftl | 246 ----------------- .../rustc_attr_parsing/src/attributes/cfg.rs | 4 +- .../src/attributes/link_attrs.rs | 13 +- compiler/rustc_attr_parsing/src/lib.rs | 2 - .../src/session_diagnostics.rs | 250 ++++++++++-------- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - 9 files changed, 142 insertions(+), 378 deletions(-) delete mode 100644 compiler/rustc_attr_parsing/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..04d65e0a4999 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3543,7 +3543,6 @@ dependencies = [ "rustc_ast_pretty", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_hir", "rustc_lexer", "rustc_macros", @@ -3780,7 +3779,6 @@ dependencies = [ "rustc_ast_lowering", "rustc_ast_passes", "rustc_ast_pretty", - "rustc_attr_parsing", "rustc_borrowck", "rustc_builtin_macros", "rustc_codegen_ssa", diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 79193f394fe4..411f3f5ccbd1 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -10,7 +10,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl deleted file mode 100644 index 3e4c1a9dfad8..000000000000 --- a/compiler/rustc_attr_parsing/messages.ftl +++ /dev/null @@ -1,246 +0,0 @@ -attr_parsing_as_needed_compatibility = - linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds - -attr_parsing_bundle_needs_static = - linking modifier `bundle` is only compatible with `static` linking kind - -attr_parsing_cfg_attr_bad_delim = wrong `cfg_attr` delimiters - -attr_parsing_deprecated_item_suggestion = - suggestions on deprecated items are unstable - .help = add `#![feature(deprecated_suggestion)]` to the crate root - .note = see #94785 for more details - -attr_parsing_doc_alias_bad_char = - {$char_} character isn't allowed in {$attr_str} - -attr_parsing_doc_alias_empty = - {$attr_str} attribute cannot have empty value - -attr_parsing_doc_alias_malformed = - doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` - -attr_parsing_doc_alias_start_end = - {$attr_str} cannot start or end with ' ' - -attr_parsing_doc_attr_not_crate_level = - `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute - -attr_parsing_doc_attribute_not_attribute = - nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]` - .help = only existing builtin attributes are allowed in core/std - -attr_parsing_doc_keyword_not_keyword = - nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]` - .help = only existing keywords are allowed in core/std - -attr_parsing_empty_confusables = - expected at least one confusable name - -attr_parsing_empty_link_name = - link name must not be empty - .label = empty link name - -attr_parsing_expected_single_version_literal = - expected single version literal - -attr_parsing_expected_version_literal = - expected a version literal - -attr_parsing_expects_feature_list = - `{$name}` expects a list of feature names - -attr_parsing_expects_features = - `{$name}` expects feature names - -attr_parsing_import_name_type_raw = - import name type can only be used with link kind `raw-dylib` - -attr_parsing_import_name_type_x86 = - import name type is only supported on x86 - -attr_parsing_incompatible_wasm_link = - `wasm_import_module` is incompatible with other arguments in `#[link]` attributes - -attr_parsing_incorrect_repr_format_align_one_arg = - incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses - -attr_parsing_incorrect_repr_format_expect_literal_integer = - incorrect `repr(align)` attribute format: `align` expects a literal integer as argument - -attr_parsing_incorrect_repr_format_generic = - incorrect `repr({$repr_arg})` attribute format - .suggestion = use parentheses instead - -attr_parsing_incorrect_repr_format_packed_expect_integer = - incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument - -attr_parsing_incorrect_repr_format_packed_one_or_zero_arg = - incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all - -attr_parsing_invalid_alignment_value = - invalid alignment value: {$error_part} - -attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute - .label = this is not an unsafe attribute - .suggestion = remove the `unsafe(...)` - .note = extraneous unsafe is not allowed in attributes - -attr_parsing_invalid_issue_string = - `issue` must be a non-zero numeric string or "none" - .must_not_be_zero = `issue` must not be "0", use "none" instead - .empty = cannot parse integer from empty string - .invalid_digit = invalid digit found in string - .pos_overflow = number too large to fit in target type - .neg_overflow = number too small to fit in target type - -attr_parsing_invalid_link_modifier = - invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed - -attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} - .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign - .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal - .label = {$descr}s are not allowed here - -attr_parsing_invalid_predicate = - invalid predicate `{$predicate}` - -attr_parsing_invalid_repr_align_need_arg = - invalid `repr(align)` attribute: `align` needs an argument - .suggestion = supply an argument here - -attr_parsing_invalid_repr_generic = - invalid `repr({$repr_arg})` attribute: {$error_part} - -attr_parsing_invalid_repr_hint_no_paren = - invalid representation hint: `{$name}` does not take a parenthesized argument list - -attr_parsing_invalid_repr_hint_no_value = - invalid representation hint: `{$name}` does not take a value - -attr_parsing_invalid_since = - 'since' must be a Rust version number, such as "1.31.0" - -attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute - -attr_parsing_limit_invalid = - `limit` must be a non-negative integer - .label = {$error_str} -attr_parsing_link_arg_unstable = - link kind `link-arg` is unstable - -attr_parsing_link_cfg_unstable = - link cfg is unstable - -attr_parsing_link_framework_apple = - link kind `framework` is only supported on Apple targets - -attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}` - .note = the value may not exceed `u16::MAX` - -attr_parsing_link_requires_name = - `#[link]` attribute requires a `name = "string"` argument - .label = missing `name` argument - -attr_parsing_meta_bad_delim = wrong meta list delimiters -attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` - -attr_parsing_missing_feature = - missing 'feature' - -attr_parsing_missing_issue = - missing 'issue' - -attr_parsing_missing_note = - missing 'note' - -attr_parsing_missing_since = - missing 'since' - -attr_parsing_multiple_modifiers = - multiple `{$modifier}` modifiers in a single `modifiers` argument - -attr_parsing_multiple_stability_levels = - multiple stability levels - -attr_parsing_naked_functions_incompatible_attribute = - attribute incompatible with `#[unsafe(naked)]` - .label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]` - .naked_attribute = function marked with `#[unsafe(naked)]` here - -attr_parsing_non_ident_feature = - 'feature' is not an identifier - -attr_parsing_null_on_export = `export_name` may not contain null characters - -attr_parsing_null_on_link_section = `link_section` may not contain null characters - -attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters - -attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters - -attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal - -attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal - -attr_parsing_raw_dylib_elf_unstable = - link kind `raw-dylib` is unstable on ELF platforms - -attr_parsing_raw_dylib_no_nul = - link name must not contain NUL characters if link kind is `raw-dylib` - -attr_parsing_raw_dylib_only_windows = - link kind `raw-dylib` is only supported on Windows targets - -attr_parsing_repr_ident = - meta item in `repr` must be an identifier - -attr_parsing_rustc_allowed_unstable_pairing = - `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute - -attr_parsing_rustc_promotable_pairing = - `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute - -attr_parsing_rustc_scalable_vector_count_out_of_range = element count in `rustc_scalable_vector` is too large: `{$n}` - .note = the value may not exceed `u16::MAX` - -attr_parsing_soft_no_args = - `soft` should not have any arguments - -attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library - -attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes - .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) - -attr_parsing_unknown_version_literal = - unknown version literal format, assuming it refers to a future version - -attr_parsing_unrecognized_repr_hint = - unrecognized representation hint - .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` - .note = for more information, visit - -attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - -attr_parsing_unstable_cfg_target_compact = - compact `cfg(target(..))` is experimental and subject to change - -attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable - .help = if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]` - -attr_parsing_unsupported_instruction_set = target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]` - -attr_parsing_unsupported_literal_suggestion = - consider removing the prefix - -attr_parsing_unused_multiple = - multiple `{$name}` attributes - .suggestion = remove this attribute - .note = attribute also specified here - -attr_parsing_whole_archive_needs_static = - linking modifier `whole-archive` is only compatible with `static` linking kind diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 3a540d80998d..ca6bfdbeaf5f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -25,7 +25,7 @@ use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg, ParsedDescription, }; -use crate::{AttributeParser, fluent_generated, parse_version, session_diagnostics}; +use crate::{AttributeParser, parse_version, session_diagnostics}; pub const CFG_TEMPLATE: AttributeTemplate = template!( List: &["predicate"], @@ -141,7 +141,7 @@ fn parse_cfg_entry_target( cx.sess(), sym::cfg_target_compact, meta_span, - fluent_generated::attr_parsing_unstable_cfg_target_compact, + "compact `cfg(target(..))` is experimental and subject to change", ) .emit(); } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 548f53a986b8..02723e96ad19 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -10,7 +10,6 @@ use rustc_target::spec::{Arch, BinaryFormat}; use super::prelude::*; use super::util::parse_single_integer; use crate::attributes::cfg::parse_cfg_entry; -use crate::fluent_generated; use crate::session_diagnostics::{ AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange, @@ -305,7 +304,7 @@ impl LinkParser { sess, sym::raw_dylib_elf, nv.value_span, - fluent_generated::attr_parsing_raw_dylib_elf_unstable, + "link kind `raw-dylib` is unstable on ELF platforms", ) .emit(); } else { @@ -320,7 +319,7 @@ impl LinkParser { sess, sym::link_arg_attribute, nv.value_span, - fluent_generated::attr_parsing_link_arg_unstable, + "link kind `link-arg` is unstable", ) .emit(); } @@ -385,13 +384,7 @@ impl LinkParser { return true; }; if !features.link_cfg() { - feature_err( - sess, - sym::link_cfg, - item.span(), - fluent_generated::attr_parsing_link_cfg_unstable, - ) - .emit(); + feature_err(sess, sym::link_cfg, item.span(), "link cfg is unstable").emit(); } *cfg = parse_cfg_entry(cx, link_cfg).ok(); true diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 349e6c234520..fe050250e354 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -113,5 +113,3 @@ pub use attributes::util::{is_builtin_attr, parse_version}; pub use context::{Early, Late, OmitDoc, ShouldEmit}; pub use interface::AttributeParser; pub use session_diagnostics::ParsedDescription; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f9748542beb9..4055b7463f30 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -11,10 +11,8 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use rustc_target::spec::TargetTuple; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_predicate, code = E0537)] +#[diag("invalid predicate `{$predicate}`", code = E0537)] pub(crate) struct InvalidPredicate { #[primary_span] pub span: Span, @@ -23,7 +21,7 @@ pub(crate) struct InvalidPredicate { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_empty)] +#[diag("{$attr_str} attribute cannot have empty value")] pub(crate) struct DocAliasEmpty<'a> { #[primary_span] pub span: Span, @@ -31,7 +29,7 @@ pub(crate) struct DocAliasEmpty<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_bad_char)] +#[diag("{$char_} character isn't allowed in {$attr_str}")] pub(crate) struct DocAliasBadChar<'a> { #[primary_span] pub span: Span, @@ -40,7 +38,7 @@ pub(crate) struct DocAliasBadChar<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_start_end)] +#[diag("{$attr_str} cannot start or end with ' '")] pub(crate) struct DocAliasStartEnd<'a> { #[primary_span] pub span: Span, @@ -48,7 +46,7 @@ pub(crate) struct DocAliasStartEnd<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_attr_not_crate_level)] +#[diag("`#![doc({$attr_name} = \"...\")]` isn't allowed as a crate-level attribute")] pub(crate) struct DocAttrNotCrateLevel { #[primary_span] pub span: Span, @@ -56,8 +54,8 @@ pub(crate) struct DocAttrNotCrateLevel { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_keyword_not_keyword)] -#[help] +#[diag("nonexistent keyword `{$keyword}` used in `#[doc(keyword = \"...\")]`")] +#[help("only existing keywords are allowed in core/std")] pub(crate) struct DocKeywordNotKeyword { #[primary_span] pub span: Span, @@ -65,8 +63,8 @@ pub(crate) struct DocKeywordNotKeyword { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_attribute_not_attribute)] -#[help] +#[diag("nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = \"...\")]`")] +#[help("only existing builtin attributes are allowed in core/std")] pub(crate) struct DocAttributeNotAttribute { #[primary_span] pub span: Span, @@ -74,28 +72,28 @@ pub(crate) struct DocAttributeNotAttribute { } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_since, code = E0542)] +#[diag("missing 'since'", code = E0542)] pub(crate) struct MissingSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_note, code = E0543)] +#[diag("missing 'note'", code = E0543)] pub(crate) struct MissingNote { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_multiple_stability_levels, code = E0544)] +#[diag("multiple stability levels", code = E0544)] pub(crate) struct MultipleStabilityLevels { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_issue_string, code = E0545)] +#[diag("`issue` must be a non-zero numeric string or \"none\"", code = E0545)] pub(crate) struct InvalidIssueString { #[primary_span] pub span: Span, @@ -108,31 +106,31 @@ pub(crate) struct InvalidIssueString { // translatable. #[derive(Subdiagnostic)] pub(crate) enum InvalidIssueStringCause { - #[label(attr_parsing_must_not_be_zero)] + #[label("`issue` must not be \"0\", use \"none\" instead")] MustNotBeZero { #[primary_span] span: Span, }, - #[label(attr_parsing_empty)] + #[label("cannot parse integer from empty string")] Empty { #[primary_span] span: Span, }, - #[label(attr_parsing_invalid_digit)] + #[label("invalid digit found in string")] InvalidDigit { #[primary_span] span: Span, }, - #[label(attr_parsing_pos_overflow)] + #[label("number too large to fit in target type")] PosOverflow { #[primary_span] span: Span, }, - #[label(attr_parsing_neg_overflow)] + #[label("number too small to fit in target type")] NegOverflow { #[primary_span] span: Span, @@ -153,21 +151,21 @@ impl InvalidIssueStringCause { } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_feature, code = E0546)] +#[diag("missing 'feature'", code = E0546)] pub(crate) struct MissingFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_non_ident_feature, code = E0546)] +#[diag("'feature' is not an identifier", code = E0546)] pub(crate) struct NonIdentFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_issue, code = E0547)] +#[diag("missing 'issue'", code = E0547)] pub(crate) struct MissingIssue { #[primary_span] pub span: Span, @@ -176,20 +174,20 @@ pub(crate) struct MissingIssue { // FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`? // It is more similar to `IncorrectReprFormatGeneric`. #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)] +#[diag("incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all", code = E0552)] pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_packed_expect_integer, code = E0552)] +#[diag("incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument", code = E0552)] pub(crate) struct IncorrectReprFormatPackedExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_hint_no_paren, code = E0552)] +#[diag("invalid representation hint: `{$name}` does not take a parenthesized argument list", code = E0552)] pub(crate) struct InvalidReprHintNoParen { #[primary_span] pub span: Span, @@ -198,7 +196,7 @@ pub(crate) struct InvalidReprHintNoParen { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_hint_no_value, code = E0552)] +#[diag("invalid representation hint: `{$name}` does not take a value", code = E0552)] pub(crate) struct InvalidReprHintNoValue { #[primary_span] pub span: Span, @@ -207,15 +205,19 @@ pub(crate) struct InvalidReprHintNoValue { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)] +#[diag("invalid `repr(align)` attribute: `align` needs an argument", code = E0589)] pub(crate) struct InvalidReprAlignNeedArg { #[primary_span] - #[suggestion(code = "align(...)", applicability = "has-placeholders")] + #[suggestion( + "supply an argument here", + code = "align(...)", + applicability = "has-placeholders" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_generic, code = E0589)] +#[diag("invalid `repr({$repr_arg})` attribute: {$error_part}", code = E0589)] pub(crate) struct InvalidReprGeneric<'a> { #[primary_span] pub span: Span, @@ -225,21 +227,21 @@ pub(crate) struct InvalidReprGeneric<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_align_one_arg, code = E0693)] +#[diag("incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses", code = E0693)] pub(crate) struct IncorrectReprFormatAlignOneArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_expect_literal_integer, code = E0693)] +#[diag("incorrect `repr(align)` attribute format: `align` expects a literal integer as argument", code = E0693)] pub(crate) struct IncorrectReprFormatExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)] +#[diag("incorrect `repr({$repr_arg})` attribute format", code = E0693)] pub(crate) struct IncorrectReprFormatGeneric { #[primary_span] pub span: Span, @@ -253,7 +255,7 @@ pub(crate) struct IncorrectReprFormatGeneric { #[derive(Subdiagnostic)] pub(crate) enum IncorrectReprFormatGenericCause { #[suggestion( - attr_parsing_suggestion, + "use parentheses instead", code = "{name}({value})", applicability = "machine-applicable" )] @@ -269,7 +271,7 @@ pub(crate) enum IncorrectReprFormatGenericCause { }, #[suggestion( - attr_parsing_suggestion, + "use parentheses instead", code = "{name}({value})", applicability = "machine-applicable" )] @@ -298,48 +300,48 @@ impl IncorrectReprFormatGenericCause { } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_promotable_pairing, code = E0717)] +#[diag("`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute", code = E0717)] pub(crate) struct RustcPromotablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)] +#[diag("`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute", code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_deprecated_item_suggestion)] +#[diag("suggestions on deprecated items are unstable")] pub(crate) struct DeprecatedItemSuggestion { #[primary_span] pub span: Span, - #[help] + #[help("add `#![feature(deprecated_suggestion)]` to the crate root")] pub is_nightly: bool, - #[note] + #[note("see #94785 for more details")] pub details: (), } #[derive(Diagnostic)] -#[diag(attr_parsing_expected_single_version_literal)] +#[diag("expected single version literal")] pub(crate) struct ExpectedSingleVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_expected_version_literal)] +#[diag("expected a version literal")] pub(crate) struct ExpectedVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_expects_feature_list)] +#[diag("`{$name}` expects a list of feature names")] pub(crate) struct ExpectsFeatureList { #[primary_span] pub span: Span, @@ -348,7 +350,7 @@ pub(crate) struct ExpectsFeatureList { } #[derive(Diagnostic)] -#[diag(attr_parsing_expects_features)] +#[diag("`{$name}` expects feature names")] pub(crate) struct ExpectsFeatures { #[primary_span] pub span: Span, @@ -357,21 +359,21 @@ pub(crate) struct ExpectsFeatures { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_since)] +#[diag("'since' must be a Rust version number, such as \"1.31.0\"")] pub(crate) struct InvalidSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_soft_no_args)] +#[diag("`soft` should not have any arguments")] pub(crate) struct SoftNoArgs { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unknown_version_literal)] +#[diag("unknown version literal format, assuming it refers to a future version")] pub(crate) struct UnknownVersionLiteral { #[primary_span] pub span: Span, @@ -379,78 +381,83 @@ pub(crate) struct UnknownVersionLiteral { // FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated. #[derive(Diagnostic)] -#[diag(attr_parsing_unused_multiple)] +#[diag("multiple `{$name}` attributes")] pub(crate) struct UnusedMultiple { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] pub this: Span, - #[note] + #[note("attribute also specified here")] pub other: Span, pub name: Symbol, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_export, code = E0648)] +#[diag("`export_name` may not contain null characters", code = E0648)] pub(crate) struct NullOnExport { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_link_section, code = E0648)] +#[diag("`link_section` may not contain null characters", code = E0648)] pub(crate) struct NullOnLinkSection { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_objc_class)] +#[diag("`objc::class!` may not contain null characters")] pub(crate) struct NullOnObjcClass { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_objc_selector)] +#[diag("`objc::selector!` may not contain null characters")] pub(crate) struct NullOnObjcSelector { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_objc_class_expected_string_literal)] +#[diag("`objc::class!` expected a string literal")] pub(crate) struct ObjcClassExpectedStringLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_objc_selector_expected_string_literal)] +#[diag("`objc::selector!` expected a string literal")] pub(crate) struct ObjcSelectorExpectedStringLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_stability_outside_std, code = E0734)] +#[diag("stability attributes may not be used outside of the standard library", code = E0734)] pub(crate) struct StabilityOutsideStd { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_empty_confusables)] +#[diag("expected at least one confusable name")] pub(crate) struct EmptyConfusables { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[help] -#[diag(attr_parsing_invalid_target)] +#[help("`#[{$name}]` can {$only}be applied to {$applied}")] +#[diag("`#[{$name}]` attribute cannot be used on {$target}")] pub(crate) struct InvalidTarget { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + #[suggestion( + "remove the attribute", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] pub span: Span, pub name: AttrPath, pub target: &'static str, @@ -459,7 +466,7 @@ pub(crate) struct InvalidTarget { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_alignment_value, code = E0589)] +#[diag("invalid alignment value: {$error_part}", code = E0589)] pub(crate) struct InvalidAlignmentValue { #[primary_span] pub span: Span, @@ -467,43 +474,49 @@ pub(crate) struct InvalidAlignmentValue { } #[derive(Diagnostic)] -#[diag(attr_parsing_repr_ident, code = E0565)] +#[diag("meta item in `repr` must be an identifier", code = E0565)] pub(crate) struct ReprIdent { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)] -#[help] -#[note] +#[diag("unrecognized representation hint", code = E0552)] +#[help( + "valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`" +)] +#[note( + "for more information, visit " +)] pub(crate) struct UnrecognizedReprHint { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)] -#[help] +#[diag("item annotated with `#[unstable_feature_bound]` should not be stable")] +#[help( + "if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`" +)] pub(crate) struct UnstableFeatureBoundIncompatibleStability { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)] +#[diag("attribute incompatible with `#[unsafe(naked)]`", code = E0736)] pub(crate) struct NakedFunctionIncompatibleAttribute { #[primary_span] - #[label] + #[label("the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`")] pub span: Span, - #[label(attr_parsing_naked_attribute)] + #[label("function marked with `#[unsafe(naked)]` here")] pub naked_span: Span, pub attr: String, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_ordinal_out_of_range)] -#[note] +#[diag("ordinal value in `link_ordinal` is too large: `{$ordinal}`")] +#[note("the value may not exceed `u16::MAX`")] pub(crate) struct LinkOrdinalOutOfRange { #[primary_span] pub span: Span, @@ -511,8 +524,8 @@ pub(crate) struct LinkOrdinalOutOfRange { } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_scalable_vector_count_out_of_range)] -#[note] +#[diag("element count in `rustc_scalable_vector` is too large: `{$n}`")] +#[note("the value may not exceed `u16::MAX`")] pub(crate) struct RustcScalableVectorCountOutOfRange { #[primary_span] pub span: Span, @@ -586,7 +599,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { if let Some(start_point_span) = byte_string { diag.span_suggestion( start_point_span, - fluent::attr_parsing_unsupported_literal_suggestion, + "consider removing the prefix", "", Applicability::MaybeIncorrect, ); @@ -751,30 +764,27 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_attr_unsafe)] -#[note] +#[diag("`{$name}` is not an unsafe attribute")] +#[note("extraneous unsafe is not allowed in attributes")] pub(crate) struct InvalidAttrUnsafe { #[primary_span] - #[label] + #[label("this is not an unsafe attribute")] pub span: Span, pub name: AttrPath, } #[derive(Diagnostic)] -#[diag(attr_parsing_unsafe_attr_outside_unsafe)] +#[diag("unsafe attribute used without unsafe")] pub(crate) struct UnsafeAttrOutsideUnsafe { #[primary_span] - #[label] + #[label("usage of unsafe attribute")] pub span: Span, #[subdiagnostic] pub suggestion: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion( - attr_parsing_unsafe_attr_outside_unsafe_suggestion, - applicability = "machine-applicable" -)] +#[multipart_suggestion("wrap the attribute in `unsafe(...)`", applicability = "machine-applicable")] pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { #[suggestion_part(code = "unsafe(")] pub left: Span, @@ -783,7 +793,7 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { } #[derive(Diagnostic)] -#[diag(attr_parsing_meta_bad_delim)] +#[diag("wrong meta list delimiters")] pub(crate) struct MetaBadDelim { #[primary_span] pub span: Span, @@ -793,7 +803,7 @@ pub(crate) struct MetaBadDelim { #[derive(Subdiagnostic)] #[multipart_suggestion( - attr_parsing_meta_bad_delim_suggestion, + "the delimiters should be `(` and `)`", applicability = "machine-applicable" )] pub(crate) struct MetaBadDelimSugg { @@ -804,7 +814,7 @@ pub(crate) struct MetaBadDelimSugg { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_meta_item)] +#[diag("expected a literal (`1u8`, `1.0f32`, `\"string\"`, etc.) here, found {$descr}")] pub(crate) struct InvalidMetaItem { #[primary_span] pub span: Span, @@ -813,12 +823,15 @@ pub(crate) struct InvalidMetaItem { pub quote_ident_sugg: Option, #[subdiagnostic] pub remove_neg_sugg: Option, - #[label] + #[label("{$descr}s are not allowed here")] pub label: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")] +#[multipart_suggestion( + "surround the identifier with quotation marks to make it into a string literal", + applicability = "machine-applicable" +)] pub(crate) struct InvalidMetaItemQuoteIdentSugg { #[suggestion_part(code = "\"")] pub before: Span, @@ -827,73 +840,80 @@ pub(crate) struct InvalidMetaItemQuoteIdentSugg { } #[derive(Subdiagnostic)] -#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")] +#[multipart_suggestion( + "negative numbers are not literals, try removing the `-` sign", + applicability = "machine-applicable" +)] pub(crate) struct InvalidMetaItemRemoveNegSugg { #[suggestion_part(code = "")] pub negative_sign: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_suffixed_literal_in_attribute)] -#[help] +#[diag("suffixed literals are not allowed in attributes")] +#[help( + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)" +)] pub(crate) struct SuffixedLiteralInAttribute { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_empty_link_name, code = E0454)] +#[diag("link name must not be empty", code = E0454)] pub(crate) struct EmptyLinkName { #[primary_span] - #[label] + #[label("empty link name")] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_framework_apple, code = E0455)] +#[diag("link kind `framework` is only supported on Apple targets", code = E0455)] pub(crate) struct LinkFrameworkApple { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incompatible_wasm_link)] +#[diag("`wasm_import_module` is incompatible with other arguments in `#[link]` attributes")] pub(crate) struct IncompatibleWasmLink { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_requires_name, code = E0459)] +#[diag("`#[link]` attribute requires a `name = \"string\"` argument", code = E0459)] pub(crate) struct LinkRequiresName { #[primary_span] - #[label] + #[label("missing `name` argument")] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_raw_dylib_no_nul)] +#[diag("link name must not contain NUL characters if link kind is `raw-dylib`")] pub(crate) struct RawDylibNoNul { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_raw_dylib_only_windows, code = E0455)] +#[diag("link kind `raw-dylib` is only supported on Windows targets", code = E0455)] pub(crate) struct RawDylibOnlyWindows { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_link_modifier)] +#[diag( + "invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed" +)] pub(crate) struct InvalidLinkModifier { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_multiple_modifiers)] +#[diag("multiple `{$modifier}` modifiers in a single `modifiers` argument")] pub(crate) struct MultipleModifiers { #[primary_span] pub span: Span, @@ -901,52 +921,54 @@ pub(crate) struct MultipleModifiers { } #[derive(Diagnostic)] -#[diag(attr_parsing_import_name_type_x86)] +#[diag("import name type is only supported on x86")] pub(crate) struct ImportNameTypeX86 { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_bundle_needs_static)] +#[diag("linking modifier `bundle` is only compatible with `static` linking kind")] pub(crate) struct BundleNeedsStatic { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_whole_archive_needs_static)] +#[diag("linking modifier `whole-archive` is only compatible with `static` linking kind")] pub(crate) struct WholeArchiveNeedsStatic { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_as_needed_compatibility)] +#[diag( + "linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds" +)] pub(crate) struct AsNeededCompatibility { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_import_name_type_raw)] +#[diag("import name type can only be used with link kind `raw-dylib`")] pub(crate) struct ImportNameTypeRaw { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_limit_invalid)] +#[diag("`limit` must be a non-negative integer")] pub(crate) struct LimitInvalid<'a> { #[primary_span] pub span: Span, - #[label] + #[label("{$error_str}")] pub value_span: Span, pub error_str: &'a str, } #[derive(Diagnostic)] -#[diag(attr_parsing_cfg_attr_bad_delim)] +#[diag("wrong `cfg_attr` delimiters")] pub(crate) struct CfgAttrBadDelim { #[primary_span] pub span: Span, @@ -955,14 +977,16 @@ pub(crate) struct CfgAttrBadDelim { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_malformed)] +#[diag( + "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of strings `#[doc(alias(\"a\", \"b\"))]`" +)] pub(crate) struct DocAliasMalformed { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unsupported_instruction_set)] +#[diag("target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`")] pub(crate) struct UnsupportedInstructionSet<'a> { #[primary_span] pub span: Span, diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..49b73e4392d1 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -12,7 +12,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..6738504e7219 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -119,7 +119,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ crate::DEFAULT_LOCALE_RESOURCE, rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE, rustc_ast_passes::DEFAULT_LOCALE_RESOURCE, - rustc_attr_parsing::DEFAULT_LOCALE_RESOURCE, rustc_borrowck::DEFAULT_LOCALE_RESOURCE, rustc_builtin_macros::DEFAULT_LOCALE_RESOURCE, rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE, From e58538c55257e73f789bd671c8d96f0fecd4ff49 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 1 Feb 2026 22:18:59 +1100 Subject: [PATCH 549/583] Rename `collect_active_jobs` to several distinct names --- compiler/rustc_interface/src/util.rs | 2 +- compiler/rustc_query_impl/src/plumbing.rs | 40 ++++++++++++++----- compiler/rustc_query_system/src/query/job.rs | 4 +- compiler/rustc_query_system/src/query/mod.rs | 5 ++- .../rustc_query_system/src/query/plumbing.rs | 22 ++++++---- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 249368fd1194..25f59f0e89df 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -254,7 +254,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; || { // Ensure there were no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs(false).expect( + QueryCtxt::new(tcx).collect_active_jobs_from_all_queries(false).expect( "failed to collect active queries in deadlock handler", ) }, diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 8be45d1fb464..d1721f1f455f 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -50,7 +50,9 @@ impl<'tcx> QueryCtxt<'tcx> { } fn depth_limit_error(self, job: QueryJobId) { - let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); + let query_map = self + .collect_active_jobs_from_all_queries(true) + .expect("failed to collect active queries"); let (info, depth) = job.find_dep_kind_root(query_map); let suggested_limit = match self.tcx.recursion_limit() { @@ -98,7 +100,7 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { tls::with_related_context(self.tcx, |icx| icx.query) } - /// Returns a map of currently active query jobs. + /// Returns a map of currently active query jobs, collected from all queries. /// /// If `require_complete` is `true`, this function locks all shards of the /// query results to produce a complete map, which always returns `Ok`. @@ -108,12 +110,15 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, /// especially when called from within a deadlock handler, unless a /// complete map is needed and no deadlock is possible at this call site. - fn collect_active_jobs(self, require_complete: bool) -> Result, QueryMap<'tcx>> { + fn collect_active_jobs_from_all_queries( + self, + require_complete: bool, + ) -> Result, QueryMap<'tcx>> { let mut jobs = QueryMap::default(); let mut complete = true; - for collect in super::COLLECT_ACTIVE_JOBS.iter() { - if collect(self.tcx, &mut jobs, require_complete).is_none() { + for gather_fn in crate::PER_QUERY_GATHER_ACTIVE_JOBS_FNS.iter() { + if gather_fn(self.tcx, &mut jobs, require_complete).is_none() { complete = false; } } @@ -731,7 +736,10 @@ macro_rules! define_queries { } } - pub(crate) fn collect_active_jobs<'tcx>( + /// Internal per-query plumbing for collecting the set of active jobs for this query. + /// + /// Should only be called through `PER_QUERY_GATHER_ACTIVE_JOBS_FNS`. + pub(crate) fn gather_active_jobs<'tcx>( tcx: TyCtxt<'tcx>, qmap: &mut QueryMap<'tcx>, require_complete: bool, @@ -741,12 +749,15 @@ macro_rules! define_queries { let name = stringify!($name); $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) }; - let res = tcx.query_system.states.$name.collect_active_jobs( + + // Call `gather_active_jobs_inner` to do the actual work. + let res = tcx.query_system.states.$name.gather_active_jobs_inner( tcx, make_frame, qmap, require_complete, ); + // this can be called during unwinding, and the function has a `try_`-prefix, so // don't `unwrap()` here, just manually check for `None` and do best-effort error // reporting. @@ -816,10 +827,17 @@ macro_rules! define_queries { // These arrays are used for iteration and can't be indexed by `DepKind`. - const COLLECT_ACTIVE_JOBS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<'tcx>, bool) -> Option<()> - ] = - &[$(query_impl::$name::collect_active_jobs),*]; + /// Used by `collect_active_jobs_from_all_queries` to iterate over all + /// queries, and gather the active jobs for each query. + /// + /// (We arbitrarily use the word "gather" when collecting the jobs for + /// each individual query, so that we have distinct function names to + /// grep for.) + const PER_QUERY_GATHER_ACTIVE_JOBS_FNS: &[ + for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<'tcx>, require_complete: bool) -> Option<()> + ] = &[ + $(query_impl::$name::gather_active_jobs),* + ]; const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache) diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 82b23b022c37..50cb58f0b4d5 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -32,6 +32,8 @@ impl<'tcx> QueryInfo> { } } +/// Map from query job IDs to job information collected by +/// [`QueryContext::collect_active_jobs_from_all_queries`]. pub type QueryMap<'tcx> = FxHashMap>; /// A value uniquely identifying an active query job. @@ -613,7 +615,7 @@ pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( let mut count_total = 0; // Make use of a partial query map if we fail to take locks collecting active queries. - let query_map = match qcx.collect_active_jobs(false) { + let query_map = match qcx.collect_active_jobs_from_all_queries(false) { Ok(query_map) => query_map, Err(query_map) => query_map, }; diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 54e5fa4d7229..dbf7395bd61a 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -166,7 +166,10 @@ pub trait QueryContext<'tcx>: HasDepContext { /// Get the query information from the TLS context. fn current_query_job(self) -> Option; - fn collect_active_jobs(self, require_complete: bool) -> Result, QueryMap<'tcx>>; + fn collect_active_jobs_from_all_queries( + self, + require_complete: bool, + ) -> Result, QueryMap<'tcx>>; /// Load a side effect associated to the node in the previous session. fn load_side_effect( diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 472839116107..fcd2e80a4fdc 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -11,7 +11,6 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::hash_table::{self, Entry, HashTable}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::LockGuard; use rustc_data_structures::{outline, sync}; use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; @@ -79,7 +78,10 @@ where self.active.lock_shards().all(|shard| shard.is_empty()) } - pub fn collect_active_jobs( + /// Internal plumbing for collecting the set of active jobs for this query. + /// + /// Should only be called from `gather_active_jobs`. + pub fn gather_active_jobs_inner( &self, qcx: Qcx, make_frame: fn(Qcx, K) -> QueryStackFrame>, @@ -88,23 +90,26 @@ where ) -> Option<()> { let mut active = Vec::new(); - let mut collect = |iter: LockGuard<'_, HashTable<(K, ActiveKeyStatus<'tcx>)>>| { - for (k, v) in iter.iter() { + // Helper to gather active jobs from a single shard. + let mut gather_shard_jobs = |shard: &HashTable<(K, ActiveKeyStatus<'tcx>)>| { + for (k, v) in shard.iter() { if let ActiveKeyStatus::Started(ref job) = *v { active.push((*k, job.clone())); } } }; + // Lock shards and gather jobs from each shard. if require_complete { for shard in self.active.lock_shards() { - collect(shard); + gather_shard_jobs(&shard); } } else { // We use try_lock_shards here since we are called from the // deadlock handler, and this shouldn't be locked. for shard in self.active.try_lock_shards() { - collect(shard?); + let shard = shard?; + gather_shard_jobs(&shard); } } @@ -294,7 +299,10 @@ where { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries"); + let query_map = qcx + .collect_active_jobs_from_all_queries(false) + .ok() + .expect("failed to collect active queries"); let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); (mk_cycle(query, qcx, error.lift()), None) From f8fe49ea9e1c997c689df921e0da2460545e9c39 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 10:18:22 +0100 Subject: [PATCH 550/583] Introduce `inline_fluent` macro --- compiler/rustc_attr_parsing/src/attributes/cfg.rs | 4 ++-- .../rustc_attr_parsing/src/attributes/link_attrs.rs | 8 +++++--- compiler/rustc_errors/src/translation.rs | 11 +++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index ca6bfdbeaf5f..3d885135cbe0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -3,7 +3,7 @@ use std::convert::identity; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, ast, token}; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{Applicability, PResult, inline_fluent}; use rustc_feature::{ AttrSuggestionStyle, AttributeTemplate, Features, GatedCfg, find_gated_cfg, template, }; @@ -141,7 +141,7 @@ fn parse_cfg_entry_target( cx.sess(), sym::cfg_target_compact, meta_span, - "compact `cfg(target(..))` is experimental and subject to change", + inline_fluent!("compact `cfg(target(..))` is experimental and subject to change"), ) .emit(); } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 02723e96ad19..c9da2f3b14bf 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,3 +1,4 @@ +use rustc_errors::inline_fluent; use rustc_feature::Features; use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; use rustc_hir::attrs::*; @@ -304,7 +305,7 @@ impl LinkParser { sess, sym::raw_dylib_elf, nv.value_span, - "link kind `raw-dylib` is unstable on ELF platforms", + inline_fluent!("link kind `raw-dylib` is unstable on ELF platforms"), ) .emit(); } else { @@ -319,7 +320,7 @@ impl LinkParser { sess, sym::link_arg_attribute, nv.value_span, - "link kind `link-arg` is unstable", + inline_fluent!("link kind `link-arg` is unstable"), ) .emit(); } @@ -384,7 +385,8 @@ impl LinkParser { return true; }; if !features.link_cfg() { - feature_err(sess, sym::link_cfg, item.span(), "link cfg is unstable").emit(); + feature_err(sess, sym::link_cfg, item.span(), inline_fluent!("link cfg is unstable")) + .emit(); } *cfg = parse_cfg_entry(cx, link_cfg).ok(); true diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 0ee2b7b06090..aba79c30f3a3 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -166,3 +166,14 @@ impl Translator { } } } + +/// This macro creates a translatable `DiagMessage` from a literal string. +/// It should be used in places where a translatable message is needed, but struct diagnostics are undesired. +/// +/// This is a macro because in the future we may want to globally register these messages. +#[macro_export] +macro_rules! inline_fluent { + ($inline: literal) => { + rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed($inline)) + }; +} From 6b3d1e060490bd735fb3cd6048532297369a1893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 3 Feb 2026 11:47:30 +0100 Subject: [PATCH 551/583] Update Cargo.lock It probably wasn't committed after an earlier change. --- src/ci/citool/Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ci/citool/Cargo.lock b/src/ci/citool/Cargo.lock index 9e1ded4d3f7c..fe1c92f049e0 100644 --- a/src/ci/citool/Cargo.lock +++ b/src/ci/citool/Cargo.lock @@ -66,9 +66,9 @@ checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "askama" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427" +checksum = "08e1676b346cadfec169374f949d7490fd80a24193d37d2afce0c047cf695e57" dependencies = [ "askama_macros", "itoa", @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994" +checksum = "7661ff56517787343f376f75db037426facd7c8d3049cef8911f1e75016f3a37" dependencies = [ "askama_parser", "basic-toml", @@ -96,18 +96,18 @@ dependencies = [ [[package]] name = "askama_macros" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46" +checksum = "713ee4dbfd1eb719c2dab859465b01fa1d21cb566684614a713a6b7a99a4e47b" dependencies = [ "askama_derive", ] [[package]] name = "askama_parser" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e" +checksum = "1d62d674238a526418b30c0def480d5beadb9d8964e7f38d635b03bf639c704c" dependencies = [ "rustc-hash", "serde", From cb9c43b7e867299db5284bffce1f498356e5167f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 3 Feb 2026 11:47:53 +0100 Subject: [PATCH 552/583] Show largest job duration changes in hours and minutes, rather than just seconds --- src/ci/citool/src/analysis.rs | 31 ++++++++++++++++++++++++++----- src/ci/citool/src/main.rs | 4 ++-- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs index 39b115154f9f..e91d27a36092 100644 --- a/src/ci/citool/src/analysis.rs +++ b/src/ci/citool/src/analysis.rs @@ -194,8 +194,8 @@ pub fn output_test_diffs( report_test_diffs(aggregated_test_diffs, job_metrics, job_info_resolver); } -/// Prints the ten largest differences in bootstrap durations. -pub fn output_largest_duration_changes( +/// Prints the ten largest differences in job durations. +pub fn output_largest_job_duration_changes( job_metrics: &HashMap, job_info_resolver: &mut JobInfoResolver, ) { @@ -237,11 +237,11 @@ pub fn output_largest_duration_changes( println!("# Job duration changes"); for (index, entry) in changes.into_iter().take(10).enumerate() { println!( - "{}. {}: {:.1}s -> {:.1}s ({:+.1}%)", + "{}. {}: {} -> {} ({:+.1}%)", index + 1, format_job_link(job_info_resolver, job_metrics, entry.job), - entry.before.as_secs_f64(), - entry.after.as_secs_f64(), + format_duration(entry.before), + format_duration(entry.after), entry.change ); } @@ -256,6 +256,27 @@ mostly for t-infra members, for simpler debugging of potential CI slow-downs."# }); } +fn format_duration(duration: Duration) -> String { + let total_secs = duration.as_secs(); + let hours = total_secs / 3600; + let minutes = (total_secs % 3600) / 60; + let seconds = total_secs % 60; + + let mut res = String::new(); + + if hours > 0 { + res.push_str(&format!("{hours}h ")); + } + if minutes > 0 { + res.push_str(&format!("{minutes}m ")); + } + if hours == 0 && seconds > 0 { + res.push_str(&format!("{seconds}s")); + } + + res.trim().to_string() +} + #[derive(Default)] struct TestSuiteRecord { passed: u64, diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index 01c0650b3c98..9b9cbe3862e3 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -17,7 +17,7 @@ use clap::Parser; use jobs::JobDatabase; use serde_yaml::Value; -use crate::analysis::{output_largest_duration_changes, output_test_diffs}; +use crate::analysis::{output_largest_job_duration_changes, output_test_diffs}; use crate::cpu_usage::load_cpu_usage; use crate::datadog::upload_datadog_metric; use crate::github::JobInfoResolver; @@ -205,7 +205,7 @@ And then open `test-dashboard/index.html` in your browser to see an overview of ); }); - output_largest_duration_changes(&metrics, &mut job_info_resolver); + output_largest_job_duration_changes(&metrics, &mut job_info_resolver); Ok(()) } From 0418f9aa42a9c9e7ca38a8c2a4a1e275c68a5154 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 3 Feb 2026 22:53:38 +1100 Subject: [PATCH 553/583] coverage: Add a test case for a previously-unknown span mismatch --- .../rustc_mir_transform/src/coverage/spans.rs | 8 ++-- .../context-mismatch-issue-147339.cov-map | 40 +++++++++++++++++++ .../context-mismatch-issue-147339.coverage | 28 +++++++++++++ .../macros/context-mismatch-issue-147339.rs | 20 ++++++++++ 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 tests/coverage/macros/context-mismatch-issue-147339.cov-map create mode 100644 tests/coverage/macros/context-mismatch-issue-147339.coverage create mode 100644 tests/coverage/macros/context-mismatch-issue-147339.rs diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index b1ce0069b43a..bdc861e2cece 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -55,12 +55,10 @@ pub(super) fn extract_refined_covspans<'tcx>( } // Each pushed covspan should have the same context as the body span. - // If it somehow doesn't, discard the covspan, or panic in debug builds. + // If it somehow doesn't, discard the covspan. if !body_span.eq_ctxt(covspan_span) { - debug_assert!( - false, - "span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}" - ); + // FIXME(Zalathar): Investigate how and why this is triggered + // by `tests/coverage/macros/context-mismatch-issue-147339.rs`. return false; } diff --git a/tests/coverage/macros/context-mismatch-issue-147339.cov-map b/tests/coverage/macros/context-mismatch-issue-147339.cov-map new file mode 100644 index 000000000000..7aa829cab72b --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.cov-map @@ -0,0 +1,40 @@ +Function name: context_mismatch_issue_147339::a (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::b (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::c (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 14, 01, 00, 0a, 01, 00, 0c, 00, 0d] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 20, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13) +Highest counter ID seen: c0 + diff --git a/tests/coverage/macros/context-mismatch-issue-147339.coverage b/tests/coverage/macros/context-mismatch-issue-147339.coverage new file mode 100644 index 000000000000..9b4fc67b8dff --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.coverage @@ -0,0 +1,28 @@ + LL| |//@ edition: 2024 + LL| | + LL| |// These nested macro expansions were found to cause span refinement to produce + LL| |// spans with a context that doesn't match the function body span, triggering + LL| |// a defensive check that discards the span. + LL| |// + LL| |// Reported in . + LL| | + LL| |macro_rules! foo { + LL| | ($($m:ident $($f:ident $v:tt)+),*) => { + LL| | $($(macro_rules! $f { () => { $v } })+)* + LL| 0| $(macro_rules! $m { () => { $(fn $f() -> i32 { $v })+ } })* + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::a + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::b + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::c + ------------------ + LL| | } + LL| |} + LL| | + LL| |foo!(m a 1 b 2, n c 3); + LL| |m!(); + LL| |n!(); + LL| | + LL| 1|fn main() {} + diff --git a/tests/coverage/macros/context-mismatch-issue-147339.rs b/tests/coverage/macros/context-mismatch-issue-147339.rs new file mode 100644 index 000000000000..80e744afc7c6 --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.rs @@ -0,0 +1,20 @@ +//@ edition: 2024 + +// These nested macro expansions were found to cause span refinement to produce +// spans with a context that doesn't match the function body span, triggering +// a defensive check that discards the span. +// +// Reported in . + +macro_rules! foo { + ($($m:ident $($f:ident $v:tt)+),*) => { + $($(macro_rules! $f { () => { $v } })+)* + $(macro_rules! $m { () => { $(fn $f() -> i32 { $v })+ } })* + } +} + +foo!(m a 1 b 2, n c 3); +m!(); +n!(); + +fn main() {} From 5e30860d311ff54116711ee3381dcea218ae870c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 13:24:04 +0100 Subject: [PATCH 554/583] Don't check variables in subdiagnostic messages --- compiler/rustc_macros/src/diagnostics/diagnostic.rs | 4 ++-- .../rustc_macros/src/diagnostics/diagnostic_builder.rs | 6 +++--- compiler/rustc_macros/src/diagnostics/message.rs | 9 +++++++-- compiler/rustc_macros/src/diagnostics/subdiagnostic.rs | 9 +++------ 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index b4270f45422e..e8356655dd9f 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -31,7 +31,7 @@ impl<'a> DiagnosticDerive<'a> { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; messages.borrow_mut().push(message.clone()); - let message = message.diag_message(variant); + let message = message.diag_message(Some(variant)); let init = quote! { let mut diag = rustc_errors::Diag::new( @@ -97,7 +97,7 @@ impl<'a> LintDiagnosticDerive<'a> { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; messages.borrow_mut().push(message.clone()); - let message = message.diag_message(variant); + let message = message.diag_message(Some(variant)); let primary_message = quote! { diag.primary_message(#message); }; diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index e6d9409a1fa3..6107b181eea2 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -456,7 +456,7 @@ impl DiagnosticDeriveVariantBuilder { applicability.set_once(quote! { #static_applicability }, span); } - let message = slug.diag_message(variant); + let message = slug.diag_message(Some(variant)); let applicability = applicability .value() .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); @@ -487,7 +487,7 @@ impl DiagnosticDeriveVariantBuilder { variant: &VariantInfo<'_>, ) -> TokenStream { let fn_name = format_ident!("span_{}", kind); - let message = message.diag_message(variant); + let message = message.diag_message(Some(variant)); quote! { diag.#fn_name( #field_binding, @@ -504,7 +504,7 @@ impl DiagnosticDeriveVariantBuilder { message: Message, variant: &VariantInfo<'_>, ) -> TokenStream { - let message = message.diag_message(variant); + let message = message.diag_message(Some(variant)); quote! { diag.#kind(#message); } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index 153abecf8937..6c8aded89f16 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -14,13 +14,18 @@ pub(crate) enum Message { } impl Message { - pub(crate) fn diag_message(&self, variant: &VariantInfo<'_>) -> TokenStream { + /// Get the diagnostic message for this diagnostic + /// The passed `variant` is used to check whether all variables in the message are used. + /// For subdiagnostics, we cannot check this. + pub(crate) fn diag_message(&self, variant: Option<&VariantInfo<'_>>) -> TokenStream { match self { Message::Slug(slug) => { quote! { crate::fluent_generated::#slug } } Message::Inline(message_span, message) => { - verify_fluent_message(*message_span, &message, variant); + if let Some(variant) = variant { + verify_fluent_message(*message_span, &message, variant); + } quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } } } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index adc968dacd5c..ac1fa984664c 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -75,7 +75,7 @@ impl SubdiagnosticDerive { has_subdiagnostic: false, is_enum, }; - builder.into_tokens(variant).unwrap_or_else(|v| v.to_compile_error()) + builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) }); quote! { @@ -497,10 +497,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } - pub(crate) fn into_tokens( - &mut self, - variant: &VariantInfo<'_>, - ) -> Result { + pub(crate) fn into_tokens(&mut self) -> Result { let kind_slugs = self.identify_kind()?; let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect(); @@ -538,7 +535,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut calls = TokenStream::new(); for (kind, slug) in kind_slugs { let message = format_ident!("__message"); - let message_stream = slug.diag_message(variant); + let message_stream = slug.diag_message(None); calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); }); let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); From 30f82aac5bb7f4a1c47f5e0c07f100eca045009d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 14:01:05 +0100 Subject: [PATCH 555/583] Convert to inline diagnostics in `rustc_query_system` --- Cargo.lock | 2 -- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_query_system/Cargo.toml | 1 - compiler/rustc_query_system/messages.ftl | 30 ---------------- compiler/rustc_query_system/src/error.rs | 46 ++++++++++++++---------- compiler/rustc_query_system/src/lib.rs | 2 -- 7 files changed, 27 insertions(+), 56 deletions(-) delete mode 100644 compiler/rustc_query_system/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..618f3f67fd04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3812,7 +3812,6 @@ dependencies = [ "rustc_pattern_analysis", "rustc_privacy", "rustc_public", - "rustc_query_system", "rustc_resolve", "rustc_session", "rustc_span", @@ -4572,7 +4571,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_hashes", "rustc_hir", "rustc_index", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..0f8b65c47b06 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -44,7 +44,6 @@ rustc_passes = { path = "../rustc_passes" } rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } rustc_privacy = { path = "../rustc_privacy" } rustc_public = { path = "../rustc_public", features = ["rustc_internal"] } -rustc_query_system = { path = "../rustc_query_system" } rustc_resolve = { path = "../rustc_resolve" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..c72dc35064b0 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -142,7 +142,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_passes::DEFAULT_LOCALE_RESOURCE, rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, rustc_privacy::DEFAULT_LOCALE_RESOURCE, - rustc_query_system::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 0ad8143c5a4f..761a299eab77 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -11,7 +11,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_query_system/messages.ftl b/compiler/rustc_query_system/messages.ftl deleted file mode 100644 index d2ab2d34c5fc..000000000000 --- a/compiler/rustc_query_system/messages.ftl +++ /dev/null @@ -1,30 +0,0 @@ -query_system_cycle = cycle detected when {$stack_bottom} - .note = see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive - -query_system_cycle_recursive_ty_alias = type aliases cannot be recursive -query_system_cycle_recursive_ty_alias_help1 = consider using a struct, enum, or union instead to break the cycle -query_system_cycle_recursive_ty_alias_help2 = see for more information - -query_system_cycle_stack_middle = ...which requires {$desc}... - -query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle - -query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again - -query_system_cycle_usage = cycle used when {$usage} - -query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node} - -query_system_increment_compilation_note1 = please follow the instructions below to create a bug report with the provided information -query_system_increment_compilation_note2 = for incremental compilation bugs, having a reproduction is vital -query_system_increment_compilation_note3 = an ideal reproduction consists of the code before and some patch that then triggers the bug when applied and compiled again -query_system_increment_compilation_note4 = as a workaround, you can run {$run_cmd} to allow your project to compile - -query_system_overflow_note = query depth increased by {$depth} when {$desc} - -query_system_query_overflow = queries overflow the depth limit! - .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) - -query_system_reentrant = internal compiler error: reentrant incremental verify failure, suppressing message diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index 4b1effe2b33d..55f2feba0d86 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -4,7 +4,7 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Subdiagnostic)] -#[note(query_system_cycle_stack_middle)] +#[note("...which requires {$desc}...")] pub(crate) struct CycleStack { #[primary_span] pub span: Span, @@ -13,24 +13,26 @@ pub(crate) struct CycleStack { #[derive(Subdiagnostic)] pub(crate) enum StackCount { - #[note(query_system_cycle_stack_single)] + #[note("...which immediately requires {$stack_bottom} again")] Single, - #[note(query_system_cycle_stack_multiple)] + #[note("...which again requires {$stack_bottom}, completing the cycle")] Multiple, } #[derive(Subdiagnostic)] pub(crate) enum Alias { - #[note(query_system_cycle_recursive_ty_alias)] - #[help(query_system_cycle_recursive_ty_alias_help1)] - #[help(query_system_cycle_recursive_ty_alias_help2)] + #[note("type aliases cannot be recursive")] + #[help("consider using a struct, enum, or union instead to break the cycle")] + #[help( + "see for more information" + )] Ty, - #[note(query_system_cycle_recursive_trait_alias)] + #[note("trait aliases cannot be recursive")] Trait, } #[derive(Subdiagnostic)] -#[note(query_system_cycle_usage)] +#[note("cycle used when {$usage}")] pub(crate) struct CycleUsage { #[primary_span] pub span: Span, @@ -38,7 +40,7 @@ pub(crate) struct CycleUsage { } #[derive(Diagnostic)] -#[diag(query_system_cycle, code = E0391)] +#[diag("cycle detected when {$stack_bottom}", code = E0391)] pub(crate) struct Cycle { #[primary_span] pub span: Span, @@ -51,28 +53,34 @@ pub(crate) struct Cycle { pub alias: Option, #[subdiagnostic] pub cycle_usage: Option, - #[note] + #[note( + "see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information" + )] pub note_span: (), } #[derive(Diagnostic)] -#[diag(query_system_reentrant)] +#[diag("internal compiler error: reentrant incremental verify failure, suppressing message")] pub(crate) struct Reentrant; #[derive(Diagnostic)] -#[diag(query_system_increment_compilation)] -#[note(query_system_increment_compilation_note1)] -#[note(query_system_increment_compilation_note2)] -#[note(query_system_increment_compilation_note3)] -#[note(query_system_increment_compilation_note4)] +#[diag("internal compiler error: encountered incremental compilation error with {$dep_node}")] +#[note("please follow the instructions below to create a bug report with the provided information")] +#[note("for incremental compilation bugs, having a reproduction is vital")] +#[note( + "an ideal reproduction consists of the code before and some patch that then triggers the bug when applied and compiled again" +)] +#[note("as a workaround, you can run {$run_cmd} to allow your project to compile")] pub(crate) struct IncrementCompilation { pub run_cmd: String, pub dep_node: String, } #[derive(Diagnostic)] -#[help] -#[diag(query_system_query_overflow)] +#[help( + "consider increasing the recursion limit by adding a `#![recursion_limit = \"{$suggested_limit}\"]` attribute to your crate (`{$crate_name}`)" +)] +#[diag("queries overflow the depth limit!")] pub struct QueryOverflow { #[primary_span] pub span: Span, @@ -83,7 +91,7 @@ pub struct QueryOverflow { } #[derive(Subdiagnostic)] -#[note(query_system_overflow_note)] +#[note("query depth increased by {$depth} when {$desc}")] pub struct QueryOverflowNote { pub desc: String, pub depth: usize, diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index cdfe3454061c..d1907a8c582d 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -14,5 +14,3 @@ mod values; pub use error::{QueryOverflow, QueryOverflowNote}; pub use values::Value; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } From b668057d794d87de368156626504d721ffa1c767 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Fri, 30 Jan 2026 03:06:46 +0000 Subject: [PATCH 556/583] Port `rustc_mir` to attribute parser --- Cargo.lock | 2 +- .../src/attributes/rustc_internal.rs | 93 +++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 8 +- .../src/session_diagnostics.rs | 4 + .../rustc_hir/src/attrs/data_structures.rs | 18 ++++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir/src/attrs/pretty_printing.rs | 9 ++ compiler/rustc_mir_dataflow/Cargo.toml | 2 +- compiler/rustc_mir_dataflow/messages.ftl | 12 --- compiler/rustc_mir_dataflow/src/errors.rs | 32 +------ .../src/framework/graphviz.rs | 94 +++++-------------- compiler/rustc_mir_dataflow/src/rustc_peek.rs | 71 ++++++-------- compiler/rustc_passes/src/check_attr.rs | 1 + 13 files changed, 186 insertions(+), 161 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..ffd11e46d98b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4338,11 +4338,11 @@ dependencies = [ "polonius-engine", "regex", "rustc_abi", - "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", "rustc_graphviz", + "rustc_hir", "rustc_index", "rustc_macros", "rustc_middle", diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 250bceecbd65..8961dc47706f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,5 +1,7 @@ +use std::path::PathBuf; + use rustc_ast::{LitIntType, LitKind, MetaItemLit}; -use rustc_hir::attrs::RustcLayoutType; +use rustc_hir::attrs::{BorrowckGraphvizFormatKind, RustcLayoutType, RustcMirKind}; use rustc_session::errors; use super::prelude::*; @@ -357,7 +359,6 @@ impl CombineAttributeParser for RustcLayoutParser { const TEMPLATE: AttributeTemplate = template!(List: &["abi", "align", "size", "homogenous_aggregate", "debug"]); - fn extend( cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser, @@ -397,6 +398,94 @@ impl CombineAttributeParser for RustcLayoutParser { } } +pub(crate) struct RustcMirParser; + +impl CombineAttributeParser for RustcMirParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_mir]; + + type Item = RustcMirKind; + + const CONVERT: ConvertFn = |items, _| AttributeKind::RustcMir(items); + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + ]); + + const TEMPLATE: AttributeTemplate = template!(List: &["arg1, arg2, ..."]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return ThinVec::new(); + }; + + list.mixed() + .filter_map(|arg| arg.meta_item()) + .filter_map(|mi| { + if let Some(ident) = mi.ident() { + match ident.name { + sym::rustc_peek_maybe_init => Some(RustcMirKind::PeekMaybeInit), + sym::rustc_peek_maybe_uninit => Some(RustcMirKind::PeekMaybeUninit), + sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness), + sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow), + sym::borrowck_graphviz_postflow => { + let Some(nv) = mi.args().name_value() else { + cx.expected_name_value( + mi.span(), + Some(sym::borrowck_graphviz_postflow), + ); + return None; + }; + let Some(path) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, None); + return None; + }; + let path = PathBuf::from(path.to_string()); + if path.file_name().is_some() { + Some(RustcMirKind::BorrowckGraphvizPostflow { path }) + } else { + cx.expected_filename_literal(nv.value_span); + None + } + } + sym::borrowck_graphviz_format => { + let Some(nv) = mi.args().name_value() else { + cx.expected_name_value( + mi.span(), + Some(sym::borrowck_graphviz_format), + ); + return None; + }; + let Some(format) = nv.value_as_ident() else { + cx.expected_identifier(nv.value_span); + return None; + }; + match format.name { + sym::two_phase => Some(RustcMirKind::BorrowckGraphvizFormat { + format: BorrowckGraphvizFormatKind::TwoPhase, + }), + _ => { + cx.expected_specific_argument(format.span, &[sym::two_phase]); + None + } + } + } + _ => None, + } + } else { + None + } + }) + .collect() + } +} pub(crate) struct RustcNonConstTraitMethodParser; impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f340b393567e..322e189e6d12 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -81,7 +81,7 @@ use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, - RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, + RustcMirParser, RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, RustcNonConstTraitMethodParser, RustcNounwindParser, RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, RustcSimdMonomorphizeLaneLimitParser, @@ -202,6 +202,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, // tidy-alphabetical-end @@ -517,6 +518,11 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { ) } + /// Error that a filename string literal was expected. + pub(crate) fn expected_filename_literal(&self, span: Span) { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedFilenameLiteral); + } + pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f9748542beb9..e67fc95e2806 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -524,6 +524,7 @@ pub(crate) enum AttributeParseErrorReason<'a> { ExpectedStringLiteral { byte_string: Option, }, + ExpectedFilenameLiteral, ExpectedIntegerLiteral, ExpectedIntegerLiteralInRange { lower_bound: isize, @@ -597,6 +598,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { diag.span_label(self.span, "expected a string literal here"); } } + AttributeParseErrorReason::ExpectedFilenameLiteral => { + diag.span_label(self.span, "expected a filename string literal here"); + } AttributeParseErrorReason::ExpectedIntegerLiteral => { diag.span_label(self.span, "expected an integer literal here"); } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fba53c913e16..6138ffc8d954 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -699,6 +699,21 @@ pub enum RustcLayoutType { Debug, } +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] +pub enum RustcMirKind { + PeekMaybeInit, + PeekMaybeUninit, + PeekLiveness, + StopAfterDataflow, + BorrowckGraphvizPostflow { path: PathBuf }, + BorrowckGraphvizFormat { format: BorrowckGraphvizFormatKind }, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] +pub enum BorrowckGraphvizFormatKind { + TwoPhase, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -1090,6 +1105,9 @@ pub enum AttributeKind { /// Represents `#[rustc_main]`. RustcMain, + /// Represents `#[rustc_mir]`. + RustcMir(ThinVec), + /// Represents `#[rustc_must_implement_one_of]` RustcMustImplementOneOf { attr_span: Span, fn_names: ThinVec }, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 26e5fc6e6257..7ec1920152a5 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -122,6 +122,7 @@ impl AttributeKind { RustcLintUntrackedQueryInformation => Yes, RustcMacroTransparency(..) => Yes, RustcMain => No, + RustcMir(..) => Yes, RustcMustImplementOneOf { .. } => No, RustcNeverReturnsNullPointer => Yes, RustcNoImplicitAutorefs => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index c2ad644688fc..bd268d2c423f 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -1,5 +1,6 @@ use std::num::NonZero; use std::ops::Deref; +use std::path::PathBuf; use rustc_abi::Align; use rustc_ast::attr::data_structures::CfgEntry; @@ -96,7 +97,15 @@ impl PrintAttribute for FxIndexMap { p.word("]"); } } +impl PrintAttribute for PathBuf { + fn should_render(&self) -> bool { + true + } + fn print_attribute(&self, p: &mut Printer) { + p.word(self.display().to_string()); + } +} macro_rules! print_skip { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index 9621f9f20bdc..e41d5da71397 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -8,11 +8,11 @@ edition = "2024" polonius-engine = "0.13.0" regex = "1" rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_graphviz = { path = "../rustc_graphviz" } +rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_mir_dataflow/messages.ftl b/compiler/rustc_mir_dataflow/messages.ftl index 5698367e42ba..3783c647b03e 100644 --- a/compiler/rustc_mir_dataflow/messages.ftl +++ b/compiler/rustc_mir_dataflow/messages.ftl @@ -1,9 +1,3 @@ -mir_dataflow_duplicate_values_for = - duplicate values for `{$name}` - -mir_dataflow_path_must_end_in_filename = - path must end in a filename - mir_dataflow_peek_argument_not_a_local = rustc_peek: argument was not a local @@ -19,11 +13,5 @@ mir_dataflow_peek_must_be_not_temporary = mir_dataflow_peek_must_be_place_or_ref_place = rustc_peek: argument expression must be either `place` or `&place` -mir_dataflow_requires_an_argument = - `{$name}` requires an argument - mir_dataflow_stop_after_dataflow_ended_compilation = stop_after_dataflow ended compilation - -mir_dataflow_unknown_formatter = - unknown formatter diff --git a/compiler/rustc_mir_dataflow/src/errors.rs b/compiler/rustc_mir_dataflow/src/errors.rs index cfacc0ec370c..9d8c34c8a1f3 100644 --- a/compiler/rustc_mir_dataflow/src/errors.rs +++ b/compiler/rustc_mir_dataflow/src/errors.rs @@ -1,35 +1,5 @@ use rustc_macros::Diagnostic; -use rustc_span::{Span, Symbol}; - -#[derive(Diagnostic)] -#[diag(mir_dataflow_path_must_end_in_filename)] -pub(crate) struct PathMustEndInFilename { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_unknown_formatter)] -pub(crate) struct UnknownFormatter { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_duplicate_values_for)] -pub(crate) struct DuplicateValuesFor { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_requires_an_argument)] -pub(crate) struct RequiresAnArgument { - #[primary_span] - pub span: Span, - pub name: Symbol, -} +use rustc_span::Span; #[derive(Diagnostic)] #[diag(mir_dataflow_stop_after_dataflow_ended_compilation)] diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 22bff3806b15..c4b9b4ce6416 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -7,6 +7,9 @@ use std::sync::OnceLock; use std::{io, ops, str}; use regex::Regex; +use rustc_graphviz as dot; +use rustc_hir::attrs::{AttributeKind, BorrowckGraphvizFormatKind, RustcMirKind}; +use rustc_hir::find_attr; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal, @@ -14,17 +17,12 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_span::def_id::DefId; -use rustc_span::{Symbol, sym}; use tracing::debug; -use {rustc_ast as ast, rustc_graphviz as dot}; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; use super::{ Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor, visit_results, }; -use crate::errors::{ - DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, -}; /// Writes a DOT file containing the results of a dataflow analysis if the user requested it via /// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are @@ -43,10 +41,7 @@ where use std::io::Write; let def_id = body.source.def_id(); - let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { - // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` - return Ok(()); - }; + let attrs = RustcMirAttrs::parse(tcx, def_id); let file = try { match attrs.output_path(A::NAME) { @@ -72,10 +67,7 @@ where Err(e) => return Err(e), }; - let style = match attrs.formatter { - Some(sym::two_phase) => OutputStyle::BeforeAndAfter, - _ => OutputStyle::AfterOnly, - }; + let style = attrs.formatter.unwrap_or(OutputStyle::AfterOnly); let mut buf = Vec::new(); @@ -98,71 +90,33 @@ where #[derive(Default)] struct RustcMirAttrs { basename_and_suffix: Option, - formatter: Option, + formatter: Option, } impl RustcMirAttrs { - fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result { - let mut result = Ok(()); + fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Self { let mut ret = RustcMirAttrs::default(); - let rustc_mir_attrs = tcx - .get_attrs(def_id, sym::rustc_mir) - .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); - - for attr in rustc_mir_attrs { - let attr_result = match attr.name() { - Some(name @ sym::borrowck_graphviz_postflow) => { - Self::set_field(&mut ret.basename_and_suffix, tcx, name, &attr, |s| { - let path = PathBuf::from(s.to_string()); - match path.file_name() { - Some(_) => Ok(path), - None => { - tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); - Err(()) + let attrs = tcx.get_all_attrs(def_id); + if let Some(rustc_mir_attrs) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) { + for attr in rustc_mir_attrs { + match attr { + RustcMirKind::BorrowckGraphvizPostflow { path } => { + ret.basename_and_suffix = Some(path.clone()); + } + RustcMirKind::BorrowckGraphvizFormat { format } => { + ret.formatter = match format { + BorrowckGraphvizFormatKind::TwoPhase => { + Some(OutputStyle::BeforeAndAfter) } - } - }) - } - Some(name @ sym::borrowck_graphviz_format) => { - Self::set_field(&mut ret.formatter, tcx, name, &attr, |s| match s { - sym::two_phase => Ok(s), - _ => { - tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); - Err(()) - } - }) - } - _ => Ok(()), - }; - - result = result.and(attr_result); + }; + } + _ => (), + }; + } } - result.map(|()| ret) - } - - fn set_field( - field: &mut Option, - tcx: TyCtxt<'_>, - name: Symbol, - attr: &ast::MetaItemInner, - mapper: impl FnOnce(Symbol) -> Result, - ) -> Result<(), ()> { - if field.is_some() { - tcx.dcx().emit_err(DuplicateValuesFor { span: attr.span(), name }); - - return Err(()); - } - - if let Some(s) = attr.value_str() { - *field = Some(mapper(s)?); - Ok(()) - } else { - tcx.dcx() - .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name().unwrap() }); - Err(()) - } + ret } /// Returns the path where dataflow results should be written, or `None` diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index a899ec1fa884..1c5b38361669 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,8 +1,8 @@ -use rustc_ast::MetaItem; +use rustc_hir::attrs::{AttributeKind, RustcMirKind}; +use rustc_hir::find_attr; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::def_id::DefId; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, sym}; use tracing::{debug, info}; use crate::errors::{ @@ -14,52 +14,37 @@ use crate::impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPl use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use crate::{Analysis, JoinSemiLattice, ResultsCursor}; -fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option { - for attr in tcx.get_attrs(def_id, sym::rustc_mir) { - let items = attr.meta_item_list(); - for item in items.iter().flat_map(|l| l.iter()) { - match item.meta_item() { - Some(mi) if mi.has_name(name) => return Some(mi.clone()), - _ => continue, - } - } - } - None -} - pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let def_id = body.source.def_id(); - if !tcx.has_attr(def_id, sym::rustc_mir) { - debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); - return; - } else { + let attrs = tcx.get_all_attrs(def_id); + if let Some(kind) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) { + let move_data = MoveData::gather_moves(body, tcx, |_| true); debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); - } + if kind.contains(&RustcMirKind::PeekMaybeInit) { + let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_inits); + } - let move_data = MoveData::gather_moves(body, tcx, |_| true); + if kind.contains(&RustcMirKind::PeekMaybeUninit) { + let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_uninits); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { - let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_inits); - } + if kind.contains(&RustcMirKind::PeekLiveness) { + let flow_liveness = + MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_liveness); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { - let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_uninits); - } - - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { - let flow_liveness = - MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_liveness); - } - - if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { - tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation); + if kind.contains(&RustcMirKind::StopAfterDataflow) { + tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation); + } + } else { + debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bd02bc551756..dd16092c99f9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -309,6 +309,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcLintUntrackedQueryInformation | AttributeKind::RustcMacroTransparency(_) | AttributeKind::RustcMain + | AttributeKind::RustcMir(_) | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcNoImplicitAutorefs | AttributeKind::RustcNonConstTraitMethod From 1722b8e06b3c334b77024f4d8d8de4a832f10df9 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 15:14:49 +0100 Subject: [PATCH 557/583] Convert to inline diagnostics in `rustc_infer` --- Cargo.lock | 2 -- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_infer/Cargo.toml | 1 - compiler/rustc_infer/messages.ftl | 5 ----- compiler/rustc_infer/src/errors.rs | 8 ++++---- compiler/rustc_infer/src/lib.rs | 2 -- 7 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 compiler/rustc_infer/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..111e5afb3e7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3795,7 +3795,6 @@ dependencies = [ "rustc_hir_typeck", "rustc_incremental", "rustc_index", - "rustc_infer", "rustc_interface", "rustc_lexer", "rustc_lint", @@ -4093,7 +4092,6 @@ version = "0.0.0" dependencies = [ "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_macros", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..b688a6710a8e 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -27,7 +27,6 @@ rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } -rustc_infer = { path = "../rustc_infer" } rustc_interface = { path = "../rustc_interface" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint = { path = "../rustc_lint" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..ce6de3e2a1f8 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -129,7 +129,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE, rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE, rustc_incremental::DEFAULT_LOCALE_RESOURCE, - rustc_infer::DEFAULT_LOCALE_RESOURCE, rustc_interface::DEFAULT_LOCALE_RESOURCE, rustc_lint::DEFAULT_LOCALE_RESOURCE, rustc_metadata::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml index 08c036148849..c4fbe89315db 100644 --- a/compiler/rustc_infer/Cargo.toml +++ b/compiler/rustc_infer/Cargo.toml @@ -10,7 +10,6 @@ doctest = false # tidy-alphabetical-start rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl deleted file mode 100644 index e51734ff7a77..000000000000 --- a/compiler/rustc_infer/messages.ftl +++ /dev/null @@ -1,5 +0,0 @@ -infer_opaque_hidden_type = - opaque type's hidden type cannot be another opaque type from the same scope - .label = one of the two opaque types used here has to be outside its defining scope - .opaque_type = opaque type whose hidden type is being assigned - .hidden_type = opaque type being used as hidden type diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs index 76ea9c3433d3..7c6e3b4ef314 100644 --- a/compiler/rustc_infer/src/errors.rs +++ b/compiler/rustc_infer/src/errors.rs @@ -2,13 +2,13 @@ use rustc_macros::Diagnostic; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(infer_opaque_hidden_type)] +#[diag("opaque type's hidden type cannot be another opaque type from the same scope")] pub(crate) struct OpaqueHiddenTypeDiag { #[primary_span] - #[label] + #[label("one of the two opaque types used here has to be outside its defining scope")] pub span: Span, - #[note(infer_opaque_type)] + #[note("opaque type whose hidden type is being assigned")] pub opaque_type: Span, - #[note(infer_hidden_type)] + #[note("opaque type being used as hidden type")] pub hidden_type: Span, } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 05ea0f813818..2a1733ef63cb 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -22,5 +22,3 @@ mod errors; pub mod infer; pub mod traits; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } From 4e4192aa48ccb1955b945b0e898decbd73007d27 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 3 Feb 2026 16:07:12 +0100 Subject: [PATCH 558/583] Try to fix `rustdoc-gui/globals.goml` flakyness --- tests/rustdoc-gui/globals.goml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml index 4f12175f6ab2..7fd9c5bfb6f2 100644 --- a/tests/rustdoc-gui/globals.goml +++ b/tests/rustdoc-gui/globals.goml @@ -6,7 +6,7 @@ include: "utils.goml" // URL query go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa" wait-for: "#search-tabs" -assert-window-property-false: {"searchIndex": null} +wait-for-window-property-false: {"searchIndex": null} // Form input go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" From dfc60ae100f424d28bb9d00979e17846335ac8a9 Mon Sep 17 00:00:00 2001 From: lapla Date: Wed, 4 Feb 2026 00:22:00 +0900 Subject: [PATCH 559/583] Distinguish error message for `#[diagnostic::on_const]` on const trait impls --- compiler/rustc_passes/messages.ftl | 4 ++++ compiler/rustc_passes/src/check_attr.rs | 18 +++++++++++++++++- .../on_const/misplaced_attr.rs | 2 +- .../on_const/misplaced_attr.stderr | 4 ++-- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index f8ff46189c05..ab89af226793 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -87,6 +87,10 @@ passes_deprecated_annotation_has_no_effect = passes_deprecated_attribute = deprecated attribute must be paired with either stable or unstable attribute +passes_diagnostic_diagnostic_on_const_only_for_non_const_trait_impls = + `#[diagnostic::on_const]` can only be applied to non-const trait impls + .label = this is a const trait impl + passes_diagnostic_diagnostic_on_const_only_for_trait_impls = `#[diagnostic::on_const]` can only be applied to trait impls .label = not a trait impl diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8cf68b280850..552985003fb5 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -67,6 +67,13 @@ struct DiagnosticOnConstOnlyForTraitImpls { item_span: Span, } +#[derive(LintDiagnostic)] +#[diag(passes_diagnostic_diagnostic_on_const_only_for_non_const_trait_impls)] +struct DiagnosticOnConstOnlyForNonConstTraitImpls { + #[label] + item_span: Span, +} + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -629,7 +636,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if target == (Target::Impl { of_trait: true }) { match item.unwrap() { ItemLike::Item(it) => match it.expect_impl().constness { - Constness::Const => {} + Constness::Const => { + let item_span = self.tcx.hir_span(hir_id); + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnConstOnlyForNonConstTraitImpls { item_span }, + ); + return; + } Constness::NotConst => return, }, ItemLike::ForeignItem => {} diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs index c0af549e2d01..f7babae3aa7c 100644 --- a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs @@ -6,7 +6,7 @@ pub struct Foo; #[diagnostic::on_const(message = "tadaa", note = "boing")] -//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to non-const trait impls impl const PartialEq for Foo { fn eq(&self, _other: &Foo) -> bool { true diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr index baa0b11f798b..f92ea501696e 100644 --- a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr @@ -13,14 +13,14 @@ note: the lint level is defined here LL | #![deny(misplaced_diagnostic_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `#[diagnostic::on_const]` can only be applied to trait impls +error: `#[diagnostic::on_const]` can only be applied to non-const trait impls --> $DIR/misplaced_attr.rs:8:1 | LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | impl const PartialEq for Foo { - | ---------------------------- not a trait impl + | ---------------------------- this is a const trait impl error: `#[diagnostic::on_const]` can only be applied to trait impls --> $DIR/misplaced_attr.rs:16:1 From 6c4abb7fabc807c8e73f72d06a06ca7479c8c397 Mon Sep 17 00:00:00 2001 From: sgasho Date: Wed, 4 Feb 2026 00:58:34 +0900 Subject: [PATCH 560/583] Fix some autodiff tests require Clto=fat --- tests/run-make/autodiff/type-trees/array-typetree/rmake.rs | 2 +- .../run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs | 1 + tests/run-make/autodiff/type-trees/nott-flag/rmake.rs | 2 ++ tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/f128-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/f16-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/f32-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/f64-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/i32-typetree/rmake.rs | 2 +- tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs | 2 +- tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs | 2 +- tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs | 2 +- 12 files changed, 13 insertions(+), 10 deletions(-) diff --git a/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs index 20b6a0669062..41805d26c6f7 100644 --- a/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("array.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs index 1c19963bc361..d740596907a3 100644 --- a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs @@ -9,6 +9,7 @@ fn main() { .arg("-Zautodiff=Enable") .arg("-Zautodiff=NoPostopt") .opt_level("0") + .arg("-Clto=fat") .emit("llvm-ir") .run(); diff --git a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs index de540b990cab..2e93d586a353 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs +++ b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs @@ -8,6 +8,7 @@ fn main() { rustc() .input("test.rs") .arg("-Zautodiff=Enable,NoTT") + .arg("-Clto=fat") .emit("llvm-ir") .arg("-o") .arg("nott.ll") @@ -17,6 +18,7 @@ fn main() { rustc() .input("test.rs") .arg("-Zautodiff=Enable") + .arg("-Clto=fat") .emit("llvm-ir") .arg("-o") .arg("with_tt.ll") diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs index 78718f3a2159..af1eb4197b3b 100644 --- a/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("recursion.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs index 44320ecdd571..b1672cddb811 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f128 TypeTree metadata is correctly generated llvm_filecheck().patterns("f128.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs index 0aebdbf55209..3e308a91aaff 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f16 TypeTree metadata is correctly generated llvm_filecheck().patterns("f16.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs index ee3ab753bf50..3faba69f5bdd 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f32 TypeTree metadata is correctly generated llvm_filecheck().patterns("f32.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs index 5fac9b23bc80..4f1c2cec51c4 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f64 TypeTree metadata is correctly generated llvm_filecheck().patterns("f64.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs index a40fd55d88ad..328d690f29c8 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that i32 TypeTree metadata is correctly generated llvm_filecheck().patterns("i32.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs index b81fb50bf1a7..0cc6b42a0317 100644 --- a/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("slice.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs index 0af1b65ee181..10499712d1e3 100644 --- a/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("struct.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs index 76913828901c..4c0458f588a8 100644 --- a/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("tuple.check").stdin_buf(rfs::read("test.ll")).run(); } From d329971fc2356d96d8f8900eb5afe1bb92768543 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 4 Feb 2026 00:22:14 +0900 Subject: [PATCH 561/583] avoid semicolon suggestion when tail expr is error add a link to the issue fix test stderr --- .../src/error_reporting/traits/suggestions.rs | 4 ++- ...cover-from-semicolon-trailing-undefined.rs | 12 +++++++++ ...r-from-semicolon-trailing-undefined.stderr | 26 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/ui/type/recover-from-semicolon-trailing-undefined.rs create mode 100644 tests/ui/type/recover-from-semicolon-trailing-undefined.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index d54f3812350d..22fa437ed5c7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -954,7 +954,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let new_obligation = self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); - if self.predicate_must_hold_modulo_regions(&new_obligation) { + if !matches!(tail_expr.kind, hir::ExprKind::Err(_)) + && self.predicate_must_hold_modulo_regions(&new_obligation) + { err.span_suggestion_short( stmt.span.with_lo(tail_expr.span.hi()), "remove this semicolon", diff --git a/tests/ui/type/recover-from-semicolon-trailing-undefined.rs b/tests/ui/type/recover-from-semicolon-trailing-undefined.rs new file mode 100644 index 000000000000..3e2860eb1343 --- /dev/null +++ b/tests/ui/type/recover-from-semicolon-trailing-undefined.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Znext-solver=globally + +// Regression test for https://github.com/rust-lang/rust/issues/151610 + +fn main() { + let x_str = { + x!("{}", x); + //~^ ERROR cannot find macro `x` in this scope + }; + println!("{}", x_str); + //~^ ERROR `()` doesn't implement `std::fmt::Display` +} diff --git a/tests/ui/type/recover-from-semicolon-trailing-undefined.stderr b/tests/ui/type/recover-from-semicolon-trailing-undefined.stderr new file mode 100644 index 000000000000..6a8295d49338 --- /dev/null +++ b/tests/ui/type/recover-from-semicolon-trailing-undefined.stderr @@ -0,0 +1,26 @@ +error: cannot find macro `x` in this scope + --> $DIR/recover-from-semicolon-trailing-undefined.rs:7:9 + | +LL | x!("{}", x); + | ^ + +error[E0277]: `()` doesn't implement `std::fmt::Display` + --> $DIR/recover-from-semicolon-trailing-undefined.rs:10:20 + | +LL | let x_str = { + | _________________- +LL | | x!("{}", x); +LL | | +LL | | }; + | |_____- this block is missing a tail expression +LL | println!("{}", x_str); + | -- ^^^^^ `()` cannot be formatted with the default formatter + | | + | required by this formatting parameter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 19e4108ed1418a8e320230ffa886f6096164576d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 2 Feb 2026 22:47:05 +0100 Subject: [PATCH 562/583] Use DEVELOPER_DIR instead of a custom xcode-select script `DEVELOPER_DIR` is the standard environment variable for overriding the Xcode version, there is no need to invoke `xcode-select --switch` manually to do this. The variable is documented in both `man xcode-select` and `man xcrun`. --- .github/workflows/ci.yml | 3 --- src/ci/citool/tests/jobs.rs | 2 +- src/ci/citool/tests/test-jobs.yml | 4 ++-- src/ci/github-actions/jobs.yml | 8 ++++---- src/ci/scripts/select-xcode.sh | 11 ----------- 5 files changed, 7 insertions(+), 21 deletions(-) delete mode 100755 src/ci/scripts/select-xcode.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6a00bb42785..4409d4f33afc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,9 +165,6 @@ jobs: - name: install sccache run: src/ci/scripts/install-sccache.sh - - name: select Xcode - run: src/ci/scripts/select-xcode.sh - - name: install clang run: src/ci/scripts/install-clang.sh diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs index 6787f00d9af8..b0309c52003f 100644 --- a/src/ci/citool/tests/jobs.rs +++ b/src/ci/citool/tests/jobs.rs @@ -6,7 +6,7 @@ const TEST_JOBS_YML_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/tes fn auto_jobs() { let stdout = get_matrix("push", "commit", "refs/heads/automation/bors/auto"); insta::assert_snapshot!(stdout, @r#" - jobs=[{"name":"aarch64-gnu","full_name":"auto - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"x86_64-gnu-llvm-18-1","full_name":"auto - x86_64-gnu-llvm-18-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DOCKER_SCRIPT":"stage_2_test_set1.sh","IMAGE":"x86_64-gnu-llvm-18","READ_ONLY_SRC":"0","RUST_BACKTRACE":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"aarch64-apple","full_name":"auto - aarch64-apple","os":"macos-14","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set rust.jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","SELECT_XCODE":"/Applications/Xcode_15.4.app","TOOLSTATE_PUBLISH":1,"USE_XCODE_CLANG":1}},{"name":"dist-i686-msvc","full_name":"auto - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"pr-check-1","full_name":"auto - pr-check-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"pr-check-2","full_name":"auto - pr-check-2","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"tidy","full_name":"auto - tidy","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true,"doc_url":"https://foo.bar"}] + jobs=[{"name":"aarch64-gnu","full_name":"auto - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"x86_64-gnu-llvm-18-1","full_name":"auto - x86_64-gnu-llvm-18-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DOCKER_SCRIPT":"stage_2_test_set1.sh","IMAGE":"x86_64-gnu-llvm-18","READ_ONLY_SRC":"0","RUST_BACKTRACE":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"aarch64-apple","full_name":"auto - aarch64-apple","os":"macos-14","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DEVELOPER_DIR":"/Applications/Xcode_15.4.app/Contents/Developer","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set rust.jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","TOOLSTATE_PUBLISH":1,"USE_XCODE_CLANG":1}},{"name":"dist-i686-msvc","full_name":"auto - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"pr-check-1","full_name":"auto - pr-check-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"pr-check-2","full_name":"auto - pr-check-2","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"tidy","full_name":"auto - tidy","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true,"doc_url":"https://foo.bar"}] run_type=auto "#); } diff --git a/src/ci/citool/tests/test-jobs.yml b/src/ci/citool/tests/test-jobs.yml index e26104905491..95cd328e7848 100644 --- a/src/ci/citool/tests/test-jobs.yml +++ b/src/ci/citool/tests/test-jobs.yml @@ -33,7 +33,7 @@ envs: # Ensure that host tooling is tested on our minimum supported macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_15.2.app + DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -112,7 +112,7 @@ auto: --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer USE_XCODE_CLANG: 1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else # supports the hardware, so only need to test it there. diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 6e07ffc43bc2..6f333ecde79b 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -459,7 +459,7 @@ auto: # Ensure that host tooling is built to support our minimum support macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer USE_XCODE_CLANG: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift @@ -475,7 +475,7 @@ auto: # FIXME(madsmtm): This might be redundant, as we're not building host tooling here (?) MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_15.2.app + DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer <<: *job-macos - name: dist-aarch64-apple @@ -496,7 +496,7 @@ auto: # supports the hardware. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 - SELECT_XCODE: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer USE_XCODE_CLANG: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift @@ -511,7 +511,7 @@ auto: --enable-sanitizers --enable-profiler --set rust.jemalloc - SELECT_XCODE: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer USE_XCODE_CLANG: 1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else # supports the hardware, so only need to test it there. diff --git a/src/ci/scripts/select-xcode.sh b/src/ci/scripts/select-xcode.sh deleted file mode 100755 index 569c4a4136d9..000000000000 --- a/src/ci/scripts/select-xcode.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# This script selects the Xcode instance to use. - -set -euo pipefail -IFS=$'\n\t' - -source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" - -if isMacOS; then - sudo xcode-select -s "${SELECT_XCODE}" -fi From c9f4f7f4436bf4023bacdd0f201eaf680eb967c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 31 Jan 2026 05:52:38 +0100 Subject: [PATCH 563/583] Move the query list to `queries.rs` --- compiler/rustc_middle/src/{query/mod.rs => queries.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/rustc_middle/src/{query/mod.rs => queries.rs} (100%) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/queries.rs similarity index 100% rename from compiler/rustc_middle/src/query/mod.rs rename to compiler/rustc_middle/src/queries.rs From 590fa1e6cb42ffdec1857d03ebeb26b5e9ab43c1 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 21:31:28 +0100 Subject: [PATCH 564/583] Convert to inline diagnostics in `rustc_ty_utils` --- Cargo.lock | 2 - compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_ty_utils/Cargo.toml | 1 - compiler/rustc_ty_utils/messages.ftl | 61 ----------------------- compiler/rustc_ty_utils/src/errors.rs | 72 ++++++++++++++------------- compiler/rustc_ty_utils/src/lib.rs | 2 - 7 files changed, 38 insertions(+), 102 deletions(-) delete mode 100644 compiler/rustc_ty_utils/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 91fce715c8cd..ee6b26aeebc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3817,7 +3817,6 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "rustc_ty_utils", "serde_json", "shlex", "tracing", @@ -4809,7 +4808,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hashes", "rustc_hir", "rustc_index", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index d97c552b412b..0c07c23c6394 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -49,7 +49,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_ty_utils = { path = "../rustc_ty_utils" } serde_json = "1.0.59" shlex = "1.0" tracing = { version = "0.1.35" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b163ae2068a6..49a100fd6757 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -143,7 +143,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_resolve::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, - rustc_ty_utils::DEFAULT_LOCALE_RESOURCE, // tidy-alphabetical-end ]; diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index ce08b300cc80..682cf941561f 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -9,7 +9,6 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ty_utils/messages.ftl b/compiler/rustc_ty_utils/messages.ftl deleted file mode 100644 index c1684bfb43b6..000000000000 --- a/compiler/rustc_ty_utils/messages.ftl +++ /dev/null @@ -1,61 +0,0 @@ -ty_utils_address_and_deref_not_supported = dereferencing or taking the address is not supported in generic constants - -ty_utils_adt_not_supported = struct/enum construction is not supported in generic constants - -ty_utils_array_not_supported = array construction is not supported in generic constants - -ty_utils_assign_not_supported = assignment is not supported in generic constants - -ty_utils_binary_not_supported = unsupported binary operation in generic constants - -ty_utils_block_not_supported = blocks are not supported in generic constants - -ty_utils_borrow_not_supported = borrowing is not supported in generic constants - -ty_utils_box_not_supported = allocations are not allowed in generic constants - -ty_utils_by_use_not_supported = .use is not allowed in generic constants - -ty_utils_closure_and_return_not_supported = closures and function keywords are not supported in generic constants - -ty_utils_const_block_not_supported = const blocks are not supported in generic constants - -ty_utils_control_flow_not_supported = control flow is not supported in generic constants - -ty_utils_field_not_supported = field access is not supported in generic constants - -ty_utils_generic_constant_too_complex = overly complex generic constant - .help = consider moving this anonymous constant into a `const` function - .maybe_supported = this operation may be supported in the future - -ty_utils_impl_trait_duplicate_arg = non-defining opaque type use in defining scope - .label = generic argument `{$arg}` used twice - .note = for this opaque type - -ty_utils_impl_trait_not_param = non-defining opaque type use in defining scope - .label = argument `{$arg}` is not a generic parameter - .note = for this opaque type - -ty_utils_index_not_supported = indexing is not supported in generic constants - -ty_utils_inline_asm_not_supported = assembly is not supported in generic constants - -ty_utils_logical_op_not_supported = unsupported operation in generic constants, short-circuiting operations would imply control flow - -ty_utils_loop_not_supported = loops and loop control flow are not supported in generic constants - -ty_utils_needs_drop_overflow = overflow while checking whether `{$query_ty}` requires drop - -ty_utils_never_to_any_not_supported = coercing the `never` type is not supported in generic constants - -ty_utils_non_primitive_simd_type = monomorphising SIMD type `{$ty}` with a non-primitive-scalar (integer/float/pointer) element type `{$e_ty}` - -ty_utils_operation_not_supported = unsupported operation in generic constants - -ty_utils_pointer_not_supported = pointer casts are not allowed in generic constants - -ty_utils_tuple_not_supported = tuple construction is not supported in generic constants - -ty_utils_unexpected_fnptr_associated_item = `FnPtr` trait with unexpected associated item - -ty_utils_yield_not_supported = coroutine control flow is not allowed in generic constants diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs index f92c405242ce..ccea5a49bd7c 100644 --- a/compiler/rustc_ty_utils/src/errors.rs +++ b/compiler/rustc_ty_utils/src/errors.rs @@ -6,18 +6,18 @@ use rustc_middle::ty::{GenericArg, Ty}; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(ty_utils_needs_drop_overflow)] +#[diag("overflow while checking whether `{$query_ty}` requires drop")] pub(crate) struct NeedsDropOverflow<'tcx> { pub query_ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(ty_utils_generic_constant_too_complex)] -#[help] +#[diag("overly complex generic constant")] +#[help("consider moving this anonymous constant into a `const` function")] pub(crate) struct GenericConstantTooComplex { #[primary_span] pub span: Span, - #[note(ty_utils_maybe_supported)] + #[note("this operation may be supported in the future")] pub maybe_supported: bool, #[subdiagnostic] pub sub: GenericConstantTooComplexSub, @@ -25,84 +25,88 @@ pub(crate) struct GenericConstantTooComplex { #[derive(Subdiagnostic)] pub(crate) enum GenericConstantTooComplexSub { - #[label(ty_utils_borrow_not_supported)] + #[label("borrowing is not supported in generic constants")] BorrowNotSupported(#[primary_span] Span), - #[label(ty_utils_address_and_deref_not_supported)] + #[label("dereferencing or taking the address is not supported in generic constants")] AddressAndDerefNotSupported(#[primary_span] Span), - #[label(ty_utils_array_not_supported)] + #[label("array construction is not supported in generic constants")] ArrayNotSupported(#[primary_span] Span), - #[label(ty_utils_block_not_supported)] + #[label("blocks are not supported in generic constants")] BlockNotSupported(#[primary_span] Span), - #[label(ty_utils_never_to_any_not_supported)] + #[label("coercing the `never` type is not supported in generic constants")] NeverToAnyNotSupported(#[primary_span] Span), - #[label(ty_utils_tuple_not_supported)] + #[label("tuple construction is not supported in generic constants")] TupleNotSupported(#[primary_span] Span), - #[label(ty_utils_index_not_supported)] + #[label("indexing is not supported in generic constants")] IndexNotSupported(#[primary_span] Span), - #[label(ty_utils_field_not_supported)] + #[label("field access is not supported in generic constants")] FieldNotSupported(#[primary_span] Span), - #[label(ty_utils_const_block_not_supported)] + #[label("const blocks are not supported in generic constants")] ConstBlockNotSupported(#[primary_span] Span), - #[label(ty_utils_adt_not_supported)] + #[label("struct/enum construction is not supported in generic constants")] AdtNotSupported(#[primary_span] Span), - #[label(ty_utils_pointer_not_supported)] + #[label("pointer casts are not allowed in generic constants")] PointerNotSupported(#[primary_span] Span), - #[label(ty_utils_yield_not_supported)] + #[label("coroutine control flow is not allowed in generic constants")] YieldNotSupported(#[primary_span] Span), - #[label(ty_utils_loop_not_supported)] + #[label("loops and loop control flow are not supported in generic constants")] LoopNotSupported(#[primary_span] Span), - #[label(ty_utils_box_not_supported)] + #[label("allocations are not allowed in generic constants")] BoxNotSupported(#[primary_span] Span), - #[label(ty_utils_binary_not_supported)] + #[label("unsupported binary operation in generic constants")] BinaryNotSupported(#[primary_span] Span), - #[label(ty_utils_by_use_not_supported)] + #[label(".use is not allowed in generic constants")] ByUseNotSupported(#[primary_span] Span), - #[label(ty_utils_logical_op_not_supported)] + #[label( + "unsupported operation in generic constants, short-circuiting operations would imply control flow" + )] LogicalOpNotSupported(#[primary_span] Span), - #[label(ty_utils_assign_not_supported)] + #[label("assignment is not supported in generic constants")] AssignNotSupported(#[primary_span] Span), - #[label(ty_utils_closure_and_return_not_supported)] + #[label("closures and function keywords are not supported in generic constants")] ClosureAndReturnNotSupported(#[primary_span] Span), - #[label(ty_utils_control_flow_not_supported)] + #[label("control flow is not supported in generic constants")] ControlFlowNotSupported(#[primary_span] Span), - #[label(ty_utils_inline_asm_not_supported)] + #[label("assembly is not supported in generic constants")] InlineAsmNotSupported(#[primary_span] Span), - #[label(ty_utils_operation_not_supported)] + #[label("unsupported operation in generic constants")] OperationNotSupported(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(ty_utils_unexpected_fnptr_associated_item)] +#[diag("`FnPtr` trait with unexpected associated item")] pub(crate) struct UnexpectedFnPtrAssociatedItem { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(ty_utils_non_primitive_simd_type)] +#[diag( + "monomorphising SIMD type `{$ty}` with a non-primitive-scalar (integer/float/pointer) element type `{$e_ty}`" +)] pub(crate) struct NonPrimitiveSimdType<'tcx> { pub ty: Ty<'tcx>, pub e_ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(ty_utils_impl_trait_duplicate_arg)] +#[diag("non-defining opaque type use in defining scope")] pub(crate) struct DuplicateArg<'tcx> { pub arg: GenericArg<'tcx>, #[primary_span] - #[label] + #[label("generic argument `{$arg}` used twice")] pub span: Span, - #[note] + #[note("for this opaque type")] pub opaque_span: Span, } #[derive(Diagnostic)] -#[diag(ty_utils_impl_trait_not_param, code = E0792)] +#[diag("non-defining opaque type use in defining scope", code = E0792)] pub(crate) struct NotParam<'tcx> { pub arg: GenericArg<'tcx>, #[primary_span] - #[label] + #[label("argument `{$arg}` is not a generic parameter")] pub span: Span, - #[note] + #[note("for this opaque type")] pub opaque_span: Span, } diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index d8b50b2d2e42..9f8f3b240890 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -31,8 +31,6 @@ pub mod sig_types; mod structural_match; mod ty; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn provide(providers: &mut Providers) { abi::provide(providers); assoc::provide(providers); From 247a022957f27f064cb468e0bbba3c3dee10e066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 31 Jan 2026 06:45:46 +0100 Subject: [PATCH 565/583] Fix references and remove inner `queries` module --- compiler/rustc_macros/src/query.rs | 4 +- .../src/rmeta/decoder/cstore_impl.rs | 7 +- compiler/rustc_middle/src/lib.rs | 2 + compiler/rustc_middle/src/queries.rs | 24 +-- compiler/rustc_middle/src/query/erase.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 23 +++ compiler/rustc_middle/src/query/plumbing.rs | 178 +++++++++--------- compiler/rustc_middle/src/util/mod.rs | 4 +- compiler/rustc_query_impl/src/lib.rs | 7 +- compiler/rustc_query_impl/src/plumbing.rs | 12 +- tests/ui-fulldeps/obtain-borrowck.rs | 2 +- 11 files changed, 136 insertions(+), 129 deletions(-) create mode 100644 compiler/rustc_middle/src/query/mod.rs diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 0e2b16d72eb1..bc6f856e8683 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -289,7 +289,7 @@ fn add_query_desc_cached_impl( cached.extend(quote! { #[allow(unused_variables, unused_braces, rustc::pass_by_value)] #[inline] - pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::query::queries::#name::Key<'tcx>) -> bool { + pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool { #ra_hint #expr } @@ -301,7 +301,7 @@ fn add_query_desc_cached_impl( let desc = quote! { #[allow(unused_variables)] - pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::query::queries::#name::Key<'tcx>) -> String { + pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::queries::#name::Key<'tcx>) -> String { let (#tcx, #key) = (tcx, key); format!(#desc) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 7bd3f7db55f9..b1f3bafd92fb 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -11,7 +11,8 @@ use rustc_middle::bug; use rustc_middle::metadata::{AmbigModChild, ModChild}; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; -use rustc_middle::query::{ExternProviders, LocalCrate}; +use rustc_middle::queries::ExternProviders; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; @@ -134,8 +135,8 @@ macro_rules! provide_one { ($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => $compute:block) => { fn $name<'tcx>( $tcx: TyCtxt<'tcx>, - def_id_arg: rustc_middle::query::queries::$name::Key<'tcx>, - ) -> rustc_middle::query::queries::$name::ProvidedValue<'tcx> { + def_id_arg: rustc_middle::queries::$name::Key<'tcx>, + ) -> rustc_middle::queries::$name::ProvidedValue<'tcx> { let _prof_timer = $tcx.prof.generic_activity(concat!("metadata_decode_entry_", stringify!($name))); diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 5682c21e4353..bb94b4c927ae 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -86,6 +86,8 @@ mod values; #[macro_use] pub mod query; #[macro_use] +pub mod queries; +#[macro_use] pub mod dep_graph; // Allows macros to refer to this crate as `::rustc_middle` diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index c9291d89be8a..3ade5cab0915 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -100,8 +100,6 @@ use rustc_span::{DUMMY_SP, LocalExpnId, Span, Symbol}; use rustc_target::spec::PanicStrategy; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; -pub use self::keys::{AsLocalKey, Key, LocalCrate}; -pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; @@ -120,7 +118,10 @@ use crate::mir::interpret::{ use crate::mir::mono::{ CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, }; -use crate::query::plumbing::CyclePlaceholder; +use crate::query::plumbing::{ + CyclePlaceholder, IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, +}; +use crate::query::{AsLocalKey, describe_as_module}; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, @@ -142,14 +143,6 @@ use crate::ty::{ }; use crate::{dep_graph, mir, thir}; -mod arena_cached; -pub mod erase; -pub(crate) mod inner; -mod keys; -pub mod on_disk_cache; -#[macro_use] -pub mod plumbing; - // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method // on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way @@ -2783,12 +2776,3 @@ rustc_queries! { rustc_with_all_queries! { define_callbacks! } rustc_feedable_queries! { define_feedable! } - -fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { - let def_id = def_id.into(); - if def_id.is_top_level_module() { - "top-level module".to_string() - } else { - format!("module `{}`", tcx.def_path_str(def_id)) - } -} diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 32071595ec28..99b1f6d8c251 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -15,7 +15,7 @@ use rustc_span::source_map::Spanned; use crate::mir::interpret::EvalToValTreeResult; use crate::mir::mono::{MonoItem, NormalizationErrorInMono}; -use crate::query::CyclePlaceholder; +use crate::query::plumbing::CyclePlaceholder; use crate::traits::solve; use crate::ty::adjustment::CoerceUnsizedInfo; use crate::ty::{self, Ty, TyCtxt}; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs new file mode 100644 index 000000000000..f0121eab053b --- /dev/null +++ b/compiler/rustc_middle/src/query/mod.rs @@ -0,0 +1,23 @@ +use rustc_hir::def_id::LocalDefId; + +pub use self::keys::{AsLocalKey, Key, LocalCrate}; +pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; +pub use crate::queries::Providers; +use crate::ty::TyCtxt; + +pub(crate) mod arena_cached; +pub mod erase; +pub(crate) mod inner; +mod keys; +pub mod on_disk_cache; +#[macro_use] +pub mod plumbing; + +pub fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { + let def_id = def_id.into(); + if def_id.is_top_level_module() { + "top-level module".to_string() + } else { + format!("module `{}`", tcx.def_path_str(def_id)) + } +} diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 78879dc9da9a..66e7c55a9d2b 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -12,10 +12,10 @@ pub use sealed::IntoQueryParam; use crate::dep_graph; use crate::dep_graph::DepKind; -use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; -use crate::query::{ +use crate::queries::{ ExternProviders, PerQueryVTables, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates, }; +use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use crate::ty::TyCtxt; pub type WillCacheOnDiskForKeyFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key) -> bool; @@ -227,8 +227,8 @@ macro_rules! separate_provide_extern_decl { ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { for<'tcx> fn( TyCtxt<'tcx>, - queries::$name::Key<'tcx>, - ) -> queries::$name::ProvidedValue<'tcx> + $name::Key<'tcx>, + ) -> $name::ProvidedValue<'tcx> }; ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { separate_provide_extern_decl!([$($modifiers)*][$($args)*]) @@ -266,94 +266,90 @@ macro_rules! define_callbacks { [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty, )* ) => { + $(#[allow(unused_lifetimes)] pub mod $name { + use super::*; + use $crate::query::erase::{self, Erased}; - #[allow(unused_lifetimes)] - pub mod queries { - $(pub mod $name { - use super::super::*; - use $crate::query::erase::{self, Erased}; + pub type Key<'tcx> = $($K)*; + pub type Value<'tcx> = $V; - pub type Key<'tcx> = $($K)*; - pub type Value<'tcx> = $V; + pub type LocalKey<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*); - pub type LocalKey<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*); + /// This type alias specifies the type returned from query providers and the type + /// used for decoding. For regular queries this is the declared returned type `V`, + /// but `arena_cache` will use `::Provided` instead. + pub type ProvidedValue<'tcx> = query_if_arena!( + [$($modifiers)*] + (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) + ($V) + ); - /// This type alias specifies the type returned from query providers and the type - /// used for decoding. For regular queries this is the declared returned type `V`, - /// but `arena_cache` will use `::Provided` instead. - pub type ProvidedValue<'tcx> = query_if_arena!( - [$($modifiers)*] - (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) - ($V) - ); + /// This function takes `ProvidedValue` and converts it to an erased `Value` by + /// allocating it on an arena if the query has the `arena_cache` modifier. The + /// value is then erased and returned. This will happen when computing the query + /// using a provider or decoding a stored result. + #[inline(always)] + pub fn provided_to_erased<'tcx>( + _tcx: TyCtxt<'tcx>, + provided_value: ProvidedValue<'tcx>, + ) -> Erased> { + // Store the provided value in an arena and get a reference + // to it, for queries with `arena_cache`. + let value: Value<'tcx> = query_if_arena!([$($modifiers)*] + { + use $crate::query::arena_cached::ArenaCached; - /// This function takes `ProvidedValue` and converts it to an erased `Value` by - /// allocating it on an arena if the query has the `arena_cache` modifier. The - /// value is then erased and returned. This will happen when computing the query - /// using a provider or decoding a stored result. - #[inline(always)] - pub fn provided_to_erased<'tcx>( - _tcx: TyCtxt<'tcx>, - provided_value: ProvidedValue<'tcx>, - ) -> Erased> { - // Store the provided value in an arena and get a reference - // to it, for queries with `arena_cache`. - let value: Value<'tcx> = query_if_arena!([$($modifiers)*] - { - use $crate::query::arena_cached::ArenaCached; - - if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { - <$V as ArenaCached>::alloc_in_arena( - |v| _tcx.query_system.arenas.$name.alloc(v), - provided_value, - ) - } else { - <$V as ArenaCached>::alloc_in_arena( - |v| _tcx.arena.dropless.alloc(v), - provided_value, - ) - } + if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.query_system.arenas.$name.alloc(v), + provided_value, + ) + } else { + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.arena.dropless.alloc(v), + provided_value, + ) } - // Otherwise, the provided value is the value. - (provided_value) - ); - erase::erase_val(value) + } + // Otherwise, the provided value is the value. + (provided_value) + ); + erase::erase_val(value) + } + + pub type Storage<'tcx> = <$($K)* as $crate::query::Key>::Cache>; + + // Ensure that keys grow no larger than 88 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] + const _: () = { + if size_of::>() > 88 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a key type `", + stringify!($($K)*), + "` that is too large" + )); } + }; - pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>; - - // Ensure that keys grow no larger than 88 bytes by accident. - // Increase this limit if necessary, but do try to keep the size low if possible - #[cfg(target_pointer_width = "64")] - const _: () = { - if size_of::>() > 88 { - panic!("{}", concat!( - "the query `", - stringify!($name), - "` has a key type `", - stringify!($($K)*), - "` that is too large" - )); - } - }; - - // Ensure that values grow no larger than 64 bytes by accident. - // Increase this limit if necessary, but do try to keep the size low if possible - #[cfg(target_pointer_width = "64")] - #[cfg(not(feature = "rustc_randomized_layouts"))] - const _: () = { - if size_of::>() > 64 { - panic!("{}", concat!( - "the query `", - stringify!($name), - "` has a value type `", - stringify!($V), - "` that is too large" - )); - } - }; - })* - } + // Ensure that values grow no larger than 64 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] + #[cfg(not(feature = "rustc_randomized_layouts"))] + const _: () = { + if size_of::>() > 64 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a value type `", + stringify!($V), + "` that is too large" + )); + } + }; + })* /// Holds per-query arenas for queries with the `arena_cache` modifier. #[derive(Default)] @@ -371,7 +367,7 @@ macro_rules! define_callbacks { #[derive(Default)] pub struct QueryCaches<'tcx> { - $($(#[$attr])* pub $name: queries::$name::Storage<'tcx>,)* + $($(#[$attr])* pub $name: $name::Storage<'tcx>,)* } impl<'tcx> TyCtxtEnsureOk<'tcx> { @@ -438,7 +434,7 @@ macro_rules! define_callbacks { /// ("Per" just makes this pluralized name more visually distinct.) pub struct PerQueryVTables<'tcx> { $( - pub $name: ::rustc_middle::query::plumbing::QueryVTable<'tcx, queries::$name::Storage<'tcx>>, + pub $name: ::rustc_middle::query::plumbing::QueryVTable<'tcx, $name::Storage<'tcx>>, )* } @@ -452,8 +448,8 @@ macro_rules! define_callbacks { pub struct Providers { $(pub $name: for<'tcx> fn( TyCtxt<'tcx>, - queries::$name::LocalKey<'tcx>, - ) -> queries::$name::ProvidedValue<'tcx>,)* + $name::LocalKey<'tcx>, + ) -> $name::ProvidedValue<'tcx>,)* } pub struct ExternProviders { @@ -490,7 +486,7 @@ macro_rules! define_callbacks { $(pub $name: for<'tcx> fn( TyCtxt<'tcx>, Span, - queries::$name::Key<'tcx>, + $name::Key<'tcx>, QueryMode, ) -> Option<$crate::query::erase::Erased<$V>>,)* } @@ -502,11 +498,11 @@ macro_rules! define_feedable { $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { $(#[$attr])* #[inline(always)] - pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { + pub fn $name(self, value: $name::ProvidedValue<'tcx>) { let key = self.key().into_query_param(); let tcx = self.tcx; - let erased_value = queries::$name::provided_to_erased(tcx, value); + let erased_value = $name::provided_to_erased(tcx, value); let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; diff --git a/compiler/rustc_middle/src/util/mod.rs b/compiler/rustc_middle/src/util/mod.rs index d5076a278eab..0bbe3856033b 100644 --- a/compiler/rustc_middle/src/util/mod.rs +++ b/compiler/rustc_middle/src/util/mod.rs @@ -2,7 +2,7 @@ pub mod bug; #[derive(Default, Copy, Clone)] pub struct Providers { - pub queries: crate::query::Providers, - pub extern_queries: crate::query::ExternProviders, + pub queries: crate::queries::Providers, + pub extern_queries: crate::queries::ExternProviders, pub hooks: crate::hooks::Providers, } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index bd69c99a32dc..baa37111c807 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -13,11 +13,12 @@ use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNodeIndex}; +use rustc_middle::queries::{ + self, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, +}; +use rustc_middle::query::AsLocalKey; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable}; -use rustc_middle::query::{ - AsLocalKey, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, queries, -}; use rustc_middle::ty::TyCtxt; use rustc_query_system::Value; use rustc_query_system::dep_graph::SerializedDepNodeIndex; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index d1721f1f455f..9ffea692df3a 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -648,7 +648,7 @@ macro_rules! define_queries { query_state: std::mem::offset_of!(QueryStates<'tcx>, $name), query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name), will_cache_on_disk_for_key_fn: should_ever_cache_on_disk!([$($modifiers)*] { - Some(::rustc_middle::query::cached::$name) + Some(queries::cached::$name) } { None }), @@ -672,7 +672,7 @@ macro_rules! define_queries { try_load_from_disk_fn: should_ever_cache_on_disk!([$($modifiers)*] { Some(|tcx, key, prev_index, index| { // Check the `cache_on_disk_if` condition for this key. - if !::rustc_middle::query::cached::$name(tcx, key) { + if !queries::cached::$name(tcx, key) { return None; } @@ -687,7 +687,7 @@ macro_rules! define_queries { }), is_loadable_from_disk_fn: should_ever_cache_on_disk!([$($modifiers)*] { Some(|tcx, key, index| -> bool { - ::rustc_middle::query::cached::$name(tcx, key) && + ::rustc_middle::queries::cached::$name(tcx, key) && $crate::plumbing::loadable_from_disk(tcx, index) }) } { @@ -747,7 +747,7 @@ macro_rules! define_queries { let make_frame = |tcx, key| { let kind = rustc_middle::dep_graph::dep_kinds::$name; let name = stringify!($name); - $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) + $crate::plumbing::create_query_frame(tcx, queries::descs::$name, key, kind, name) }; // Call `gather_active_jobs_inner` to do the actual work. @@ -817,8 +817,8 @@ macro_rules! define_queries { } } - pub fn make_query_vtables<'tcx>() -> ::rustc_middle::query::PerQueryVTables<'tcx> { - ::rustc_middle::query::PerQueryVTables { + pub fn make_query_vtables<'tcx>() -> queries::PerQueryVTables<'tcx> { + queries::PerQueryVTables { $( $name: query_impl::$name::make_query_vtable(), )* diff --git a/tests/ui-fulldeps/obtain-borrowck.rs b/tests/ui-fulldeps/obtain-borrowck.rs index a562d0ccd3df..ea2dab614ec1 100644 --- a/tests/ui-fulldeps/obtain-borrowck.rs +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -37,7 +37,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_interface::Config; use rustc_interface::interface::Compiler; -use rustc_middle::query::queries::mir_borrowck::ProvidedValue; +use rustc_middle::queries::mir_borrowck::ProvidedValue; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; From 9de16dd3eb3ff76198125d30db0b787198855427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 3 Feb 2026 10:13:45 +0100 Subject: [PATCH 566/583] Remove imports in `queries` only used by `define_callbacks!` --- compiler/rustc_middle/src/queries.rs | 9 +++----- compiler/rustc_middle/src/query/mod.rs | 1 + compiler/rustc_middle/src/query/plumbing.rs | 24 ++++++++++----------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 3ade5cab0915..ca8e5e90318d 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -49,7 +49,7 @@ //! ## Query Expansion and Code Generation //! //! The [`rustc_macros::rustc_queries`] macro expands each query definition into: -//! - A method on [`TyCtxt`] (and [`TyCtxtAt`]) for invoking the query. +//! - A method on [`TyCtxt`] (and [`crate::query::TyCtxtAt`]) for invoking the query. //! - Provider traits and structs for supplying the query's value. //! - Caching and dependency graph integration. //! - Support for incremental compilation, disk caching, and arena allocation as controlled by the modifiers. @@ -87,7 +87,6 @@ use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, Trait use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; -use rustc_query_system::query::{QueryMode, QueryState}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{ @@ -118,10 +117,8 @@ use crate::mir::interpret::{ use crate::mir::mono::{ CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, }; -use crate::query::plumbing::{ - CyclePlaceholder, IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, -}; -use crate::query::{AsLocalKey, describe_as_module}; +use crate::query::describe_as_module; +use crate::query::plumbing::CyclePlaceholder; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f0121eab053b..24a38e70ff6f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1,4 +1,5 @@ use rustc_hir::def_id::LocalDefId; +pub use rustc_query_system::query::{QueryMode, QueryState}; pub use self::keys::{AsLocalKey, Key, LocalCrate}; pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 66e7c55a9d2b..0b7dc3092719 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -189,8 +189,8 @@ macro_rules! query_ensure_select { } macro_rules! query_helper_param_ty { - (DefId) => { impl IntoQueryParam }; - (LocalDefId) => { impl IntoQueryParam }; + (DefId) => { impl $crate::query::IntoQueryParam }; + (LocalDefId) => { impl $crate::query::IntoQueryParam }; ($K:ty) => { $K }; } @@ -213,7 +213,7 @@ macro_rules! local_key_if_separate_extern { $($K)* }; ([(separate_provide_extern) $($rest:tt)*] $($K:tt)*) => { - <$($K)* as AsLocalKey>::LocalKey + <$($K)* as $crate::query::AsLocalKey>::LocalKey }; ([$other:tt $($modifiers:tt)*] $($K:tt)*) => { local_key_if_separate_extern!([$($modifiers)*] $($K)*) @@ -370,7 +370,7 @@ macro_rules! define_callbacks { $($(#[$attr])* pub $name: $name::Storage<'tcx>,)* } - impl<'tcx> TyCtxtEnsureOk<'tcx> { + impl<'tcx> $crate::query::TyCtxtEnsureOk<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name( @@ -382,13 +382,13 @@ macro_rules! define_callbacks { self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), false, ) })* } - impl<'tcx> TyCtxtEnsureDone<'tcx> { + impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { @@ -396,7 +396,7 @@ macro_rules! define_callbacks { self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), true, ); })* @@ -412,7 +412,7 @@ macro_rules! define_callbacks { })* } - impl<'tcx> TyCtxtAt<'tcx> { + impl<'tcx> $crate::query::TyCtxtAt<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V @@ -424,7 +424,7 @@ macro_rules! define_callbacks { self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, self.span, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), )) })* } @@ -441,7 +441,7 @@ macro_rules! define_callbacks { #[derive(Default)] pub struct QueryStates<'tcx> { $( - pub $name: QueryState<'tcx, $($K)*>, + pub $name: $crate::query::QueryState<'tcx, $($K)*>, )* } @@ -487,7 +487,7 @@ macro_rules! define_callbacks { TyCtxt<'tcx>, Span, $name::Key<'tcx>, - QueryMode, + $crate::query::QueryMode, ) -> Option<$crate::query::erase::Erased<$V>>,)* } }; @@ -495,7 +495,7 @@ macro_rules! define_callbacks { macro_rules! define_feedable { ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { - $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { + $(impl<'tcx, K: $crate::query::IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { $(#[$attr])* #[inline(always)] pub fn $name(self, value: $name::ProvidedValue<'tcx>) { From d457ffd4f4589dfdd1f8fd2ef1c3d784018cf127 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 22:44:03 +0100 Subject: [PATCH 567/583] Convert to inline diagnostics in `rustc_monomorphize` --- Cargo.lock | 2 - compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_monomorphize/Cargo.toml | 1 - compiler/rustc_monomorphize/messages.ftl | 82 ---------------- compiler/rustc_monomorphize/src/errors.rs | 109 ++++++++++++++++------ compiler/rustc_monomorphize/src/lib.rs | 2 - 7 files changed, 82 insertions(+), 116 deletions(-) delete mode 100644 compiler/rustc_monomorphize/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 91fce715c8cd..b7dd59d857fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3805,7 +3805,6 @@ dependencies = [ "rustc_mir_build", "rustc_mir_dataflow", "rustc_mir_transform", - "rustc_monomorphize", "rustc_parse", "rustc_passes", "rustc_pattern_analysis", @@ -4384,7 +4383,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_macros", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index d97c552b412b..28972d1c35ec 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -37,7 +37,6 @@ rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" } rustc_mir_dataflow = { path = "../rustc_mir_dataflow" } rustc_mir_transform = { path = "../rustc_mir_transform" } -rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" } rustc_passes = { path = "../rustc_passes" } rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b163ae2068a6..80724c6a24f5 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -134,7 +134,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_mir_build::DEFAULT_LOCALE_RESOURCE, rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE, rustc_mir_transform::DEFAULT_LOCALE_RESOURCE, - rustc_monomorphize::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_passes::DEFAULT_LOCALE_RESOURCE, rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 0829d52283ab..552c092ef7c4 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl deleted file mode 100644 index 9c791208c093..000000000000 --- a/compiler/rustc_monomorphize/messages.ftl +++ /dev/null @@ -1,82 +0,0 @@ -monomorphize_abi_error_disabled_vector_type = - this function {$is_call -> - [true] call - *[false] definition - } uses {$is_scalable -> - [true] scalable - *[false] SIMD - } vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> - [true] {" "}in the caller - *[false] {""} - } - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable="{$required_feature}")]`) - -monomorphize_abi_error_unsupported_unsized_parameter = - this function {$is_call -> - [true] call - *[false] definition - } uses unsized type `{$ty}` which is not supported with the chosen ABI - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = only rustic ABIs support unsized parameters - -monomorphize_abi_error_unsupported_vector_type = - this function {$is_call -> - [true] call - *[false] definition - } uses SIMD vector type `{$ty}` which is not currently supported with the chosen ABI - .label = function {$is_call -> - [true] called - *[false] defined - } here - -monomorphize_abi_required_target_feature = - this function {$is_call -> - [true] call - *[false] definition - } uses ABI "{$abi}" which requires the `{$required_feature}` target feature, which is not enabled{$is_call -> - [true] {" "}in the caller - *[false] {""} - } - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable="{$required_feature}")]`) - -monomorphize_couldnt_dump_mono_stats = - unexpected error occurred while dumping monomorphization stats: {$error} - -monomorphize_encountered_error_while_instantiating = - the above error was encountered while instantiating `{$kind} {$instance}` - -monomorphize_encountered_error_while_instantiating_global_asm = - the above error was encountered while instantiating `global_asm` - -monomorphize_large_assignments = - moving {$size} bytes - .label = value moved from here - .note = the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` - -monomorphize_no_optimized_mir = - missing optimized MIR for `{$instance}` in the crate `{$crate_name}` - .note = missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?) - -monomorphize_recursion_limit = - reached the recursion limit while instantiating `{$instance}` - .note = `{$def_path_str}` defined here - -monomorphize_start_not_found = using `fn main` requires the standard library - .help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]` - -monomorphize_static_initializer_cyclic = static initializer forms a cycle involving `{$head}` - .label = part of this cycle - .note = cyclic static initializers are not supported for target `{$target}` - -monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 723649f22117..d045ae0b92cb 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -3,37 +3,41 @@ use rustc_middle::ty::{Instance, Ty}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] -#[diag(monomorphize_recursion_limit)] +#[diag("reached the recursion limit while instantiating `{$instance}`")] pub(crate) struct RecursionLimit<'tcx> { #[primary_span] pub span: Span, pub instance: Instance<'tcx>, - #[note] + #[note("`{$def_path_str}` defined here")] pub def_span: Span, pub def_path_str: String, } #[derive(Diagnostic)] -#[diag(monomorphize_no_optimized_mir)] +#[diag("missing optimized MIR for `{$instance}` in the crate `{$crate_name}`")] pub(crate) struct NoOptimizedMir { - #[note] + #[note( + "missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?)" + )] pub span: Span, pub crate_name: Symbol, pub instance: String, } #[derive(LintDiagnostic)] -#[diag(monomorphize_large_assignments)] -#[note] +#[diag("moving {$size} bytes")] +#[note( + "the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = \"...\"]`" +)] pub(crate) struct LargeAssignmentsLint { - #[label] + #[label("value moved from here")] pub span: Span, pub size: u64, pub limit: u64, } #[derive(Diagnostic)] -#[diag(monomorphize_symbol_already_defined)] +#[diag("symbol `{$symbol}` is already defined")] pub(crate) struct SymbolAlreadyDefined { #[primary_span] pub span: Option, @@ -41,13 +45,13 @@ pub(crate) struct SymbolAlreadyDefined { } #[derive(Diagnostic)] -#[diag(monomorphize_couldnt_dump_mono_stats)] +#[diag("unexpected error occurred while dumping monomorphization stats: {$error}")] pub(crate) struct CouldntDumpMonoStats { pub error: String, } #[derive(Diagnostic)] -#[diag(monomorphize_encountered_error_while_instantiating)] +#[diag("the above error was encountered while instantiating `{$kind} {$instance}`")] pub(crate) struct EncounteredErrorWhileInstantiating<'tcx> { #[primary_span] pub span: Span, @@ -56,23 +60,41 @@ pub(crate) struct EncounteredErrorWhileInstantiating<'tcx> { } #[derive(Diagnostic)] -#[diag(monomorphize_encountered_error_while_instantiating_global_asm)] +#[diag("the above error was encountered while instantiating `global_asm`")] pub(crate) struct EncounteredErrorWhileInstantiatingGlobalAsm { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(monomorphize_start_not_found)] -#[help] +#[diag("using `fn main` requires the standard library")] +#[help( + "use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`" +)] pub(crate) struct StartNotFound; #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_disabled_vector_type)] -#[help] +#[diag("this function {$is_call -> + [true] call + *[false] definition +} uses {$is_scalable -> + [true] scalable + *[false] SIMD +} vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> + [true] {\" \"}in the caller + *[false] {\"\"} +}")] +#[help( + "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" +)] pub(crate) struct AbiErrorDisabledVectorType<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub required_feature: &'a str, pub ty: Ty<'a>, @@ -83,11 +105,21 @@ pub(crate) struct AbiErrorDisabledVectorType<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_unsupported_unsized_parameter)] -#[help] +#[diag( + "this function {$is_call -> + [true] call + *[false] definition + } uses unsized type `{$ty}` which is not supported with the chosen ABI" +)] +#[help("only rustic ABIs support unsized parameters")] pub(crate) struct AbiErrorUnsupportedUnsizedParameter<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub ty: Ty<'a>, /// Whether this is a problem at a call site or at a declaration. @@ -95,10 +127,20 @@ pub(crate) struct AbiErrorUnsupportedUnsizedParameter<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_unsupported_vector_type)] +#[diag( + "this function {$is_call -> + [true] call + *[false] definition + } uses SIMD vector type `{$ty}` which is not currently supported with the chosen ABI" +)] pub(crate) struct AbiErrorUnsupportedVectorType<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub ty: Ty<'a>, /// Whether this is a problem at a call site or at a declaration. @@ -106,11 +148,24 @@ pub(crate) struct AbiErrorUnsupportedVectorType<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_required_target_feature)] -#[help] +#[diag("this function {$is_call -> + [true] call + *[false] definition +} uses ABI \"{$abi}\" which requires the `{$required_feature}` target feature, which is not enabled{$is_call -> + [true] {\" \"}in the caller + *[false] {\"\"} +}")] +#[help( + "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" +)] pub(crate) struct AbiRequiredTargetFeature<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> +[true] called +*[false] defined +} here" + )] pub span: Span, pub required_feature: &'a str, pub abi: &'a str, @@ -119,12 +174,12 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_static_initializer_cyclic)] -#[note] +#[diag("static initializer forms a cycle involving `{$head}`")] +#[note("cyclic static initializers are not supported for target `{$target}`")] pub(crate) struct StaticInitializerCyclic<'a> { #[primary_span] pub span: Span, - #[label] + #[label("part of this cycle")] pub labels: Vec, pub head: &'a str, pub target: &'a str, diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 30f6934bb937..51753563b67c 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -20,8 +20,6 @@ mod mono_checks; mod partitioning; mod util; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxtAt<'tcx>, source_ty: Ty<'tcx>, From 4cacfc00bcb875c04318b516139d0d8cf559ff7c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 15:08:00 +0100 Subject: [PATCH 568/583] Convert to inline diagnostics in `rustc_incremental` --- Cargo.lock | 2 - compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_incremental/Cargo.toml | 1 - compiler/rustc_incremental/messages.ftl | 102 ---------------------- compiler/rustc_incremental/src/errors.rs | 106 +++++++++++++---------- compiler/rustc_incremental/src/lib.rs | 2 - 7 files changed, 62 insertions(+), 153 deletions(-) delete mode 100644 compiler/rustc_incremental/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 2be69cf0b23b..2bcf7d52310e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3789,7 +3789,6 @@ dependencies = [ "rustc_hir_analysis", "rustc_hir_pretty", "rustc_hir_typeck", - "rustc_incremental", "rustc_index", "rustc_interface", "rustc_lexer", @@ -4048,7 +4047,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_fs_util", "rustc_graphviz", "rustc_hashes", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index bdc577622b24..f888c54cefcd 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -23,7 +23,6 @@ rustc_feature = { path = "../rustc_feature" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } -rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } rustc_interface = { path = "../rustc_interface" } rustc_lexer = { path = "../rustc_lexer" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index cdc9959605c1..cdf636f97d45 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -124,7 +124,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_expand::DEFAULT_LOCALE_RESOURCE, rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE, rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE, - rustc_incremental::DEFAULT_LOCALE_RESOURCE, rustc_interface::DEFAULT_LOCALE_RESOURCE, rustc_lint::DEFAULT_LOCALE_RESOURCE, rustc_metadata::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index db0a58418875..ae96cc62e54a 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -9,7 +9,6 @@ rand = "0.9.0" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hashes = { path = "../rustc_hashes" } diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl deleted file mode 100644 index bbc1fab05dfe..000000000000 --- a/compiler/rustc_incremental/messages.ftl +++ /dev/null @@ -1,102 +0,0 @@ -incremental_assert_loaded = - we asserted that an existing incremental cache directory should be successfully loaded, but it was not - -incremental_assert_not_loaded = - we asserted that the incremental cache should not be loaded, but it was loaded - -incremental_assertion_auto = - `except` specified DepNodes that can not be affected for "{$name}": "{$e}" - -incremental_associated_value_expected = expected an associated value - -incremental_associated_value_expected_for = associated value expected for `{$ident}` - -incremental_canonicalize_path = incremental compilation: error canonicalizing path `{$path}`: {$err} - -incremental_cargo_help_1 = - incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental) -incremental_cargo_help_2 = - the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir) - -incremental_copy_workproduct_to_cache = - error copying object file `{$from}` to incremental directory as `{$to}`: {$err} - -incremental_corrupt_file = corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant. - -incremental_create_dep_graph = failed to create dependency graph at `{$path}`: {$err} - -incremental_create_incr_comp_dir = - could not create incremental compilation {$tag} directory `{$path}`: {$err} - -incremental_create_lock = - incremental compilation: could not create session directory lock file: {$lock_err} -incremental_create_new = failed to create {$name} at `{$path}`: {$err} - -incremental_delete_full = error deleting incremental compilation session directory `{$path}`: {$err} - -incremental_delete_incompatible = - failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err} - -incremental_delete_lock = - error deleting lock file for incremental compilation session directory `{$path}`: {$err} - -incremental_delete_old = unable to delete old {$name} at `{$path}`: {$err} - -incremental_delete_partial = failed to delete partly initialized session dir `{$path}`: {$err} - -incremental_delete_workproduct = file-system error deleting outdated file `{$path}`: {$err} - -incremental_finalize = error finalizing incremental compilation session directory `{$path}`: {$err} - -incremental_finalized_gc_failed = - failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err} - -incremental_hard_link_failed = - hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}` - -incremental_invalid_gc_failed = - failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err} - -incremental_load_dep_graph = could not load dep-graph from `{$path}`: {$err} - -incremental_lock_unsupported = - the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation - -incremental_missing_depnode = missing `DepNode` variant - -incremental_missing_if_this_changed = no `#[rustc_if_this_changed]` annotation detected - -incremental_move_dep_graph = failed to move dependency graph from `{$from}` to `{$to}`: {$err} - -incremental_no_cfg = no cfg attribute - -incremental_no_path = no path from `{$source}` to `{$target}` - -incremental_not_clean = `{$dep_node_str}` should be clean but is not - -incremental_not_dirty = `{$dep_node_str}` should be dirty but is not - -incremental_not_loaded = `{$dep_node_str}` should have been loaded from disk but it was not - -incremental_ok = OK - -incremental_repeated_depnode_label = dep-node label `{$label}` is repeated - -incremental_session_gc_failed = - failed to garbage collect incremental compilation session directory `{$path}`: {$err} - -incremental_unchecked_clean = found unchecked `#[rustc_clean]` attribute - -incremental_undefined_clean_dirty_assertions = - clean/dirty auto-assertions not yet defined for {$kind} - -incremental_undefined_clean_dirty_assertions_item = - clean/dirty auto-assertions not yet defined for Node::Item.node={$kind} - -incremental_unknown_rustc_clean_argument = unknown `rustc_clean` argument - -incremental_unrecognized_depnode = unrecognized `DepNode` variant: {$name} - -incremental_unrecognized_depnode_label = dep-node label `{$label}` not recognized - -incremental_write_new = failed to write {$name} to `{$path}`: {$err} diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index dbc72d085be9..65109fdec03a 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -4,7 +4,7 @@ use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol}; #[derive(Diagnostic)] -#[diag(incremental_unrecognized_depnode)] +#[diag("unrecognized `DepNode` variant: {$name}")] pub(crate) struct UnrecognizedDepNode { #[primary_span] pub span: Span, @@ -12,28 +12,28 @@ pub(crate) struct UnrecognizedDepNode { } #[derive(Diagnostic)] -#[diag(incremental_missing_depnode)] +#[diag("missing `DepNode` variant")] pub(crate) struct MissingDepNode { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_missing_if_this_changed)] +#[diag("no `#[rustc_if_this_changed]` annotation detected")] pub(crate) struct MissingIfThisChanged { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_ok)] +#[diag("OK")] pub(crate) struct Ok { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_no_path)] +#[diag("no path from `{$source}` to `{$target}`")] pub(crate) struct NoPath { #[primary_span] pub span: Span, @@ -42,7 +42,7 @@ pub(crate) struct NoPath { } #[derive(Diagnostic)] -#[diag(incremental_assertion_auto)] +#[diag("`except` specified DepNodes that can not be affected for \"{$name}\": \"{$e}\"")] pub(crate) struct AssertionAuto<'a> { #[primary_span] pub span: Span, @@ -51,7 +51,7 @@ pub(crate) struct AssertionAuto<'a> { } #[derive(Diagnostic)] -#[diag(incremental_undefined_clean_dirty_assertions_item)] +#[diag("clean/dirty auto-assertions not yet defined for Node::Item.node={$kind}")] pub(crate) struct UndefinedCleanDirtyItem { #[primary_span] pub span: Span, @@ -59,7 +59,7 @@ pub(crate) struct UndefinedCleanDirtyItem { } #[derive(Diagnostic)] -#[diag(incremental_undefined_clean_dirty_assertions)] +#[diag("clean/dirty auto-assertions not yet defined for {$kind}")] pub(crate) struct UndefinedCleanDirty { #[primary_span] pub span: Span, @@ -67,7 +67,7 @@ pub(crate) struct UndefinedCleanDirty { } #[derive(Diagnostic)] -#[diag(incremental_repeated_depnode_label)] +#[diag("dep-node label `{$label}` is repeated")] pub(crate) struct RepeatedDepNodeLabel<'a> { #[primary_span] pub span: Span, @@ -75,7 +75,7 @@ pub(crate) struct RepeatedDepNodeLabel<'a> { } #[derive(Diagnostic)] -#[diag(incremental_unrecognized_depnode_label)] +#[diag("dep-node label `{$label}` not recognized")] pub(crate) struct UnrecognizedDepNodeLabel<'a> { #[primary_span] pub span: Span, @@ -83,7 +83,7 @@ pub(crate) struct UnrecognizedDepNodeLabel<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_dirty)] +#[diag("`{$dep_node_str}` should be dirty but is not")] pub(crate) struct NotDirty<'a> { #[primary_span] pub span: Span, @@ -91,7 +91,7 @@ pub(crate) struct NotDirty<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_clean)] +#[diag("`{$dep_node_str}` should be clean but is not")] pub(crate) struct NotClean<'a> { #[primary_span] pub span: Span, @@ -99,7 +99,7 @@ pub(crate) struct NotClean<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_loaded)] +#[diag("`{$dep_node_str}` should have been loaded from disk but it was not")] pub(crate) struct NotLoaded<'a> { #[primary_span] pub span: Span, @@ -107,21 +107,21 @@ pub(crate) struct NotLoaded<'a> { } #[derive(Diagnostic)] -#[diag(incremental_unknown_rustc_clean_argument)] +#[diag("unknown `rustc_clean` argument")] pub(crate) struct UnknownRustcCleanArgument { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_no_cfg)] +#[diag("no cfg attribute")] pub(crate) struct NoCfg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_associated_value_expected_for)] +#[diag("associated value expected for `{$ident}`")] pub(crate) struct AssociatedValueExpectedFor { #[primary_span] pub span: Span, @@ -129,21 +129,21 @@ pub(crate) struct AssociatedValueExpectedFor { } #[derive(Diagnostic)] -#[diag(incremental_associated_value_expected)] +#[diag("expected an associated value")] pub(crate) struct AssociatedValueExpected { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_unchecked_clean)] +#[diag("found unchecked `#[rustc_clean]` attribute")] pub(crate) struct UncheckedClean { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_delete_old)] +#[diag("unable to delete old {$name} at `{$path}`: {$err}")] pub(crate) struct DeleteOld<'a> { pub name: &'a str, pub path: PathBuf, @@ -151,7 +151,7 @@ pub(crate) struct DeleteOld<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_new)] +#[diag("failed to create {$name} at `{$path}`: {$err}")] pub(crate) struct CreateNew<'a> { pub name: &'a str, pub path: PathBuf, @@ -159,7 +159,7 @@ pub(crate) struct CreateNew<'a> { } #[derive(Diagnostic)] -#[diag(incremental_write_new)] +#[diag("failed to write {$name} to `{$path}`: {$err}")] pub(crate) struct WriteNew<'a> { pub name: &'a str, pub path: PathBuf, @@ -167,14 +167,14 @@ pub(crate) struct WriteNew<'a> { } #[derive(Diagnostic)] -#[diag(incremental_canonicalize_path)] +#[diag("incremental compilation: error canonicalizing path `{$path}`: {$err}")] pub(crate) struct CanonicalizePath { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_create_incr_comp_dir)] +#[diag("could not create incremental compilation {$tag} directory `{$path}`: {$err}")] pub(crate) struct CreateIncrCompDir<'a> { pub tag: &'a str, pub path: &'a Path, @@ -182,96 +182,112 @@ pub(crate) struct CreateIncrCompDir<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_lock)] +#[diag("incremental compilation: could not create session directory lock file: {$lock_err}")] pub(crate) struct CreateLock<'a> { pub lock_err: std::io::Error, pub session_dir: &'a Path, - #[note(incremental_lock_unsupported)] + #[note( + "the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation" + )] pub is_unsupported_lock: bool, - #[help(incremental_cargo_help_1)] - #[help(incremental_cargo_help_2)] + #[help( + "incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental)" + )] + #[help( + "the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir)" + )] pub is_cargo: bool, } #[derive(Diagnostic)] -#[diag(incremental_delete_lock)] +#[diag("error deleting lock file for incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct DeleteLock<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_hard_link_failed)] +#[diag( + "hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}`" +)] pub(crate) struct HardLinkFailed<'a> { pub path: &'a Path, } #[derive(Diagnostic)] -#[diag(incremental_delete_partial)] +#[diag("failed to delete partly initialized session dir `{$path}`: {$err}")] pub(crate) struct DeletePartial<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_delete_full)] +#[diag("error deleting incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct DeleteFull<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_finalize)] +#[diag("error finalizing incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct Finalize<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_invalid_gc_failed)] +#[diag( + "failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err}" +)] pub(crate) struct InvalidGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_finalized_gc_failed)] +#[diag( + "failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err}" +)] pub(crate) struct FinalizedGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_session_gc_failed)] +#[diag("failed to garbage collect incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct SessionGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_assert_not_loaded)] +#[diag("we asserted that the incremental cache should not be loaded, but it was loaded")] pub(crate) struct AssertNotLoaded; #[derive(Diagnostic)] -#[diag(incremental_assert_loaded)] +#[diag( + "we asserted that an existing incremental cache directory should be successfully loaded, but it was not" +)] pub(crate) struct AssertLoaded; #[derive(Diagnostic)] -#[diag(incremental_delete_incompatible)] +#[diag( + "failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err}" +)] pub(crate) struct DeleteIncompatible { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_load_dep_graph)] +#[diag("could not load dep-graph from `{$path}`: {$err}")] pub(crate) struct LoadDepGraph { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_move_dep_graph)] +#[diag("failed to move dependency graph from `{$from}` to `{$to}`: {$err}")] pub(crate) struct MoveDepGraph<'a> { pub from: &'a Path, pub to: &'a Path, @@ -279,14 +295,14 @@ pub(crate) struct MoveDepGraph<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_dep_graph)] +#[diag("failed to create dependency graph at `{$path}`: {$err}")] pub(crate) struct CreateDepGraph<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_copy_workproduct_to_cache)] +#[diag("error copying object file `{$from}` to incremental directory as `{$to}`: {$err}")] pub(crate) struct CopyWorkProductToCache<'a> { pub from: &'a Path, pub to: &'a Path, @@ -294,14 +310,16 @@ pub(crate) struct CopyWorkProductToCache<'a> { } #[derive(Diagnostic)] -#[diag(incremental_delete_workproduct)] +#[diag("file-system error deleting outdated file `{$path}`: {$err}")] pub(crate) struct DeleteWorkProduct<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_corrupt_file)] +#[diag( + "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant." +)] pub(crate) struct CorruptFile<'a> { pub path: &'a Path, } diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index e750810c0119..591ade379e6a 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -21,5 +21,3 @@ pub fn provide(providers: &mut Providers) { providers.hooks.save_dep_graph = |tcx| tcx.sess.time("serialize_dep_graph", || persist::save_dep_graph(tcx)); } - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } From 4fdd085e2879417094636f252c476f5d1f7737be Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 23:01:11 +0100 Subject: [PATCH 569/583] Convert to inline diagnostics in `rustc_mir_dataflow` --- Cargo.lock | 2 -- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_mir_dataflow/Cargo.toml | 1 - compiler/rustc_mir_dataflow/messages.ftl | 17 ----------------- compiler/rustc_mir_dataflow/src/errors.rs | 12 ++++++------ compiler/rustc_mir_dataflow/src/lib.rs | 2 -- 7 files changed, 6 insertions(+), 30 deletions(-) delete mode 100644 compiler/rustc_mir_dataflow/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 2be69cf0b23b..659118e47ff5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3799,7 +3799,6 @@ dependencies = [ "rustc_metadata", "rustc_middle", "rustc_mir_build", - "rustc_mir_dataflow", "rustc_mir_transform", "rustc_monomorphize", "rustc_parse", @@ -4333,7 +4332,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_graphviz", "rustc_hir", "rustc_index", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index bdc577622b24..7f01b0e5bd96 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -33,7 +33,6 @@ rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" } -rustc_mir_dataflow = { path = "../rustc_mir_dataflow" } rustc_mir_transform = { path = "../rustc_mir_transform" } rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index cdc9959605c1..55f78f0e01ab 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -130,7 +130,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_metadata::DEFAULT_LOCALE_RESOURCE, rustc_middle::DEFAULT_LOCALE_RESOURCE, rustc_mir_build::DEFAULT_LOCALE_RESOURCE, - rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE, rustc_mir_transform::DEFAULT_LOCALE_RESOURCE, rustc_monomorphize::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index e41d5da71397..156caa7030a0 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -10,7 +10,6 @@ regex = "1" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_mir_dataflow/messages.ftl b/compiler/rustc_mir_dataflow/messages.ftl deleted file mode 100644 index 3783c647b03e..000000000000 --- a/compiler/rustc_mir_dataflow/messages.ftl +++ /dev/null @@ -1,17 +0,0 @@ -mir_dataflow_peek_argument_not_a_local = - rustc_peek: argument was not a local - -mir_dataflow_peek_argument_untracked = - rustc_peek: argument untracked - -mir_dataflow_peek_bit_not_set = - rustc_peek: bit not set - -mir_dataflow_peek_must_be_not_temporary = - dataflow::sanity_check cannot feed a non-temp to rustc_peek - -mir_dataflow_peek_must_be_place_or_ref_place = - rustc_peek: argument expression must be either `place` or `&place` - -mir_dataflow_stop_after_dataflow_ended_compilation = - stop_after_dataflow ended compilation diff --git a/compiler/rustc_mir_dataflow/src/errors.rs b/compiler/rustc_mir_dataflow/src/errors.rs index 9d8c34c8a1f3..051c0305cf2b 100644 --- a/compiler/rustc_mir_dataflow/src/errors.rs +++ b/compiler/rustc_mir_dataflow/src/errors.rs @@ -2,39 +2,39 @@ use rustc_macros::Diagnostic; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(mir_dataflow_stop_after_dataflow_ended_compilation)] +#[diag("stop_after_dataflow ended compilation")] pub(crate) struct StopAfterDataFlowEndedCompilation; #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_must_be_place_or_ref_place)] +#[diag("rustc_peek: argument expression must be either `place` or `&place`")] pub(crate) struct PeekMustBePlaceOrRefPlace { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_must_be_not_temporary)] +#[diag("dataflow::sanity_check cannot feed a non-temp to rustc_peek")] pub(crate) struct PeekMustBeNotTemporary { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_bit_not_set)] +#[diag("rustc_peek: bit not set")] pub(crate) struct PeekBitNotSet { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_argument_not_a_local)] +#[diag("rustc_peek: argument was not a local")] pub(crate) struct PeekArgumentNotALocal { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_argument_untracked)] +#[diag("rustc_peek: argument untracked")] pub(crate) struct PeekArgumentUntracked { #[primary_span] pub span: Span, diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 485925e7b50c..5f1cf1250152 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -34,8 +34,6 @@ pub mod rustc_peek; mod un_derefer; pub mod value_analysis; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub struct MoveDataTypingEnv<'tcx> { pub move_data: MoveData<'tcx>, pub typing_env: ty::TypingEnv<'tcx>, From 828bead9e33014793e32ba8d32eab270cc03a58d Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Tue, 3 Feb 2026 14:16:06 +0000 Subject: [PATCH 570/583] Use glob imports for attribute parsers --- compiler/rustc_attr_parsing/src/context.rs | 118 ++++++--------------- 1 file changed, 35 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 322e189e6d12..fa9f5b585926 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -16,90 +16,42 @@ use rustc_session::lint::{Lint, LintId}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use crate::AttributeParser; -use crate::attributes::allow_unstable::{ - AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, -}; -use crate::attributes::body::CoroutineParser; -use crate::attributes::cfi_encoding::CfiEncodingParser; -use crate::attributes::codegen_attrs::{ - ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser, - NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, - PatchableFunctionEntryParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, - TargetFeatureParser, ThreadLocalParser, TrackCallerParser, UsedParser, -}; -use crate::attributes::confusables::ConfusablesParser; -use crate::attributes::crate_level::{ - CrateNameParser, CrateTypeParser, MoveSizeLimitParser, NeedsPanicRuntimeParser, - NoBuiltinsParser, NoCoreParser, NoMainParser, NoStdParser, PanicRuntimeParser, - PatternComplexityLimitParser, ProfilerRuntimeParser, RecursionLimitParser, - RustcCoherenceIsCoreParser, RustcPreserveUbChecksParser, TypeLengthLimitParser, - WindowsSubsystemParser, -}; -use crate::attributes::debugger::DebuggerViualizerParser; -use crate::attributes::deprecation::DeprecationParser; -use crate::attributes::do_not_recommend::DoNotRecommendParser; -use crate::attributes::doc::DocParser; -use crate::attributes::dummy::DummyParser; -use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::instruction_set::InstructionSetParser; -use crate::attributes::link_attrs::{ - CompilerBuiltinsParser, ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, - LinkOrdinalParser, LinkParser, LinkSectionParser, LinkageParser, NeedsAllocatorParser, - StdInternalSymbolParser, -}; -use crate::attributes::lint_helpers::{ - AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, - RustcShouldNotBeCalledOnConstItems, -}; -use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; -use crate::attributes::macro_attrs::{ - AllowInternalUnsafeParser, CollapseDebugInfoParser, MacroEscapeParser, MacroExportParser, - MacroUseParser, -}; -use crate::attributes::must_not_suspend::MustNotSuspendParser; -use crate::attributes::must_use::MustUseParser; -use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; -use crate::attributes::no_link::NoLinkParser; -use crate::attributes::non_exhaustive::NonExhaustiveParser; +// Glob imports to avoid big, bitrotty import lists +use crate::attributes::allow_unstable::*; +use crate::attributes::body::*; +use crate::attributes::cfi_encoding::*; +use crate::attributes::codegen_attrs::*; +use crate::attributes::confusables::*; +use crate::attributes::crate_level::*; +use crate::attributes::debugger::*; +use crate::attributes::deprecation::*; +use crate::attributes::do_not_recommend::*; +use crate::attributes::doc::*; +use crate::attributes::dummy::*; +use crate::attributes::inline::*; +use crate::attributes::instruction_set::*; +use crate::attributes::link_attrs::*; +use crate::attributes::lint_helpers::*; +use crate::attributes::loop_match::*; +use crate::attributes::macro_attrs::*; +use crate::attributes::must_not_suspend::*; +use crate::attributes::must_use::*; +use crate::attributes::no_implicit_prelude::*; +use crate::attributes::no_link::*; +use crate::attributes::non_exhaustive::*; use crate::attributes::path::PathParser as PathAttributeParser; -use crate::attributes::pin_v2::PinV2Parser; -use crate::attributes::proc_macro_attrs::{ - ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, -}; -use crate::attributes::prototype::CustomMirParser; -use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; -use crate::attributes::rustc_allocator::{ - RustcAllocatorParser, RustcAllocatorZeroedParser, RustcAllocatorZeroedVariantParser, - RustcDeallocatorParser, RustcReallocatorParser, -}; -use crate::attributes::rustc_dump::{ - RustcDumpDefParentsParser, RustcDumpItemBoundsParser, RustcDumpPredicatesParser, - RustcDumpUserArgsParser, RustcDumpVtableParser, -}; -use crate::attributes::rustc_internal::{ - RustcHasIncoherentInherentImplsParser, RustcHiddenTypeOfOpaquesParser, RustcLayoutParser, - RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, - RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, - RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, - RustcMirParser, RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, - RustcNoImplicitAutorefsParser, RustcNonConstTraitMethodParser, RustcNounwindParser, - RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, - RustcSimdMonomorphizeLaneLimitParser, -}; -use crate::attributes::semantics::MayDangleParser; -use crate::attributes::stability::{ - BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, -}; -use crate::attributes::test_attrs::{ - IgnoreParser, RustcVarianceOfOpaquesParser, RustcVarianceParser, ShouldPanicParser, -}; -use crate::attributes::traits::{ - AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser, - DynIncompatibleTraitParser, FundamentalParser, MarkerParser, ParenSugarParser, PointeeParser, - SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, - UnsafeSpecializationMarkerParser, -}; -use crate::attributes::transparency::TransparencyParser; +use crate::attributes::pin_v2::*; +use crate::attributes::proc_macro_attrs::*; +use crate::attributes::prototype::*; +use crate::attributes::repr::*; +use crate::attributes::rustc_allocator::*; +use crate::attributes::rustc_dump::*; +use crate::attributes::rustc_internal::*; +use crate::attributes::semantics::*; +use crate::attributes::stability::*; +use crate::attributes::test_attrs::*; +use crate::attributes::traits::*; +use crate::attributes::transparency::*; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::parser::{ArgParser, RefPathParser}; use crate::session_diagnostics::{ From 07e4a994d61dccda69135a7b50ce69f6d93deec8 Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Thu, 29 Jan 2026 14:07:41 +0530 Subject: [PATCH 571/583] citool: report debuginfo test statistics Extend CI postprocessing to aggregate pass/fail/ignored counts for debuginfo tests. Tests are identified via their reported suite names (e.g. debuginfo-gdb, debuginfo-lldb or debuginfo-cdb). Signed-off-by: Usman Akinyemi --- src/build_helper/src/metrics.rs | 23 +++++++++++++++++++++++ src/ci/citool/src/analysis.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs index 07157e364158..98dd952033e5 100644 --- a/src/build_helper/src/metrics.rs +++ b/src/build_helper/src/metrics.rs @@ -111,6 +111,29 @@ pub struct JsonStepSystemStats { pub cpu_utilization_percent: f64, } +#[derive(Eq, Hash, PartialEq, Debug)] +pub enum DebuggerKind { + Gdb, + Lldb, + Cdb, +} + +impl DebuggerKind { + pub fn debuginfo_kind(name: &str) -> Option { + let name = name.to_ascii_lowercase(); + + if name.contains("debuginfo-gdb") { + Some(DebuggerKind::Gdb) + } else if name.contains("debuginfo-lldb") { + Some(DebuggerKind::Lldb) + } else if name.contains("debuginfo-cdb") { + Some(DebuggerKind::Cdb) + } else { + None + } + } +} + fn null_as_f64_nan<'de, D: serde::Deserializer<'de>>(d: D) -> Result { use serde::Deserialize as _; Option::::deserialize(d).map(|f| f.unwrap_or(f64::NAN)) diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs index 39b115154f9f..ec65d9dcb528 100644 --- a/src/ci/citool/src/analysis.rs +++ b/src/ci/citool/src/analysis.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::time::Duration; use build_helper::metrics::{ - BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name, + BuildStep, DebuggerKind, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name, format_build_steps, }; @@ -139,11 +139,39 @@ fn record_test_suites(metrics: &JsonRoot) { let table = render_table(aggregated); println!("\n# Test results\n"); println!("{table}"); + report_debuginfo_statistics(&suites); } else { eprintln!("No test suites found in metrics"); } } +fn report_debuginfo_statistics(suites: &[&TestSuite]) { + let mut debugger_test_record: HashMap = HashMap::new(); + for suite in suites { + if let TestSuiteMetadata::Compiletest { .. } = suite.metadata { + for test in &suite.tests { + if let Some(kind) = DebuggerKind::debuginfo_kind(&test.name) { + let record = + debugger_test_record.entry(kind).or_insert(TestSuiteRecord::default()); + match test.outcome { + TestOutcome::Passed => record.passed += 1, + TestOutcome::Ignored { .. } => record.ignored += 1, + TestOutcome::Failed => record.failed += 1, + } + } + } + } + } + + println!("## DebugInfo Test Statistics"); + for (kind, record) in debugger_test_record { + println!( + "- {:?}: Passed ✅={}, Failed ❌={}, Ignored 🚫={}", + kind, record.passed, record.failed, record.ignored + ); + } +} + fn render_table(suites: BTreeMap) -> String { use std::fmt::Write; From dcdffe8d808b84f2208c0dcb817bc46b0242e34a Mon Sep 17 00:00:00 2001 From: cezarbbb Date: Mon, 12 Jan 2026 14:48:05 +0800 Subject: [PATCH 572/583] link modifier `export-symbols`: export all global symbols from selected uptream c static libraries --- .../src/attributes/link_attrs.rs | 21 +++- .../src/session_diagnostics.rs | 9 +- compiler/rustc_codegen_ssa/src/back/link.rs | 95 +++++++++++++++++-- .../rustc_hir/src/attrs/data_structures.rs | 6 +- compiler/rustc_interface/src/tests.rs | 18 ++-- compiler/rustc_metadata/src/native_libs.rs | 2 +- .../rustc_session/src/config/native_libs.rs | 15 ++- compiler/rustc_span/src/symbol.rs | 1 + .../cdylib-export-c-library-symbols/foo.c | 1 + .../cdylib-export-c-library-symbols/foo.rs | 10 ++ .../foo_export.rs | 10 ++ .../cdylib-export-c-library-symbols/rmake.rs | 36 +++++++ ...ure-gate-link-arg-attribute.in_flag.stderr | 2 +- tests/ui/link-native-libs/link-arg-error2.rs | 5 + .../link-native-libs/link-arg-error2.stderr | 2 + .../ui/link-native-libs/link-arg-from-rs2.rs | 7 ++ .../link-native-libs/link-arg-from-rs2.stderr | 8 ++ .../link-attr-validation-late.stderr | 6 +- .../modifiers-bad.blank.stderr | 2 +- .../modifiers-bad.no-prefix.stderr | 2 +- .../modifiers-bad.prefix-only.stderr | 2 +- .../modifiers-bad.unknown.stderr | 2 +- 22 files changed, 226 insertions(+), 36 deletions(-) create mode 100644 tests/run-make/cdylib-export-c-library-symbols/foo.c create mode 100644 tests/run-make/cdylib-export-c-library-symbols/foo.rs create mode 100644 tests/run-make/cdylib-export-c-library-symbols/foo_export.rs create mode 100644 tests/run-make/cdylib-export-c-library-symbols/rmake.rs create mode 100644 tests/ui/link-native-libs/link-arg-error2.rs create mode 100644 tests/ui/link-native-libs/link-arg-error2.stderr create mode 100644 tests/ui/link-native-libs/link-arg-from-rs2.rs create mode 100644 tests/ui/link-native-libs/link-arg-from-rs2.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index c9da2f3b14bf..6c52be9bd229 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -12,10 +12,10 @@ use super::prelude::*; use super::util::parse_single_integer; use crate::attributes::cfg::parse_cfg_entry; use crate::session_diagnostics::{ - AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86, - IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange, - LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, - WholeArchiveNeedsStatic, + AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, + ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, + LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, + NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -165,6 +165,14 @@ impl CombineAttributeParser for LinkParser { cx.emit_err(BundleNeedsStatic { span }); } + (sym::export_symbols, Some(NativeLibKind::Static { export_symbols, .. })) => { + assign_modifier(export_symbols) + } + + (sym::export_symbols, _) => { + cx.emit_err(ExportSymbolsNeedsStatic { span }); + } + (sym::verbatim, _) => assign_modifier(&mut verbatim), ( @@ -190,6 +198,7 @@ impl CombineAttributeParser for LinkParser { span, &[ sym::bundle, + sym::export_symbols, sym::verbatim, sym::whole_dash_archive, sym::as_dash_needed, @@ -285,7 +294,9 @@ impl LinkParser { }; let link_kind = match link_kind { - kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None }, + kw::Static => { + NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None } + } sym::dylib => NativeLibKind::Dylib { as_needed: None }, sym::framework => { if !sess.target.is_like_darwin { diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index fb4d9c660a19..b0a334210f74 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -909,7 +909,7 @@ pub(crate) struct RawDylibOnlyWindows { #[derive(Diagnostic)] #[diag( - "invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed" + "invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols" )] pub(crate) struct InvalidLinkModifier { #[primary_span] @@ -938,6 +938,13 @@ pub(crate) struct BundleNeedsStatic { pub span: Span, } +#[derive(Diagnostic)] +#[diag("linking modifier `export-symbols` is only compatible with `static` linking kind")] +pub(crate) struct ExportSymbolsNeedsStatic { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("linking modifier `whole-archive` is only compatible with `static` linking kind")] pub(crate) struct WholeArchiveNeedsStatic { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c8109db86e2f..5a732382c981 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -11,10 +11,11 @@ use std::{env, fmt, fs, io, mem, str}; use find_msvc_tools; use itertools::Itertools; +use object::{Object, ObjectSection, ObjectSymbol}; use regex::Regex; use rustc_arena::TypedArena; use rustc_attr_parsing::eval_config_entry; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; @@ -2185,6 +2186,71 @@ fn add_rpath_args( } } +fn add_c_staticlib_symbols( + sess: &Session, + lib: &NativeLib, + out: &mut Vec<(String, SymbolExportKind)>, +) -> io::Result<()> { + let file_path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); + + let archive_map = unsafe { Mmap::map(File::open(&file_path)?)? }; + + let archive = object::read::archive::ArchiveFile::parse(&*archive_map) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + for member in archive.members() { + let member = member.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let data = member + .data(&*archive_map) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + // clang LTO: raw LLVM bitcode + if data.starts_with(b"BC\xc0\xde") { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "LLVM bitcode object in C static library (LTO not supported)", + )); + } + + let object = object::File::parse(&*data) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + // gcc / clang ELF / Mach-O LTO + if object.sections().any(|s| { + s.name().map(|n| n.starts_with(".gnu.lto_") || n == ".llvm.lto").unwrap_or(false) + }) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "LTO object in C static library is not supported", + )); + } + + for symbol in object.symbols() { + if symbol.scope() != object::SymbolScope::Dynamic { + continue; + } + + let name = match symbol.name() { + Ok(n) => n, + Err(_) => continue, + }; + + let export_kind = match symbol.kind() { + object::SymbolKind::Text => SymbolExportKind::Text, + object::SymbolKind::Data => SymbolExportKind::Data, + _ => continue, + }; + + // FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. + // Need to be resolved. + out.push((name.to_string(), export_kind)); + } + } + + Ok(()) +} + /// Produce the linker command line containing linker path and arguments. /// /// When comments in the function say "order-(in)dependent" they mean order-dependence between @@ -2217,6 +2283,25 @@ fn linker_with_args( ); let link_output_kind = link_output_kind(sess, crate_type); + let mut export_symbols = codegen_results.crate_info.exported_symbols[&crate_type].clone(); + + if crate_type == CrateType::Cdylib { + let mut seen = FxHashSet::default(); + + for lib in &codegen_results.crate_info.used_libraries { + if let NativeLibKind::Static { export_symbols: Some(true), .. } = lib.kind + && seen.insert((lib.name, lib.verbatim)) + { + if let Err(err) = add_c_staticlib_symbols(&sess, lib, &mut export_symbols) { + sess.dcx().fatal(format!( + "failed to process C static library `{}`: {}", + lib.name, err + )); + } + } + } + } + // ------------ Early order-dependent options ------------ // If we're building something like a dynamic library then some platforms @@ -2224,11 +2309,7 @@ fn linker_with_args( // dynamic library. // Must be passed before any libraries to prevent the symbols to export from being thrown away, // at least on some platforms (e.g. windows-gnu). - cmd.export_symbols( - tmpdir, - crate_type, - &codegen_results.crate_info.exported_symbols[&crate_type], - ); + cmd.export_symbols(tmpdir, crate_type, &export_symbols); // Can be used for adding custom CRT objects or overriding order-dependent options above. // FIXME: In practice built-in target specs use this for arbitrary order-independent options, @@ -2678,7 +2759,7 @@ fn add_native_libs_from_crate( let name = lib.name.as_str(); let verbatim = lib.verbatim; match lib.kind { - NativeLibKind::Static { bundle, whole_archive } => { + NativeLibKind::Static { bundle, whole_archive, .. } => { if link_static { let bundle = bundle.unwrap_or(true); let whole_archive = whole_archive == Some(true); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 6138ffc8d954..a53eff4637ff 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -331,6 +331,8 @@ pub enum NativeLibKind { bundle: Option, /// Whether to link static library without throwing any object files away whole_archive: Option, + /// Whether to export c static library symbols + export_symbols: Option, }, /// Dynamic library (e.g. `libfoo.so` on Linux) /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC). @@ -363,8 +365,8 @@ pub enum NativeLibKind { impl NativeLibKind { pub fn has_modifiers(&self) -> bool { match self { - NativeLibKind::Static { bundle, whole_archive } => { - bundle.is_some() || whole_archive.is_some() + NativeLibKind::Static { bundle, whole_archive, export_symbols } => { + bundle.is_some() || whole_archive.is_some() || export_symbols.is_some() } NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0f60e86e0ca3..b0272d726bc3 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -379,7 +379,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -401,7 +401,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -423,13 +423,13 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { name: String::from("b"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -445,7 +445,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -467,7 +467,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -501,7 +501,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -528,7 +528,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -549,7 +549,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index b160b3fe9bb3..0c06d1be9a3f 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -161,7 +161,7 @@ fn find_bundled_library( tcx: TyCtxt<'_>, ) -> Option { let sess = tcx.sess; - if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind + if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = kind && tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::StaticLib)) && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true)) { diff --git a/compiler/rustc_session/src/config/native_libs.rs b/compiler/rustc_session/src/config/native_libs.rs index 71d3e222c8a1..28e2d0f94104 100644 --- a/compiler/rustc_session/src/config/native_libs.rs +++ b/compiler/rustc_session/src/config/native_libs.rs @@ -53,7 +53,9 @@ fn parse_native_lib(cx: &ParseNativeLibCx<'_>, value: &str) -> NativeLib { let NativeLibParts { kind, modifiers, name, new_name } = split_native_lib_value(value); let kind = kind.map_or(NativeLibKind::Unspecified, |kind| match kind { - "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, + "static" => { + NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None } + } "dylib" => NativeLibKind::Dylib { as_needed: None }, "framework" => NativeLibKind::Framework { as_needed: None }, "link-arg" => { @@ -105,7 +107,7 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li Some(("-", m)) => (m, false), _ => cx.early_dcx.early_fatal( "invalid linking modifier syntax, expected '+' or '-' prefix \ - before one of: bundle, verbatim, whole-archive, as-needed", + before one of: bundle, verbatim, whole-archive, as-needed, export-symbols", ), }; @@ -125,6 +127,13 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li ("bundle", _) => early_dcx .early_fatal("linking modifier `bundle` is only compatible with `static` linking kind"), + ("export-symbols", NativeLibKind::Static { export_symbols, .. }) => { + assign_modifier(export_symbols) + } + ("export-symbols", _) => early_dcx.early_fatal( + "linking modifier `export-symbols` is only compatible with `static` linking kind", + ), + ("verbatim", _) => assign_modifier(&mut native_lib.verbatim), ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { @@ -151,7 +160,7 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li _ => early_dcx.early_fatal(format!( "unknown linking modifier `{modifier}`, expected one \ - of: bundle, verbatim, whole-archive, as-needed" + of: bundle, verbatim, whole-archive, as-needed, export-symbols" )), } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b0ef95d10ffa..c88dd0948b2a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1002,6 +1002,7 @@ symbols! { explicit_tail_calls, export_name, export_stable, + export_symbols: "export-symbols", expr, expr_2021, expr_fragment_specifier_2024, diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo.c b/tests/run-make/cdylib-export-c-library-symbols/foo.c new file mode 100644 index 000000000000..a062aca03b31 --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo.c @@ -0,0 +1 @@ +void my_function() {} diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo.rs b/tests/run-make/cdylib-export-c-library-symbols/foo.rs new file mode 100644 index 000000000000..ac641aaed00f --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo.rs @@ -0,0 +1,10 @@ +extern "C" { + pub fn my_function(); +} + +#[no_mangle] +pub extern "C" fn rust_entry() { + unsafe { + my_function(); + } +} diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs b/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs new file mode 100644 index 000000000000..1eda294ef41c --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs @@ -0,0 +1,10 @@ +extern "C" { + fn my_function(); +} + +#[no_mangle] +pub extern "C" fn rust_entry() { + unsafe { + my_function(); + } +} diff --git a/tests/run-make/cdylib-export-c-library-symbols/rmake.rs b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs new file mode 100644 index 000000000000..cb237eceedad --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs @@ -0,0 +1,36 @@ +//@ ignore-nvptx64 +//@ ignore-wasm +//@ ignore-cross-compile +// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. +// Need to be resolved. +//@ ignore-windows +//@ ignore-apple +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib, cc, dynamic_lib_name, is_darwin, llvm_nm, rustc}; + +fn main() { + cc().input("foo.c").arg("-c").out_exe("foo.o").run(); + build_native_static_lib("foo"); + + rustc().input("foo.rs").arg("-lstatic=foo").crate_type("cdylib").run(); + + let out = llvm_nm() + .input(dynamic_lib_name("foo")) + .run() + .assert_stdout_not_contains_regex("T *my_function"); + + rustc().input("foo_export.rs").arg("-lstatic:+export-symbols=foo").crate_type("cdylib").run(); + + if is_darwin() { + let out = llvm_nm() + .input(dynamic_lib_name("foo_export")) + .run() + .assert_stdout_contains("T _my_function"); + } else { + let out = llvm_nm() + .input(dynamic_lib_name("foo_export")) + .run() + .assert_stdout_contains("T my_function"); + } +} diff --git a/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr b/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr index 4d65db3c66d0..218a4769d954 100644 --- a/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr +++ b/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier `link-arg`, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier `link-arg`, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/link-arg-error2.rs b/tests/ui/link-native-libs/link-arg-error2.rs new file mode 100644 index 000000000000..a51dec0614b5 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-error2.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -l link-arg:+export-symbols=arg -Z unstable-options + +fn main() {} + +//~? ERROR linking modifier `export-symbols` is only compatible with `static` linking kind diff --git a/tests/ui/link-native-libs/link-arg-error2.stderr b/tests/ui/link-native-libs/link-arg-error2.stderr new file mode 100644 index 000000000000..61bcf7dba282 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-error2.stderr @@ -0,0 +1,2 @@ +error: linking modifier `export-symbols` is only compatible with `static` linking kind + diff --git a/tests/ui/link-native-libs/link-arg-from-rs2.rs b/tests/ui/link-native-libs/link-arg-from-rs2.rs new file mode 100644 index 000000000000..3074fec6c1c8 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-from-rs2.rs @@ -0,0 +1,7 @@ +#![feature(link_arg_attribute)] + +#[link(kind = "link-arg", name = "arg", modifiers = "+export-symbols")] +//~^ ERROR linking modifier `export-symbols` is only compatible with `static` linking kind +extern "C" {} + +pub fn main() {} diff --git a/tests/ui/link-native-libs/link-arg-from-rs2.stderr b/tests/ui/link-native-libs/link-arg-from-rs2.stderr new file mode 100644 index 000000000000..af3b25253e05 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-from-rs2.stderr @@ -0,0 +1,8 @@ +error: linking modifier `export-symbols` is only compatible with `static` linking kind + --> $DIR/link-arg-from-rs2.rs:3:53 + | +LL | #[link(kind = "link-arg", name = "arg", modifiers = "+export-symbols")] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/link-native-libs/link-attr-validation-late.stderr b/tests/ui/link-native-libs/link-attr-validation-late.stderr index b09431f923aa..4a4a19375207 100644 --- a/tests/ui/link-native-libs/link-attr-validation-late.stderr +++ b/tests/ui/link-native-libs/link-attr-validation-late.stderr @@ -178,13 +178,13 @@ LL | #[link(name = "...", wasm_import_module())] | = note: for more information, visit -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols --> $DIR/link-attr-validation-late.rs:31:34 | LL | #[link(name = "...", modifiers = "")] | ^^ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols --> $DIR/link-attr-validation-late.rs:32:34 | LL | #[link(name = "...", modifiers = "no-plus-minus")] @@ -196,7 +196,7 @@ error[E0539]: malformed `link` attribute input LL | #[link(name = "...", modifiers = "+unknown")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------^^ | | - | valid arguments are "bundle", "verbatim", "whole-archive" or "as-needed" + | valid arguments are "bundle", "export-symbols", "verbatim", "whole-archive" or "as-needed" | = note: for more information, visit diff --git a/tests/ui/link-native-libs/modifiers-bad.blank.stderr b/tests/ui/link-native-libs/modifiers-bad.blank.stderr index ea36af0b4cfa..6a1953e008ee 100644 --- a/tests/ui/link-native-libs/modifiers-bad.blank.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.blank.stderr @@ -1,2 +1,2 @@ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr b/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr index ea36af0b4cfa..6a1953e008ee 100644 --- a/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr @@ -1,2 +1,2 @@ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr b/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr index 1e701374688f..46720cf0b15e 100644 --- a/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier ``, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier ``, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.unknown.stderr b/tests/ui/link-native-libs/modifiers-bad.unknown.stderr index 75950ad9c64c..d47694a5aeca 100644 --- a/tests/ui/link-native-libs/modifiers-bad.unknown.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.unknown.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier `ferris`, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier `ferris`, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols From ac160bba1244aa1309cfa60cf0abd3c1a0aa3668 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 3 Feb 2026 21:19:37 +0800 Subject: [PATCH 573/583] ci: Optimize loongarch64-linux dist builders Tune the build configuration for loongarch64-linux targets to speed up rustc. Changes include: - Enable jemalloc and rust thin-lto. - Set codegen-units=1 and disable debug assertions. These changes reduce rustc-perf compile time by ~17%. --- src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile | 5 ++++- src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile index 4b86ed32fd55..96acc5e97e94 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -50,6 +50,9 @@ ENV RUST_CONFIGURE_ARGS \ --enable-full-tools \ --enable-profiler \ --enable-sanitizers \ - --disable-docs + --disable-docs \ + --set rust.jemalloc \ + --set rust.lto=thin \ + --set rust.codegen-units=1 ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $TARGETS diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile index 763b29ae1c5e..44efc1089017 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile @@ -33,6 +33,9 @@ ENV RUST_CONFIGURE_ARGS \ --enable-profiler \ --enable-sanitizers \ --disable-docs \ + --set rust.jemalloc \ + --set rust.lto=thin \ + --set rust.codegen-units=1 \ --set target.loongarch64-unknown-linux-musl.crt-static=false \ --musl-root-loongarch64=/x-tools/loongarch64-unknown-linux-musl/loongarch64-unknown-linux-musl/sysroot/usr From 8e62b1d0e119e498ad51f44475b376b20d60fea8 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 4 Feb 2026 03:55:50 +0000 Subject: [PATCH 574/583] Fix set_times_nofollow for directory on windows --- library/std/src/fs/tests.rs | 51 +++++++++++++++++++++++++++++++ library/std/src/sys/fs/windows.rs | 4 +-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 6d0b261dcadc..42f3ccc340b2 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -2301,6 +2301,57 @@ fn test_fs_set_times() { } } +#[test] +fn test_fs_set_times_on_dir() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + let dir_path = tmp.join("testdir"); + fs::create_dir(&dir_path).unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + match fs::set_times(&dir_path, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting directory times: {e:?}"), + Ok(_) => {} + } + + let metadata = fs::metadata(&dir_path).unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!(metadata.created().unwrap(), created); + } +} + #[test] fn test_fs_set_times_follows_symlink() { #[cfg(target_vendor = "apple")] diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index fc8aec2f3f7c..74854cdeb498 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1556,7 +1556,7 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); - opts.write(true); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); let file = File::open_native(p, &opts)?; file.set_times(times) @@ -1564,7 +1564,7 @@ pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); - opts.write(true); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); let file = File::open_native(p, &opts)?; From c77779e5294722c372ee834306118810e930f96e Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 3 Feb 2026 22:15:36 -0800 Subject: [PATCH 575/583] rustbook/README.md: add missing `)` To match the opening `(` before the reference to PR 127786 --- src/tools/rustbook/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustbook/README.md b/src/tools/rustbook/README.md index d9570c23ead1..a9fd1b75b572 100644 --- a/src/tools/rustbook/README.md +++ b/src/tools/rustbook/README.md @@ -10,7 +10,7 @@ This is invoked automatically when building mdbook-style documentation, for exam ## Cargo workspace -This package defines a separate cargo workspace from the main Rust workspace for a few reasons (ref [#127786](https://github.com/rust-lang/rust/pull/127786): +This package defines a separate cargo workspace from the main Rust workspace for a few reasons (ref [#127786](https://github.com/rust-lang/rust/pull/127786)): - Avoids requiring checking out submodules for developers who are not working on the documentation. Otherwise, some submodules such as those that have custom preprocessors would be required for cargo to find the dependencies. - Avoids problems with updating dependencies. Unfortunately this workspace has a rather large set of dependencies, which can make coordinating updates difficult (see [#127890](https://github.com/rust-lang/rust/issues/127890)). From e7c142cc895f4a56e71d5e477bcb562b47cd1d16 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 15:22:54 +0100 Subject: [PATCH 576/583] Convert to inline diagnostics in `rustc_interface` --- Cargo.lock | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_interface/Cargo.toml | 1 - compiler/rustc_interface/messages.ftl | 56 -------------------- compiler/rustc_interface/src/errors.rs | 62 ++++++++++++++--------- compiler/rustc_interface/src/interface.rs | 12 +---- compiler/rustc_interface/src/lib.rs | 2 - 7 files changed, 41 insertions(+), 94 deletions(-) delete mode 100644 compiler/rustc_interface/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 4cbc98beae11..1631d5362612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4114,7 +4114,6 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hir", "rustc_hir_analysis", diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 0ab71ad9981b..3059a4fefc61 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -124,7 +124,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_expand::DEFAULT_LOCALE_RESOURCE, rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE, rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE, - rustc_interface::DEFAULT_LOCALE_RESOURCE, rustc_lint::DEFAULT_LOCALE_RESOURCE, rustc_metadata::DEFAULT_LOCALE_RESOURCE, rustc_middle::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index d785030b5f2c..3a3b4d59db86 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -19,7 +19,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl deleted file mode 100644 index d898e9bf7d1e..000000000000 --- a/compiler/rustc_interface/messages.ftl +++ /dev/null @@ -1,56 +0,0 @@ -interface_abi_required_feature = - target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly - .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -interface_abi_required_feature_issue = for more information, see issue #116344 - -interface_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}` - -interface_crate_name_invalid = crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen - -interface_emoji_identifier = - identifiers cannot contain emoji: `{$ident}` - -interface_error_writing_dependencies = - error writing dependencies to `{$path}`: {$error} - -interface_failed_writing_file = - failed to write file {$path}: {$error}" - -interface_ferris_identifier = - Ferris cannot be used as an identifier - .suggestion = try using their name instead - -interface_generated_file_conflicts_with_directory = - the generated executable for the input file "{$input_path}" conflicts with the existing directory "{$dir_path}" - -interface_ignoring_extra_filename = ignoring -C extra-filename flag due to -o flag - -interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag - -interface_input_file_would_be_overwritten = - the input file "{$path}" would be overwritten by the generated executable - -interface_mixed_bin_crate = - cannot mix `bin` crate type with others - -interface_mixed_proc_macro_crate = - cannot mix `proc-macro` crate type with others - -interface_multiple_output_types_adaption = - due to multiple output types requested, the explicitly specified output file name will be adapted for each output type - -interface_multiple_output_types_to_stdout = can't use option `-o` or `--emit` to write multiple output types to stdout -interface_out_dir_error = - failed to find or create the directory specified by `--out-dir` - -interface_proc_macro_crate_panic_abort = - building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic - -interface_temps_dir_error = - failed to find or create the directory specified by `--temps-dir` - -interface_unsupported_crate_type_for_codegen_backend = - dropping unsupported crate type `{$crate_type}` for codegen backend `{$codegen_backend}` - -interface_unsupported_crate_type_for_target = - dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index aee8ec20e14d..7e4671889f57 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -7,7 +7,9 @@ use rustc_span::{Span, Symbol}; use rustc_target::spec::TargetTuple; #[derive(Diagnostic)] -#[diag(interface_crate_name_does_not_match)] +#[diag( + "`--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}`" +)] pub(crate) struct CrateNameDoesNotMatch { #[primary_span] pub(crate) span: Span, @@ -16,23 +18,27 @@ pub(crate) struct CrateNameDoesNotMatch { } #[derive(Diagnostic)] -#[diag(interface_crate_name_invalid)] +#[diag("crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen")] pub(crate) struct CrateNameInvalid<'a> { pub(crate) crate_name: &'a str, } #[derive(Diagnostic)] -#[diag(interface_ferris_identifier)] +#[diag("Ferris cannot be used as an identifier")] pub struct FerrisIdentifier { #[primary_span] pub spans: Vec, - #[suggestion(code = "{ferris_fix}", applicability = "maybe-incorrect")] + #[suggestion( + "try using their name instead", + code = "{ferris_fix}", + applicability = "maybe-incorrect" + )] pub first_span: Span, pub ferris_fix: &'static str, } #[derive(Diagnostic)] -#[diag(interface_emoji_identifier)] +#[diag("identifiers cannot contain emoji: `{$ident}`")] pub struct EmojiIdentifier { #[primary_span] pub spans: Vec, @@ -40,86 +46,96 @@ pub struct EmojiIdentifier { } #[derive(Diagnostic)] -#[diag(interface_mixed_bin_crate)] +#[diag("cannot mix `bin` crate type with others")] pub struct MixedBinCrate; #[derive(Diagnostic)] -#[diag(interface_mixed_proc_macro_crate)] +#[diag("cannot mix `proc-macro` crate type with others")] pub struct MixedProcMacroCrate; #[derive(Diagnostic)] -#[diag(interface_error_writing_dependencies)] +#[diag("error writing dependencies to `{$path}`: {$error}")] pub struct ErrorWritingDependencies<'a> { pub path: &'a Path, pub error: io::Error, } #[derive(Diagnostic)] -#[diag(interface_input_file_would_be_overwritten)] +#[diag("the input file \"{$path}\" would be overwritten by the generated executable")] pub struct InputFileWouldBeOverWritten<'a> { pub path: &'a Path, } #[derive(Diagnostic)] -#[diag(interface_generated_file_conflicts_with_directory)] +#[diag( + "the generated executable for the input file \"{$input_path}\" conflicts with the existing directory \"{$dir_path}\"" +)] pub struct GeneratedFileConflictsWithDirectory<'a> { pub input_path: &'a Path, pub dir_path: &'a Path, } #[derive(Diagnostic)] -#[diag(interface_temps_dir_error)] +#[diag("failed to find or create the directory specified by `--temps-dir`")] pub struct TempsDirError; #[derive(Diagnostic)] -#[diag(interface_out_dir_error)] +#[diag("failed to find or create the directory specified by `--out-dir`")] pub struct OutDirError; #[derive(Diagnostic)] -#[diag(interface_failed_writing_file)] +#[diag("failed to write file {$path}: {$error}\"")] pub struct FailedWritingFile<'a> { pub path: &'a Path, pub error: io::Error, } #[derive(Diagnostic)] -#[diag(interface_proc_macro_crate_panic_abort)] +#[diag( + "building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic" +)] pub struct ProcMacroCratePanicAbort; #[derive(Diagnostic)] -#[diag(interface_multiple_output_types_adaption)] +#[diag( + "due to multiple output types requested, the explicitly specified output file name will be adapted for each output type" +)] pub struct MultipleOutputTypesAdaption; #[derive(Diagnostic)] -#[diag(interface_ignoring_extra_filename)] +#[diag("ignoring -C extra-filename flag due to -o flag")] pub struct IgnoringExtraFilename; #[derive(Diagnostic)] -#[diag(interface_ignoring_out_dir)] +#[diag("ignoring --out-dir flag due to -o flag")] pub struct IgnoringOutDir; #[derive(Diagnostic)] -#[diag(interface_multiple_output_types_to_stdout)] +#[diag("can't use option `-o` or `--emit` to write multiple output types to stdout")] pub struct MultipleOutputTypesToStdout; #[derive(Diagnostic)] -#[diag(interface_abi_required_feature)] -#[note] -#[note(interface_abi_required_feature_issue)] +#[diag( + "target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly" +)] +#[note( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +#[note("for more information, see issue #116344 ")] pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, } #[derive(Diagnostic)] -#[diag(interface_unsupported_crate_type_for_codegen_backend)] +#[diag("dropping unsupported crate type `{$crate_type}` for codegen backend `{$codegen_backend}`")] pub(crate) struct UnsupportedCrateTypeForCodegenBackend { pub(crate) crate_type: CrateType, pub(crate) codegen_backend: &'static str, } #[derive(Diagnostic)] -#[diag(interface_unsupported_crate_type_for_target)] +#[diag("dropping unsupported crate type `{$crate_type}` for target `{$target_triple}`")] pub(crate) struct UnsupportedCrateTypeForTarget<'a> { pub(crate) crate_type: CrateType, pub(crate) target_triple: &'a TargetTuple, diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index f76e8d4632fc..916618cb5049 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -55,11 +55,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { let psess = ParseSess::emitter_with_note( - vec![ - crate::DEFAULT_LOCALE_RESOURCE, - rustc_parse::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, - ], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); @@ -131,11 +127,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch for s in specs { let psess = ParseSess::emitter_with_note( - vec![ - crate::DEFAULT_LOCALE_RESOURCE, - rustc_parse::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, - ], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--check-cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index ce2398fab919..b5e4a384861f 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -21,5 +21,3 @@ pub use queries::Linker; #[cfg(test)] mod tests; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } From ae21d43933419d80a2471afbd26d6e288e6666a7 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 22:28:34 +0100 Subject: [PATCH 577/583] Convert to inline diagnostics in `rustc_privacy` --- Cargo.lock | 2 -- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_privacy/Cargo.toml | 1 - compiler/rustc_privacy/messages.ftl | 39 --------------------- compiler/rustc_privacy/src/errors.rs | 50 ++++++++++++++++++--------- compiler/rustc_privacy/src/lib.rs | 2 -- 7 files changed, 33 insertions(+), 63 deletions(-) delete mode 100644 compiler/rustc_privacy/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 4cbc98beae11..e209821c5d70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3803,7 +3803,6 @@ dependencies = [ "rustc_parse", "rustc_passes", "rustc_pattern_analysis", - "rustc_privacy", "rustc_public", "rustc_resolve", "rustc_session", @@ -4487,7 +4486,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_macros", "rustc_middle", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index d184b6c8947c..ee6ffcca29ad 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -37,7 +37,6 @@ rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" } rustc_passes = { path = "../rustc_passes" } rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } -rustc_privacy = { path = "../rustc_privacy" } rustc_public = { path = "../rustc_public", features = ["rustc_internal"] } rustc_resolve = { path = "../rustc_resolve" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 0ab71ad9981b..ffed24d72c41 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -134,7 +134,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_passes::DEFAULT_LOCALE_RESOURCE, rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, - rustc_privacy::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index c8bfdb913041..ff59c19b6bb6 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_privacy/messages.ftl b/compiler/rustc_privacy/messages.ftl deleted file mode 100644 index 43c34a109d72..000000000000 --- a/compiler/rustc_privacy/messages.ftl +++ /dev/null @@ -1,39 +0,0 @@ -privacy_field_is_private = - {$len -> - [1] field - *[other] fields - } {$field_names} of {$variant_descr} `{$def_path_str}` {$len -> - [1] is - *[other] are - } private - .label = in this type -privacy_field_is_private_is_update_syntax_label = {$rest_len -> - [1] field - *[other] fields - } {$rest_field_names} {$rest_len -> - [1] is - *[other] are - } private -privacy_field_is_private_label = private field - -privacy_from_private_dep_in_public_interface = - {$kind} `{$descr}` from private dependency '{$krate}' in public interface - -privacy_in_public_interface = {$vis_descr} {$kind} `{$descr}` in public interface - .label = can't leak {$vis_descr} {$kind} - .visibility_label = `{$descr}` declared as {$vis_descr} - -privacy_item_is_private = {$kind} `{$descr}` is private - .label = private {$kind} - -privacy_private_interface_or_bounds_lint = {$ty_kind} `{$ty_descr}` is more private than the item `{$item_descr}` - .item_label = {$item_kind} `{$item_descr}` is reachable at visibility `{$item_vis_descr}` - .ty_note = but {$ty_kind} `{$ty_descr}` is only usable at visibility `{$ty_vis_descr}` - -privacy_report_effective_visibility = {$descr} - -privacy_unnameable_types_lint = {$kind} `{$descr}` is reachable but cannot be named - .label = reachable at visibility `{$reachable_vis}`, but can only be named at visibility `{$reexported_vis}` - -privacy_unnamed_item_is_private = {$kind} is private - .label = private {$kind} diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index 4d1d58c08528..af4f0d61aa11 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -4,11 +4,17 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] -#[diag(privacy_field_is_private, code = E0451)] +#[diag("{$len -> + [1] field + *[other] fields +} {$field_names} of {$variant_descr} `{$def_path_str}` {$len -> + [1] is + *[other] are +} private", code = E0451)] pub(crate) struct FieldIsPrivate { #[primary_span] pub span: MultiSpan, - #[label] + #[label("in this type")] pub struct_span: Option, pub field_names: String, pub variant_descr: &'static str, @@ -20,14 +26,22 @@ pub(crate) struct FieldIsPrivate { #[derive(Subdiagnostic)] pub(crate) enum FieldIsPrivateLabel { - #[label(privacy_field_is_private_is_update_syntax_label)] + #[label( + "{$rest_len -> + [1] field + *[other] fields + } {$rest_field_names} {$rest_len -> + [1] is + *[other] are + } private" + )] IsUpdateSyntax { #[primary_span] span: Span, rest_field_names: String, rest_len: usize, }, - #[label(privacy_field_is_private_label)] + #[label("private field")] Other { #[primary_span] span: Span, @@ -35,17 +49,17 @@ pub(crate) enum FieldIsPrivateLabel { } #[derive(Diagnostic)] -#[diag(privacy_item_is_private)] +#[diag("{$kind} `{$descr}` is private")] pub(crate) struct ItemIsPrivate<'a> { #[primary_span] - #[label] + #[label("private {$kind}")] pub span: Span, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, } #[derive(Diagnostic)] -#[diag(privacy_unnamed_item_is_private)] +#[diag("{$kind} is private")] pub(crate) struct UnnamedItemIsPrivate { #[primary_span] pub span: Span, @@ -53,20 +67,20 @@ pub(crate) struct UnnamedItemIsPrivate { } #[derive(Diagnostic)] -#[diag(privacy_in_public_interface, code = E0446)] +#[diag("{$vis_descr} {$kind} `{$descr}` in public interface", code = E0446)] pub(crate) struct InPublicInterface<'a> { #[primary_span] - #[label] + #[label("can't leak {$vis_descr} {$kind}")] pub span: Span, pub vis_descr: &'static str, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, - #[label(privacy_visibility_label)] + #[label("`{$descr}` declared as {$vis_descr}")] pub vis_span: Span, } #[derive(Diagnostic)] -#[diag(privacy_report_effective_visibility)] +#[diag("{$descr}")] pub(crate) struct ReportEffectiveVisibility { #[primary_span] pub span: Span, @@ -74,7 +88,7 @@ pub(crate) struct ReportEffectiveVisibility { } #[derive(LintDiagnostic)] -#[diag(privacy_from_private_dep_in_public_interface)] +#[diag("{$kind} `{$descr}` from private dependency '{$krate}' in public interface")] pub(crate) struct FromPrivateDependencyInPublicInterface<'a> { pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, @@ -82,9 +96,11 @@ pub(crate) struct FromPrivateDependencyInPublicInterface<'a> { } #[derive(LintDiagnostic)] -#[diag(privacy_unnameable_types_lint)] +#[diag("{$kind} `{$descr}` is reachable but cannot be named")] pub(crate) struct UnnameableTypesLint<'a> { - #[label] + #[label( + "reachable at visibility `{$reachable_vis}`, but can only be named at visibility `{$reexported_vis}`" + )] pub span: Span, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, @@ -96,14 +112,14 @@ pub(crate) struct UnnameableTypesLint<'a> { // They will replace private-in-public errors and compatibility lints in future. // See https://rust-lang.github.io/rfcs/2145-type-privacy.html for more details. #[derive(LintDiagnostic)] -#[diag(privacy_private_interface_or_bounds_lint)] +#[diag("{$ty_kind} `{$ty_descr}` is more private than the item `{$item_descr}`")] pub(crate) struct PrivateInterfacesOrBoundsLint<'a> { - #[label(privacy_item_label)] + #[label("{$item_kind} `{$item_descr}` is reachable at visibility `{$item_vis_descr}`")] pub item_span: Span, pub item_kind: &'a str, pub item_descr: DiagArgFromDisplay<'a>, pub item_vis_descr: &'a str, - #[note(privacy_ty_note)] + #[note("but {$ty_kind} `{$ty_descr}` is only usable at visibility `{$ty_vis_descr}`")] pub ty_span: Span, pub ty_kind: &'a str, pub ty_descr: DiagArgFromDisplay<'a>, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 333677521023..4a88ea0cc445 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -39,8 +39,6 @@ use rustc_span::hygiene::Transparency; use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - //////////////////////////////////////////////////////////////////////////////// // Generic infrastructure used to implement specific visitors below. //////////////////////////////////////////////////////////////////////////////// From 7dbbab63e242e2e09902930b630cc0898bc55537 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 31 Jan 2026 23:27:51 +0100 Subject: [PATCH 578/583] Emit a future error warning for duplicate doc attribute --- .../rustc_attr_parsing/src/attributes/doc.rs | 13 ++++++++-- compiler/rustc_lint_defs/src/builtin.rs | 2 +- tests/rustdoc-ui/deprecated-attrs.rs | 4 ++- tests/rustdoc-ui/deprecated-attrs.stderr | 12 ++++++--- tests/rustdoc-ui/doc-cfg-2.rs | 1 + tests/rustdoc-ui/doc-cfg-2.stderr | 26 +++++++++++-------- tests/rustdoc-ui/doc-include-suggestion.rs | 4 ++- .../rustdoc-ui/doc-include-suggestion.stderr | 8 ++++-- tests/rustdoc-ui/doctest/doc-test-attr.rs | 1 + tests/rustdoc-ui/doctest/doc-test-attr.stderr | 12 ++++++--- tests/rustdoc-ui/lints/doc-attr-2.rs | 1 + tests/rustdoc-ui/lints/doc-attr-2.stderr | 14 ++++++---- tests/rustdoc-ui/lints/doc-spotlight.fixed | 1 + tests/rustdoc-ui/lints/doc-spotlight.rs | 1 + tests/rustdoc-ui/lints/doc-spotlight.stderr | 8 ++++-- tests/rustdoc-ui/lints/doc_cfg_hide.rs | 1 + tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 12 ++++++--- tests/rustdoc-ui/lints/duplicated-attr.rs | 6 +++++ tests/rustdoc-ui/lints/duplicated-attr.stderr | 20 ++++++++++++++ .../lints/invalid-crate-level-lint.rs | 1 + .../lints/invalid-crate-level-lint.stderr | 12 ++++++--- tests/rustdoc-ui/lints/invalid-doc-attr.rs | 1 + .../rustdoc-ui/lints/invalid-doc-attr.stderr | 21 +++++++++------ tests/ui/attributes/doc-attr.rs | 1 + tests/ui/attributes/doc-attr.stderr | 20 ++++++++------ ...atures-note-version-and-pr-issue-141619.rs | 1 + ...es-note-version-and-pr-issue-141619.stderr | 10 ++++--- tests/ui/repr/invalid_repr_list_help.rs | 1 + tests/ui/repr/invalid_repr_list_help.stderr | 18 ++++++++----- 29 files changed, 166 insertions(+), 67 deletions(-) create mode 100644 tests/rustdoc-ui/lints/duplicated-attr.rs create mode 100644 tests/rustdoc-ui/lints/duplicated-attr.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index a287c0c5a930..8b1966a836f1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -131,8 +131,17 @@ impl DocParser { return; } - if self.attribute.no_crate_inject.is_some() { - cx.duplicate_key(path.span(), sym::no_crate_inject); + if let Some(used_span) = self.attribute.no_crate_inject { + let unused_span = path.span(); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: true, + }, + unused_span, + ); return; } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d1d5d0a56ead..f4e6e93356c7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3458,7 +3458,7 @@ declare_lint! { /// but this lint was introduced to avoid breaking any existing /// crates which included them. pub INVALID_DOC_ATTRIBUTES, - Deny, + Warn, "detects invalid `#[doc(...)]` attributes", } diff --git a/tests/rustdoc-ui/deprecated-attrs.rs b/tests/rustdoc-ui/deprecated-attrs.rs index 26aaf0d46808..dcca3114fb7d 100644 --- a/tests/rustdoc-ui/deprecated-attrs.rs +++ b/tests/rustdoc-ui/deprecated-attrs.rs @@ -1,11 +1,13 @@ //@ compile-flags: --passes unknown-pass +#![deny(invalid_doc_attributes)] +//~^ NOTE + #![doc(no_default_passes)] //~^ ERROR unknown `doc` attribute `no_default_passes` //~| NOTE no longer functions //~| NOTE see issue #44136 //~| NOTE `doc(no_default_passes)` is now a no-op -//~| NOTE `#[deny(invalid_doc_attributes)]` on by default #![doc(passes = "collapse-docs unindent-comments")] //~^ ERROR unknown `doc` attribute `passes` //~| NOTE no longer functions diff --git a/tests/rustdoc-ui/deprecated-attrs.stderr b/tests/rustdoc-ui/deprecated-attrs.stderr index 6135b1496925..e32a1eb3370d 100644 --- a/tests/rustdoc-ui/deprecated-attrs.stderr +++ b/tests/rustdoc-ui/deprecated-attrs.stderr @@ -4,17 +4,21 @@ warning: the `passes` flag no longer functions = help: you may want to use --document-private-items error: unknown `doc` attribute `no_default_passes` - --> $DIR/deprecated-attrs.rs:3:8 + --> $DIR/deprecated-attrs.rs:6:8 | LL | #![doc(no_default_passes)] | ^^^^^^^^^^^^^^^^^ no longer functions | = note: `doc` attribute `no_default_passes` no longer functions; see issue #44136 = note: `doc(no_default_passes)` is now a no-op - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/deprecated-attrs.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: unknown `doc` attribute `passes` - --> $DIR/deprecated-attrs.rs:9:8 + --> $DIR/deprecated-attrs.rs:11:8 | LL | #![doc(passes = "collapse-docs unindent-comments")] | ^^^^^^ no longer functions @@ -23,7 +27,7 @@ LL | #![doc(passes = "collapse-docs unindent-comments")] = note: `doc(passes)` is now a no-op error: unknown `doc` attribute `plugins` - --> $DIR/deprecated-attrs.rs:14:8 + --> $DIR/deprecated-attrs.rs:16:8 | LL | #![doc(plugins = "xxx")] | ^^^^^^^ no longer functions diff --git a/tests/rustdoc-ui/doc-cfg-2.rs b/tests/rustdoc-ui/doc-cfg-2.rs index 7a5d1f3e3dbb..f615e96bbc6b 100644 --- a/tests/rustdoc-ui/doc-cfg-2.rs +++ b/tests/rustdoc-ui/doc-cfg-2.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #[doc(cfg(foo), cfg(bar))] diff --git a/tests/rustdoc-ui/doc-cfg-2.stderr b/tests/rustdoc-ui/doc-cfg-2.stderr index 1272e569897b..a842cbc40288 100644 --- a/tests/rustdoc-ui/doc-cfg-2.stderr +++ b/tests/rustdoc-ui/doc-cfg-2.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:3:11 + --> $DIR/doc-cfg-2.rs:4:11 | LL | #[doc(cfg(foo), cfg(bar))] | ^^^ @@ -10,7 +10,7 @@ LL | #[doc(cfg(foo), cfg(bar))] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `bar` - --> $DIR/doc-cfg-2.rs:3:21 + --> $DIR/doc-cfg-2.rs:4:21 | LL | #[doc(cfg(foo), cfg(bar))] | ^^^ @@ -19,45 +19,49 @@ LL | #[doc(cfg(foo), cfg(bar))] = note: see for more information about checking conditional configuration error: only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]` - --> $DIR/doc-cfg-2.rs:6:16 + --> $DIR/doc-cfg-2.rs:7:16 | LL | #[doc(auto_cfg(42))] | ^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-cfg-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:7:21 + --> $DIR/doc-cfg-2.rs:8:21 | LL | #[doc(auto_cfg(hide(true)))] | ^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:8:21 + --> $DIR/doc-cfg-2.rs:9:21 | LL | #[doc(auto_cfg(hide(42)))] | ^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:9:21 + --> $DIR/doc-cfg-2.rs:10:21 | LL | #[doc(auto_cfg(hide("a")))] | ^^^ error: expected boolean for `#[doc(auto_cfg = ...)]` - --> $DIR/doc-cfg-2.rs:10:18 + --> $DIR/doc-cfg-2.rs:11:18 | LL | #[doc(auto_cfg = 42)] | ^^ error: expected boolean for `#[doc(auto_cfg = ...)]` - --> $DIR/doc-cfg-2.rs:11:18 + --> $DIR/doc-cfg-2.rs:12:18 | LL | #[doc(auto_cfg = "a")] | ^^^ warning: unexpected `cfg` condition name: `feature` - --> $DIR/doc-cfg-2.rs:14:21 + --> $DIR/doc-cfg-2.rs:15:21 | LL | #[doc(auto_cfg(hide(feature = "windows")))] | ^^^^^^^^^^^^^^^^^^^ @@ -66,7 +70,7 @@ LL | #[doc(auto_cfg(hide(feature = "windows")))] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:16:21 + --> $DIR/doc-cfg-2.rs:17:21 | LL | #[doc(auto_cfg(hide(foo)))] | ^^^ diff --git a/tests/rustdoc-ui/doc-include-suggestion.rs b/tests/rustdoc-ui/doc-include-suggestion.rs index aff0a24ace86..1295805ae1a0 100644 --- a/tests/rustdoc-ui/doc-include-suggestion.rs +++ b/tests/rustdoc-ui/doc-include-suggestion.rs @@ -1,6 +1,8 @@ +#![deny(invalid_doc_attributes)] +//~^ NOTE + #[doc(include = "external-cross-doc.md")] //~^ ERROR unknown `doc` attribute `include` //~| HELP use `doc = include_str!` instead // FIXME(#85497): make this a deny instead so it's more clear what's happening -//~| NOTE on by default pub struct NeedMoreDocs; diff --git a/tests/rustdoc-ui/doc-include-suggestion.stderr b/tests/rustdoc-ui/doc-include-suggestion.stderr index ea5261e5bbd4..1587984b6e6e 100644 --- a/tests/rustdoc-ui/doc-include-suggestion.stderr +++ b/tests/rustdoc-ui/doc-include-suggestion.stderr @@ -1,10 +1,14 @@ error: unknown `doc` attribute `include` - --> $DIR/doc-include-suggestion.rs:1:7 + --> $DIR/doc-include-suggestion.rs:4:7 | LL | #[doc(include = "external-cross-doc.md")] | ^^^^^^^ help: use `doc = include_str!` instead: `#[doc = include_str!("external-cross-doc.md")]` | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-include-suggestion.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/doc-test-attr.rs b/tests/rustdoc-ui/doctest/doc-test-attr.rs index 8570252c4493..e664f75a9cf7 100644 --- a/tests/rustdoc-ui/doctest/doc-test-attr.rs +++ b/tests/rustdoc-ui/doctest/doc-test-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![doc(test)] diff --git a/tests/rustdoc-ui/doctest/doc-test-attr.stderr b/tests/rustdoc-ui/doctest/doc-test-attr.stderr index cf7bce66ef40..98e7b315ea57 100644 --- a/tests/rustdoc-ui/doctest/doc-test-attr.stderr +++ b/tests/rustdoc-ui/doctest/doc-test-attr.stderr @@ -1,19 +1,23 @@ error: `#[doc(test(...)]` takes a list of attributes - --> $DIR/doc-test-attr.rs:3:8 + --> $DIR/doc-test-attr.rs:4:8 | LL | #![doc(test)] | ^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-test-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#[doc(test(...)]` takes a list of attributes - --> $DIR/doc-test-attr.rs:5:13 + --> $DIR/doc-test-attr.rs:6:13 | LL | #![doc(test = "hello")] | ^^^^^^^^^ error: unknown `doc(test)` attribute `a` - --> $DIR/doc-test-attr.rs:7:13 + --> $DIR/doc-test-attr.rs:8:13 | LL | #![doc(test(a))] | ^ diff --git a/tests/rustdoc-ui/lints/doc-attr-2.rs b/tests/rustdoc-ui/lints/doc-attr-2.rs index e5198e347523..9802fb5d95a5 100644 --- a/tests/rustdoc-ui/lints/doc-attr-2.rs +++ b/tests/rustdoc-ui/lints/doc-attr-2.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![doc(as_ptr)] //~^ ERROR unknown `doc` attribute `as_ptr` diff --git a/tests/rustdoc-ui/lints/doc-attr-2.stderr b/tests/rustdoc-ui/lints/doc-attr-2.stderr index c2bb45c5785e..f96eab1aba61 100644 --- a/tests/rustdoc-ui/lints/doc-attr-2.stderr +++ b/tests/rustdoc-ui/lints/doc-attr-2.stderr @@ -1,25 +1,29 @@ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr-2.rs:4:7 + --> $DIR/doc-attr-2.rs:5:7 | LL | #[doc(as_ptr)] | ^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-attr-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: unknown `doc` attribute `foo::bar` - --> $DIR/doc-attr-2.rs:8:7 + --> $DIR/doc-attr-2.rs:9:7 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^ error: unknown `doc` attribute `crate::bar::baz` - --> $DIR/doc-attr-2.rs:8:17 + --> $DIR/doc-attr-2.rs:9:17 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^^^^^^^^ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr-2.rs:1:8 + --> $DIR/doc-attr-2.rs:2:8 | LL | #![doc(as_ptr)] | ^^^^^^ diff --git a/tests/rustdoc-ui/lints/doc-spotlight.fixed b/tests/rustdoc-ui/lints/doc-spotlight.fixed index 0f8f11a9430e..98b0fac87898 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.fixed +++ b/tests/rustdoc-ui/lints/doc-spotlight.fixed @@ -1,5 +1,6 @@ //@ run-rustfix #![feature(doc_notable_trait)] +#![deny(invalid_doc_attributes)] #[doc(notable_trait)] //~^ ERROR unknown `doc` attribute `spotlight` diff --git a/tests/rustdoc-ui/lints/doc-spotlight.rs b/tests/rustdoc-ui/lints/doc-spotlight.rs index c1f90dd442b2..a0d874bdcb21 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.rs +++ b/tests/rustdoc-ui/lints/doc-spotlight.rs @@ -1,5 +1,6 @@ //@ run-rustfix #![feature(doc_notable_trait)] +#![deny(invalid_doc_attributes)] #[doc(spotlight)] //~^ ERROR unknown `doc` attribute `spotlight` diff --git a/tests/rustdoc-ui/lints/doc-spotlight.stderr b/tests/rustdoc-ui/lints/doc-spotlight.stderr index 9682a3c0c8be..8c6f9738bdc0 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.stderr +++ b/tests/rustdoc-ui/lints/doc-spotlight.stderr @@ -1,12 +1,16 @@ error: unknown `doc` attribute `spotlight` - --> $DIR/doc-spotlight.rs:4:7 + --> $DIR/doc-spotlight.rs:5:7 | LL | #[doc(spotlight)] | ^^^^^^^^^ help: use `notable_trait` instead | = note: `doc(spotlight)` was renamed to `doc(notable_trait)` = note: `doc(spotlight)` is now a no-op - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-spotlight.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index 397b21393e5c..6c190f9befac 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #![doc(auto_cfg(hide = "test"))] //~ ERROR #![doc(auto_cfg(hide))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index acbe6ef69dd5..a5ec8fdf5d34 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -1,19 +1,23 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items - --> $DIR/doc_cfg_hide.rs:2:17 + --> $DIR/doc_cfg_hide.rs:3:17 | LL | #![doc(auto_cfg(hide = "test"))] | ^^^^^^^^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc_cfg_hide.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items - --> $DIR/doc_cfg_hide.rs:3:17 + --> $DIR/doc_cfg_hide.rs:4:17 | LL | #![doc(auto_cfg(hide))] | ^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc_cfg_hide.rs:4:22 + --> $DIR/doc_cfg_hide.rs:5:22 | LL | #![doc(auto_cfg(hide(not(windows))))] | ^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/lints/duplicated-attr.rs b/tests/rustdoc-ui/lints/duplicated-attr.rs new file mode 100644 index 000000000000..b89908d700f4 --- /dev/null +++ b/tests/rustdoc-ui/lints/duplicated-attr.rs @@ -0,0 +1,6 @@ +#![deny(invalid_doc_attributes)] +#![expect(unused_attributes)] +#![doc(test(no_crate_inject))] +#![doc(test(no_crate_inject))] +//~^ ERROR +//~| WARN diff --git a/tests/rustdoc-ui/lints/duplicated-attr.stderr b/tests/rustdoc-ui/lints/duplicated-attr.stderr new file mode 100644 index 000000000000..3682710bb54a --- /dev/null +++ b/tests/rustdoc-ui/lints/duplicated-attr.stderr @@ -0,0 +1,20 @@ +error: unused attribute + --> $DIR/duplicated-attr.rs:4:13 + | +LL | #![doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/duplicated-attr.rs:3:13 + | +LL | #![doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/duplicated-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs b/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs index 275e20e80a17..afb0a5987deb 100644 --- a/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs +++ b/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #[doc(test(no_crate_inject))] diff --git a/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr b/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr index fdb95e7de41c..7569cf575e51 100644 --- a/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr +++ b/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr @@ -1,14 +1,18 @@ error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:3:12 + --> $DIR/invalid-crate-level-lint.rs:4:12 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ | = note: read for more information - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid-crate-level-lint.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:7:17 + --> $DIR/invalid-crate-level-lint.rs:8:17 | LL | #![doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ @@ -16,7 +20,7 @@ LL | #![doc(test(no_crate_inject))] = note: read for more information error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:10:16 + --> $DIR/invalid-crate-level-lint.rs:11:16 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr.rs b/tests/rustdoc-ui/lints/invalid-doc-attr.rs index 169d092d7e17..60c215222d7f 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr.rs +++ b/tests/rustdoc-ui/lints/invalid-doc-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![feature(doc_masked)] diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr.stderr index e431b8df2263..2e9b9f0a8cfb 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr.stderr +++ b/tests/rustdoc-ui/lints/invalid-doc-attr.stderr @@ -1,5 +1,5 @@ error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:7:7 + --> $DIR/invalid-doc-attr.rs:8:7 | LL | #[doc(inline)] | ^^^^^^ only applicable on `use` items @@ -8,10 +8,14 @@ LL | pub fn foo() {} | ------------ not a `use` item | = note: read for more information - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid-doc-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: conflicting doc inlining attributes - --> $DIR/invalid-doc-attr.rs:17:7 + --> $DIR/invalid-doc-attr.rs:18:7 | LL | #[doc(inline)] | ^^^^^^ this attribute... @@ -21,7 +25,7 @@ LL | #[doc(no_inline)] = help: remove one of the conflicting attributes error: this attribute can only be applied to an `extern crate` item - --> $DIR/invalid-doc-attr.rs:23:7 + --> $DIR/invalid-doc-attr.rs:24:7 | LL | #[doc(masked)] | ^^^^^^ only applicable on `extern crate` items @@ -32,7 +36,7 @@ LL | pub struct Masked; = note: read for more information error: this attribute cannot be applied to an `extern crate self` item - --> $DIR/invalid-doc-attr.rs:27:7 + --> $DIR/invalid-doc-attr.rs:28:7 | LL | #[doc(masked)] | ^^^^^^ not applicable on `extern crate self` items @@ -41,9 +45,10 @@ LL | pub extern crate self as reexport; | --------------------------------- `extern crate self` defined here error: this attribute can only be applied to an `extern crate` item - --> $DIR/invalid-doc-attr.rs:4:8 + --> $DIR/invalid-doc-attr.rs:5:8 | -LL | / #![crate_type = "lib"] +LL | / #![deny(invalid_doc_attributes)] +LL | | #![crate_type = "lib"] LL | | #![feature(doc_masked)] LL | | LL | | #![doc(masked)] @@ -55,7 +60,7 @@ LL | | pub extern crate self as reexport; = note: read for more information error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:12:11 + --> $DIR/invalid-doc-attr.rs:13:11 | LL | #[doc(inline)] | ^^^^^^ only applicable on `use` items diff --git a/tests/ui/attributes/doc-attr.rs b/tests/ui/attributes/doc-attr.rs index 8d733be93567..b45f211f6222 100644 --- a/tests/ui/attributes/doc-attr.rs +++ b/tests/ui/attributes/doc-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![doc(as_ptr)] //~^ ERROR unknown `doc` attribute diff --git a/tests/ui/attributes/doc-attr.stderr b/tests/ui/attributes/doc-attr.stderr index dfc0e8ad5b6f..005810fa23fa 100644 --- a/tests/ui/attributes/doc-attr.stderr +++ b/tests/ui/attributes/doc-attr.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:9:1 + --> $DIR/doc-attr.rs:10:1 | LL | #[doc(123)] | ^^^^^^---^^ @@ -7,7 +7,7 @@ LL | #[doc(123)] | expected this to be of the form `... = "..."` error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:11:1 + --> $DIR/doc-attr.rs:12:1 | LL | #[doc("hello", "bar")] | ^^^^^^-------^^^^^^^^^ @@ -15,7 +15,7 @@ LL | #[doc("hello", "bar")] | expected this to be of the form `... = "..."` error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:11:1 + --> $DIR/doc-attr.rs:12:1 | LL | #[doc("hello", "bar")] | ^^^^^^^^^^^^^^^-----^^ @@ -23,27 +23,31 @@ LL | #[doc("hello", "bar")] | expected this to be of the form `... = "..."` error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:5:7 + --> $DIR/doc-attr.rs:6:7 | LL | #[doc(as_ptr)] | ^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: unknown `doc` attribute `foo::bar` - --> $DIR/doc-attr.rs:14:7 + --> $DIR/doc-attr.rs:15:7 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^ error: unknown `doc` attribute `crate::bar::baz` - --> $DIR/doc-attr.rs:14:17 + --> $DIR/doc-attr.rs:15:17 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^^^^^^^^ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:2:8 + --> $DIR/doc-attr.rs:3:8 | LL | #![doc(as_ptr)] | ^^^^^^ diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs index d8c5f48f9fd9..a7dde7c97f6a 100644 --- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs +++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(external_doc)] //~ ERROR feature has been removed #![doc(include("README.md"))] //~ ERROR unknown `doc` attribute `include` diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr index d9556560b01c..a009898761df 100644 --- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr +++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr @@ -1,5 +1,5 @@ error[E0557]: feature has been removed - --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:1:12 + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:2:12 | LL | #![feature(external_doc)] | ^^^^^^^^^^^^ feature has been removed @@ -8,12 +8,16 @@ LL | #![feature(external_doc)] = note: use #[doc = include_str!("filename")] instead, which handles macro invocations error: unknown `doc` attribute `include` - --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:2:8 + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:3:8 | LL | #![doc(include("README.md"))] | ^^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/repr/invalid_repr_list_help.rs b/tests/ui/repr/invalid_repr_list_help.rs index 77f2a68537b3..e8bf5fdcd3fd 100644 --- a/tests/ui/repr/invalid_repr_list_help.rs +++ b/tests/ui/repr/invalid_repr_list_help.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #[repr(uwu)] //~ERROR: unrecognized representation hint diff --git a/tests/ui/repr/invalid_repr_list_help.stderr b/tests/ui/repr/invalid_repr_list_help.stderr index f9d1593275ee..322650814828 100644 --- a/tests/ui/repr/invalid_repr_list_help.stderr +++ b/tests/ui/repr/invalid_repr_list_help.stderr @@ -1,5 +1,5 @@ error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:3:8 + --> $DIR/invalid_repr_list_help.rs:4:8 | LL | #[repr(uwu)] | ^^^ @@ -8,7 +8,7 @@ LL | #[repr(uwu)] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:6:8 + --> $DIR/invalid_repr_list_help.rs:7:8 | LL | #[repr(uwu = "a")] | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | #[repr(uwu = "a")] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:9:8 + --> $DIR/invalid_repr_list_help.rs:10:8 | LL | #[repr(uwu(4))] | ^^^^^^ @@ -26,7 +26,7 @@ LL | #[repr(uwu(4))] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:14:8 + --> $DIR/invalid_repr_list_help.rs:15:8 | LL | #[repr(uwu, u8)] | ^^^ @@ -35,7 +35,7 @@ LL | #[repr(uwu, u8)] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:19:8 + --> $DIR/invalid_repr_list_help.rs:20:8 | LL | #[repr(uwu)] | ^^^ @@ -44,12 +44,16 @@ LL | #[repr(uwu)] = note: for more information, visit error: unknown `doc` attribute `owo` - --> $DIR/invalid_repr_list_help.rs:20:7 + --> $DIR/invalid_repr_list_help.rs:21:7 | LL | #[doc(owo)] | ^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid_repr_list_help.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors From 6d713489d00a87a683f253a242d71e717c8fcf26 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 1 Feb 2026 00:58:51 +0100 Subject: [PATCH 579/583] Make more doc attribute parsing error into future warnings --- .../rustc_attr_parsing/src/attributes/doc.rs | 19 ++++++++-- compiler/rustc_lint/messages.ftl | 4 +++ compiler/rustc_lint/src/early/diagnostics.rs | 2 ++ compiler/rustc_lint/src/lints.rs | 5 +++ compiler/rustc_lint_defs/src/lib.rs | 1 + tests/rustdoc-ui/lints/doc-attr.rs | 4 +++ tests/rustdoc-ui/lints/doc-attr.stderr | 36 ++++++++++--------- tests/rustdoc-ui/lints/invalid-doc-attr-2.rs | 7 ++++ .../lints/invalid-doc-attr-2.stderr | 21 +++++++++++ 9 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 tests/rustdoc-ui/lints/invalid-doc-attr-2.rs create mode 100644 tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 8b1966a836f1..1bf5352ee838 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -591,7 +591,7 @@ impl DocParser { let suggestions = cx.suggestions(); let span = cx.attr_span; cx.emit_lint( - rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT, + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None }, span, ); @@ -604,14 +604,27 @@ impl DocParser { self.parse_single_doc_attr_item(cx, mip); } MetaItemOrLitParser::Lit(lit) => { - cx.expected_name_value(lit.span, None); + // FIXME: Remove the lint and uncomment line after beta backport is + // done. + // cx.expected_name_value(lit.span, None); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + lit.span, + ); } } } } ArgParser::NameValue(nv) => { if nv.value_as_str().is_none() { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + // FIXME: Remove the lint and uncomment line after beta backport is done. + // cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + nv.value_span, + ); } else { unreachable!( "Should have been handled at the same time as sugar-syntaxed doc comments" diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 867b937d4090..ebee380765c6 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -558,6 +558,10 @@ lint_macro_expr_fragment_specifier_2024_migration = lint_malformed_attribute = malformed lint attribute input +lint_malformed_doc = + malformed `doc` attribute input + .warn = {-lint_previously_accepted} + lint_map_unit_fn = `Iterator::map` call that discard the iterator's values .note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated .function_label = this function returns `()`, which is likely not what you wanted diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 377b3c439453..1360ba2d9c1c 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -428,5 +428,7 @@ pub fn decorate_attribute_lint( sugg: suggested.map(|s| lints::UnknownCrateTypesSuggestion { span, snippet: s }), } .decorate_lint(diag), + + &AttributeLintKind::MalformedDoc => lints::MalformedDoc.decorate_lint(diag), } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a20d90e1227e..0b5197d7ebdf 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3185,6 +3185,11 @@ pub(crate) struct UnusedDuplicate { pub warning: bool, } +#[derive(LintDiagnostic)] +#[diag(lint_malformed_doc)] +#[warning] +pub(crate) struct MalformedDoc; + #[derive(LintDiagnostic)] #[diag(lint_unsafe_attr_outside_unsafe)] pub(crate) struct UnsafeAttrOutsideUnsafeLint { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 4c78287b7784..ea2e67b8869b 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -826,6 +826,7 @@ pub enum AttributeLintKind { span: Span, suggested: Option, }, + MalformedDoc, } pub type RegisteredTools = FxIndexSet; diff --git a/tests/rustdoc-ui/lints/doc-attr.rs b/tests/rustdoc-ui/lints/doc-attr.rs index b27faa81cb92..f04961aa930a 100644 --- a/tests/rustdoc-ui/lints/doc-attr.rs +++ b/tests/rustdoc-ui/lints/doc-attr.rs @@ -1,8 +1,12 @@ #![crate_type = "lib"] +#![deny(invalid_doc_attributes)] #[doc(123)] //~^ ERROR malformed `doc` attribute +//~| WARN #[doc("hello", "bar")] //~^ ERROR malformed `doc` attribute //~| ERROR malformed `doc` attribute +//~| WARN +//~| WARN fn bar() {} diff --git a/tests/rustdoc-ui/lints/doc-attr.stderr b/tests/rustdoc-ui/lints/doc-attr.stderr index 8f8c6000b364..5b6f48acb3be 100644 --- a/tests/rustdoc-ui/lints/doc-attr.stderr +++ b/tests/rustdoc-ui/lints/doc-attr.stderr @@ -1,27 +1,31 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:3:1 +error: malformed `doc` attribute input + --> $DIR/doc-attr.rs:4:7 | LL | #[doc(123)] - | ^^^^^^---^^ - | | - | expected this to be of the form `... = "..."` + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/doc-attr.rs:2:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:5:1 +error: malformed `doc` attribute input + --> $DIR/doc-attr.rs:7:7 | LL | #[doc("hello", "bar")] - | ^^^^^^-------^^^^^^^^^ - | | - | expected this to be of the form `... = "..."` + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:5:1 +error: malformed `doc` attribute input + --> $DIR/doc-attr.rs:7:16 | LL | #[doc("hello", "bar")] - | ^^^^^^^^^^^^^^^-----^^ - | | - | expected this to be of the form `... = "..."` + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0539`. diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs b/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs new file mode 100644 index 000000000000..de99d5a9e078 --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs @@ -0,0 +1,7 @@ +#![deny(invalid_doc_attributes)] + +#![doc("other attribute")] +//~^ ERROR +//~| WARN +#![doc] +//~^ ERROR diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr new file mode 100644 index 000000000000..bfd926a2a517 --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr @@ -0,0 +1,21 @@ +error: malformed `doc` attribute input + --> $DIR/invalid-doc-attr-2.rs:3:8 + | +LL | #![doc("other attribute")] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/invalid-doc-attr-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: valid forms for the attribute are `#![doc = "string"]`, `#![doc(alias)]`, `#![doc(attribute)]`, `#![doc(auto_cfg)]`, `#![doc(cfg)]`, `#![doc(fake_variadic)]`, `#![doc(hidden)]`, `#![doc(html_favicon_url)]`, `#![doc(html_logo_url)]`, `#![doc(html_no_source)]`, `#![doc(html_playground_url)]`, `#![doc(html_root_url)]`, `#![doc(include)]`, `#![doc(inline)]`, `#![doc(issue_tracker_base_url)]`, `#![doc(keyword)]`, `#![doc(masked)]`, `#![doc(no_default_passes)]`, `#![doc(no_inline)]`, `#![doc(notable_trait)]`, `#![doc(passes)]`, `#![doc(plugins)]`, `#![doc(rust_logo)]`, `#![doc(search_unbox)]`, `#![doc(spotlight)]`, and `#![doc(test)]` + --> $DIR/invalid-doc-attr-2.rs:6:1 + | +LL | #![doc] + | ^^^^^^^ + +error: aborting due to 2 previous errors + From c910511cab40e005fffbd5c31939f89448d030a8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 1 Feb 2026 14:59:29 +0100 Subject: [PATCH 580/583] Move remaining doc attribute parsing errors to warnings --- .../rustc_attr_parsing/src/attributes/doc.rs | 98 +++++++--- compiler/rustc_lint/messages.ftl | 8 + compiler/rustc_lint/src/early/diagnostics.rs | 4 + compiler/rustc_lint/src/lints.rs | 10 + compiler/rustc_lint_defs/src/lib.rs | 2 + tests/rustdoc-ui/bad-render-options.rs | 37 ++-- tests/rustdoc-ui/bad-render-options.stderr | 97 +++++----- tests/rustdoc-ui/doc-cfg.rs | 7 +- tests/rustdoc-ui/doc-cfg.stderr | 21 +-- tests/rustdoc-ui/lints/doc-attr.rs | 6 +- tests/rustdoc-ui/lints/doc-attr.stderr | 6 +- .../lints/invalid-doc-attr-2.stderr | 2 +- tests/rustdoc-ui/lints/invalid-doc-attr-3.rs | 22 +++ .../lints/invalid-doc-attr-3.stderr | 55 ++++++ tests/ui/attributes/doc-attr.rs | 9 +- tests/ui/attributes/doc-attr.stderr | 53 +++--- tests/ui/attributes/doc-test-literal.rs | 5 +- tests/ui/attributes/doc-test-literal.stderr | 16 +- tests/ui/attributes/malformed-attrs.rs | 7 +- tests/ui/attributes/malformed-attrs.stderr | 172 ++++++++---------- tests/ui/malformed/malformed-regressions.rs | 3 +- .../ui/malformed/malformed-regressions.stderr | 36 ++-- tests/ui/malformed/malformed-special-attrs.rs | 2 + .../malformed/malformed-special-attrs.stderr | 8 +- 24 files changed, 404 insertions(+), 282 deletions(-) create mode 100644 tests/rustdoc-ui/lints/invalid-doc-attr-3.rs create mode 100644 tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 1bf5352ee838..f8968639f98c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -70,6 +70,42 @@ fn check_attr_crate_level(cx: &mut AcceptContext<'_, '_, S>, span: Spa true } +// FIXME: To be removed once merged and replace with `cx.expected_name_value(span, _name)`. +fn expected_name_value( + cx: &mut AcceptContext<'_, '_, S>, + span: Span, + _name: Option, +) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::ExpectedNameValue, + span, + ); +} + +// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead. +fn expected_no_args(cx: &mut AcceptContext<'_, '_, S>, span: Span) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::ExpectedNoArgs, + span, + ); +} + +// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead. +// cx.expected_string_literal(span, _actual_literal); +fn expected_string_literal( + cx: &mut AcceptContext<'_, '_, S>, + span: Span, + _actual_literal: Option<&MetaItemLit>, +) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + span, + ); +} + fn parse_keyword_and_attribute( cx: &mut AcceptContext<'_, '_, S>, path: &OwnedPathParser, @@ -78,12 +114,12 @@ fn parse_keyword_and_attribute( attr_name: Symbol, ) { let Some(nv) = args.name_value() else { - cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym()); + expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; let Some(value) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); return; }; @@ -127,7 +163,7 @@ impl DocParser { match path.word_sym() { Some(sym::no_crate_inject) => { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } @@ -153,7 +189,14 @@ impl DocParser { } Some(sym::attr) => { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + // FIXME: remove this method once merged and uncomment the line below instead. + // cx.expected_list(cx.attr_span, args); + let span = cx.attr_span; + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + span, + ); return; }; @@ -255,7 +298,7 @@ impl DocParser { inline: DocInline, ) { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } @@ -337,7 +380,14 @@ impl DocParser { match sub_item.args() { a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { let Some(name) = sub_item.path().word_sym() else { - cx.expected_identifier(sub_item.path().span()); + // FIXME: remove this method once merged and uncomment the line + // below instead. + // cx.expected_identifier(sub_item.path().span()); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + sub_item.path().span(), + ); continue; }; if let Ok(CfgEntry::NameValue { name, value, .. }) = @@ -400,7 +450,7 @@ impl DocParser { macro_rules! no_args { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } @@ -419,7 +469,7 @@ impl DocParser { macro_rules! no_args_and_not_crate_level { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } let span = path.span(); @@ -432,7 +482,7 @@ impl DocParser { macro_rules! no_args_and_crate_level { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } let span = path.span(); @@ -445,12 +495,12 @@ impl DocParser { macro_rules! string_arg_and_crate_level { ($ident: ident) => {{ let Some(nv) = args.name_value() else { - cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym()); + expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; let Some(s) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); return; }; @@ -521,7 +571,14 @@ impl DocParser { self.parse_single_test_doc_attr_item(cx, mip); } MetaItemOrLitParser::Lit(lit) => { - cx.unexpected_literal(lit.span); + // FIXME: remove this method once merged and uncomment the line + // below instead. + // cx.unexpected_literal(lit.span); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + lit.span, + ); } } } @@ -604,27 +661,14 @@ impl DocParser { self.parse_single_doc_attr_item(cx, mip); } MetaItemOrLitParser::Lit(lit) => { - // FIXME: Remove the lint and uncomment line after beta backport is - // done. - // cx.expected_name_value(lit.span, None); - cx.emit_lint( - rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::MalformedDoc, - lit.span, - ); + expected_name_value(cx, lit.span, None); } } } } ArgParser::NameValue(nv) => { if nv.value_as_str().is_none() { - // FIXME: Remove the lint and uncomment line after beta backport is done. - // cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); - cx.emit_lint( - rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::MalformedDoc, - nv.value_span, - ); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); } else { unreachable!( "Should have been handled at the same time as sugar-syntaxed doc comments" diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index ebee380765c6..2f5b7ed26952 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -326,6 +326,14 @@ lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} +lint_expected_name_value = + expected this to be of the form `... = "..."` + .warn = {-lint_previously_accepted} + +lint_expected_no_args = + didn't expect any arguments here + .warn = {-lint_previously_accepted} + lint_for_loops_over_fallibles = for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 1360ba2d9c1c..c0ab0d1f0040 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -430,5 +430,9 @@ pub fn decorate_attribute_lint( .decorate_lint(diag), &AttributeLintKind::MalformedDoc => lints::MalformedDoc.decorate_lint(diag), + + &AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.decorate_lint(diag), + + &AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.decorate_lint(diag), } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0b5197d7ebdf..1a87cc013e79 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3190,6 +3190,16 @@ pub(crate) struct UnusedDuplicate { #[warning] pub(crate) struct MalformedDoc; +#[derive(LintDiagnostic)] +#[diag(lint_expected_no_args)] +#[warning] +pub(crate) struct ExpectedNoArgs; + +#[derive(LintDiagnostic)] +#[diag(lint_expected_name_value)] +#[warning] +pub(crate) struct ExpectedNameValue; + #[derive(LintDiagnostic)] #[diag(lint_unsafe_attr_outside_unsafe)] pub(crate) struct UnsafeAttrOutsideUnsafeLint { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ea2e67b8869b..b3e5b93cf2fc 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -827,6 +827,8 @@ pub enum AttributeLintKind { suggested: Option, }, MalformedDoc, + ExpectedNoArgs, + ExpectedNameValue, } pub type RegisteredTools = FxIndexSet; diff --git a/tests/rustdoc-ui/bad-render-options.rs b/tests/rustdoc-ui/bad-render-options.rs index 0522f68cb6c2..c85a818511d7 100644 --- a/tests/rustdoc-ui/bad-render-options.rs +++ b/tests/rustdoc-ui/bad-render-options.rs @@ -1,29 +1,30 @@ // regression test for https://github.com/rust-lang/rust/issues/149187 +#![deny(invalid_doc_attributes)] #![doc(html_favicon_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_favicon_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_logo_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_logo_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_playground_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_playground_url = "..."` +//~^ ERROR +//~| WARN #![doc(issue_tracker_base_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `issue_tracker_base_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_favicon_url = 1)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_logo_url = 2)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_playground_url = 3)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(issue_tracker_base_url = 4)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_no_source = "asdf")] -//~^ ERROR malformed `doc` attribute -//~| NOTE didn't expect any arguments here +//~^ ERROR +//~| WARN diff --git a/tests/rustdoc-ui/bad-render-options.stderr b/tests/rustdoc-ui/bad-render-options.stderr index 28d4533a6edb..dac6bc231c37 100644 --- a/tests/rustdoc-ui/bad-render-options.stderr +++ b/tests/rustdoc-ui/bad-render-options.stderr @@ -1,76 +1,79 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:3:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:4:8 | LL | #![doc(html_favicon_url)] - | ^^^^^^^----------------^^ - | | - | expected this to be of the form `html_favicon_url = "..."` + | ^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/bad-render-options.rs:2:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:6:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:7:8 | LL | #![doc(html_logo_url)] - | ^^^^^^^-------------^^ - | | - | expected this to be of the form `html_logo_url = "..."` + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:9:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:10:8 | LL | #![doc(html_playground_url)] - | ^^^^^^^-------------------^^ - | | - | expected this to be of the form `html_playground_url = "..."` + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:12:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:13:8 | LL | #![doc(issue_tracker_base_url)] - | ^^^^^^^----------------------^^ - | | - | expected this to be of the form `issue_tracker_base_url = "..."` + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:15:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:16:27 | LL | #![doc(html_favicon_url = 1)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:18:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:19:24 | LL | #![doc(html_logo_url = 2)] - | ^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:21:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:22:30 | LL | #![doc(html_playground_url = 3)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:24:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:25:33 | LL | #![doc(issue_tracker_base_url = 4)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0565]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:27:1 +error: didn't expect any arguments here + --> $DIR/bad-render-options.rs:28:23 | LL | #![doc(html_no_source = "asdf")] - | ^^^^^^^^^^^^^^^^^^^^^^--------^^ - | | - | didn't expect any arguments here + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: aborting due to 9 previous errors -Some errors have detailed explanations: E0539, E0565. -For more information about an error, try `rustc --explain E0539`. diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index f30d80aa9cda..abaea9719280 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -1,9 +1,10 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #[doc(cfg(), cfg(foo, bar))] -//~^ ERROR malformed `doc` attribute input -//~| ERROR malformed `doc` attribute input +//~^ ERROR +//~| ERROR #[doc(cfg())] //~ ERROR #[doc(cfg(foo, bar))] //~ ERROR -#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR +#[doc(auto_cfg(hide(foo::bar)))] pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index ce16ec31d875..fa25a441e9b7 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -1,5 +1,5 @@ error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:3:1 + --> $DIR/doc-cfg.rs:4:1 | LL | #[doc(cfg(), cfg(foo, bar))] | ^^^^^^^^^--^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[doc(cfg(), cfg(foo, bar))] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:3:1 + --> $DIR/doc-cfg.rs:4:1 | LL | #[doc(cfg(), cfg(foo, bar))] | ^^^^^^^^^^^^^^^^----------^^ @@ -15,7 +15,7 @@ LL | #[doc(cfg(), cfg(foo, bar))] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:6:1 + --> $DIR/doc-cfg.rs:7:1 | LL | #[doc(cfg())] | ^^^^^^^^^--^^ @@ -23,22 +23,13 @@ LL | #[doc(cfg())] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:7:1 + --> $DIR/doc-cfg.rs:8:1 | LL | #[doc(cfg(foo, bar))] | ^^^^^^^^^----------^^ | | | expected a single argument here -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:8:1 - | -LL | #[doc(auto_cfg(hide(foo::bar)))] - | ^^^^^^^^^^^^^^^^^^^^--------^^^^ - | | - | expected a valid identifier here +error: aborting due to 4 previous errors -error: aborting due to 5 previous errors - -Some errors have detailed explanations: E0539, E0805. -For more information about an error, try `rustc --explain E0539`. +For more information about this error, try `rustc --explain E0805`. diff --git a/tests/rustdoc-ui/lints/doc-attr.rs b/tests/rustdoc-ui/lints/doc-attr.rs index f04961aa930a..46d56e7f5962 100644 --- a/tests/rustdoc-ui/lints/doc-attr.rs +++ b/tests/rustdoc-ui/lints/doc-attr.rs @@ -2,11 +2,11 @@ #![deny(invalid_doc_attributes)] #[doc(123)] -//~^ ERROR malformed `doc` attribute +//~^ ERROR //~| WARN #[doc("hello", "bar")] -//~^ ERROR malformed `doc` attribute -//~| ERROR malformed `doc` attribute +//~^ ERROR +//~| ERROR //~| WARN //~| WARN fn bar() {} diff --git a/tests/rustdoc-ui/lints/doc-attr.stderr b/tests/rustdoc-ui/lints/doc-attr.stderr index 5b6f48acb3be..263b068e092d 100644 --- a/tests/rustdoc-ui/lints/doc-attr.stderr +++ b/tests/rustdoc-ui/lints/doc-attr.stderr @@ -1,4 +1,4 @@ -error: malformed `doc` attribute input +error: expected this to be of the form `... = "..."` --> $DIR/doc-attr.rs:4:7 | LL | #[doc(123)] @@ -11,7 +11,7 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: malformed `doc` attribute input +error: expected this to be of the form `... = "..."` --> $DIR/doc-attr.rs:7:7 | LL | #[doc("hello", "bar")] @@ -19,7 +19,7 @@ LL | #[doc("hello", "bar")] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error: malformed `doc` attribute input +error: expected this to be of the form `... = "..."` --> $DIR/doc-attr.rs:7:16 | LL | #[doc("hello", "bar")] diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr index bfd926a2a517..2457352bb342 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr @@ -1,4 +1,4 @@ -error: malformed `doc` attribute input +error: expected this to be of the form `... = "..."` --> $DIR/invalid-doc-attr-2.rs:3:8 | LL | #![doc("other attribute")] diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs b/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs new file mode 100644 index 000000000000..1d2e4445140a --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs @@ -0,0 +1,22 @@ +#![deny(invalid_doc_attributes)] + +#![doc(test(no_crate_inject = 1))] +//~^ ERROR +//~| WARN +#![doc(test(attr = 1))] +//~^ ERROR +//~| WARN + +#[doc(hidden = true)] +//~^ ERROR +//~| WARN +#[doc(hidden("or you will be fired"))] +//~^ ERROR +//~| WARN +#[doc(hidden = "handled transparently by codegen")] +//~^ ERROR +//~| WARN +#[doc = 1] +//~^ ERROR +//~| WARN +pub struct X; diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr new file mode 100644 index 000000000000..9cec930174ce --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr @@ -0,0 +1,55 @@ +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:10:14 + | +LL | #[doc(hidden = true)] + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/invalid-doc-attr-3.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:13:13 + | +LL | #[doc(hidden("or you will be fired"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:16:14 + | +LL | #[doc(hidden = "handled transparently by codegen")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: malformed `doc` attribute input + --> $DIR/invalid-doc-attr-3.rs:19:9 + | +LL | #[doc = 1] + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:3:29 + | +LL | #![doc(test(no_crate_inject = 1))] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: malformed `doc` attribute input + --> $DIR/invalid-doc-attr-3.rs:6:1 + | +LL | #![doc(test(attr = 1))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 6 previous errors + diff --git a/tests/ui/attributes/doc-attr.rs b/tests/ui/attributes/doc-attr.rs index b45f211f6222..8c81bc18d6ae 100644 --- a/tests/ui/attributes/doc-attr.rs +++ b/tests/ui/attributes/doc-attr.rs @@ -8,10 +8,13 @@ pub fn foo() {} #[doc(123)] -//~^ ERROR malformed `doc` attribute +//~^ ERROR +//~| WARN #[doc("hello", "bar")] -//~^ ERROR malformed `doc` attribute -//~| ERROR malformed `doc` attribute +//~^ ERROR +//~| ERROR +//~| WARN +//~| WARN #[doc(foo::bar, crate::bar::baz = "bye")] //~^ ERROR unknown `doc` attribute //~| ERROR unknown `doc` attribute diff --git a/tests/ui/attributes/doc-attr.stderr b/tests/ui/attributes/doc-attr.stderr index 005810fa23fa..79d9fb5bea7e 100644 --- a/tests/ui/attributes/doc-attr.stderr +++ b/tests/ui/attributes/doc-attr.stderr @@ -1,27 +1,3 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:10:1 - | -LL | #[doc(123)] - | ^^^^^^---^^ - | | - | expected this to be of the form `... = "..."` - -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:12:1 - | -LL | #[doc("hello", "bar")] - | ^^^^^^-------^^^^^^^^^ - | | - | expected this to be of the form `... = "..."` - -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:12:1 - | -LL | #[doc("hello", "bar")] - | ^^^^^^^^^^^^^^^-----^^ - | | - | expected this to be of the form `... = "..."` - error: unknown `doc` attribute `as_ptr` --> $DIR/doc-attr.rs:6:7 | @@ -34,14 +10,38 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:10:7 + | +LL | #[doc(123)] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:13:7 + | +LL | #[doc("hello", "bar")] + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:13:16 + | +LL | #[doc("hello", "bar")] + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + error: unknown `doc` attribute `foo::bar` - --> $DIR/doc-attr.rs:15:7 + --> $DIR/doc-attr.rs:18:7 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^ error: unknown `doc` attribute `crate::bar::baz` - --> $DIR/doc-attr.rs:15:17 + --> $DIR/doc-attr.rs:18:17 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^^^^^^^^ @@ -54,4 +54,3 @@ LL | #![doc(as_ptr)] error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/attributes/doc-test-literal.rs b/tests/ui/attributes/doc-test-literal.rs index f9776e9924bd..ceb8967ea229 100644 --- a/tests/ui/attributes/doc-test-literal.rs +++ b/tests/ui/attributes/doc-test-literal.rs @@ -1,4 +1,7 @@ +#![deny(invalid_doc_attributes)] + #![doc(test(""))] -//~^ ERROR malformed `doc` attribute input +//~^ ERROR +//~| WARN fn main() {} diff --git a/tests/ui/attributes/doc-test-literal.stderr b/tests/ui/attributes/doc-test-literal.stderr index 2d70d5d206f0..c26aa94ec1af 100644 --- a/tests/ui/attributes/doc-test-literal.stderr +++ b/tests/ui/attributes/doc-test-literal.stderr @@ -1,11 +1,15 @@ -error[E0565]: malformed `doc` attribute input - --> $DIR/doc-test-literal.rs:1:1 +error: malformed `doc` attribute input + --> $DIR/doc-test-literal.rs:3:13 | LL | #![doc(test(""))] - | ^^^^^^^^^^^^--^^^ - | | - | didn't expect a literal here + | ^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/doc-test-literal.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index b0d8fd774f74..6dc3086b63e1 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -1,5 +1,6 @@ // This file contains a bunch of malformed attributes. // We enable a bunch of features to not get feature-gate errs in this test. +#![deny(invalid_doc_attributes)] #![feature(rustc_attrs)] #![feature(rustc_allow_const_fn_unstable)] #![feature(allow_internal_unstable)] @@ -39,8 +40,7 @@ #[deprecated = 5] //~^ ERROR malformed #[doc] -//~^ ERROR valid forms for the attribute are -//~| WARN this was previously accepted by the compiler +//~^ ERROR #[rustc_macro_transparency] //~^ ERROR malformed //~| ERROR attribute cannot be used on @@ -75,8 +75,7 @@ //~^ ERROR malformed //~| WARN crate-level attribute should be an inner attribute #[doc] -//~^ ERROR valid forms for the attribute are -//~| WARN this was previously accepted by the compiler +//~^ ERROR #[target_feature] //~^ ERROR malformed #[export_stable = 1] diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index f817a0b0d91b..22e222efa435 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `cfg` attribute input - --> $DIR/malformed-attrs.rs:108:1 + --> $DIR/malformed-attrs.rs:107:1 | LL | #[cfg] | ^^^^^^ @@ -10,7 +10,7 @@ LL | #[cfg] = note: for more information, visit error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-attrs.rs:110:1 + --> $DIR/malformed-attrs.rs:109:1 | LL | #[cfg_attr] | ^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL | #[cfg_attr] = note: for more information, visit error[E0463]: can't find crate for `wloop` - --> $DIR/malformed-attrs.rs:218:1 + --> $DIR/malformed-attrs.rs:217:1 | LL | extern crate wloop; | ^^^^^^^^^^^^^^^^^^^ can't find crate error: malformed `allow` attribute input - --> $DIR/malformed-attrs.rs:184:1 + --> $DIR/malformed-attrs.rs:183:1 | LL | #[allow] | ^^^^^^^^ @@ -43,7 +43,7 @@ LL | #[allow(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `expect` attribute input - --> $DIR/malformed-attrs.rs:186:1 + --> $DIR/malformed-attrs.rs:185:1 | LL | #[expect] | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | #[expect(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `warn` attribute input - --> $DIR/malformed-attrs.rs:188:1 + --> $DIR/malformed-attrs.rs:187:1 | LL | #[warn] | ^^^^^^^ @@ -75,7 +75,7 @@ LL | #[warn(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `deny` attribute input - --> $DIR/malformed-attrs.rs:190:1 + --> $DIR/malformed-attrs.rs:189:1 | LL | #[deny] | ^^^^^^^ @@ -91,7 +91,7 @@ LL | #[deny(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `forbid` attribute input - --> $DIR/malformed-attrs.rs:192:1 + --> $DIR/malformed-attrs.rs:191:1 | LL | #[forbid] | ^^^^^^^^^ @@ -107,25 +107,25 @@ LL | #[forbid(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:105:1 + --> $DIR/malformed-attrs.rs:104:1 | LL | #[proc_macro = 18] | ^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:122:1 + --> $DIR/malformed-attrs.rs:121:1 | LL | #[proc_macro_attribute = 19] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:129:1 + --> $DIR/malformed-attrs.rs:128:1 | LL | #[proc_macro_derive] | ^^^^^^^^^^^^^^^^^^^^ error[E0658]: allow_internal_unsafe side-steps the unsafe_code lint - --> $DIR/malformed-attrs.rs:223:1 + --> $DIR/malformed-attrs.rs:222:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | #[allow_internal_unsafe = 1] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0539]: malformed `windows_subsystem` attribute input - --> $DIR/malformed-attrs.rs:26:1 + --> $DIR/malformed-attrs.rs:27:1 | LL | #![windows_subsystem] | ^^^-----------------^ @@ -150,25 +150,25 @@ LL | #![windows_subsystem = "windows"] | +++++++++++ error[E0539]: malformed `export_name` attribute input - --> $DIR/malformed-attrs.rs:29:1 + --> $DIR/malformed-attrs.rs:30:1 | LL | #[unsafe(export_name)] | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_name = "name"]` error: `rustc_allow_const_fn_unstable` expects a list of feature names - --> $DIR/malformed-attrs.rs:31:1 + --> $DIR/malformed-attrs.rs:32:1 | LL | #[rustc_allow_const_fn_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `allow_internal_unstable` expects a list of feature names - --> $DIR/malformed-attrs.rs:34:1 + --> $DIR/malformed-attrs.rs:35:1 | LL | #[allow_internal_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0539]: malformed `rustc_confusables` attribute input - --> $DIR/malformed-attrs.rs:36:1 + --> $DIR/malformed-attrs.rs:37:1 | LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | #[rustc_confusables] | help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` error: `#[rustc_confusables]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:36:1 + --> $DIR/malformed-attrs.rs:37:1 | LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | #[rustc_confusables] = help: `#[rustc_confusables]` can only be applied to inherent methods error[E0539]: malformed `deprecated` attribute input - --> $DIR/malformed-attrs.rs:39:1 + --> $DIR/malformed-attrs.rs:40:1 | LL | #[deprecated = 5] | ^^^^^^^^^^^^^^^-^ @@ -349,7 +349,7 @@ LL | #[crate_name] | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` error[E0539]: malformed `target_feature` attribute input - --> $DIR/malformed-attrs.rs:80:1 + --> $DIR/malformed-attrs.rs:79:1 | LL | #[target_feature] | ^^^^^^^^^^^^^^^^^ @@ -358,7 +358,7 @@ LL | #[target_feature] | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` error[E0565]: malformed `export_stable` attribute input - --> $DIR/malformed-attrs.rs:82:1 + --> $DIR/malformed-attrs.rs:81:1 | LL | #[export_stable = 1] | ^^^^^^^^^^^^^^^^---^ @@ -367,7 +367,7 @@ LL | #[export_stable = 1] | help: must be of the form: `#[export_stable]` error[E0539]: malformed `link` attribute input - --> $DIR/malformed-attrs.rs:84:1 + --> $DIR/malformed-attrs.rs:83:1 | LL | #[link] | ^^^^^^^ expected this to be a list @@ -375,7 +375,7 @@ LL | #[link] = note: for more information, visit error[E0539]: malformed `link_name` attribute input - --> $DIR/malformed-attrs.rs:88:1 + --> $DIR/malformed-attrs.rs:87:1 | LL | #[link_name] | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]` @@ -383,7 +383,7 @@ LL | #[link_name] = note: for more information, visit error[E0539]: malformed `link_section` attribute input - --> $DIR/malformed-attrs.rs:92:1 + --> $DIR/malformed-attrs.rs:91:1 | LL | #[link_section] | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]` @@ -391,7 +391,7 @@ LL | #[link_section] = note: for more information, visit error[E0539]: malformed `coverage` attribute input - --> $DIR/malformed-attrs.rs:94:1 + --> $DIR/malformed-attrs.rs:93:1 | LL | #[coverage] | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument @@ -404,13 +404,13 @@ LL | #[coverage(on)] | ++++ error[E0539]: malformed `sanitize` attribute input - --> $DIR/malformed-attrs.rs:96:1 + --> $DIR/malformed-attrs.rs:95:1 | LL | #[sanitize] | ^^^^^^^^^^^ expected this to be a list error[E0565]: malformed `no_implicit_prelude` attribute input - --> $DIR/malformed-attrs.rs:101:1 + --> $DIR/malformed-attrs.rs:100:1 | LL | #[no_implicit_prelude = 23] | ^^^^^^^^^^^^^^^^^^^^^^----^ @@ -419,7 +419,7 @@ LL | #[no_implicit_prelude = 23] | help: must be of the form: `#[no_implicit_prelude]` error[E0565]: malformed `proc_macro` attribute input - --> $DIR/malformed-attrs.rs:105:1 + --> $DIR/malformed-attrs.rs:104:1 | LL | #[proc_macro = 18] | ^^^^^^^^^^^^^----^ @@ -428,7 +428,7 @@ LL | #[proc_macro = 18] | help: must be of the form: `#[proc_macro]` error[E0539]: malformed `instruction_set` attribute input - --> $DIR/malformed-attrs.rs:112:1 + --> $DIR/malformed-attrs.rs:111:1 | LL | #[instruction_set] | ^^^^^^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | #[instruction_set] = note: for more information, visit error[E0539]: malformed `patchable_function_entry` attribute input - --> $DIR/malformed-attrs.rs:114:1 + --> $DIR/malformed-attrs.rs:113:1 | LL | #[patchable_function_entry] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -448,7 +448,7 @@ LL | #[patchable_function_entry] | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` error[E0565]: malformed `coroutine` attribute input - --> $DIR/malformed-attrs.rs:117:5 + --> $DIR/malformed-attrs.rs:116:5 | LL | #[coroutine = 63] || {} | ^^^^^^^^^^^^----^ @@ -457,7 +457,7 @@ LL | #[coroutine = 63] || {} | help: must be of the form: `#[coroutine]` error[E0565]: malformed `proc_macro_attribute` attribute input - --> $DIR/malformed-attrs.rs:122:1 + --> $DIR/malformed-attrs.rs:121:1 | LL | #[proc_macro_attribute = 19] | ^^^^^^^^^^^^^^^^^^^^^^^----^ @@ -466,7 +466,7 @@ LL | #[proc_macro_attribute = 19] | help: must be of the form: `#[proc_macro_attribute]` error[E0539]: malformed `must_use` attribute input - --> $DIR/malformed-attrs.rs:125:1 + --> $DIR/malformed-attrs.rs:124:1 | LL | #[must_use = 1] | ^^^^^^^^^^^^^-^ @@ -484,7 +484,7 @@ LL + #[must_use] | error[E0539]: malformed `proc_macro_derive` attribute input - --> $DIR/malformed-attrs.rs:129:1 + --> $DIR/malformed-attrs.rs:128:1 | LL | #[proc_macro_derive] | ^^^^^^^^^^^^^^^^^^^^ expected this to be a list @@ -498,7 +498,7 @@ LL | #[proc_macro_derive(TraitName, attributes(name1, name2, ...))] | ++++++++++++++++++++++++++++++++++++++++++ error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input - --> $DIR/malformed-attrs.rs:134:1 + --> $DIR/malformed-attrs.rs:133:1 | LL | #[rustc_layout_scalar_valid_range_start] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -507,7 +507,7 @@ LL | #[rustc_layout_scalar_valid_range_start] | help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]` error[E0539]: malformed `rustc_layout_scalar_valid_range_end` attribute input - --> $DIR/malformed-attrs.rs:136:1 + --> $DIR/malformed-attrs.rs:135:1 | LL | #[rustc_layout_scalar_valid_range_end] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -516,7 +516,7 @@ LL | #[rustc_layout_scalar_valid_range_end] | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` error[E0539]: malformed `must_not_suspend` attribute input - --> $DIR/malformed-attrs.rs:138:1 + --> $DIR/malformed-attrs.rs:137:1 | LL | #[must_not_suspend()] | ^^^^^^^^^^^^^^^^^^--^ @@ -532,7 +532,7 @@ LL + #[must_not_suspend] | error[E0539]: malformed `cfi_encoding` attribute input - --> $DIR/malformed-attrs.rs:140:1 + --> $DIR/malformed-attrs.rs:139:1 | LL | #[cfi_encoding = ""] | ^^^^^^^^^^^^^^^^^--^ @@ -541,7 +541,7 @@ LL | #[cfi_encoding = ""] | help: must be of the form: `#[cfi_encoding = "encoding"]` error[E0565]: malformed `marker` attribute input - --> $DIR/malformed-attrs.rs:161:1 + --> $DIR/malformed-attrs.rs:160:1 | LL | #[marker = 3] | ^^^^^^^^^---^ @@ -550,7 +550,7 @@ LL | #[marker = 3] | help: must be of the form: `#[marker]` error[E0565]: malformed `fundamental` attribute input - --> $DIR/malformed-attrs.rs:163:1 + --> $DIR/malformed-attrs.rs:162:1 | LL | #[fundamental()] | ^^^^^^^^^^^^^--^ @@ -559,7 +559,7 @@ LL | #[fundamental()] | help: must be of the form: `#[fundamental]` error[E0565]: malformed `ffi_pure` attribute input - --> $DIR/malformed-attrs.rs:171:5 + --> $DIR/malformed-attrs.rs:170:5 | LL | #[unsafe(ffi_pure = 1)] | ^^^^^^^^^^^^^^^^^^---^^ @@ -568,7 +568,7 @@ LL | #[unsafe(ffi_pure = 1)] | help: must be of the form: `#[ffi_pure]` error[E0539]: malformed `link_ordinal` attribute input - --> $DIR/malformed-attrs.rs:173:5 + --> $DIR/malformed-attrs.rs:172:5 | LL | #[link_ordinal] | ^^^^^^^^^^^^^^^ @@ -579,7 +579,7 @@ LL | #[link_ordinal] = note: for more information, visit error[E0565]: malformed `ffi_const` attribute input - --> $DIR/malformed-attrs.rs:177:5 + --> $DIR/malformed-attrs.rs:176:5 | LL | #[unsafe(ffi_const = 1)] | ^^^^^^^^^^^^^^^^^^^---^^ @@ -588,13 +588,13 @@ LL | #[unsafe(ffi_const = 1)] | help: must be of the form: `#[ffi_const]` error[E0539]: malformed `linkage` attribute input - --> $DIR/malformed-attrs.rs:179:5 + --> $DIR/malformed-attrs.rs:178:5 | LL | #[linkage] | ^^^^^^^^^^ expected this to be of the form `linkage = "..."` error[E0539]: malformed `debugger_visualizer` attribute input - --> $DIR/malformed-attrs.rs:194:1 + --> $DIR/malformed-attrs.rs:193:1 | LL | #[debugger_visualizer] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -605,7 +605,7 @@ LL | #[debugger_visualizer] = note: for more information, visit error[E0565]: malformed `automatically_derived` attribute input - --> $DIR/malformed-attrs.rs:196:1 + --> $DIR/malformed-attrs.rs:195:1 | LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^----^ @@ -614,7 +614,7 @@ LL | #[automatically_derived = 18] | help: must be of the form: `#[automatically_derived]` error[E0565]: malformed `non_exhaustive` attribute input - --> $DIR/malformed-attrs.rs:204:1 + --> $DIR/malformed-attrs.rs:203:1 | LL | #[non_exhaustive = 1] | ^^^^^^^^^^^^^^^^^---^ @@ -623,7 +623,7 @@ LL | #[non_exhaustive = 1] | help: must be of the form: `#[non_exhaustive]` error[E0565]: malformed `thread_local` attribute input - --> $DIR/malformed-attrs.rs:210:1 + --> $DIR/malformed-attrs.rs:209:1 | LL | #[thread_local()] | ^^^^^^^^^^^^^^--^ @@ -632,7 +632,7 @@ LL | #[thread_local()] | help: must be of the form: `#[thread_local]` error[E0565]: malformed `no_link` attribute input - --> $DIR/malformed-attrs.rs:214:1 + --> $DIR/malformed-attrs.rs:213:1 | LL | #[no_link()] | ^^^^^^^^^--^ @@ -641,7 +641,7 @@ LL | #[no_link()] | help: must be of the form: `#[no_link]` error[E0539]: malformed `macro_use` attribute input - --> $DIR/malformed-attrs.rs:216:1 + --> $DIR/malformed-attrs.rs:215:1 | LL | #[macro_use = 1] | ^^^^^^^^^^^^---^ @@ -659,7 +659,7 @@ LL + #[macro_use] | error[E0539]: malformed `macro_export` attribute input - --> $DIR/malformed-attrs.rs:221:1 + --> $DIR/malformed-attrs.rs:220:1 | LL | #[macro_export = 18] | ^^^^^^^^^^^^^^^----^ @@ -676,7 +676,7 @@ LL + #[macro_export] | error[E0565]: malformed `allow_internal_unsafe` attribute input - --> $DIR/malformed-attrs.rs:223:1 + --> $DIR/malformed-attrs.rs:222:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^---^ @@ -685,7 +685,7 @@ LL | #[allow_internal_unsafe = 1] | help: must be of the form: `#[allow_internal_unsafe]` error[E0565]: malformed `type_const` attribute input - --> $DIR/malformed-attrs.rs:149:5 + --> $DIR/malformed-attrs.rs:148:5 | LL | #[type_const = 1] | ^^^^^^^^^^^^^---^ @@ -694,7 +694,7 @@ LL | #[type_const = 1] | help: must be of the form: `#[type_const]` error: attribute should be applied to `const fn` - --> $DIR/malformed-attrs.rs:31:1 + --> $DIR/malformed-attrs.rs:32:1 | LL | #[rustc_allow_const_fn_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -706,7 +706,7 @@ LL | | } | |_- not a `const fn` warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/malformed-attrs.rs:84:1 + --> $DIR/malformed-attrs.rs:83:1 | LL | #[link] | ^^^^^^^ @@ -733,7 +733,7 @@ LL | #[repr] | ^^^^^^^ warning: missing options for `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:144:1 + --> $DIR/malformed-attrs.rs:143:1 | LL | #[diagnostic::on_unimplemented] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -742,7 +742,7 @@ LL | #[diagnostic::on_unimplemented] = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: malformed `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:146:1 + --> $DIR/malformed-attrs.rs:145:1 | LL | #[diagnostic::on_unimplemented = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -750,14 +750,16 @@ LL | #[diagnostic::on_unimplemented = 1] = help: only `message`, `note` and `label` are allowed as options error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:41:1 + --> $DIR/malformed-attrs.rs:42:1 | LL | #[doc] | ^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default +note: the lint level is defined here + --> $DIR/malformed-attrs.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` --> $DIR/malformed-attrs.rs:52:1 @@ -767,6 +769,7 @@ LL | #[inline = 5] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` --> $DIR/malformed-attrs.rs:74:1 @@ -775,7 +778,7 @@ LL | #[crate_name] | ^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/malformed-attrs.rs:116:1 + --> $DIR/malformed-attrs.rs:115:1 | LL | / fn test() { LL | | #[coroutine = 63] || {} @@ -788,12 +791,9 @@ error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, ` | LL | #[doc] | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 warning: `#[link_name]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:88:1 + --> $DIR/malformed-attrs.rs:87:1 | LL | #[link_name] | ^^^^^^^^^^^^ @@ -802,7 +802,7 @@ LL | #[link_name] = help: `#[link_name]` can be applied to foreign functions and foreign statics error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:98:1 + --> $DIR/malformed-attrs.rs:97:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -811,7 +811,7 @@ LL | #[ignore()] = note: for more information, see issue #57571 warning: `#[no_implicit_prelude]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:101:1 + --> $DIR/malformed-attrs.rs:100:1 | LL | #[no_implicit_prelude = 23] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -820,13 +820,13 @@ LL | #[no_implicit_prelude = 23] = help: `#[no_implicit_prelude]` can be applied to crates and modules warning: `#[diagnostic::do_not_recommend]` does not expect any arguments - --> $DIR/malformed-attrs.rs:155:1 + --> $DIR/malformed-attrs.rs:154:1 | LL | #[diagnostic::do_not_recommend()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` attribute cannot be used on modules - --> $DIR/malformed-attrs.rs:196:1 + --> $DIR/malformed-attrs.rs:195:1 | LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -835,7 +835,7 @@ LL | #[automatically_derived = 18] = help: `#[automatically_derived]` can only be applied to trait impl blocks error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:230:1 + --> $DIR/malformed-attrs.rs:229:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ @@ -844,7 +844,7 @@ LL | #[ignore = 1] = note: for more information, see issue #57571 error[E0308]: mismatched types - --> $DIR/malformed-attrs.rs:117:23 + --> $DIR/malformed-attrs.rs:116:23 | LL | fn test() { | - help: a return type might be missing here: `-> _` @@ -852,24 +852,13 @@ LL | #[coroutine = 63] || {} | ^^^^^ expected `()`, found coroutine | = note: expected unit type `()` - found coroutine `{coroutine@$DIR/malformed-attrs.rs:117:23: 117:25}` + found coroutine `{coroutine@$DIR/malformed-attrs.rs:116:23: 116:25}` error: aborting due to 76 previous errors; 8 warnings emitted Some errors have detailed explanations: E0308, E0463, E0539, E0565, E0658, E0805. For more information about an error, try `rustc --explain E0308`. Future incompatibility report: Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:41:1 - | -LL | #[doc] - | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - -Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` --> $DIR/malformed-attrs.rs:52:1 | @@ -880,20 +869,9 @@ LL | #[inline = 5] = note: for more information, see issue #57571 = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default -Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:77:1 - | -LL | #[doc] - | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:98:1 + --> $DIR/malformed-attrs.rs:97:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -904,7 +882,7 @@ LL | #[ignore()] Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:230:1 + --> $DIR/malformed-attrs.rs:229:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ diff --git a/tests/ui/malformed/malformed-regressions.rs b/tests/ui/malformed/malformed-regressions.rs index c0f8c0d15bb8..63b918520ec0 100644 --- a/tests/ui/malformed/malformed-regressions.rs +++ b/tests/ui/malformed/malformed-regressions.rs @@ -1,5 +1,6 @@ +#![deny(invalid_doc_attributes)] + #[doc] //~ ERROR valid forms for the attribute are -//~^ WARN this was previously accepted #[ignore()] //~ ERROR valid forms for the attribute are //~^ WARN this was previously accepted #[inline = ""] //~ ERROR valid forms for the attribute are diff --git a/tests/ui/malformed/malformed-regressions.stderr b/tests/ui/malformed/malformed-regressions.stderr index 2bf6ff3a9e7a..283834a48552 100644 --- a/tests/ui/malformed/malformed-regressions.stderr +++ b/tests/ui/malformed/malformed-regressions.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `link` attribute input - --> $DIR/malformed-regressions.rs:7:1 + --> $DIR/malformed-regressions.rs:8:1 | LL | #[link] | ^^^^^^^ expected this to be a list @@ -7,7 +7,7 @@ LL | #[link] = note: for more information, visit error[E0539]: malformed `link` attribute input - --> $DIR/malformed-regressions.rs:10:1 + --> $DIR/malformed-regressions.rs:11:1 | LL | #[link = ""] | ^^^^^^^----^ @@ -17,7 +17,7 @@ LL | #[link = ""] = note: for more information, visit warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/malformed-regressions.rs:7:1 + --> $DIR/malformed-regressions.rs:8:1 | LL | #[link] | ^^^^^^^ @@ -29,26 +29,29 @@ LL | fn main() {} = note: requested on the command line with `-W unused-attributes` error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-regressions.rs:1:1 + --> $DIR/malformed-regressions.rs:3:1 | LL | #[doc] | ^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default +note: the lint level is defined here + --> $DIR/malformed-regressions.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-regressions.rs:3:1 + --> $DIR/malformed-regressions.rs:4:1 | LL | #[ignore()] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/malformed-regressions.rs:5:1 + --> $DIR/malformed-regressions.rs:6:1 | LL | #[inline = ""] | ^^^^^^^^^^^^^^ @@ -60,19 +63,8 @@ error: aborting due to 5 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0539`. Future incompatibility report: Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-regressions.rs:1:1 - | -LL | #[doc] - | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - -Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-regressions.rs:3:1 + --> $DIR/malformed-regressions.rs:4:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -83,7 +75,7 @@ LL | #[ignore()] Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/malformed-regressions.rs:5:1 + --> $DIR/malformed-regressions.rs:6:1 | LL | #[inline = ""] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/malformed/malformed-special-attrs.rs b/tests/ui/malformed/malformed-special-attrs.rs index 05b7ebe46666..f0e66ef0f835 100644 --- a/tests/ui/malformed/malformed-special-attrs.rs +++ b/tests/ui/malformed/malformed-special-attrs.rs @@ -1,3 +1,5 @@ +#![deny(invalid_doc_attributes)] + #[cfg_attr] //~ ERROR malformed `cfg_attr` attribute struct S1; diff --git a/tests/ui/malformed/malformed-special-attrs.stderr b/tests/ui/malformed/malformed-special-attrs.stderr index 91e5939eb1f9..a2501d2aa398 100644 --- a/tests/ui/malformed/malformed-special-attrs.stderr +++ b/tests/ui/malformed/malformed-special-attrs.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-special-attrs.rs:1:1 + --> $DIR/malformed-special-attrs.rs:3:1 | LL | #[cfg_attr] | ^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | #[cfg_attr] = note: for more information, visit error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-special-attrs.rs:4:1 + --> $DIR/malformed-special-attrs.rs:6:1 | LL | #[cfg_attr = ""] | ^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL | #[cfg_attr = ""] = note: for more information, visit error: malformed `derive` attribute input - --> $DIR/malformed-special-attrs.rs:7:1 + --> $DIR/malformed-special-attrs.rs:9:1 | LL | #[derive] | ^^^^^^^^^ help: must be of the form: `#[derive(Trait1, Trait2, ...)]` error: malformed `derive` attribute input - --> $DIR/malformed-special-attrs.rs:10:1 + --> $DIR/malformed-special-attrs.rs:12:1 | LL | #[derive = ""] | ^^^^^^^^^^^^^^ help: must be of the form: `#[derive(Trait1, Trait2, ...)]` From 0f9b0d38410c1d17e6c8f3186595110a79702eb6 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 21:50:59 +0100 Subject: [PATCH 581/583] Convert to inline diagnostics in `rustc_session` --- Cargo.lock | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_interface/src/interface.rs | 4 +- compiler/rustc_session/Cargo.toml | 1 - compiler/rustc_session/messages.ftl | 149 ----------------- compiler/rustc_session/src/errors.rs | 187 ++++++++++++---------- compiler/rustc_session/src/lib.rs | 2 - 7 files changed, 106 insertions(+), 239 deletions(-) delete mode 100644 compiler/rustc_session/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 1631d5362612..49563a61d2dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4641,7 +4641,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hashes", "rustc_hir", diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 3059a4fefc61..268040d3cb7d 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -135,7 +135,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, rustc_privacy::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, rustc_ty_utils::DEFAULT_LOCALE_RESOURCE, // tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 916618cb5049..bb22bd20ac2a 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -55,7 +55,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { let psess = ParseSess::emitter_with_note( - vec![rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); @@ -127,7 +127,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch for s in specs { let psess = ParseSess::emitter_with_note( - vec![rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--check-cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index aebac3880d2f..d66e04f58106 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -12,7 +12,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl deleted file mode 100644 index 5c851cb90a66..000000000000 --- a/compiler/rustc_session/messages.ftl +++ /dev/null @@ -1,149 +0,0 @@ -session_apple_deployment_target_invalid = - failed to parse deployment target specified in {$env_var}: {$error} - -session_apple_deployment_target_too_low = - deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} - -session_binary_float_literal_not_supported = binary float literal is not supported -session_branch_protection_requires_aarch64 = `-Zbranch-protection` is only supported on aarch64 - -session_cannot_enable_crt_static_linux = sanitizer is incompatible with statically linked libc, disable it using `-C target-feature=-crt-static` - -session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}` - -session_cli_feature_diagnostic_help = - add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable - -session_crate_name_empty = crate name must not be empty - -session_embed_source_insufficient_dwarf_version = `-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version} - -session_embed_source_requires_debug_info = `-Zembed-source=y` requires debug information to be enabled - -session_expr_parentheses_needed = parentheses are required to parse this as an expression - -session_failed_to_create_profiler = failed to create profiler: {$err} - -session_feature_diagnostic_for_issue = - see issue #{$n} for more information - -session_feature_diagnostic_help = - add `#![feature({$feature})]` to the crate attributes to enable - -session_feature_diagnostic_suggestion = - add `#![feature({$feature})]` to the crate attributes to enable - -session_feature_suggest_upgrade_compiler = - this compiler was built on {$date}; consider upgrading it if it is out of date - -session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions - -session_file_write_fail = failed to write `{$path}` due to error `{$err}` - -session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64 - -session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models - -session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported - -session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target - .note = compatible flavors are: {$compatible_list} - -session_indirect_branch_cs_prefix_requires_x86_or_x86_64 = `-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64 - -session_instrumentation_not_supported = {$us} instrumentation is not supported for this target - -session_int_literal_too_large = integer literal is too large - .note = value exceeds limit of `{$limit}` - -session_invalid_character_in_crate_name = invalid character {$character} in crate name: `{$crate_name}` - -session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal - .label = invalid suffix `{$suffix}` - .help = valid suffixes are `f32` and `f64` - -session_invalid_float_literal_width = invalid width `{$width}` for float literal - .help = valid widths are 32 and 64 - -session_invalid_int_literal_width = invalid width `{$width}` for integer literal - .help = valid widths are 8, 16, 32, 64 and 128 - -session_invalid_literal_suffix = suffixes on {$kind} literals are invalid - .label = invalid suffix `{$suffix}` - -session_invalid_num_literal_base_prefix = invalid base prefix for number literal - .note = base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase - .suggestion = try making the prefix lowercase - -session_invalid_num_literal_suffix = invalid suffix `{$suffix}` for number literal - .label = invalid suffix `{$suffix}` - .help = the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) - -session_linker_plugin_lto_windows_not_supported = linker plugin based LTO is not supported together with `-C prefer-dynamic` when targeting Windows-like targets - -session_must_be_name_of_associated_function = must be a name of an associated function - -session_not_circumvent_feature = `-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature gates, except when testing error paths in the CTFE engine - -session_not_supported = not supported - -session_octal_float_literal_not_supported = octal float literal is not supported - -session_profile_sample_use_file_does_not_exist = file `{$path}` passed to `-C profile-sample-use` does not exist - -session_profile_use_file_does_not_exist = file `{$path}` passed to `-C profile-use` does not exist - -session_sanitizer_cfi_canonical_jump_tables_requires_cfi = `-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi` - -session_sanitizer_cfi_generalize_pointers_requires_cfi = `-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi` - -session_sanitizer_cfi_normalize_integers_requires_cfi = `-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi` - -session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto` - -session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1` - -session_sanitizer_kcfi_arity_requires_kcfi = `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi` - -session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort` - -session_sanitizer_not_supported = {$us} sanitizer is not supported for this target - -session_sanitizers_not_supported = {$us} sanitizers are not supported for this target - -session_skipping_const_checks = skipping const checks - -session_soft_float_deprecated = - `-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead - .note = it will be removed or ignored in a future version of Rust -session_soft_float_deprecated_issue = see issue #129893 for more information - -session_soft_float_ignored = - `-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets - .note = this may become a hard error in a future version of Rust - -session_split_debuginfo_unstable_platform = `-Csplit-debuginfo={$debuginfo}` is unstable on this platform - -session_split_lto_unit_requires_lto = `-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto` - -session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` - -session_target_small_data_threshold_not_supported = `-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored - -session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored - -session_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag - .controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}` - .incoherent = manually setting a built-in cfg can and does create incoherent behaviors - -session_unleashed_feature_help_named = skipping check for `{$gate}` feature -session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate - -session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto` - -session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is not supported -session_unsupported_dwarf_version_help = supported DWARF versions are 2, 3, 4 and 5 - -session_unsupported_reg_struct_return_arch = `-Zreg-struct-return` is only supported on x86 -session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3) -session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86 diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 54e792fd7b59..0c6a33f22808 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -15,9 +15,11 @@ use crate::parse::ParseSess; #[derive(Diagnostic)] pub(crate) enum AppleDeploymentTarget { - #[diag(session_apple_deployment_target_invalid)] + #[diag("failed to parse deployment target specified in {$env_var}: {$error}")] Invalid { env_var: &'static str, error: ParseIntError }, - #[diag(session_apple_deployment_target_too_low)] + #[diag( + "deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}" + )] TooLow { env_var: &'static str, version: String, os_min: String }, } @@ -34,13 +36,13 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for FeatureGateError { } #[derive(Subdiagnostic)] -#[note(session_feature_diagnostic_for_issue)] +#[note("see issue #{$n} for more information")] pub(crate) struct FeatureDiagnosticForIssue { pub(crate) n: NonZero, } #[derive(Subdiagnostic)] -#[note(session_feature_suggest_upgrade_compiler)] +#[note("this compiler was built on {$date}; consider upgrading it if it is out of date")] pub(crate) struct SuggestUpgradeCompiler { date: &'static str, } @@ -58,14 +60,14 @@ impl SuggestUpgradeCompiler { } #[derive(Subdiagnostic)] -#[help(session_feature_diagnostic_help)] +#[help("add `#![feature({$feature})]` to the crate attributes to enable")] pub(crate) struct FeatureDiagnosticHelp { pub(crate) feature: Symbol, } #[derive(Subdiagnostic)] #[suggestion( - session_feature_diagnostic_suggestion, + "add `#![feature({$feature})]` to the crate attributes to enable", applicability = "maybe-incorrect", code = "#![feature({feature})]\n" )] @@ -76,169 +78,181 @@ pub struct FeatureDiagnosticSuggestion { } #[derive(Subdiagnostic)] -#[help(session_cli_feature_diagnostic_help)] +#[help("add `-Zcrate-attr=\"feature({$feature})\"` to the command-line options to enable")] pub(crate) struct CliFeatureDiagnosticHelp { pub(crate) feature: Symbol, } #[derive(Diagnostic)] -#[diag(session_must_be_name_of_associated_function)] +#[diag("must be a name of an associated function")] pub struct MustBeNameOfAssociatedFunction { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(session_not_circumvent_feature)] +#[diag( + "`-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature gates, except when testing error paths in the CTFE engine" +)] pub(crate) struct NotCircumventFeature; #[derive(Diagnostic)] -#[diag(session_linker_plugin_lto_windows_not_supported)] +#[diag( + "linker plugin based LTO is not supported together with `-C prefer-dynamic` when targeting Windows-like targets" +)] pub(crate) struct LinkerPluginToWindowsNotSupported; #[derive(Diagnostic)] -#[diag(session_profile_use_file_does_not_exist)] +#[diag("file `{$path}` passed to `-C profile-use` does not exist")] pub(crate) struct ProfileUseFileDoesNotExist<'a> { pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_profile_sample_use_file_does_not_exist)] +#[diag("file `{$path}` passed to `-C profile-sample-use` does not exist")] pub(crate) struct ProfileSampleUseFileDoesNotExist<'a> { pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_target_requires_unwind_tables)] +#[diag("target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`")] pub(crate) struct TargetRequiresUnwindTables; #[derive(Diagnostic)] -#[diag(session_instrumentation_not_supported)] +#[diag("{$us} instrumentation is not supported for this target")] pub(crate) struct InstrumentationNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_sanitizer_not_supported)] +#[diag("{$us} sanitizer is not supported for this target")] pub(crate) struct SanitizerNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_sanitizers_not_supported)] +#[diag("{$us} sanitizers are not supported for this target")] pub(crate) struct SanitizersNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_cannot_mix_and_match_sanitizers)] +#[diag("`-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}`")] pub(crate) struct CannotMixAndMatchSanitizers { pub(crate) first: String, pub(crate) second: String, } #[derive(Diagnostic)] -#[diag(session_cannot_enable_crt_static_linux)] +#[diag( + "sanitizer is incompatible with statically linked libc, disable it using `-C target-feature=-crt-static`" +)] pub(crate) struct CannotEnableCrtStaticLinux; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_requires_lto)] +#[diag("`-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`")] pub(crate) struct SanitizerCfiRequiresLto; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_requires_single_codegen_unit)] +#[diag("`-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`")] pub(crate) struct SanitizerCfiRequiresSingleCodegenUnit; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_canonical_jump_tables_requires_cfi)] +#[diag("`-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiCanonicalJumpTablesRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_generalize_pointers_requires_cfi)] +#[diag("`-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)] +#[diag("`-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_kcfi_arity_requires_kcfi)] +#[diag("`-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerKcfiArityRequiresKcfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_kcfi_requires_panic_abort)] +#[diag("`-Z sanitizer=kcfi` requires `-C panic=abort`")] pub(crate) struct SanitizerKcfiRequiresPanicAbort; #[derive(Diagnostic)] -#[diag(session_split_lto_unit_requires_lto)] +#[diag("`-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`")] pub(crate) struct SplitLtoUnitRequiresLto; #[derive(Diagnostic)] -#[diag(session_unstable_virtual_function_elimination)] +#[diag("`-Zvirtual-function-elimination` requires `-Clto`")] pub(crate) struct UnstableVirtualFunctionElimination; #[derive(Diagnostic)] -#[diag(session_unsupported_dwarf_version)] -#[help(session_unsupported_dwarf_version_help)] +#[diag("requested DWARF version {$dwarf_version} is not supported")] +#[help("supported DWARF versions are 2, 3, 4 and 5")] pub(crate) struct UnsupportedDwarfVersion { pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] -#[diag(session_embed_source_insufficient_dwarf_version)] +#[diag( + "`-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version}" +)] pub(crate) struct EmbedSourceInsufficientDwarfVersion { pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] -#[diag(session_embed_source_requires_debug_info)] +#[diag("`-Zembed-source=y` requires debug information to be enabled")] pub(crate) struct EmbedSourceRequiresDebugInfo; #[derive(Diagnostic)] -#[diag(session_target_stack_protector_not_supported)] +#[diag( + "`-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored" +)] pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) stack_protector: StackProtector, pub(crate) target_triple: &'a TargetTuple, } #[derive(Diagnostic)] -#[diag(session_target_small_data_threshold_not_supported)] +#[diag( + "`-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored" +)] pub(crate) struct SmallDataThresholdNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } #[derive(Diagnostic)] -#[diag(session_branch_protection_requires_aarch64)] +#[diag("`-Zbranch-protection` is only supported on aarch64")] pub(crate) struct BranchProtectionRequiresAArch64; #[derive(Diagnostic)] -#[diag(session_split_debuginfo_unstable_platform)] +#[diag("`-Csplit-debuginfo={$debuginfo}` is unstable on this platform")] pub(crate) struct SplitDebugInfoUnstablePlatform { pub(crate) debuginfo: SplitDebuginfo, } #[derive(Diagnostic)] -#[diag(session_file_is_not_writeable)] +#[diag("output file {$file} is not writeable -- check its permissions")] pub(crate) struct FileIsNotWriteable<'a> { pub(crate) file: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_file_write_fail)] +#[diag("failed to write `{$path}` due to error `{$err}`")] pub(crate) struct FileWriteFail<'a> { pub(crate) path: &'a std::path::Path, pub(crate) err: String, } #[derive(Diagnostic)] -#[diag(session_crate_name_empty)] +#[diag("crate name must not be empty")] pub(crate) struct CrateNameEmpty { #[primary_span] pub(crate) span: Option, } #[derive(Diagnostic)] -#[diag(session_invalid_character_in_crate_name)] +#[diag("invalid character {$character} in crate name: `{$crate_name}`")] pub(crate) struct InvalidCharacterInCrateName { #[primary_span] pub(crate) span: Option, @@ -247,7 +261,10 @@ pub(crate) struct InvalidCharacterInCrateName { } #[derive(Subdiagnostic)] -#[multipart_suggestion(session_expr_parentheses_needed, applicability = "machine-applicable")] +#[multipart_suggestion( + "parentheses are required to parse this as an expression", + applicability = "machine-applicable" +)] pub struct ExprParenthesesNeeded { #[suggestion_part(code = "(")] left: Span, @@ -262,7 +279,7 @@ impl ExprParenthesesNeeded { } #[derive(Diagnostic)] -#[diag(session_skipping_const_checks)] +#[diag("skipping const checks")] pub(crate) struct SkippingConstChecks { #[subdiagnostic] pub(crate) unleashed_features: Vec, @@ -270,13 +287,13 @@ pub(crate) struct SkippingConstChecks { #[derive(Subdiagnostic)] pub(crate) enum UnleashedFeatureHelp { - #[help(session_unleashed_feature_help_named)] + #[help("skipping check for `{$gate}` feature")] Named { #[primary_span] span: Span, gate: Symbol, }, - #[help(session_unleashed_feature_help_unnamed)] + #[help("skipping check that does not even have a feature gate")] Unnamed { #[primary_span] span: Span, @@ -284,10 +301,10 @@ pub(crate) enum UnleashedFeatureHelp { } #[derive(Diagnostic)] -#[diag(session_invalid_literal_suffix)] +#[diag("suffixes on {$kind} literals are invalid")] struct InvalidLiteralSuffix<'a> { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, // FIXME(#100717) kind: &'a str, @@ -295,8 +312,8 @@ struct InvalidLiteralSuffix<'a> { } #[derive(Diagnostic)] -#[diag(session_invalid_int_literal_width)] -#[help] +#[diag("invalid width `{$width}` for integer literal")] +#[help("valid widths are 8, 16, 32, 64 and 128")] struct InvalidIntLiteralWidth { #[primary_span] span: Span, @@ -304,28 +321,32 @@ struct InvalidIntLiteralWidth { } #[derive(Diagnostic)] -#[diag(session_invalid_num_literal_base_prefix)] -#[note] +#[diag("invalid base prefix for number literal")] +#[note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase")] struct InvalidNumLiteralBasePrefix { #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")] + #[suggestion( + "try making the prefix lowercase", + applicability = "maybe-incorrect", + code = "{fixed}" + )] span: Span, fixed: String, } #[derive(Diagnostic)] -#[diag(session_invalid_num_literal_suffix)] -#[help] +#[diag("invalid suffix `{$suffix}` for number literal")] +#[help("the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)")] struct InvalidNumLiteralSuffix { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, suffix: String, } #[derive(Diagnostic)] -#[diag(session_invalid_float_literal_width)] -#[help] +#[diag("invalid width `{$width}` for float literal")] +#[help("valid widths are 32 and 64")] struct InvalidFloatLiteralWidth { #[primary_span] span: Span, @@ -333,18 +354,18 @@ struct InvalidFloatLiteralWidth { } #[derive(Diagnostic)] -#[diag(session_invalid_float_literal_suffix)] -#[help] +#[diag("invalid suffix `{$suffix}` for float literal")] +#[help("valid suffixes are `f32` and `f64`")] struct InvalidFloatLiteralSuffix { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, suffix: String, } #[derive(Diagnostic)] -#[diag(session_int_literal_too_large)] -#[note] +#[diag("integer literal is too large")] +#[note("value exceeds limit of `{$limit}`")] struct IntLiteralTooLarge { #[primary_span] span: Span, @@ -352,26 +373,26 @@ struct IntLiteralTooLarge { } #[derive(Diagnostic)] -#[diag(session_hexadecimal_float_literal_not_supported)] +#[diag("hexadecimal float literal is not supported")] struct HexadecimalFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } #[derive(Diagnostic)] -#[diag(session_octal_float_literal_not_supported)] +#[diag("octal float literal is not supported")] struct OctalFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } #[derive(Diagnostic)] -#[diag(session_binary_float_literal_not_supported)] +#[diag("binary float literal is not supported")] struct BinaryFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } @@ -457,60 +478,60 @@ pub fn create_lit_error(psess: &ParseSess, err: LitError, lit: token::Lit, span: } #[derive(Diagnostic)] -#[diag(session_incompatible_linker_flavor)] -#[note] +#[diag("linker flavor `{$flavor}` is incompatible with the current target")] +#[note("compatible flavors are: {$compatible_list}")] pub(crate) struct IncompatibleLinkerFlavor { pub(crate) flavor: &'static str, pub(crate) compatible_list: String, } #[derive(Diagnostic)] -#[diag(session_function_return_requires_x86_or_x86_64)] +#[diag("`-Zfunction-return` (except `keep`) is only supported on x86 and x86_64")] pub(crate) struct FunctionReturnRequiresX86OrX8664; #[derive(Diagnostic)] -#[diag(session_function_return_thunk_extern_requires_non_large_code_model)] +#[diag("`-Zfunction-return=thunk-extern` is only supported on non-large code models")] pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel; #[derive(Diagnostic)] -#[diag(session_indirect_branch_cs_prefix_requires_x86_or_x86_64)] +#[diag("`-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64")] pub(crate) struct IndirectBranchCsPrefixRequiresX86OrX8664; #[derive(Diagnostic)] -#[diag(session_unsupported_regparm)] +#[diag("`-Zregparm={$regparm}` is unsupported (valid values 0-3)")] pub(crate) struct UnsupportedRegparm { pub(crate) regparm: u32, } #[derive(Diagnostic)] -#[diag(session_unsupported_regparm_arch)] +#[diag("`-Zregparm=N` is only supported on x86")] pub(crate) struct UnsupportedRegparmArch; #[derive(Diagnostic)] -#[diag(session_unsupported_reg_struct_return_arch)] +#[diag("`-Zreg-struct-return` is only supported on x86")] pub(crate) struct UnsupportedRegStructReturnArch; #[derive(Diagnostic)] -#[diag(session_failed_to_create_profiler)] +#[diag("failed to create profiler: {$err}")] pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, } #[derive(Diagnostic)] -#[diag(session_soft_float_ignored)] -#[note] +#[diag("`-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets")] +#[note("this may become a hard error in a future version of Rust")] pub(crate) struct SoftFloatIgnored; #[derive(Diagnostic)] -#[diag(session_soft_float_deprecated)] -#[note] -#[note(session_soft_float_deprecated_issue)] +#[diag("`-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead")] +#[note("it will be removed or ignored in a future version of Rust")] +#[note("see issue #129893 for more information")] pub(crate) struct SoftFloatDeprecated; #[derive(LintDiagnostic)] -#[diag(session_unexpected_builtin_cfg)] -#[note(session_controlled_by)] -#[note(session_incoherent)] +#[diag("unexpected `--cfg {$cfg}` flag")] +#[note("config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`")] +#[note("manually setting a built-in cfg can and does create incoherent behaviors")] pub(crate) struct UnexpectedBuiltinCfg { pub(crate) cfg: String, pub(crate) cfg_name: Symbol, diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 90108e911044..98731a235d41 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -32,8 +32,6 @@ pub mod output; pub use getopts; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. From d2a0557afb3f7826266e24ab4b539e8efcb5d92f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:35:28 +0000 Subject: [PATCH 582/583] Convert to inline diagnostics in all codegen backends --- Cargo.lock | 1 - compiler/rustc_codegen_cranelift/src/lib.rs | 5 - compiler/rustc_codegen_gcc/messages.ftl | 8 -- compiler/rustc_codegen_gcc/src/errors.rs | 8 +- compiler/rustc_codegen_gcc/src/lib.rs | 7 - compiler/rustc_codegen_llvm/Cargo.toml | 1 - compiler/rustc_codegen_llvm/messages.ftl | 75 ----------- compiler/rustc_codegen_llvm/src/errors.rs | 124 +++++++++++------- compiler/rustc_codegen_llvm/src/lib.rs | 6 - .../rustc_codegen_ssa/src/traits/backend.rs | 4 - compiler/rustc_interface/src/interface.rs | 5 +- compiler/rustc_interface/src/util.rs | 4 - .../codegen-backend/auxiliary/the_backend.rs | 4 - 13 files changed, 79 insertions(+), 173 deletions(-) delete mode 100644 compiler/rustc_codegen_gcc/messages.ftl delete mode 100644 compiler/rustc_codegen_llvm/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 4cbc98beae11..d61826dd96f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3635,7 +3635,6 @@ dependencies = [ "rustc_codegen_ssa", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hashes", "rustc_hir", diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 7361a6af4178..a49dc9be3458 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -125,11 +125,6 @@ pub struct CraneliftCodegenBackend { } impl CodegenBackend for CraneliftCodegenBackend { - fn locale_resource(&self) -> &'static str { - // FIXME(rust-lang/rust#100717) - cranelift codegen backend is not yet translated - "" - } - fn name(&self) -> &'static str { "cranelift" } diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl deleted file mode 100644 index b9b77b7d18c6..000000000000 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ /dev/null @@ -1,8 +0,0 @@ -codegen_gcc_unwinding_inline_asm = - GCC backend does not support unwinding from inline asm - -codegen_gcc_copy_bitcode = failed to copy bitcode to object file: {$err} - -codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err}) - -codegen_gcc_explicit_tail_calls_unsupported = explicit tail calls with the 'become' keyword are not implemented in the GCC backend diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index b252c39c0c05..f5815e723392 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -2,24 +2,24 @@ use rustc_macros::Diagnostic; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(codegen_gcc_unwinding_inline_asm)] +#[diag("GCC backend does not support unwinding from inline asm")] pub(crate) struct UnwindingInlineAsm { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(codegen_gcc_copy_bitcode)] +#[diag("failed to copy bitcode to object file: {$err}")] pub(crate) struct CopyBitcode { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_gcc_lto_bitcode_from_rlib)] +#[diag("failed to get bitcode from object file for LTO ({$gcc_err})")] pub(crate) struct LtoBitcodeFromRlib { pub gcc_err: String, } #[derive(Diagnostic)] -#[diag(codegen_gcc_explicit_tail_calls_unsupported)] +#[diag("explicit tail calls with the 'become' keyword are not implemented in the GCC backend")] pub(crate) struct ExplicitTailCallsUnsupported; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 00bea0222622..cc88fd02435e 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -27,7 +27,6 @@ extern crate rustc_ast; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; -extern crate rustc_fluent_macro; extern crate rustc_fs_util; extern crate rustc_hir; extern crate rustc_index; @@ -105,8 +104,6 @@ use tempfile::TempDir; use crate::back::lto::ModuleBuffer; use crate::gcc_util::{target_cpu, to_gcc_features}; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub struct PrintOnPanic String>(pub F); impl String> Drop for PrintOnPanic { @@ -197,10 +194,6 @@ fn load_libgccjit_if_needed(libgccjit_target_lib_file: &Path) { } impl CodegenBackend for GccCodegenBackend { - fn locale_resource(&self) -> &'static str { - crate::DEFAULT_LOCALE_RESOURCE - } - fn name(&self) -> &'static str { "gcc" } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 9741436aacb7..90c87494c3c5 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -23,7 +23,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl deleted file mode 100644 index 85cb7499cca4..000000000000 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ /dev/null @@ -1,75 +0,0 @@ -codegen_llvm_autodiff_component_missing = autodiff backend not found in the sysroot: {$err} - .note = it will be distributed via rustup in the future - -codegen_llvm_autodiff_component_unavailable = failed to load our autodiff backend: {$err} - -codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable -codegen_llvm_autodiff_without_lto = using the autodiff feature requires setting `lto="fat"` in your Cargo.toml - -codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} - -codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture - -codegen_llvm_from_llvm_diag = {$message} - -codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message} - -codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" -codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} - -codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$err}) - -codegen_llvm_mismatch_data_layout = - data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}` - -codegen_llvm_offload_bundleimages_failed = call to BundleImages failed, `host.out` was not created -codegen_llvm_offload_embed_failed = call to EmbedBufferInModule failed, `host.o` was not created -codegen_llvm_offload_no_abs_path = using the `-Z offload=Host=/absolute/path/to/host.out` flag requires an absolute path -codegen_llvm_offload_no_host_out = using the `-Z offload=Host=/absolute/path/to/host.out` flag must point to a `host.out` file -codegen_llvm_offload_nonexisting = the given path/file to `host.out` does not exist. Did you forget to run the device compilation first? -codegen_llvm_offload_without_enable = using the offload feature requires -Z offload= -codegen_llvm_offload_without_fat_lto = using the offload feature requires -C lto=fat - -codegen_llvm_parse_bitcode = failed to parse bitcode for LTO module -codegen_llvm_parse_bitcode_with_llvm_err = failed to parse bitcode for LTO module: {$llvm_err} - -codegen_llvm_parse_target_machine_config = - failed to parse target machine config to target machine: {$error} - -codegen_llvm_prepare_autodiff = failed to prepare autodiff: src: {$src}, target: {$target}, {$error} -codegen_llvm_prepare_autodiff_with_llvm_err = failed to prepare autodiff: {$llvm_err}, src: {$src}, target: {$target}, {$error} -codegen_llvm_prepare_thin_lto_context = failed to prepare thin LTO context -codegen_llvm_prepare_thin_lto_context_with_llvm_err = failed to prepare thin LTO context: {$llvm_err} - -codegen_llvm_prepare_thin_lto_module = failed to prepare thin LTO module -codegen_llvm_prepare_thin_lto_module_with_llvm_err = failed to prepare thin LTO module: {$llvm_err} - -codegen_llvm_run_passes = failed to run LLVM passes -codegen_llvm_run_passes_with_llvm_err = failed to run LLVM passes: {$llvm_err} - -codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0 = `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later. - -codegen_llvm_sanitizer_memtag_requires_mte = - `-Zsanitizer=memtag` requires `-Ctarget-feature=+mte` - -codegen_llvm_serialize_module = failed to serialize module {$name} -codegen_llvm_serialize_module_with_llvm_err = failed to serialize module {$name}: {$llvm_err} - -codegen_llvm_symbol_already_defined = - symbol `{$symbol_name}` is already defined - -codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple} -codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err} - -codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo - -codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err} - -codegen_llvm_write_ir = failed to write LLVM IR to {$path} -codegen_llvm_write_ir_with_llvm_err = failed to write LLVM IR to {$path}: {$llvm_err} - -codegen_llvm_write_output = could not write output to {$path} -codegen_llvm_write_output_with_llvm_err = could not write output to {$path}: {$llvm_err} - -codegen_llvm_write_thinlto_key = error while writing ThinLTO key data: {$err} -codegen_llvm_write_thinlto_key_with_llvm_err = error while writing ThinLTO key data: {$err}: {$llvm_err} diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index bd42cf556966..23fa05f3d02f 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -2,14 +2,12 @@ use std::ffi::CString; use std::path::Path; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, inline_fluent}; use rustc_macros::Diagnostic; use rustc_span::Span; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] -#[diag(codegen_llvm_symbol_already_defined)] +#[diag("symbol `{$symbol_name}` is already defined")] pub(crate) struct SymbolAlreadyDefined<'a> { #[primary_span] pub span: Span, @@ -17,7 +15,7 @@ pub(crate) struct SymbolAlreadyDefined<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_sanitizer_memtag_requires_mte)] +#[diag("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`")] pub(crate) struct SanitizerMemtagRequiresMte; pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); @@ -27,89 +25,97 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { let diag: Diag<'_, G> = self.0.into_diag(dcx, level); let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); let message = dcx.eagerly_translate_to_string(message.clone(), diag.args.iter()); - Diag::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config) - .with_arg("error", message) + Diag::new( + dcx, + level, + inline_fluent!("failed to parse target machine config to target machine: {$error}"), + ) + .with_arg("error", message) } } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_component_unavailable)] +#[diag("failed to load our autodiff backend: {$err}")] pub(crate) struct AutoDiffComponentUnavailable { pub err: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_component_missing)] -#[note] +#[diag("autodiff backend not found in the sysroot: {$err}")] +#[note("it will be distributed via rustup in the future")] pub(crate) struct AutoDiffComponentMissing { pub err: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_without_lto)] +#[diag("using the autodiff feature requires setting `lto=\"fat\"` in your Cargo.toml")] pub(crate) struct AutoDiffWithoutLto; #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_without_enable)] +#[diag("using the autodiff feature requires -Z autodiff=Enable")] pub(crate) struct AutoDiffWithoutEnable; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_without_enable)] +#[diag("using the offload feature requires -Z offload=")] pub(crate) struct OffloadWithoutEnable; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_without_fat_lto)] +#[diag("using the offload feature requires -C lto=fat")] pub(crate) struct OffloadWithoutFatLTO; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_no_abs_path)] +#[diag("using the `-Z offload=Host=/absolute/path/to/host.out` flag requires an absolute path")] pub(crate) struct OffloadWithoutAbsPath; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_no_host_out)] +#[diag( + "using the `-Z offload=Host=/absolute/path/to/host.out` flag must point to a `host.out` file" +)] pub(crate) struct OffloadWrongFileName; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_nonexisting)] +#[diag( + "the given path/file to `host.out` does not exist. Did you forget to run the device compilation first?" +)] pub(crate) struct OffloadNonexistingPath; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_bundleimages_failed)] +#[diag("call to BundleImages failed, `host.out` was not created")] pub(crate) struct OffloadBundleImagesFailed; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_embed_failed)] +#[diag("call to EmbedBufferInModule failed, `host.o` was not created")] pub(crate) struct OffloadEmbedFailed; #[derive(Diagnostic)] -#[diag(codegen_llvm_lto_bitcode_from_rlib)] +#[diag("failed to get bitcode from object file for LTO ({$err})")] pub(crate) struct LtoBitcodeFromRlib { pub err: String, } #[derive(Diagnostic)] pub enum LlvmError<'a> { - #[diag(codegen_llvm_write_output)] + #[diag("could not write output to {$path}")] WriteOutput { path: &'a Path }, - #[diag(codegen_llvm_target_machine)] + #[diag("could not create LLVM TargetMachine for triple: {$triple}")] CreateTargetMachine { triple: SmallCStr }, - #[diag(codegen_llvm_run_passes)] + #[diag("failed to run LLVM passes")] RunLlvmPasses, - #[diag(codegen_llvm_serialize_module)] + #[diag("failed to serialize module {$name}")] SerializeModule { name: &'a str }, - #[diag(codegen_llvm_write_ir)] + #[diag("failed to write LLVM IR to {$path}")] WriteIr { path: &'a Path }, - #[diag(codegen_llvm_prepare_thin_lto_context)] + #[diag("failed to prepare thin LTO context")] PrepareThinLtoContext, - #[diag(codegen_llvm_load_bitcode)] + #[diag("failed to load bitcode of module \"{$name}\"")] LoadBitcode { name: CString }, - #[diag(codegen_llvm_write_thinlto_key)] + #[diag("error while writing ThinLTO key data: {$err}")] WriteThinLtoKey { err: std::io::Error }, - #[diag(codegen_llvm_prepare_thin_lto_module)] + #[diag("failed to prepare thin LTO module")] PrepareThinLtoModule, - #[diag(codegen_llvm_parse_bitcode)] + #[diag("failed to parse bitcode for LTO module")] ParseBitcode, - #[diag(codegen_llvm_prepare_autodiff)] + #[diag("failed to prepare autodiff: src: {$src}, target: {$target}, {$error}")] PrepareAutoDiff { src: String, target: String, error: String }, } @@ -119,17 +125,31 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { use LlvmError::*; let msg_with_llvm_err = match &self.0 { - WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err, - CreateTargetMachine { .. } => fluent::codegen_llvm_target_machine_with_llvm_err, - RunLlvmPasses => fluent::codegen_llvm_run_passes_with_llvm_err, - SerializeModule { .. } => fluent::codegen_llvm_serialize_module_with_llvm_err, - WriteIr { .. } => fluent::codegen_llvm_write_ir_with_llvm_err, - PrepareThinLtoContext => fluent::codegen_llvm_prepare_thin_lto_context_with_llvm_err, - LoadBitcode { .. } => fluent::codegen_llvm_load_bitcode_with_llvm_err, - WriteThinLtoKey { .. } => fluent::codegen_llvm_write_thinlto_key_with_llvm_err, - PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err, - ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err, - PrepareAutoDiff { .. } => fluent::codegen_llvm_prepare_autodiff_with_llvm_err, + WriteOutput { .. } => inline_fluent!("could not write output to {$path}: {$llvm_err}"), + CreateTargetMachine { .. } => inline_fluent!( + "could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}" + ), + RunLlvmPasses => inline_fluent!("failed to run LLVM passes: {$llvm_err}"), + SerializeModule { .. } => { + inline_fluent!("failed to serialize module {$name}: {$llvm_err}") + } + WriteIr { .. } => inline_fluent!("failed to write LLVM IR to {$path}: {$llvm_err}"), + PrepareThinLtoContext => { + inline_fluent!("failed to prepare thin LTO context: {$llvm_err}") + } + LoadBitcode { .. } => { + inline_fluent!("failed to load bitcode of module \"{$name}\": {$llvm_err}") + } + WriteThinLtoKey { .. } => { + inline_fluent!("error while writing ThinLTO key data: {$err}: {$llvm_err}") + } + PrepareThinLtoModule => { + inline_fluent!("failed to prepare thin LTO module: {$llvm_err}") + } + ParseBitcode => inline_fluent!("failed to parse bitcode for LTO module: {$llvm_err}"), + PrepareAutoDiff { .. } => inline_fluent!( + "failed to prepare autodiff: {$llvm_err}, src: {$src}, target: {$target}, {$error}" + ), }; self.0 .into_diag(dcx, level) @@ -139,7 +159,7 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_from_llvm_optimization_diag)] +#[diag("{$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}")] pub(crate) struct FromLlvmOptimizationDiag<'a> { pub filename: &'a str, pub line: std::ffi::c_uint, @@ -150,32 +170,36 @@ pub(crate) struct FromLlvmOptimizationDiag<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_from_llvm_diag)] +#[diag("{$message}")] pub(crate) struct FromLlvmDiag { pub message: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_write_bytecode)] +#[diag("failed to write bytecode to {$path}: {$err}")] pub(crate) struct WriteBytecode<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_llvm_copy_bitcode)] +#[diag("failed to copy bitcode to object file: {$err}")] pub(crate) struct CopyBitcode { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_llvm_unknown_debuginfo_compression)] +#[diag( + "unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo" +)] pub(crate) struct UnknownCompression { pub algorithm: &'static str, } #[derive(Diagnostic)] -#[diag(codegen_llvm_mismatch_data_layout)] +#[diag( + "data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}`" +)] pub(crate) struct MismatchedDataLayout<'a> { pub rustc_target: &'a str, pub rustc_layout: &'a str, @@ -184,11 +208,11 @@ pub(crate) struct MismatchedDataLayout<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_fixed_x18_invalid_arch)] +#[diag("the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture")] pub(crate) struct FixedX18InvalidArch<'a> { pub arch: &'a str, } #[derive(Diagnostic)] -#[diag(codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0)] +#[diag("`-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.")] pub(crate) struct SanitizerKcfiArityRequiresLLVM2100; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5879132eb9fb..8bcf99cbb316 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -74,8 +74,6 @@ mod typetree; mod va_arg; mod value; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub(crate) use macros::TryFromU32; #[derive(Clone)] @@ -241,10 +239,6 @@ impl LlvmCodegenBackend { } impl CodegenBackend for LlvmCodegenBackend { - fn locale_resource(&self) -> &'static str { - crate::DEFAULT_LOCALE_RESOURCE - } - fn name(&self) -> &'static str { "llvm" } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 625551d17d9d..ebedcc4b756d 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -37,10 +37,6 @@ pub trait BackendTypes { } pub trait CodegenBackend { - /// Locale resources for diagnostic messages - a string the content of the Fluent resource. - /// Called before `init` so that all other functions are able to emit translatable diagnostics. - fn locale_resource(&self) -> &'static str; - fn name(&self) -> &'static str; fn init(&self, _sess: &Session) {} diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index f76e8d4632fc..9165842dbdd0 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -463,9 +463,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")), }; - let mut locale_resources = config.locale_resources; - locale_resources.push(codegen_backend.locale_resource()); - let mut sess = rustc_session::build_session( config.opts, CompilerIO { @@ -476,7 +473,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se }, bundle, config.registry, - locale_resources, + config.locale_resources, config.lint_caps, target, util::rustc_version_str().unwrap_or("unknown"), diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 25f59f0e89df..458e7ad32143 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -361,10 +361,6 @@ pub struct DummyCodegenBackend { } impl CodegenBackend for DummyCodegenBackend { - fn locale_resource(&self) -> &'static str { - "" - } - fn name(&self) -> &'static str { "dummy" } diff --git a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 48f328f4fad3..c15c2dea4c19 100644 --- a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -29,10 +29,6 @@ use rustc_session::config::OutputFilenames; struct TheBackend; impl CodegenBackend for TheBackend { - fn locale_resource(&self) -> &'static str { - "" - } - fn name(&self) -> &'static str { "the-backend" } From 8c404a764919b9155c0ac68978185d7db80cc351 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 5 Feb 2026 04:37:12 +0000 Subject: [PATCH 583/583] Prepare for merging from rust-lang/rust This updates the rust-version file to db3e99bbab28c6ca778b13222becdea54533d908. --- library/stdarch/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch/rust-version b/library/stdarch/rust-version index ccc0b55d4dc5..aa3876b14a22 100644 --- a/library/stdarch/rust-version +++ b/library/stdarch/rust-version @@ -1 +1 @@ -873d4682c7d285540b8f28bfe637006cef8918a6 +db3e99bbab28c6ca778b13222becdea54533d908

(&mut reader, buf).map_err(|err| ServerError { + let res = Response::read(&mut reader, buf).map_err(|err| ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)), })?; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index 1b6590693354..bb0dde472860 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -8,7 +8,7 @@ use paths::Utf8PathBuf; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; -use crate::{Codec, ProcMacroKind}; +use crate::{ProcMacroKind, transport::json}; /// Represents requests sent from the client to the proc-macro-srv. #[derive(Debug, Serialize, Deserialize)] @@ -155,20 +155,40 @@ impl ExpnGlobals { } pub trait Message: serde::Serialize + DeserializeOwned { - fn read(inp: &mut dyn BufRead, buf: &mut C::Buf) -> io::Result> { - Ok(match C::read(inp, buf)? { + type Buf; + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result>; + fn write(self, out: &mut dyn Write) -> io::Result<()>; +} + +impl Message for Request { + type Buf = String; + + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> { + Ok(match json::read(inp, buf)? { None => None, - Some(buf) => Some(C::decode(buf)?), + Some(buf) => Some(json::decode(buf)?), }) } - fn write(self, out: &mut dyn Write) -> io::Result<()> { - let value = C::encode(&self)?; - C::write(out, &value) + fn write(self, out: &mut dyn Write) -> io::Result<()> { + let value = json::encode(&self)?; + json::write(out, &value) } } -impl Message for Request {} -impl Message for Response {} +impl Message for Response { + type Buf = String; + + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> { + Ok(match json::read(inp, buf)? { + None => None, + Some(buf) => Some(json::decode(buf)?), + }) + } + fn write(self, out: &mut dyn Write) -> io::Result<()> { + let value = json::encode(&self)?; + json::write(out, &value) + } +} #[cfg(test)] mod tests { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index 3acd0b292a31..e4b121b033d3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -27,7 +27,6 @@ use semver::Version; use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use std::{fmt, io, sync::Arc, time::SystemTime}; -pub use crate::transport::codec::Codec; use crate::{ bidirectional_protocol::SubCallback, pool::ProcMacroServerPool, process::ProcMacroServerProcess, }; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 2f5bef69abd5..9f80880965b8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -17,7 +17,7 @@ use span::Span; use stdx::JodChild; use crate::{ - Codec, ProcMacro, ProcMacroKind, ProtocolFormat, ServerError, + ProcMacro, ProcMacroKind, ProtocolFormat, ServerError, bidirectional_protocol::{self, SubCallback, msg::BidirectionalMessage, reject_subrequests}, legacy_protocol::{self, SpanMode}, version, @@ -305,17 +305,17 @@ impl ProcMacroServerProcess { result } - pub(crate) fn send_task( + pub(crate) fn send_task_legacy( &self, send: impl FnOnce( &mut dyn Write, &mut dyn BufRead, Request, - &mut C::Buf, + &mut String, ) -> Result, ServerError>, req: Request, ) -> Result { - self.with_locked_io::(|writer, reader, buf| { + self.with_locked_io(String::new(), |writer, reader, buf| { send(writer, reader, req, buf).and_then(|res| { res.ok_or_else(|| { let message = "proc-macro server did not respond with data".to_owned(); @@ -331,13 +331,12 @@ impl ProcMacroServerProcess { }) } - pub(crate) fn with_locked_io( + fn with_locked_io( &self, - f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut C::Buf) -> Result, + mut buf: B, + f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut B) -> Result, ) -> Result { let state = &mut *self.state.lock().unwrap(); - let mut buf = C::Buf::default(); - f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| { if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { match state.process.exit_err() { @@ -352,13 +351,13 @@ impl ProcMacroServerProcess { }) } - pub(crate) fn run_bidirectional( + pub(crate) fn run_bidirectional( &self, initial: BidirectionalMessage, callback: SubCallback<'_>, ) -> Result { - self.with_locked_io::(|writer, reader, buf| { - bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback) + self.with_locked_io(Vec::new(), |writer, reader, buf| { + bidirectional_protocol::run_conversation(writer, reader, buf, initial, callback) }) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs index b7a1d8f7322c..e31d6a589d94 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs @@ -1,3 +1,3 @@ //! Contains construct for transport of messages. -pub mod codec; -pub mod framing; +pub mod json; +pub mod postcard; From 0bab96e98f7799b97c16f4738b2b68d6e5affb11 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 30 Jan 2026 00:12:14 +0530 Subject: [PATCH 367/583] adapt proc-macro-srv-cli and test --- .../proc-macro-srv-cli/src/main_loop.rs | 72 +++++++++---------- .../proc-macro-srv-cli/tests/common/utils.rs | 11 ++- 2 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 70e1e091c197..758629fd1fd6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,9 +1,6 @@ //! The main loop of the proc-macro server. use proc_macro_api::{ - Codec, ProtocolFormat, - bidirectional_protocol::msg as bidirectional, - legacy_protocol::msg as legacy, - transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, + ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, }; use std::{ @@ -40,14 +37,12 @@ pub fn run( format: ProtocolFormat, ) -> io::Result<()> { match format { - ProtocolFormat::JsonLegacy => run_old::(stdin, stdout), - ProtocolFormat::BidirectionalPostcardPrototype => { - run_new::(stdin, stdout) - } + ProtocolFormat::JsonLegacy => run_old(stdin, stdout), + ProtocolFormat::BidirectionalPostcardPrototype => run_new(stdin, stdout), } } -fn run_new( +fn run_new( stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), ) -> io::Result<()> { @@ -61,7 +56,7 @@ fn run_new( } } - let mut buf = C::Buf::default(); + let mut buf = Vec::default(); let env_snapshot = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env_snapshot); @@ -69,7 +64,7 @@ fn run_new( let mut span_mode = legacy::SpanMode::Id; 'outer: loop { - let req_opt = bidirectional::BidirectionalMessage::read::(stdin, &mut buf)?; + let req_opt = bidirectional::BidirectionalMessage::read(stdin, &mut buf)?; let Some(req) = req_opt else { break 'outer; }; @@ -84,11 +79,11 @@ fn run_new( .collect() }); - send_response::(stdout, bidirectional::Response::ListMacros(res))?; + send_response(stdout, bidirectional::Response::ListMacros(res))?; } bidirectional::Request::ApiVersionCheck {} => { - send_response::( + send_response( stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), )?; @@ -96,10 +91,10 @@ fn run_new( bidirectional::Request::SetConfig(config) => { span_mode = config.span_mode; - send_response::(stdout, bidirectional::Response::SetConfig(config))?; + send_response(stdout, bidirectional::Response::SetConfig(config))?; } bidirectional::Request::ExpandMacro(task) => { - handle_expand::(&srv, stdin, stdout, &mut buf, span_mode, *task)?; + handle_expand(&srv, stdin, stdout, &mut buf, span_mode, *task)?; } }, _ => continue, @@ -109,21 +104,21 @@ fn run_new( Ok(()) } -fn handle_expand( +fn handle_expand( srv: &proc_macro_srv::ProcMacroSrv<'_>, stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), - buf: &mut C::Buf, + buf: &mut Vec, span_mode: legacy::SpanMode, task: bidirectional::ExpandMacro, ) -> io::Result<()> { match span_mode { - legacy::SpanMode::Id => handle_expand_id::(srv, stdout, task), - legacy::SpanMode::RustAnalyzer => handle_expand_ra::(srv, stdin, stdout, buf, task), + legacy::SpanMode::Id => handle_expand_id(srv, stdout, task), + legacy::SpanMode::RustAnalyzer => handle_expand_ra(srv, stdin, stdout, buf, task), } } -fn handle_expand_id( +fn handle_expand_id( srv: &proc_macro_srv::ProcMacroSrv<'_>, stdout: &mut dyn Write, task: bidirectional::ExpandMacro, @@ -164,34 +159,34 @@ fn handle_expand_id( }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(stdout, bidirectional::Response::ExpandMacro(res)) + send_response(stdout, bidirectional::Response::ExpandMacro(res)) } -struct ProcMacroClientHandle<'a, C: Codec> { +struct ProcMacroClientHandle<'a> { stdin: &'a mut (dyn BufRead + Send + Sync), stdout: &'a mut (dyn Write + Send + Sync), - buf: &'a mut C::Buf, + buf: &'a mut Vec, } -impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { +impl<'a> ProcMacroClientHandle<'a> { fn roundtrip( &mut self, req: bidirectional::SubRequest, ) -> Option { let msg = bidirectional::BidirectionalMessage::SubRequest(req); - if msg.write::(&mut *self.stdout).is_err() { + if msg.write(&mut *self.stdout).is_err() { return None; } - match bidirectional::BidirectionalMessage::read::(&mut *self.stdin, self.buf) { + match bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) { Ok(Some(msg)) => Some(msg), _ => None, } } } -impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { +impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Some(bidirectional::BidirectionalMessage::SubResponse( @@ -260,11 +255,11 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } } -fn handle_expand_ra( +fn handle_expand_ra( srv: &proc_macro_srv::ProcMacroSrv<'_>, stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), - buf: &mut C::Buf, + buf: &mut Vec, task: bidirectional::ExpandMacro, ) -> io::Result<()> { let bidirectional::ExpandMacro { @@ -309,7 +304,7 @@ fn handle_expand_ra( def_site, call_site, mixed_site, - Some(&mut ProcMacroClientHandle:: { stdin, stdout, buf }), + Some(&mut ProcMacroClientHandle { stdin, stdout, buf }), ) .map(|it| { ( @@ -325,10 +320,10 @@ fn handle_expand_ra( .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response(stdout, bidirectional::Response::ExpandMacroExtended(res)) } -fn run_old( +fn run_old( stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), ) -> io::Result<()> { @@ -342,9 +337,9 @@ fn run_old( } } - let mut buf = C::Buf::default(); - let mut read_request = || legacy::Request::read::(stdin, &mut buf); - let mut write_response = |msg: legacy::Response| msg.write::(stdout); + let mut buf = String::default(); + let mut read_request = || legacy::Request::read(stdin, &mut buf); + let mut write_response = |msg: legacy::Response| msg.write(stdout); let env = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env); @@ -473,10 +468,7 @@ fn run_old( Ok(()) } -fn send_response( - stdout: &mut dyn Write, - resp: bidirectional::Response, -) -> io::Result<()> { +fn send_response(stdout: &mut dyn Write, resp: bidirectional::Response) -> io::Result<()> { let resp = bidirectional::BidirectionalMessage::Response(resp); - resp.write::(stdout) + resp.write(stdout) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs index 85c394734b33..3049e9800405 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -12,7 +12,6 @@ use proc_macro_api::{ BidirectionalMessage, Request as BiRequest, Response as BiResponse, SubRequest, SubResponse, }, legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap}, - transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, }; use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange}; use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder}; @@ -210,12 +209,12 @@ impl TestProtocol for JsonLegacy { type Response = Response; fn request(&self, writer: &mut dyn Write, req: Request) { - req.write::(writer).expect("failed to write request"); + req.write(writer).expect("failed to write request"); } fn receive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response { let mut buf = String::new(); - Response::read::(reader, &mut buf) + Response::read(reader, &mut buf) .expect("failed to read response") .expect("no response received") } @@ -238,14 +237,14 @@ where fn request(&self, writer: &mut dyn Write, req: BiRequest) { let msg = BidirectionalMessage::Request(req); - msg.write::(writer).expect("failed to write request"); + msg.write(writer).expect("failed to write request"); } fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse { let mut buf = Vec::new(); loop { - let msg = BidirectionalMessage::read::(reader, &mut buf) + let msg = BidirectionalMessage::read(reader, &mut buf) .expect("failed to read message") .expect("no message received"); @@ -254,7 +253,7 @@ where BidirectionalMessage::SubRequest(sr) => { let reply = (self.callback)(sr).expect("subrequest callback failed"); let msg = BidirectionalMessage::SubResponse(reply); - msg.write::(writer).expect("failed to write subresponse"); + msg.write(writer).expect("failed to write subresponse"); } other => panic!("unexpected message: {other:?}"), } From c943cd9b8cd07b06d8be37e9c309fb3c96f6d795 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 20:43:03 +0200 Subject: [PATCH 368/583] fix redirects --- src/doc/rustc-dev-guide/book.toml | 42 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml index 18ca2c85a617..5712a364f760 100644 --- a/src/doc/rustc-dev-guide/book.toml +++ b/src/doc/rustc-dev-guide/book.toml @@ -58,19 +58,19 @@ warning-policy = "error" [output.html.redirect] "/borrow_check.html" = "borrow-check.html" -"/borrow_check/drop_check.html" = "borrow-check/drop-check.html" -"/borrow_check/moves_and_initialization.html" = "borrow-check/moves-and-initialization.html" -"/borrow_check/moves_and_initialization/move_paths.html" = "borrow-check/moves-and-initialization/move-paths.html" -"/borrow_check/opaque-types-region-inference-restrictions.html" = "borrow-check/opaque-types-region-inference-restrictions.html" -"/borrow_check/region_inference.html" = "borrow-check/region-inference.html" -"/borrow_check/region_inference/closure_constraints.html" = "borrow-check/region-inference/closure-constraints.html" -"/borrow_check/region_inference/constraint_propagation.html" = "borrow-check/region-inference/constraint-propagation.html" -"/borrow_check/region_inference/error_reporting.html" = "borrow-check/region-inference/error-reporting.html" -"/borrow_check/region_inference/lifetime_parameters.html" = "borrow-check/region-inference/lifetime-parameters.html" -"/borrow_check/region_inference/member_constraints.html" = "borrow-check/region-inference/member-constraints.html" -"/borrow_check/region_inference/placeholders_and_universes.html" = "borrow-check/region-inference/placeholders-and-universes.html" -"/borrow_check/two_phase_borrows.html" = "borrow-check/two-phase-borrows.html" -"/borrow_check/type_check.html" = "borrow-check/type-check.html" +"/borrow_check/drop_check.html" = "/borrow-check/drop-check.html" +"/borrow_check/moves_and_initialization.html" = "/borrow-check/moves-and-initialization.html" +"/borrow_check/moves_and_initialization/move_paths.html" = "/borrow-check/moves-and-initialization/move-paths.html" +"/borrow_check/opaque-types-region-inference-restrictions.html" = "/borrow-check/opaque-types-region-inference-restrictions.html" +"/borrow_check/region_inference.html" = "/borrow-check/region-inference.html" +"/borrow_check/region_inference/closure_constraints.html" = "/borrow-check/region-inference/closure-constraints.html" +"/borrow_check/region_inference/constraint_propagation.html" = "/borrow-check/region-inference/constraint-propagation.html" +"/borrow_check/region_inference/error_reporting.html" = "/borrow-check/region-inference/error-reporting.html" +"/borrow_check/region_inference/lifetime_parameters.html" = "/borrow-check/region-inference/lifetime-parameters.html" +"/borrow_check/region_inference/member_constraints.html" = "/borrow-check/region-inference/member-constraints.html" +"/borrow_check/region_inference/placeholders_and_universes.html" = "/borrow-check/region-inference/placeholders-and-universes.html" +"/borrow_check/two_phase_borrows.html" = "/borrow-check/two-phase-borrows.html" +"/borrow_check/type_check.html" = "/borrow-check/type-check.html" "/compiletest.html" = "tests/compiletest.html" "/diagnostics/diagnostic-codes.html" = "error-codes.html" "/diagnostics/sessiondiagnostic.html" = "diagnostic-structs.html" @@ -78,18 +78,18 @@ warning-policy = "error" "/generic_parameters_summary.html" = "generic-parameters-summary.html" "/implementing_new_features.html" = "implementing-new-features.html" "/miri.html" = "const-eval/interpret.html" -"/profiling/with_perf.html" = "profiling/with-perf.html" -"/profiling/with_rustc_perf.html" = "profiling/with-rustc-perf.html" -"/profiling/wpa_profiling.html" = "profiling/wpa-profiling.html" +"/profiling/with_perf.html" = "with-perf.html" +"/profiling/with_rustc_perf.html" = "with-rustc-perf.html" +"/profiling/wpa_profiling.html" = "wpa-profiling.html" "/stabilization_guide.html" = "stabilization-guide.html" "/stabilization_report_template.html" = "stabilization-report-template.html" "/tests/fuchsia.html" = "ecosystem-test-jobs/fuchsia.html" "/tests/headers.html" = "directives.html" "/tests/integration.html" = "ecosystem.html" "/tests/rust-for-linux.html" = "ecosystem-test-jobs/rust-for-linux.html" -"/ty_module/binders.html" = "ty-module/binders.html" -"/ty_module/early_binder.html" = "ty-module/early-binder.html" -"/ty_module/generic_arguments.html" = "ty-module/generic-arguments.html" -"/ty_module/instantiating_binders.html" = "ty-module/instantiating-binders.html" -"/ty_module/param_ty_const_regions.html" = "ty-module/param-ty-const-regions.html" +"/ty_module/binders.html" = "/ty-module/binders.html" +"/ty_module/early_binder.html" = "/ty-module/early-binder.html" +"/ty_module/generic_arguments.html" = "/ty-module/generic-arguments.html" +"/ty_module/instantiating_binders.html" = "/ty-module/instantiating-binders.html" +"/ty_module/param_ty_const_regions.html" = "/ty-module/param-ty-const-regions.html" "/typing_parameter_envs.html" = "typing-parameter-envs.html" From f0bb4e649ff171d2dae737562503b801e62aa0a6 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 29 Jan 2026 18:52:52 +0000 Subject: [PATCH 369/583] Prepare for merging from rust-lang/rust This updates the rust-version file to 370143facfb348ad3b29749c0393402d76b280c3. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index c8a3b401a6e1..795271ee0ef0 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -0462e8f7e51f20692b02d68efee68bb28a6f4457 +370143facfb348ad3b29749c0393402d76b280c3 From b128539624b5ebb98e8eaa6a217ca199821abe36 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 20:57:08 +0200 Subject: [PATCH 370/583] sembr src/tracing.md --- src/doc/rustc-dev-guide/src/tracing.md | 81 ++++++++++++++------------ 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 1005ad2c2346..33f9387355ac 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -1,14 +1,15 @@ # Using tracing to debug the compiler The compiler has a lot of [`debug!`] (or `trace!`) calls, which print out logging information -at many points. These are very useful to at least narrow down the location of +at many points. +These are very useful to at least narrow down the location of a bug if not to find it entirely, or just to orient yourself as to why the compiler is doing a particular thing. [`debug!`]: https://docs.rs/tracing/0.1/tracing/macro.debug.html -To see the logs, you need to set the `RUSTC_LOG` environment variable to your -log filter. The full syntax of the log filters can be found in the [rustdoc +To see the logs, you need to set the `RUSTC_LOG` environment variable to your log filter. +The full syntax of the log filters can be found in the [rustdoc of `tracing-subscriber`](https://docs.rs/tracing-subscriber/0.2.24/tracing_subscriber/filter/struct.EnvFilter.html#directives). ## Function level filters @@ -47,69 +48,74 @@ RUSTC_LOG=rustc_borrowck[do_mir_borrowck] ### I don't want all calls If you are compiling libcore, you likely don't want *all* borrowck dumps, but only one -for a specific function. You can filter function calls by their arguments by regexing them. +for a specific function. +You can filter function calls by their arguments by regexing them. ``` RUSTC_LOG=[do_mir_borrowck{id=\.\*from_utf8_unchecked\.\*}] ``` -will only give you the logs of borrowchecking `from_utf8_unchecked`. Note that you will -still get a short message per ignored `do_mir_borrowck`, but none of the things inside those -calls. This helps you in looking through the calls that are happening and helps you adjust +will only give you the logs of borrowchecking `from_utf8_unchecked`. +Note that you will +still get a short message per ignored `do_mir_borrowck`, but none of the things inside those calls. +This helps you in looking through the calls that are happening and helps you adjust your regex if you mistyped it. ## Query level filters Every [query](query.md) is automatically tagged with a logging span so that -you can display all log messages during the execution of the query. For -example, if you want to log everything during type checking: +you can display all log messages during the execution of the query. +For example, if you want to log everything during type checking: ``` RUSTC_LOG=[typeck] ``` The query arguments are included as a tracing field which means that you can -filter on the debug display of the arguments. For example, the `typeck` query -has an argument `key: LocalDefId` of what is being checked. You can use a -regex to match on that `LocalDefId` to log type checking for a specific +filter on the debug display of the arguments. +For example, the `typeck` query has an argument `key: LocalDefId` of what is being checked. +You can use a regex to match on that `LocalDefId` to log type checking for a specific function: ``` RUSTC_LOG=[typeck{key=.*name_of_item.*}] ``` -Different queries have different arguments. You can find a list of queries and -their arguments in +Different queries have different arguments. +You can find a list of queries and their arguments in [`rustc_middle/src/query/mod.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_middle/src/query/mod.rs#L18). ## Broad module level filters You can also use filters similar to the `log` crate's filters, which will enable -everything within a specific module. This is often too verbose and too unstructured, +everything within a specific module. +This is often too verbose and too unstructured, so it is recommended to use function level filters. Your log filter can be just `debug` to get all `debug!` output and higher (e.g., it will also include `info!`), or `path::to::module` to get *all* output (which will include `trace!`) from a particular module, or -`path::to::module=debug` to get `debug!` output and higher from a particular -module. +`path::to::module=debug` to get `debug!` output and higher from a particular module. For example, to get the `debug!` output and higher for a specific module, you can run the compiler with `RUSTC_LOG=path::to::module=debug rustc my-file.rs`. All `debug!` output will then appear in standard error. -Note that you can use a partial path and the filter will still work. For -example, if you want to see `info!` output from only +Note that you can use a partial path and the filter will still work. +For example, if you want to see `info!` output from only `rustdoc::passes::collect_intra_doc_links`, you could use `RUSTDOC_LOG=rustdoc::passes::collect_intra_doc_links=info` *or* you could use `RUSTDOC_LOG=rustdoc::passes::collect_intra=info`. -If you are developing rustdoc, use `RUSTDOC_LOG` instead. If you are developing -Miri, use `MIRI_LOG` instead. You get the idea :) +If you are developing rustdoc, use `RUSTDOC_LOG` instead. +If you are developing Miri, use `MIRI_LOG` instead. +You get the idea :) See the [`tracing`] crate's docs, and specifically the docs for [`debug!`] to -see the full syntax you can use. (Note: unlike the compiler, the [`tracing`] -crate and its examples use the `RUSTC_LOG` environment variable. rustc, rustdoc, +see the full syntax you can use. +(Note: unlike the compiler, the [`tracing`] +crate and its examples use the `RUSTC_LOG` environment variable. +rustc, rustdoc, and other tools set custom environment variables.) **Note that unless you use a very strict filter, the logger will emit a lot of @@ -157,18 +163,20 @@ $ RUSTDOC_LOG=rustdoc=debug rustdoc +stage1 my-file.rs ## Log colors By default, rustc (and other tools, like rustdoc and Miri) will be smart about -when to use ANSI colors in the log output. If they are outputting to a terminal, +when to use ANSI colors in the log output. +If they are outputting to a terminal, they will use colors, and if they are outputting to a file or being piped -somewhere else, they will not. However, it's hard to read log output in your +somewhere else, they will not. +However, it's hard to read log output in your terminal unless you have a very strict filter, so you may want to pipe the -output to a pager like `less`. But then there won't be any colors, which makes -it hard to pick out what you're looking for! +output to a pager like `less`. +But then there won't be any colors, which makes it hard to pick out what you're looking for! You can override whether to have colors in log output with the `RUSTC_LOG_COLOR` environment variable (or `RUSTDOC_LOG_COLOR` for rustdoc, or `MIRI_LOG_COLOR` for Miri, etc.). There are three options: `auto` (the default), `always`, and -`never`. So, if you want to enable colors when piping to `less`, use something -similar to this command: +`never`. +So, if you want to enable colors when piping to `less`, use something similar to this command: ```bash # The `-R` switch tells less to print ANSI colors without escaping them. @@ -176,8 +184,8 @@ $ RUSTC_LOG=debug RUSTC_LOG_COLOR=always rustc +stage1 ... | less -R ``` Note that `MIRI_LOG_COLOR` will only color logs that come from Miri, not logs -from rustc functions that Miri calls. Use `RUSTC_LOG_COLOR` to color logs from -rustc. +from rustc functions that Miri calls. +Use `RUSTC_LOG_COLOR` to color logs from rustc. ## How to keep or remove `debug!` and `trace!` calls from the resulting binary @@ -186,8 +194,7 @@ calls to `debug!` and `trace!` are only included in the program if `rust.debug-logging=true` is turned on in bootstrap.toml (it is turned off by default), so if you don't see `DEBUG` logs, especially if you run the compiler with `RUSTC_LOG=rustc rustc some.rs` and only see -`INFO` logs, make sure that `debug-logging=true` is turned on in your -bootstrap.toml. +`INFO` logs, make sure that `debug-logging=true` is turned on in your bootstrap.toml. ## Logging etiquette and conventions @@ -196,8 +203,8 @@ about the performance of adding "unnecessary" calls to `debug!` and leaving them commit - they won't slow down the performance of what we ship. That said, there can also be excessive tracing calls, especially -when they are redundant with other calls nearby or in functions called from -here. There is no perfect balance to hit here, and is left to the reviewer's +when they are redundant with other calls nearby or in functions called from here. +There is no perfect balance to hit here, and is left to the reviewer's discretion to decide whether to let you leave `debug!` statements in or whether to ask you to remove them before merging. @@ -219,8 +226,8 @@ debug!(x = ?random_operation(tcx)); ``` Then if someone runs a debug `rustc` with `RUSTC_LOG=rustc::foo`, then -`random_operation()` will run. `RUSTC_LOG` filters that do not enable this -debug statement will not execute `random_operation`. +`random_operation()` will run. +`RUSTC_LOG` filters that do not enable this debug statement will not execute `random_operation`. This means that you should not put anything too expensive or likely to crash there - that would annoy anyone who wants to use logging for that module. From 481d49de092f245ee2186f8eccdab5ed8b461c47 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 21:08:19 +0200 Subject: [PATCH 371/583] missing word --- src/doc/rustc-dev-guide/src/tracing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 33f9387355ac..75bce30d3af1 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -204,7 +204,7 @@ commit - they won't slow down the performance of what we ship. That said, there can also be excessive tracing calls, especially when they are redundant with other calls nearby or in functions called from here. -There is no perfect balance to hit here, and is left to the reviewer's +There is no perfect balance to hit here, and it is left to the reviewer's discretion to decide whether to let you leave `debug!` statements in or whether to ask you to remove them before merging. From c4718ea1d92ddabc90fc2aac2d7d556a7fcef815 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 21:08:37 +0200 Subject: [PATCH 372/583] missing pause --- src/doc/rustc-dev-guide/src/tracing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 75bce30d3af1..28c0bcc737ca 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -205,7 +205,7 @@ commit - they won't slow down the performance of what we ship. That said, there can also be excessive tracing calls, especially when they are redundant with other calls nearby or in functions called from here. There is no perfect balance to hit here, and it is left to the reviewer's -discretion to decide whether to let you leave `debug!` statements in or whether to ask +discretion to decide whether to let you leave `debug!` statements in, or whether to ask you to remove them before merging. It may be preferable to use `trace!` over `debug!` for very noisy logs. From 4264da6869bfbd8a314990dcf567389ab0e416af Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 29 Jan 2026 01:27:22 -0800 Subject: [PATCH 373/583] Add `shift_{left,right}` on slices cc tracking issue 151772 --- library/core/src/slice/mod.rs | 213 +++++++++++++++++++++++++++++++ library/coretests/tests/lib.rs | 1 + library/coretests/tests/slice.rs | 38 ++++++ 3 files changed, 252 insertions(+) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 3e1eeba4e92e..e449ceadf19d 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3939,6 +3939,219 @@ impl [T] { } } + /// Moves the elements of this slice `N` places to the left, returning the ones + /// that "fall off" the front, and putting `inserted` at the end. + /// + /// Equivalently, you can think of concatenating `self` and `inserted` into one + /// long sequence, then returning the left-most `N` items and the rest into `self`: + /// + /// ```text + /// self (before) inserted + /// vvvvvvvvvvvvvvv vvv + /// [1, 2, 3, 4, 5] [9] + /// ↙ ↙ ↙ ↙ ↙ ↙ + /// [1] [2, 3, 4, 5, 9] + /// ^^^ ^^^^^^^^^^^^^^^ + /// returned self (after) + /// ``` + /// + /// See also [`Self::shift_right`] and compare [`Self::rotate_left`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_shift)] + /// + /// // Same as the diagram above + /// let mut a = [1, 2, 3, 4, 5]; + /// let inserted = [9]; + /// let returned = a.shift_left(inserted); + /// assert_eq!(returned, [1]); + /// assert_eq!(a, [2, 3, 4, 5, 9]); + /// + /// // You can shift multiple items at a time + /// let mut a = *b"Hello world"; + /// assert_eq!(a.shift_left(*b" peace"), *b"Hello "); + /// assert_eq!(a, *b"world peace"); + /// + /// // The name comes from this operation's similarity to bitshifts + /// let mut a: u8 = 0b10010110; + /// a <<= 3; + /// assert_eq!(a, 0b10110000_u8); + /// let mut a: [_; 8] = [1, 0, 0, 1, 0, 1, 1, 0]; + /// a.shift_left([0; 3]); + /// assert_eq!(a, [1, 0, 1, 1, 0, 0, 0, 0]); + /// + /// // Remember you can sub-slice to affect less that the whole slice. + /// // For example, this is similar to `.remove(1)` + `.insert(4, 'Z')` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// assert_eq!(a[1..=4].shift_left(['Z']), ['b']); + /// assert_eq!(a, ['a', 'c', 'd', 'e', 'Z', 'f']); + /// + /// // If the size matches it's equivalent to `mem::replace` + /// let mut a = [1, 2, 3]; + /// assert_eq!(a.shift_left([7, 8, 9]), [1, 2, 3]); + /// assert_eq!(a, [7, 8, 9]); + /// + /// // Some of the "inserted" elements end up returned if the slice is too short + /// let mut a = []; + /// assert_eq!(a.shift_left([1, 2, 3]), [1, 2, 3]); + /// let mut a = [9]; + /// assert_eq!(a.shift_left([1, 2, 3]), [9, 1, 2]); + /// assert_eq!(a, [3]); + /// ``` + #[unstable(feature = "slice_shift", issue = "151772")] + pub const fn shift_left(&mut self, inserted: [T; N]) -> [T; N] { + if let Some(shift) = self.len().checked_sub(N) { + // SAFETY: Having just checked that the inserted/returned arrays are + // shorter than (or the same length as) the slice: + // 1. The read for the items to return is in-bounds + // 2. We can `memmove` the slice over to cover the items we're returning + // to ensure those aren't double-dropped + // 3. Then we write (in-bounds for the same reason as the read) the + // inserted items atop the items of the slice that we just duplicated + // + // And none of this can panic, so there's no risk of intermediate unwinds. + unsafe { + let ptr = self.as_mut_ptr(); + let returned = ptr.cast_array::().read(); + ptr.copy_from(ptr.add(N), shift); + ptr.add(shift).cast_array::().write(inserted); + returned + } + } else { + // SAFETY: Having checked that the slice is strictly shorter than the + // inserted/returned arrays, it means we'll be copying the whole slice + // into the returned array, but that's not enough on its own. We also + // need to copy some of the inserted array into the returned array, + // with the rest going into the slice. Because `&mut` is exclusive + // and we own both `inserted` and `returned`, they're all disjoint + // allocations from each other as we can use `nonoverlapping` copies. + // + // We avoid double-frees by `ManuallyDrop`ing the inserted items, + // since we always copy them to other locations that will drop them + // instead. Plus nothing in here can panic -- it's just memcpy three + // times -- so there's no intermediate unwinds to worry about. + unsafe { + let len = self.len(); + let slice = self.as_mut_ptr(); + let inserted = mem::ManuallyDrop::new(inserted); + let inserted = (&raw const inserted).cast::(); + + let mut returned = MaybeUninit::<[T; N]>::uninit(); + let ptr = returned.as_mut_ptr().cast::(); + ptr.copy_from_nonoverlapping(slice, len); + ptr.add(len).copy_from_nonoverlapping(inserted, N - len); + slice.copy_from_nonoverlapping(inserted.add(N - len), len); + returned.assume_init() + } + } + } + + /// Moves the elements of this slice `N` places to the right, returning the ones + /// that "fall off" the back, and putting `inserted` at the beginning. + /// + /// Equivalently, you can think of concatenating `inserted` and `self` into one + /// long sequence, then returning the right-most `N` items and the rest into `self`: + /// + /// ```text + /// inserted self (before) + /// vvv vvvvvvvvvvvvvvv + /// [0] [5, 6, 7, 8, 9] + /// ↘ ↘ ↘ ↘ ↘ ↘ + /// [0, 5, 6, 7, 8] [9] + /// ^^^^^^^^^^^^^^^ ^^^ + /// self (after) returned + /// ``` + /// + /// See also [`Self::shift_left`] and compare [`Self::rotate_right`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_shift)] + /// + /// // Same as the diagram above + /// let mut a = [5, 6, 7, 8, 9]; + /// let inserted = [0]; + /// let returned = a.shift_right(inserted); + /// assert_eq!(returned, [9]); + /// assert_eq!(a, [0, 5, 6, 7, 8]); + /// + /// // The name comes from this operation's similarity to bitshifts + /// let mut a: u8 = 0b10010110; + /// a >>= 3; + /// assert_eq!(a, 0b00010010_u8); + /// let mut a: [_; 8] = [1, 0, 0, 1, 0, 1, 1, 0]; + /// a.shift_right([0; 3]); + /// assert_eq!(a, [0, 0, 0, 1, 0, 0, 1, 0]); + /// + /// // Remember you can sub-slice to affect less that the whole slice. + /// // For example, this is similar to `.remove(4)` + `.insert(1, 'Z')` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// assert_eq!(a[1..=4].shift_right(['Z']), ['e']); + /// assert_eq!(a, ['a', 'Z', 'b', 'c', 'd', 'f']); + /// + /// // If the size matches it's equivalent to `mem::replace` + /// let mut a = [1, 2, 3]; + /// assert_eq!(a.shift_right([7, 8, 9]), [1, 2, 3]); + /// assert_eq!(a, [7, 8, 9]); + /// + /// // Some of the "inserted" elements end up returned if the slice is too short + /// let mut a = []; + /// assert_eq!(a.shift_right([1, 2, 3]), [1, 2, 3]); + /// let mut a = [9]; + /// assert_eq!(a.shift_right([1, 2, 3]), [2, 3, 9]); + /// assert_eq!(a, [1]); + /// ``` + #[unstable(feature = "slice_shift", issue = "151772")] + pub const fn shift_right(&mut self, inserted: [T; N]) -> [T; N] { + if let Some(shift) = self.len().checked_sub(N) { + // SAFETY: Having just checked that the inserted/returned arrays are + // shorter than (or the same length as) the slice: + // 1. The read for the items to return is in-bounds + // 2. We can `memmove` the slice over to cover the items we're returning + // to ensure those aren't double-dropped + // 3. Then we write (in-bounds for the same reason as the read) the + // inserted items atop the items of the slice that we just duplicated + // + // And none of this can panic, so there's no risk of intermediate unwinds. + unsafe { + let ptr = self.as_mut_ptr(); + let returned = ptr.add(shift).cast_array::().read(); + ptr.add(N).copy_from(ptr, shift); + ptr.cast_array::().write(inserted); + returned + } + } else { + // SAFETY: Having checked that the slice is strictly shorter than the + // inserted/returned arrays, it means we'll be copying the whole slice + // into the returned array, but that's not enough on its own. We also + // need to copy some of the inserted array into the returned array, + // with the rest going into the slice. Because `&mut` is exclusive + // and we own both `inserted` and `returned`, they're all disjoint + // allocations from each other as we can use `nonoverlapping` copies. + // + // We avoid double-frees by `ManuallyDrop`ing the inserted items, + // since we always copy them to other locations that will drop them + // instead. Plus nothing in here can panic -- it's just memcpy three + // times -- so there's no intermediate unwinds to worry about. + unsafe { + let len = self.len(); + let slice = self.as_mut_ptr(); + let inserted = mem::ManuallyDrop::new(inserted); + let inserted = (&raw const inserted).cast::(); + + let mut returned = MaybeUninit::<[T; N]>::uninit(); + let ptr = returned.as_mut_ptr().cast::(); + ptr.add(N - len).copy_from_nonoverlapping(slice, len); + ptr.copy_from_nonoverlapping(inserted.add(len), N - len); + slice.copy_from_nonoverlapping(inserted, len); + returned.assume_init() + } + } + } + /// Fills `self` with elements by cloning `value`. /// /// # Examples diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 8cca714b7393..b6dd130aa35f 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -101,6 +101,7 @@ #![feature(slice_index_methods)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] +#![feature(slice_shift)] #![feature(slice_split_once)] #![feature(sliceindex_wrappers)] #![feature(split_array)] diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 6f60f71e8a47..2bb62f36bb0e 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -2507,3 +2507,41 @@ fn test_slice_from_raw_parts_in_const() { assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); assert_eq!(EMPTY_SLICE.len(), 0); } + +#[test] +fn test_shift_left() { + #[track_caller] + fn case( + mut a: [i32; M], + i: [i32; N], + j: [i32; N], + b: [i32; M], + ) { + assert_eq!((a.shift_left(i), a), (j, b)); + } + case([], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], []); + case([1], [2, 3, 4, 5], [1, 2, 3, 4], [5]); + case([1, 2], [3, 4, 5], [1, 2, 3], [4, 5]); + case([1, 2, 3], [4, 5], [1, 2], [3, 4, 5]); + case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); + case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); +} + +#[test] +fn test_shift_right() { + #[track_caller] + fn case( + i: [i32; N], + mut a: [i32; M], + b: [i32; M], + j: [i32; N], + ) { + assert_eq!((a.shift_right(i), a), (j, b)); + } + case([], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], []); + case([1], [2, 3, 4, 5], [1, 2, 3, 4], [5]); + case([1, 2], [3, 4, 5], [1, 2, 3], [4, 5]); + case([1, 2, 3], [4, 5], [1, 2], [3, 4, 5]); + case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); + case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); +} From 83eae6253a198d33b11315f194be4190eded7a68 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 21:15:07 +0200 Subject: [PATCH 374/583] sembr src/tests/compiletest.md --- .../rustc-dev-guide/src/tests/compiletest.md | 327 +++++++++--------- 1 file changed, 158 insertions(+), 169 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 626525fa804d..06a4728c93e1 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -2,8 +2,8 @@ ## Introduction -`compiletest` is the main test harness of the Rust test suite. It allows test -authors to organize large numbers of tests (the Rust compiler has many +`compiletest` is the main test harness of the Rust test suite. +It allows test authors to organize large numbers of tests (the Rust compiler has many thousands), efficient test execution (parallel execution is supported), and allows the test author to configure behavior and expected results of both individual and groups of tests. @@ -22,9 +22,10 @@ individual and groups of tests. `compiletest` may check test code for compile-time or run-time success/failure. Tests are typically organized as a Rust source file with annotations in comments -before and/or within the test code. These comments serve to direct `compiletest` -on if or how to run the test, what behavior to expect, and more. See -[directives](directives.md) and the test suite documentation below for more details +before and/or within the test code. +These comments serve to direct `compiletest` +on if or how to run the test, what behavior to expect, and more. +See [directives](directives.md) and the test suite documentation below for more details on these annotations. See the [Adding new tests](adding.md) and [Best practices](best-practices.md) @@ -40,16 +41,18 @@ Additionally, bootstrap accepts several common arguments directly, e.g. `x test --no-capture --force-rerun --run --pass`. Compiletest itself tries to avoid running tests when the artifacts that are -involved (mainly the compiler) haven't changed. You can use `x test --test-args +involved (mainly the compiler) haven't changed. +You can use `x test --test-args --force-rerun` to rerun a test even when none of the inputs have changed. ## Test suites -All of the tests are in the [`tests`] directory. The tests are organized into -"suites", with each suite in a separate subdirectory. Each test suite behaves a -little differently, with different compiler behavior and different checks for -correctness. For example, the [`tests/incremental`] directory contains tests for -incremental compilation. The various suites are defined in +All of the tests are in the [`tests`] directory. +The tests are organized into "suites", with each suite in a separate subdirectory. +Each test suite behaves a +little differently, with different compiler behavior and different checks for correctness. +For example, the [`tests/incremental`] directory contains tests for incremental compilation. +The various suites are defined in [`src/tools/compiletest/src/common.rs`] in the `pub enum Mode` declaration. The following test suites are available, with links for more information: @@ -105,10 +108,9 @@ Run-make tests pertaining to rustdoc are typically named `run-make/rustdoc-*/`. ### Pretty-printer tests -The tests in [`tests/pretty`] exercise the "pretty-printing" functionality of -`rustc`. The `-Z unpretty` CLI option for `rustc` causes it to translate the -input source into various different formats, such as the Rust source after macro -expansion. +The tests in [`tests/pretty`] exercise the "pretty-printing" functionality of `rustc`. +The `-Z unpretty` CLI option for `rustc` causes it to translate the +input source into various different formats, such as the Rust source after macro expansion. The pretty-printer tests have several [directives](directives.md) described below. These commands can significantly change the behavior of the test, but the @@ -125,17 +127,20 @@ If any of the commands above fail, then the test fails. The directives for pretty-printing tests are: - `pretty-mode` specifies the mode pretty-print tests should run in (that is, - the argument to `-Zunpretty`). The default is `normal` if not specified. + the argument to `-Zunpretty`). + The default is `normal` if not specified. - `pretty-compare-only` causes a pretty test to only compare the pretty-printed - output (stopping after step 3 from above). It will not try to compile the - expanded output to type check it. This is needed for a pretty-mode that does - not expand to valid Rust, or for other situations where the expanded output - cannot be compiled. + output (stopping after step 3 from above). + It will not try to compile the expanded output to type check it. + This is needed for a pretty-mode that does + not expand to valid Rust, or for other situations where the expanded output cannot be compiled. - `pp-exact` is used to ensure a pretty-print test results in specific output. If specified without a value, then it means the pretty-print output should - match the original source. If specified with a value, as in `//@ + match the original source. + If specified with a value, as in `//@ pp-exact:foo.pp`, it will ensure that the pretty-printed output matches the - contents of the given file. Otherwise, if `pp-exact` is not specified, then + contents of the given file. + Otherwise, if `pp-exact` is not specified, then the pretty-printed output will be pretty-printed one more time, and the output of the two pretty-printing rounds will be compared to ensure that the pretty-printed output converges to a steady state. @@ -144,13 +149,12 @@ The directives for pretty-printing tests are: ### Incremental tests -The tests in [`tests/incremental`] exercise incremental compilation. They use -[`revisions` directive](#revisions) to tell compiletest to run the compiler in a +The tests in [`tests/incremental`] exercise incremental compilation. +They use [`revisions` directive](#revisions) to tell compiletest to run the compiler in a series of steps. Compiletest starts with an empty directory with the `-C incremental` flag, and -then runs the compiler for each revision, reusing the incremental results from -previous steps. +then runs the compiler for each revision, reusing the incremental results from previous steps. The revisions should start with: @@ -158,8 +162,7 @@ The revisions should start with: * `rfail` — the test should compile successfully, but the executable should fail to run * `cfail` — the test should fail to compile -To make the revisions unique, you should add a suffix like `rpass1` and -`rpass2`. +To make the revisions unique, you should add a suffix like `rpass1` and `rpass2`. To simulate changing the source, compiletest also passes a `--cfg` flag with the current revision name. @@ -183,20 +186,20 @@ fn main() { foo(); } ``` `cfail` tests support the `forbid-output` directive to specify that a certain -substring must not appear anywhere in the compiler output. This can be useful to -ensure certain errors do not appear, but this can be fragile as error messages -change over time, and a test may no longer be checking the right thing but will -still pass. +substring must not appear anywhere in the compiler output. +This can be useful to ensure certain errors do not appear, but this can be fragile as error messages +change over time, and a test may no longer be checking the right thing but will still pass. `cfail` tests support the `should-ice` directive to specify that a test should -cause an Internal Compiler Error (ICE). This is a highly specialized directive +cause an Internal Compiler Error (ICE). +This is a highly specialized directive to check that the incremental cache continues to work after an ICE. -Incremental tests may use the attribute `#[rustc_clean(...)]` attribute. This attribute compares -the fingerprint from the current compilation session with the previous one. +Incremental tests may use the attribute `#[rustc_clean(...)]` attribute. +This attribute compares the fingerprint from the current compilation session with the previous one. The first revision should never have an active `rustc_clean` attribute, since it will always be dirty. -In the default mode, it asserts that the fingerprints must be the same. +In the default mode, it asserts that the fingerprints must be the same. The attribute takes the following arguments: * `cfg=""` — checks the cfg condition ``, and only runs the check if the config condition evaluates to true. @@ -204,9 +207,10 @@ The attribute takes the following arguments: * `except=",,..."` — asserts that the query results for the listed queries must be different, rather than the same. * `loaded_from_disk=",,..."` — asserts that the query results for the listed queries - were actually loaded from disk (not just marked green). + were actually loaded from disk (not just marked green). This can be useful to ensure that a test is actually exercising the deserialization - logic for a particular query result. This can be combined with `except`. + logic for a particular query result. + This can be combined with `except`. A simple example of a test using `rustc_clean` is the [hello_world test]. @@ -215,9 +219,9 @@ A simple example of a test using `rustc_clean` is the [hello_world test]. ### Debuginfo tests -The tests in [`tests/debuginfo`] test debuginfo generation. They build a -program, launch a debugger, and issue commands to the debugger. A single test -can work with cdb, gdb, and lldb. +The tests in [`tests/debuginfo`] test debuginfo generation. +They build a program, launch a debugger, and issue commands to the debugger. +A single test can work with cdb, gdb, and lldb. Most tests should have the `//@ compile-flags: -g` directive or something similar to generate the appropriate debuginfo. @@ -228,8 +232,7 @@ The debuginfo tests consist of a series of debugger commands along with "check" lines which specify output that is expected from the debugger. The commands are comments of the form `// $DEBUGGER-command:$COMMAND` where -`$DEBUGGER` is the debugger being used and `$COMMAND` is the debugger command -to execute. +`$DEBUGGER` is the debugger being used and `$COMMAND` is the debugger command to execute. The debugger values can be: @@ -245,8 +248,7 @@ The command to check the output are of the form `// $DEBUGGER-check:$OUTPUT` where `$OUTPUT` is the output to expect. For example, the following will build the test, start the debugger, set a -breakpoint, launch the program, inspect a value, and check what the debugger -prints: +breakpoint, launch the program, inspect a value, and check what the debugger prints: ```rust,ignore //@ compile-flags: -g @@ -268,17 +270,16 @@ the debugger currently being used: - `min-cdb-version: 10.0.18317.1001` — ignores the test if the version of cdb is below the given version -- `min-gdb-version: 8.2` — ignores the test if the version of gdb is below the - given version +- `min-gdb-version: 8.2` — ignores the test if the version of gdb is below the given version - `ignore-gdb-version: 9.2` — ignores the test if the version of gdb is equal to the given version - `ignore-gdb-version: 7.11.90 - 8.0.9` — ignores the test if the version of gdb is in a range (inclusive) -- `min-lldb-version: 310` — ignores the test if the version of lldb is below - the given version -- `rust-lldb` — ignores the test if lldb is not contain the Rust plugin. NOTE: - The "Rust" version of LLDB doesn't exist anymore, so this will always be - ignored. This should probably be removed. +- `min-lldb-version: 310` — ignores the test if the version of lldb is below the given version +- `rust-lldb` — ignores the test if lldb is not contain the Rust plugin. + NOTE: The "Rust" version of LLDB doesn't exist anymore, so this will always be + ignored. + This should probably be removed. By passing the `--debugger` option to compiletest, you can specify a single debugger to run tests with. For example, `./x test tests/debuginfo -- --debugger gdb` will only test GDB commands. @@ -311,17 +312,18 @@ For example, `./x test tests/debuginfo -- --debugger gdb` will only test GDB com ### Codegen tests -The tests in [`tests/codegen-llvm`] test LLVM code generation. They compile the -test with the `--emit=llvm-ir` flag to emit LLVM IR. They then run the LLVM -[FileCheck] tool. The test is annotated with various `// CHECK` comments to -check the generated code. See the [FileCheck] documentation for a tutorial and -more information. +The tests in [`tests/codegen-llvm`] test LLVM code generation. +They compile the test with the `--emit=llvm-ir` flag to emit LLVM IR. +They then run the LLVM [FileCheck] tool. +The test is annotated with various `// CHECK` comments to check the generated code. +See the [FileCheck] documentation for a tutorial and more information. See also the [assembly tests](#assembly-tests) for a similar set of tests. By default, codegen tests will have `//@ needs-target-std` *implied* (that the target needs to support std), *unless* the `#![no_std]`/`#![no_core]` attribute -was specified in the test source. You can override this behavior and explicitly +was specified in the test source. +You can override this behavior and explicitly write `//@ needs-target-std` to only run the test when target supports std, even if the test is `#![no_std]`/`#![no_core]`. @@ -334,17 +336,15 @@ If you need to work with `#![no_std]` cross-compiling tests, consult the ### Assembly tests -The tests in [`tests/assembly-llvm`] test LLVM assembly output. They compile the test -with the `--emit=asm` flag to emit a `.s` file with the assembly output. They -then run the LLVM [FileCheck] tool. +The tests in [`tests/assembly-llvm`] test LLVM assembly output. +They compile the test with the `--emit=asm` flag to emit a `.s` file with the assembly output. +They then run the LLVM [FileCheck] tool. Each test should be annotated with the `//@ assembly-output:` directive with a -value of either `emit-asm` or `ptx-linker` to indicate the type of assembly -output. +value of either `emit-asm` or `ptx-linker` to indicate the type of assembly output. -Then, they should be annotated with various `// CHECK` comments to check the -assembly output. See the [FileCheck] documentation for a tutorial and more -information. +Then, they should be annotated with various `// CHECK` comments to check the assembly output. +See the [FileCheck] documentation for a tutorial and more information. See also the [codegen tests](#codegen-tests) for a similar set of tests. @@ -364,13 +364,11 @@ monomorphization collection pass, i.e., `-Zprint-mono-items`, and then special annotations in the file are used to compare against that. Then, the test should be annotated with comments of the form `//~ MONO_ITEM -name` where `name` is the monomorphized string printed by rustc like `fn ::foo`. +name` where `name` is the monomorphized string printed by rustc like `fn ::foo`. To check for CGU partitioning, a comment of the form `//~ MONO_ITEM name @@ cgu` -where `cgu` is a space separated list of the CGU names and the linkage -information in brackets. For example: `//~ MONO_ITEM static function::FOO @@ -statics[Internal]` +where `cgu` is a space separated list of the CGU names and the linkage information in brackets. +For example: `//~ MONO_ITEM static function::FOO @@ statics[Internal]` [`tests/codegen-units`]: https://github.com/rust-lang/rust/tree/HEAD/tests/codegen-units @@ -378,8 +376,8 @@ statics[Internal]` ### Mir-opt tests The tests in [`tests/mir-opt`] check parts of the generated MIR to make sure it -is generated correctly and is doing the expected optimizations. Check out the -[MIR Optimizations](../mir/optimizations.md) chapter for more. +is generated correctly and is doing the expected optimizations. +Check out the [MIR Optimizations](../mir/optimizations.md) chapter for more. Compiletest will build the test with several flags to dump the MIR output and set a baseline for optimizations: @@ -391,23 +389,24 @@ set a baseline for optimizations: * `-Zdump-mir-exclude-pass-number` The test should be annotated with `// EMIT_MIR` comments that specify files that -will contain the expected MIR output. You can use `x test --bless` to create the -initial expected files. +will contain the expected MIR output. +You can use `x test --bless` to create the initial expected files. There are several forms the `EMIT_MIR` comment can take: - `// EMIT_MIR $MIR_PATH.mir` — This will check that the given filename matches - the exact output from the MIR dump. For example, + the exact output from the MIR dump. + For example, `my_test.main.SimplifyCfg-elaborate-drops.after.mir` will load that file from the test directory, and compare it against the dump from rustc. Checking the "after" file (which is after optimization) is useful if you are - interested in the final state after an optimization. Some rare cases may want - to use the "before" file for completeness. + interested in the final state after an optimization. + Some rare cases may want to use the "before" file for completeness. - `// EMIT_MIR $MIR_PATH.diff` — where `$MIR_PATH` is the filename of the MIR - dump, such as `my_test_name.my_function.EarlyOtherwiseBranch`. Compiletest - will diff the `.before.mir` and `.after.mir` files, and compare the diff + dump, such as `my_test_name.my_function.EarlyOtherwiseBranch`. + Compiletest will diff the `.before.mir` and `.after.mir` files, and compare the diff output to the expected `.diff` file from the `EMIT_MIR` comment. This is useful if you want to see how an optimization changes the MIR. @@ -417,8 +416,8 @@ There are several forms the `EMIT_MIR` comment can take: check that the output matches the given file. By default 32 bit and 64 bit targets use the same dump files, which can be -problematic in the presence of pointers in constants or other bit width -dependent things. In that case you can add `// EMIT_MIR_FOR_EACH_BIT_WIDTH` to +problematic in the presence of pointers in constants or other bit width dependent things. +In that case you can add `// EMIT_MIR_FOR_EACH_BIT_WIDTH` to your test, causing separate files to be generated for 32bit and 64bit systems. [`tests/mir-opt`]: https://github.com/rust-lang/rust/tree/HEAD/tests/mir-opt @@ -428,9 +427,8 @@ your test, causing separate files to be generated for 32bit and 64bit systems. The tests in [`tests/run-make`] and [`tests/run-make-cargo`] are general-purpose tests using Rust *recipes*, which are small programs (`rmake.rs`) allowing -arbitrary Rust code such as `rustc` invocations, and is supported by a -[`run_make_support`] library. Using Rust recipes provide the ultimate in -flexibility. +arbitrary Rust code such as `rustc` invocations, and is supported by a [`run_make_support`] library. +Using Rust recipes provide the ultimate in flexibility. `run-make` tests should be used if no other test suites better suit your needs. @@ -441,9 +439,9 @@ faster-to-iterate test suite). ### `build-std` tests -The tests in [`tests/build-std`] check that `-Zbuild-std` works. This is currently -just a run-make test suite with a single recipe. The recipe generates test cases -and runs them in parallel. +The tests in [`tests/build-std`] check that `-Zbuild-std` works. +This is currently just a run-make test suite with a single recipe. +The recipe generates test cases and runs them in parallel. [`tests/build-std`]: https://github.com/rust-lang/rust/tree/HEAD/tests/build-std @@ -457,18 +455,16 @@ If you need new utilities or functionality, consider extending and improving the [`run_make_support`] library. Compiletest directives like `//@ only-` or `//@ ignore-` are -supported in `rmake.rs`, like in UI tests. However, revisions or building -auxiliary via directives are not currently supported. +supported in `rmake.rs`, like in UI tests. +However, revisions or building auxiliary via directives are not currently supported. `rmake.rs` and `run-make-support` may *not* use any nightly/unstable features, -as they must be compilable by a stage 0 rustc that may be a beta or even stable -rustc. +as they must be compilable by a stage 0 rustc that may be a beta or even stable rustc. #### Quickly check if `rmake.rs` tests can be compiled You can quickly check if `rmake.rs` tests can be compiled without having to -build stage1 rustc by forcing `rmake.rs` to be compiled with the stage0 -compiler: +build stage1 rustc by forcing `rmake.rs` to be compiled with the stage0 compiler: ```bash $ COMPILETEST_FORCE_STAGE0=1 x test --stage 0 tests/run-make/ @@ -525,7 +521,8 @@ Then add a corresponding entry to `"rust-analyzer.linkedProjects"` ### Coverage tests The tests in [`tests/coverage`] are shared by multiple test modes that test -coverage instrumentation in different ways. Running the `coverage` test suite +coverage instrumentation in different ways. +Running the `coverage` test suite will automatically run each test in all of the different coverage modes. Each mode also has an alias to run the coverage tests in just that mode: @@ -543,31 +540,28 @@ Each mode also has an alias to run the coverage tests in just that mode: ``` If a particular test should not be run in one of the coverage test modes for -some reason, use the `//@ ignore-coverage-map` or `//@ ignore-coverage-run` -directives. +some reason, use the `//@ ignore-coverage-map` or `//@ ignore-coverage-run` directives. #### `coverage-map` suite In `coverage-map` mode, these tests verify the mappings between source code -regions and coverage counters that are emitted by LLVM. They compile the test -with `--emit=llvm-ir`, then use a custom tool ([`src/tools/coverage-dump`]) to -extract and pretty-print the coverage mappings embedded in the IR. These tests -don't require the profiler runtime, so they run in PR CI jobs and are easy to +regions and coverage counters that are emitted by LLVM. +They compile the test with `--emit=llvm-ir`, then use a custom tool ([`src/tools/coverage-dump`]) to +extract and pretty-print the coverage mappings embedded in the IR. +These tests don't require the profiler runtime, so they run in PR CI jobs and are easy to run/bless locally. These coverage map tests can be sensitive to changes in MIR lowering or MIR -optimizations, producing mappings that are different but produce identical -coverage reports. +optimizations, producing mappings that are different but produce identical coverage reports. As a rule of thumb, any PR that doesn't change coverage-specific code should **feel free to re-bless** the `coverage-map` tests as necessary, without -worrying about the actual changes, as long as the `coverage-run` tests still -pass. +worrying about the actual changes, as long as the `coverage-run` tests still pass. #### `coverage-run` suite -In `coverage-run` mode, these tests perform an end-to-end test of coverage -reporting. They compile a test program with coverage instrumentation, run that +In `coverage-run` mode, these tests perform an end-to-end test of coverage reporting. +They compile a test program with coverage instrumentation, run that program to produce raw coverage data, and then use LLVM tools to process that data into a human-readable code coverage report. @@ -587,8 +581,8 @@ as part of the full set of CI jobs used for merging. #### `coverage-run-rustdoc` suite The tests in [`tests/coverage-run-rustdoc`] also run instrumented doctests and -include them in the coverage report. This avoids having to build rustdoc when -only running the main `coverage` suite. +include them in the coverage report. +This avoids having to build rustdoc when only running the main `coverage` suite. [`tests/coverage`]: https://github.com/rust-lang/rust/tree/HEAD/tests/coverage [`src/tools/coverage-dump`]: https://github.com/rust-lang/rust/tree/HEAD/src/tools/coverage-dump @@ -597,13 +591,12 @@ only running the main `coverage` suite. ### Crash tests [`tests/crashes`] serve as a collection of tests that are expected to cause the -compiler to ICE, panic or crash in some other way, so that accidental fixes are -tracked. Formerly, this was done at but +compiler to ICE, panic or crash in some other way, so that accidental fixes are tracked. +Formerly, this was done at but doing it inside the rust-lang/rust testsuite is more convenient. -It is imperative that a test in the suite causes rustc to ICE, panic, or -crash in some other way. A test will "pass" if rustc exits with an exit status -other than 1 or 0. +It is imperative that a test in the suite causes rustc to ICE, panic, or crash in some other way. +A test will "pass" if rustc exits with an exit status other than 1 or 0. If you want to see verbose stdout/stderr, you need to set `COMPILETEST_VERBOSE_CRASHES=1`, e.g. @@ -612,16 +605,15 @@ If you want to see verbose stdout/stderr, you need to set $ COMPILETEST_VERBOSE_CRASHES=1 ./x test tests/crashes/999999.rs --stage 1 ``` -Anyone can add ["untracked" crashes] from the issue tracker. It's strongly -recommended to include test cases from several issues in a single PR. +Anyone can add ["untracked" crashes] from the issue tracker. +It's strongly recommended to include test cases from several issues in a single PR. When you do so, each issue number should be noted in the file name (`12345.rs` -should suffice) and also inside the file by means of a `//@ known-bug: #12345` -directive. Please [label][labeling] the relevant issues with `S-bug-has-test` -once your PR is merged. +should suffice) and also inside the file by means of a `//@ known-bug: #12345` directive. +Please [label][labeling] the relevant issues with `S-bug-has-test` once your PR is merged. If you happen to fix one of the crashes, please move it to a fitting -subdirectory in `tests/ui` and give it a meaningful name. Please add a doc -comment at the top of the file explaining why this test exists, even better if +subdirectory in `tests/ui` and give it a meaningful name. +Please add a doc comment at the top of the file explaining why this test exists, even better if you can briefly explain how the example causes rustc to crash previously and what was done to prevent rustc to ICE / panic / crash. @@ -635,8 +627,8 @@ Fixes #MMMMM to the description of your pull request will ensure the corresponding tickets be closed automatically upon merge. -Make sure that your fix actually fixes the root cause of the issue and not just -a subset first. The issue numbers can be found in the file name or the `//@ +Make sure that your fix actually fixes the root cause of the issue and not just a subset first. +The issue numbers can be found in the file name or the `//@ known-bug` directive inside the test file. [`tests/crashes`]: https://github.com/rust-lang/rust/tree/HEAD/tests/crashes @@ -654,8 +646,8 @@ There are multiple [directives](directives.md) to assist with that: - `aux-codegen-backend` - `proc-macro` -`aux-build` will build a separate crate from the named source file. The source -file should be in a directory called `auxiliary` beside the test file. +`aux-build` will build a separate crate from the named source file. +The source file should be in a directory called `auxiliary` beside the test file. ```rust,ignore //@ aux-build: my-helper.rs @@ -665,44 +657,48 @@ extern crate my_helper; ``` The aux crate will be built as a dylib if possible (unless on a platform that -does not support them, or the `no-prefer-dynamic` header is specified in the aux -file). The `-L` flag is used to find the extern crates. +does not support them, or the `no-prefer-dynamic` header is specified in the aux file). +The `-L` flag is used to find the extern crates. -`aux-crate` is very similar to `aux-build`. However, it uses the `--extern` flag +`aux-crate` is very similar to `aux-build`. +However, it uses the `--extern` flag to link to the extern crate to make the crate be available as an extern prelude. That allows you to specify the additional syntax of the `--extern` flag, such as -renaming a dependency. For example, `//@ aux-crate:foo=bar.rs` will compile +renaming a dependency. +For example, `//@ aux-crate:foo=bar.rs` will compile `auxiliary/bar.rs` and make it available under then name `foo` within the test. -This is similar to how Cargo does dependency renaming. It is also possible to +This is similar to how Cargo does dependency renaming. +It is also possible to specify [`--extern` modifiers](https://github.com/rust-lang/rust/issues/98405). For example, `//@ aux-crate:noprelude:foo=bar.rs`. -`aux-bin` is similar to `aux-build` but will build a binary instead of a -library. The binary will be available in `auxiliary/bin` relative to the working -directory of the test. +`aux-bin` is similar to `aux-build` but will build a binary instead of a library. +The binary will be available in `auxiliary/bin` relative to the working directory of the test. `aux-codegen-backend` is similar to `aux-build`, but will then pass the compiled -dylib to `-Zcodegen-backend` when building the main file. This will only work -for tests in `tests/ui-fulldeps`, since it requires the use of compiler crates. +dylib to `-Zcodegen-backend` when building the main file. +This will only work for tests in `tests/ui-fulldeps`, since it requires the use of compiler crates. ### Auxiliary proc-macro If you want a proc-macro dependency, then you can use the `proc-macro` directive. This directive behaves just like `aux-build`, i.e. that you should place the proc-macro test auxiliary file under a `auxiliary` folder under the -same parent folder as the main test file. However, it also has four additional +same parent folder as the main test file. +However, it also has four additional preset behavior compared to `aux-build` for the proc-macro test auxiliary: 1. The aux test file is built with `--crate-type=proc-macro`. 2. The aux test file is built without `-C prefer-dynamic`, i.e. it will not try to produce a dylib for the aux crate. 3. The aux crate is made available to the test file via extern prelude with - `--extern `. Note that since UI tests default to edition + `--extern `. + Note that since UI tests default to edition 2015, you still need to specify `extern ` unless the main test file is using an edition that is 2018 or newer if you want to use the aux crate name in a `use` import. -4. The `proc_macro` crate is made available as an extern prelude module. Same - edition 2015 vs newer edition distinction for `extern proc_macro;` applies. +4. The `proc_macro` crate is made available as an extern prelude module. + Same edition 2015 vs newer edition distinction for `extern proc_macro;` applies. For example, you might have a test `tests/ui/cat/meow.rs` and proc-macro auxiliary `tests/ui/cat/auxiliary/whiskers.rs`: @@ -744,19 +740,19 @@ pub fn identity(ts: TokenStream) -> TokenStream { ## Revisions -Revisions allow a single test file to be used for multiple tests. This is done -by adding a special directive at the top of the file: +Revisions allow a single test file to be used for multiple tests. +This is done by adding a special directive at the top of the file: ```rust,ignore //@ revisions: foo bar baz ``` This will result in the test being compiled (and tested) three times, once with -`--cfg foo`, once with `--cfg bar`, and once with `--cfg baz`. You can therefore -use `#[cfg(foo)]` etc within the test to tweak each of these results. +`--cfg foo`, once with `--cfg bar`, and once with `--cfg baz`. +You can therefore use `#[cfg(foo)]` etc within the test to tweak each of these results. -You can also customize directives and expected error messages to a particular -revision. To do this, add `[revision-name]` after the `//@` for directives, and +You can also customize directives and expected error messages to a particular revision. +To do this, add `[revision-name]` after the `//@` for directives, and after `//` for UI error annotations, like so: ```rust,ignore @@ -769,8 +765,7 @@ fn test_foo() { } ``` -Multiple revisions can be specified in a comma-separated list, such as -`//[foo,bar,baz]~^`. +Multiple revisions can be specified in a comma-separated list, such as `//[foo,bar,baz]~^`. In test suites that use the LLVM [FileCheck] tool, the current revision name is also registered as an additional prefix for FileCheck directives: @@ -787,14 +782,13 @@ also registered as an additional prefix for FileCheck directives: fn main() {} ``` -Note that not all directives have meaning when customized to a revision. For -example, the `ignore-test` directives (and all "ignore" directives) currently -only apply to the test as a whole, not to particular revisions. The only -directives that are intended to really work when customized to a revision are +Note that not all directives have meaning when customized to a revision. +For example, the `ignore-test` directives (and all "ignore" directives) currently +only apply to the test as a whole, not to particular revisions. +The only directives that are intended to really work when customized to a revision are error patterns and compiler flags. - -The following test suites support revisions: + The following test suites support revisions: - ui - assembly @@ -802,14 +796,13 @@ The following test suites support revisions: - coverage - debuginfo - rustdoc UI tests -- incremental (these are special in that they inherently cannot be run in - parallel) +- incremental (these are special in that they inherently cannot be run in parallel) ### Ignoring unused revision names Normally, revision names mentioned in other directives and error annotations -must correspond to an actual revision declared in a `revisions` directive. This is -enforced by an `./x test tidy` check. +must correspond to an actual revision declared in a `revisions` directive. +This is enforced by an `./x test tidy` check. If a revision name needs to be temporarily removed from the revision list for some reason, the above check can be suppressed by adding the revision name to an @@ -825,8 +818,7 @@ used to compare the behavior of all tests with different compiler flags enabled. This can help highlight what differences might appear with certain flags, and check for any problems that might arise. -To run the tests in a different mode, you need to pass the `--compare-mode` CLI -flag: +To run the tests in a different mode, you need to pass the `--compare-mode` CLI flag: ```bash ./x test tests/ui --compare-mode=chalk @@ -836,20 +828,17 @@ The possible compare modes are: - `polonius` — Runs with Polonius with `-Zpolonius`. - `chalk` — Runs with Chalk with `-Zchalk`. -- `split-dwarf` — Runs with unpacked split-DWARF with - `-Csplit-debuginfo=unpacked`. -- `split-dwarf-single` — Runs with packed split-DWARF with - `-Csplit-debuginfo=packed`. +- `split-dwarf` — Runs with unpacked split-DWARF with `-Csplit-debuginfo=unpacked`. +- `split-dwarf-single` — Runs with packed split-DWARF with `-Csplit-debuginfo=packed`. See [UI compare modes](ui.md#compare-modes) for more information about how UI tests support different output for different modes. -In CI, compare modes are only used in one Linux builder, and only with the -following settings: +In CI, compare modes are only used in one Linux builder, and only with the following settings: -- `tests/debuginfo`: Uses `split-dwarf` mode. This helps ensure that none of the - debuginfo tests are affected when enabling split-DWARF. +- `tests/debuginfo`: Uses `split-dwarf` mode. + This helps ensure that none of the debuginfo tests are affected when enabling split-DWARF. -Note that compare modes are separate to [revisions](#revisions). All revisions -are tested when running `./x test tests/ui`, however compare-modes must be +Note that compare modes are separate to [revisions](#revisions). +All revisions are tested when running `./x test tests/ui`, however compare-modes must be manually run individually via the `--compare-mode` flag. From b643676267642909e67e5479dc47abc676cccdb7 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 21:57:55 +0200 Subject: [PATCH 375/583] misc improvements --- src/doc/rustc-dev-guide/src/tests/compiletest.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 06a4728c93e1..f0da634129ee 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -204,9 +204,9 @@ The attribute takes the following arguments: * `cfg=""` — checks the cfg condition ``, and only runs the check if the config condition evaluates to true. This can be used to only run the `rustc_clean` attribute in a specific revision. -* `except=",,..."` — asserts that the query results for the listed queries must be different, +* `except=",,..."` — asserts that the query results for the listed queries must be different, rather than the same. -* `loaded_from_disk=",,..."` — asserts that the query results for the listed queries +* `loaded_from_disk=",,..."` — asserts that the query results for the listed queries were actually loaded from disk (not just marked green). This can be useful to ensure that a test is actually exercising the deserialization logic for a particular query result. @@ -288,12 +288,12 @@ For example, `./x test tests/debuginfo -- --debugger gdb` will only test GDB com > > If you want to run lldb debuginfo tests locally, then currently on Windows it > is required that: -> +> > - You have Python 3.10 installed. > - You have `python310.dll` available in your `PATH` env var. This is not > provided by the standard Python installer you obtain from `python.org`; you > need to add this to `PATH` manually. -> +> > Otherwise the lldb debuginfo tests can produce crashes in mysterious ways. [`tests/debuginfo`]: https://github.com/rust-lang/rust/tree/HEAD/tests/debuginfo @@ -613,9 +613,9 @@ Please [label][labeling] the relevant issues with `S-bug-has-test` once your PR If you happen to fix one of the crashes, please move it to a fitting subdirectory in `tests/ui` and give it a meaningful name. -Please add a doc comment at the top of the file explaining why this test exists, even better if -you can briefly explain how the example causes rustc to crash previously and -what was done to prevent rustc to ICE / panic / crash. +Please add a doc comment at the top of the file explaining why this test exists. +Even better will be if you can briefly explain how the example caused rustc to crash previously, +and what was done to fix it. Adding @@ -698,7 +698,7 @@ preset behavior compared to `aux-build` for the proc-macro test auxiliary: test file is using an edition that is 2018 or newer if you want to use the aux crate name in a `use` import. 4. The `proc_macro` crate is made available as an extern prelude module. - Same edition 2015 vs newer edition distinction for `extern proc_macro;` applies. + The same edition 2015 vs newer edition distinction for `extern proc_macro;` applies. For example, you might have a test `tests/ui/cat/meow.rs` and proc-macro auxiliary `tests/ui/cat/auxiliary/whiskers.rs`: From 93049c34888173bec0e849f69df23ff7359a85ae Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 22:06:56 +0200 Subject: [PATCH 376/583] fix sembr tool corner case --- src/doc/rustc-dev-guide/ci/sembr/src/main.rs | 1 + src/doc/rustc-dev-guide/src/tests/compiletest.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 6f4ce4415f04..1de0638deb97 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -83,6 +83,7 @@ fn ignore(line: &str, in_code_block: bool) -> bool { || line.contains(" etc.") || line.contains("i.e.") || line.contains("et. al") + || line.contains(" The following test suites support revisions: + +The following test suites support revisions: - ui - assembly From 03b8ebb91c2331180bce6273026b88f8bb94d215 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 22:09:02 +0200 Subject: [PATCH 377/583] sembr src/tests/ui.md --- src/doc/rustc-dev-guide/src/tests/ui.md | 315 ++++++++++++------------ 1 file changed, 157 insertions(+), 158 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index b7cf446c4a57..bd058fbed02e 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -1,20 +1,18 @@ # UI tests -UI tests are a particular [test suite](compiletest.md#test-suites) of -compiletest. +UI tests are a particular [test suite](compiletest.md#test-suites) of compiletest. ## Introduction The tests in [`tests/ui`] are a collection of general-purpose tests which primarily focus on validating the console output of the compiler, but can be -used for many other purposes. For example, tests can also be configured to [run -the resulting program](#controlling-passfail-expectations) to verify its -behavior. +used for many other purposes. +For example, tests can also be configured to [run +the resulting program](#controlling-passfail-expectations) to verify its behavior. For a survey of each subdirectory's purpose under `tests/ui`, consult the [README.md](https://github.com/rust-lang/rust/tree/HEAD/tests/ui/README.md). -This is useful if you write a new test, and are looking for a category to -place it in. +This is useful if you write a new test, and are looking for a category to place it in. If you need to work with `#![no_std]` cross-compiling tests, consult the [`minicore` test auxiliary](./minicore.md) chapter. @@ -28,61 +26,63 @@ A test consists of a Rust source file located in the `tests/ui` directory. and testing category - placing tests directly in `tests/ui` is not permitted. Compiletest will use `rustc` to compile the test, and compare the output against -the expected output which is stored in a `.stdout` or `.stderr` file located -next to the test. See [Output comparison](#output-comparison) for more. +the expected output which is stored in a `.stdout` or `.stderr` file located next to the test. +See [Output comparison](#output-comparison) for more. -Additionally, errors and warnings should be annotated with comments within the -source file. See [Error annotations](#error-annotations) for more. +Additionally, errors and warnings should be annotated with comments within the source file. +See [Error annotations](#error-annotations) for more. Compiletest [directives](directives.md) in the form of special comments prefixed with `//@` control how the test is compiled and what the expected behavior is. -Tests are expected to fail to compile, since most tests are testing compiler -errors. You can change that behavior with a directive, see [Controlling +Tests are expected to fail to compile, since most tests are testing compiler errors. +You can change that behavior with a directive, see [Controlling pass/fail expectations](#controlling-passfail-expectations). -By default, a test is built as an executable binary. If you need a different -crate type, you can use the `#![crate_type]` attribute to set it as needed. +By default, a test is built as an executable binary. +If you need a different crate type, you can use the `#![crate_type]` attribute to set it as needed. ## Output comparison UI tests store the expected output from the compiler in `.stderr` and `.stdout` -snapshots next to the test. You normally generate these files with the `--bless` -CLI option, and then inspect them manually to verify they contain what you -expect. +snapshots next to the test. +You normally generate these files with the `--bless` +CLI option, and then inspect them manually to verify they contain what you expect. The output is normalized to ignore unwanted differences, see the -[Normalization](#normalization) section. If the file is missing, then -compiletest expects the corresponding output to be empty. +[Normalization](#normalization) section. +If the file is missing, then compiletest expects the corresponding output to be empty. A common reason to use normalization, revisions, and most of the other following tools, -is to account for platform differences. Consider alternatives to these tools, like +is to account for platform differences. +Consider alternatives to these tools, like e.g. using the `extern "rust-invalid"` ABI that is invalid on every platform instead of fixing the test to use cross-compilation and testing every possibly-invalid ABI. -There can be multiple stdout/stderr files. The general form is: +There can be multiple stdout/stderr files. +The general form is: ```text *test-name*`.`*revision*`.`*compare_mode*`.`*extension* ``` -- *test-name* cannot contain dots. This is so that the general form of test +- *test-name* cannot contain dots. + This is so that the general form of test output filenames have a predictable form we can pattern match on in order to track stray test output files. -- *revision* is the [revision](#cfg-revisions) name. This is not included when - not using revisions. -- *compare_mode* is the [compare mode](#compare-modes). This will only be - checked when the given compare mode is active. If the file does not exist, +- *revision* is the [revision](#cfg-revisions) name. + This is not included when not using revisions. +- *compare_mode* is the [compare mode](#compare-modes). + This will only be checked when the given compare mode is active. + If the file does not exist, then compiletest will check for a file without the compare mode. - *extension* is the kind of output being checked: - `stderr` — compiler stderr - `stdout` — compiler stdout - `run.stderr` — stderr when running the test - `run.stdout` — stdout when running the test - - `64bit.stderr` — compiler stderr with `stderr-per-bitwidth` directive on a - 64-bit target - - `32bit.stderr` — compiler stderr with `stderr-per-bitwidth` directive on a - 32-bit target + - `64bit.stderr` — compiler stderr with `stderr-per-bitwidth` directive on a 64-bit target + - `32bit.stderr` — compiler stderr with `stderr-per-bitwidth` directive on a 32-bit target A simple example would be `foo.stderr` next to a `foo.rs` test. A more complex example would be `foo.my-revision.polonius.stderr`. @@ -90,17 +90,16 @@ A more complex example would be `foo.my-revision.polonius.stderr`. There are several [directives](directives.md) which will change how compiletest will check for output files: -- `stderr-per-bitwidth` — checks separate output files based on the target - pointer width. Consider using the `normalize-stderr` directive instead (see - [Normalization](#normalization)). +- `stderr-per-bitwidth` — checks separate output files based on the target pointer width. + Consider using the `normalize-stderr` directive instead (see [Normalization](#normalization)). - `dont-check-compiler-stderr` — Ignores stderr from the compiler. - `dont-check-compiler-stdout` — Ignores stdout from the compiler. - `compare-output-by-lines` — Some tests have non-deterministic orders of output, so we need to compare by lines. UI tests run with `-Zdeduplicate-diagnostics=no` flag which disables rustc's -built-in diagnostic deduplication mechanism. This means you may see some -duplicate messages in the output. This helps illuminate situations where -duplicate diagnostics are being generated. +built-in diagnostic deduplication mechanism. +This means you may see some duplicate messages in the output. +This helps illuminate situations where duplicate diagnostics are being generated. ### Normalization @@ -109,22 +108,22 @@ platforms, mainly about filenames. Compiletest makes the following replacements on the compiler output: -- The directory where the test is defined is replaced with `$DIR`. Example: - `/path/to/rust/tests/ui/error-codes` +- The directory where the test is defined is replaced with `$DIR`. + Example: `/path/to/rust/tests/ui/error-codes` - The directory to the standard library source is replaced with `$SRC_DIR`. Example: `/path/to/rust/library` - Line and column numbers for paths in `$SRC_DIR` are replaced with `LL:COL`. This helps ensure that changes to the layout of the standard library do not - cause widespread changes to the `.stderr` files. Example: - `$SRC_DIR/alloc/src/sync.rs:53:46` -- The base directory where the test's output goes is replaced with - `$TEST_BUILD_DIR`. This only comes up in a few rare circumstances. Example: - `/path/to/rust/build/x86_64-unknown-linux-gnu/test/ui` + cause widespread changes to the `.stderr` files. + Example: `$SRC_DIR/alloc/src/sync.rs:53:46` +- The base directory where the test's output goes is replaced with `$TEST_BUILD_DIR`. + This only comes up in a few rare circumstances. + Example: `/path/to/rust/build/x86_64-unknown-linux-gnu/test/ui` - The real directory to the standard library source is replaced with `$SRC_DIR_REAL`. - The real directory to the compiler source is replaced with `$COMPILER_DIR_REAL`. - Tabs are replaced with `\t`. -- Backslashes (`\`) are converted to forward slashes (`/`) within paths (using a - heuristic). This helps normalize differences with Windows-style paths. +- Backslashes (`\`) are converted to forward slashes (`/`) within paths (using a heuristic). + This helps normalize differences with Windows-style paths. - CRLF newlines are converted to LF. - Error line annotations like `//~ ERROR some message` are removed. - Various v0 and legacy symbol hashes are replaced with placeholders like @@ -135,8 +134,8 @@ the compiler itself to apply some changes to the diagnostic output to make it more suitable for UI testing. For example, it will anonymize line numbers in the output (line numbers -prefixing each source line are replaced with `LL`). In extremely rare -situations, this mode can be disabled with the directive `//@ +prefixing each source line are replaced with `LL`). +In extremely rare situations, this mode can be disabled with the directive `//@ compile-flags: -Z ui-testing=no`. When using `-Z ui-testing=no`, the `--diagnostic-width` argument should also @@ -144,12 +143,14 @@ be set to avoid tests failing or passing depending on the width of the terminal from which the UI test suite is being run. Note: The line and column numbers for `-->` lines pointing to the test are *not* -normalized, and left as-is. This ensures that the compiler continues to point to -the correct location, and keeps the stderr files readable. Ideally all -line/column information would be retained, but small changes to the source +normalized, and left as-is. +This ensures that the compiler continues to point to +the correct location, and keeps the stderr files readable. +Ideally all line/column information would be retained, but small changes to the source causes large diffs, and more frequent merge conflicts and test errors. -Sometimes these built-in normalizations are not enough. In such cases, you may +Sometimes these built-in normalizations are not enough. +In such cases, you may provide custom normalization rules using `normalize-*` directives, e.g. ```rust,ignore @@ -161,8 +162,8 @@ provide custom normalization rules using `normalize-*` directives, e.g. This tells the test, on 32-bit platforms, whenever the compiler writes `fn() (32 bits)` to stderr, it should be normalized to read `fn() ($PTR bits)` instead. -Similar for 64-bit. The replacement is performed by regexes using default regex -flavor provided by `regex` crate. +Similar for 64-bit. +The replacement is performed by regexes using default regex flavor provided by `regex` crate. The corresponding reference file will use the normalized output to test both 32-bit and 64-bit platforms: @@ -175,16 +176,15 @@ The corresponding reference file will use the normalized output to test both ... ``` -Please see [`ui/transmute/main.rs`][mrs] and [`main.stderr`] for a concrete -usage example. +Please see [`ui/transmute/main.rs`][mrs] and [`main.stderr`] for a concrete usage example. [mrs]: https://github.com/rust-lang/rust/blob/HEAD/tests/ui/transmute/main.rs [`main.stderr`]: https://github.com/rust-lang/rust/blob/HEAD/tests/ui/transmute/main.stderr ## Error annotations -Error annotations specify the errors that the compiler is expected to emit. They -are "attached" to the line in source where the error is located. +Error annotations specify the errors that the compiler is expected to emit. +They are "attached" to the line in source where the error is located. ```rust,ignore fn main() { @@ -193,30 +193,30 @@ fn main() { ``` Although UI tests have a `.stderr` file which contains the entire compiler -output, UI tests require that errors are also annotated within the source. This -redundancy helps avoid mistakes since the `.stderr` files are usually -auto-generated. It also helps to directly see where the error spans are expected -to point to by looking at one file instead of having to compare the `.stderr` -file with the source. Finally, they ensure that no additional unexpected errors -are generated. +output, UI tests require that errors are also annotated within the source. +This redundancy helps avoid mistakes since the `.stderr` files are usually +auto-generated. +It also helps to directly see where the error spans are expected +to point to by looking at one file instead of having to compare the `.stderr` file with the source. +Finally, they ensure that no additional unexpected errors are generated. They have several forms, but generally are a comment with the diagnostic level -(such as `ERROR`) and a substring of the expected error output. You don't have -to write out the entire message, just make sure to include the important part of +(such as `ERROR`) and a substring of the expected error output. +You don't have to write out the entire message, just make sure to include the important part of the message to make it self-documenting. -Most error annotations need to match with the line of the diagnostic. There are -several ways to match the message with the line (see the examples below): +Most error annotations need to match with the line of the diagnostic. +There are several ways to match the message with the line (see the examples below): * `~`: Associates the error level and message with the *current* line -* `~^`: Associates the error level and message with the *previous* error - annotation line. Each caret (`^`) that you add adds a line to this, so `~^^^` +* `~^`: Associates the error level and message with the *previous* error annotation line. + Each caret (`^`) that you add adds a line to this, so `~^^^` is three lines above the error annotation line. * `~|`: Associates the error level and message with the *same* line as the *previous comment*. This is more convenient than using multiple carets when there are multiple messages associated with the same line. -* `~v`: Associates the error level and message with the *next* error - annotation line. Each symbol (`v`) that you add adds a line to this, so `~vvv` +* `~v`: Associates the error level and message with the *next* error annotation line. + Each symbol (`v`) that you add adds a line to this, so `~vvv` is three lines below the error annotation line. Example: @@ -232,8 +232,9 @@ The space character between `//~` (or other variants) and the subsequent text is negligible (i.e. there is no semantic difference between `//~ ERROR` and `//~ERROR` although the former is more common in the codebase). -`~? ` (example being `~? ERROR`) -is used to match diagnostics _without_ line info at all, +`~? +` (example being `~? +ERROR`) is used to match diagnostics _without_ line info at all, or where the line info is outside the main test file[^main test file]. These annotations can be placed on any line in the test file. @@ -260,8 +261,8 @@ fn main() { #### Positioned below error line -Use the `//~^` idiom with number of carets in the string to indicate the number -of lines above. In the example below, the error line is four lines above the +Use the `//~^` idiom with number of carets in the string to indicate the number of lines above. +In the example below, the error line is four lines above the error annotation line so four carets are included in the annotation. ```rust,ignore @@ -296,8 +297,8 @@ fn main() { #### Positioned above error line -Use the `//~v` idiom with number of v's in the string to indicate the number -of lines below. This is typically used in lexer or parser tests matching on errors like unclosed +Use the `//~v` idiom with number of v's in the string to indicate the number of lines below. +This is typically used in lexer or parser tests matching on errors like unclosed delimiter or unclosed literal happening at the end of file. ```rust,ignore @@ -337,8 +338,8 @@ fn main() { ``` We want to ensure this shows "index out of bounds", but we cannot use the `ERROR` -annotation since the runtime error doesn't have any span. Then it's time to use the -`error-pattern` directive: +annotation since the runtime error doesn't have any span. +Then it's time to use the `error-pattern` directive: ```rust,ignore //@ error-pattern: index out of bounds @@ -385,7 +386,8 @@ by the compiler instead of or in addition to structured json. `//~` by default. Other kinds only need to be line-annotated if at least one annotation of that kind appears -in the test file. For example, one `//~ NOTE` will also require all other `//~ NOTE`s in the file +in the test file. +For example, one `//~ NOTE` will also require all other `//~ NOTE`s in the file to be written out explicitly. Use directive `//@ dont-require-annotations` to opt out of exhaustive annotations. @@ -398,15 +400,16 @@ for example secondary lines of multiline diagnostics, or ubiquitous diagnostics like `aborting due to N previous errors`. UI tests use the `-A unused` flag by default to ignore all unused warnings, as -unused warnings are usually not the focus of a test. However, simple code -samples often have unused warnings. If the test is specifically testing an +unused warnings are usually not the focus of a test. +However, simple code samples often have unused warnings. +If the test is specifically testing an unused warning, just add the appropriate `#![warn(unused)]` attribute as needed. ### `cfg` revisions When using [revisions](compiletest.md#revisions), different messages can be -conditionally checked based on the current revision. This is done by placing the -revision cfg name in brackets like this: +conditionally checked based on the current revision. +This is done by placing the revision cfg name in brackets like this: ```rust,ignore //@ edition:2018 @@ -428,7 +431,8 @@ In this example, the second error message is only emitted in the `mir` revision. The `thir` revision only emits the first error. If the `cfg` causes the compiler to emit different output, then a test can have -multiple `.stderr` files for the different outputs. In the example above, there +multiple `.stderr` files for the different outputs. +In the example above, there would be a `.mir.stderr` and `.thir.stderr` file with the different outputs of the different revisions. @@ -439,10 +443,10 @@ the different revisions. ## Controlling pass/fail expectations By default, a UI test is expected to **generate a compile error** because most -of the tests are checking for invalid input and error diagnostics. However, you -can also make UI tests where compilation is expected to succeed, and you can -even run the resulting program. Just add one of the following -[directives](directives.md): +of the tests are checking for invalid input and error diagnostics. +However, you can also make UI tests where compilation is expected to succeed, and you can +even run the resulting program. +Just add one of the following [directives](directives.md): - Pass directives: - `//@ check-pass` — compilation should succeed but skip codegen @@ -460,32 +464,33 @@ even run the resulting program. Just add one of the following - Second time is to ensure that the full compile fails - `//@ run-fail` — compilation should succeed, but running the resulting binary should make it exit with a code in the range `1..=127` which - indicates regular failure. On targets without unwind support, crashes - are also accepted. + indicates regular failure. + On targets without unwind support, crashes are also accepted. - `//@ run-crash` — compilation should succeed, but running the resulting - binary should fail with a crash. Crashing is defined as "not exiting with - a code in the range `0..=127`". Example on Linux: Termination by `SIGABRT` - or `SIGSEGV`. Example on Windows: Exiting with the code for - `STATUS_ILLEGAL_INSTRUCTION` (`0xC000001D`). + binary should fail with a crash. + Crashing is defined as "not exiting with a code in the range `0..=127`". + Example on Linux: Termination by `SIGABRT` or `SIGSEGV`. + Example on Windows: Exiting with the code for `STATUS_ILLEGAL_INSTRUCTION` (`0xC000001D`). - `//@ run-fail-or-crash` — compilation should succeed, but running the - resulting binary should either `run-fail` or `run-crash`. Useful if a test - crashes on some targets but just fails on others. + resulting binary should either `run-fail` or `run-crash`. + Useful if a test crashes on some targets but just fails on others. -For `run-pass`. `run-fail`, `run-crash` and `run-fail-or-crash` tests, by +For `run-pass`. +`run-fail`, `run-crash` and `run-fail-or-crash` tests, by default the output of the program itself is not checked. -If you want to check the output of running the program, include the -`check-run-results` directive. This will check for a `.run.stderr` and +If you want to check the output of running the program, include the `check-run-results` directive. +This will check for a `.run.stderr` and `.run.stdout` files to compare against the actual output of the program. -Tests with the `*-pass` directives can be overridden with the `--pass` -command-line option: +Tests with the `*-pass` directives can be overridden with the `--pass` command-line option: ```sh ./x test tests/ui --pass check ``` -The `--pass` option only affects UI tests. Using `--pass check` can run the UI +The `--pass` option only affects UI tests. +Using `--pass check` can run the UI test suite much faster (roughly twice as fast on my system), though obviously not exercising as much. @@ -496,13 +501,12 @@ test won't work properly with that override. ## Known bugs The `known-bug` directive may be used for tests that demonstrate a known bug -that has not yet been fixed. Adding tests for known bugs is helpful for several -reasons, including: +that has not yet been fixed. +Adding tests for known bugs is helpful for several reasons, including: -1. Maintaining a functional test that can be conveniently reused when the bug is - fixed. -2. Providing a sentinel that will fail if the bug is incidentally fixed. This - can alert the developer so they know that the associated issue has been fixed +1. Maintaining a functional test that can be conveniently reused when the bug is fixed. +2. Providing a sentinel that will fail if the bug is incidentally fixed. + This can alert the developer so they know that the associated issue has been fixed and can possibly be closed. This directive takes comma-separated issue numbers as arguments, or `"unknown"`: @@ -513,21 +517,21 @@ This directive takes comma-separated issue numbers as arguments, or `"unknown"`: - `//@ known-bug: unknown` (when there is no known issue yet; preferably open one if it does not already exist) -Do not include [error annotations](#error-annotations) in a test with -`known-bug`. The test should still include other normal directives and -stdout/stderr files. +Do not include [error annotations](#error-annotations) in a test with `known-bug`. +The test should still include other normal directives and stdout/stderr files. ## Test organization When deciding where to place a test file, please try to find a subdirectory that -best matches what you are trying to exercise. Do your best to keep things -organized. Admittedly it can be difficult as some tests can overlap different +best matches what you are trying to exercise. +Do your best to keep things organized. +Admittedly it can be difficult as some tests can overlap different categories, and the existing layout may not fit well. -Name the test by a concise description of what the test is checking. Avoid -including the issue number in the test name. See [best -practices](best-practices.md) for a more in-depth discussion of this. +Name the test by a concise description of what the test is checking. +Avoid including the issue number in the test name. +See [best practices](best-practices.md) for a more in-depth discussion of this. Ideally, the test should be added to a directory that helps identify what piece of code is being tested here (e.g., @@ -535,30 +539,29 @@ of code is being tested here (e.g., When writing a new feature, you may want to **create a subdirectory to store your tests**. For example, if you are implementing RFC 1234 ("Widgets"), then it -might make sense to put the tests in a directory like -`tests/ui/rfc1234-widgets/`. +might make sense to put the tests in a directory like `tests/ui/rfc1234-widgets/`. In other cases, there may already be a suitable directory. -Over time, the [`tests/ui`] directory has grown very fast. There is a check in -[tidy](intro.md#tidy) that will ensure none of the subdirectories has more than -1000 entries. Having too many files causes problems because it isn't editor/IDE -friendly and the GitHub UI won't show more than 1000 entries. However, since -`tests/ui` (UI test root directory) and `tests/ui/issues` directories have more -than 1000 entries, we set a different limit for those directories. So, please -avoid putting a new test there and try to find a more relevant place. +Over time, the [`tests/ui`] directory has grown very fast. +There is a check in [tidy](intro.md#tidy) that will ensure none of the subdirectories has more than +1000 entries. +Having too many files causes problems because it isn't editor/IDE +friendly and the GitHub UI won't show more than 1000 entries. +However, since `tests/ui` (UI test root directory) and `tests/ui/issues` directories have more +than 1000 entries, we set a different limit for those directories. +So, please avoid putting a new test there and try to find a more relevant place. -For example, if your test is related to closures, you should put it in -`tests/ui/closures`. When you reach the limit, you could increase it by tweaking -[here][ui test tidy]. +For example, if your test is related to closures, you should put it in `tests/ui/closures`. +When you reach the limit, you could increase it by tweaking [here][ui test tidy]. [ui test tidy]: https://github.com/rust-lang/rust/blob/HEAD/src/tools/tidy/src/ui_tests.rs ## Rustfix tests UI tests can validate that diagnostic suggestions apply correctly and that the -resulting changes compile correctly. This can be done with the `run-rustfix` -directive: +resulting changes compile correctly. +This can be done with the `run-rustfix` directive: ```rust,ignore //@ run-rustfix @@ -574,37 +577,34 @@ pub struct not_camel_case {} Rustfix tests should have a file with the `.fixed` extension which contains the source file after the suggestion has been applied. -- When the test is run, compiletest first checks that the correct lint/warning - is generated. -- Then, it applies the suggestion and compares against `.fixed` (they must - match). -- Finally, the fixed source is compiled, and this compilation is required to - succeed. +- When the test is run, compiletest first checks that the correct lint/warning is generated. +- Then, it applies the suggestion and compares against `.fixed` (they must match). +- Finally, the fixed source is compiled, and this compilation is required to succeed. Usually when creating a rustfix test you will generate the `.fixed` file automatically with the `x test --bless` option. The `run-rustfix` directive will cause *all* suggestions to be applied, even if -they are not [`MachineApplicable`](../diagnostics.md#suggestions). If this is a -problem, then you can add the `rustfix-only-machine-applicable` directive in -addition to `run-rustfix`. This should be used if there is a mixture of -different suggestion levels, and some of the non-machine-applicable ones do not -apply cleanly. +they are not [`MachineApplicable`](../diagnostics.md#suggestions). +If this is a problem, then you can add the `rustfix-only-machine-applicable` directive in +addition to `run-rustfix`. +This should be used if there is a mixture of +different suggestion levels, and some of the non-machine-applicable ones do not apply cleanly. ## Compare modes [Compare modes](compiletest.md#compare-modes) can be used to run all tests with -different flags from what they are normally compiled with. In some cases, this -might result in different output from the compiler. To support this, different +different flags from what they are normally compiled with. +In some cases, this might result in different output from the compiler. +To support this, different output files can be saved which contain the output based on the compare mode. For example, when using the Polonius mode, a test `foo.rs` will first look for -expected output in `foo.polonius.stderr`, falling back to the usual `foo.stderr` -if not found. This is useful as different modes can sometimes result in -different diagnostics and behavior. This can help track which tests have -differences between the modes, and to visually inspect those diagnostic -differences. +expected output in `foo.polonius.stderr`, falling back to the usual `foo.stderr` if not found. +This is useful as different modes can sometimes result in different diagnostics and behavior. +This can help track which tests have +differences between the modes, and to visually inspect those diagnostic differences. If in the rare case you encounter a test that has different behavior, you can run something like the following to generate the alternate stderr file: @@ -618,15 +618,15 @@ Currently none of the compare modes are checked in CI for UI tests. ## `rustc_*` TEST attributes The compiler defines several perma-unstable `#[rustc_*]` attributes gated behind -the internal feature `rustc_attrs` that dump extra compiler-internal -information. See the corresponding subsection in [compiler debugging] for more -details. +the internal feature `rustc_attrs` that dump extra compiler-internal information. +See the corresponding subsection in [compiler debugging] for more details. They can be used in tests to more precisely, legibly and easily test internal compiler state in cases where it would otherwise be very hard to do the same -with "user-facing" Rust alone. Indeed, one could say that this slightly abuses -the term "UI" (*user* interface) and turns such UI tests from black-box tests -into white-box ones. Use them carefully and sparingly. +with "user-facing" Rust alone. +Indeed, one could say that this slightly abuses +the term "UI" (*user* interface) and turns such UI tests from black-box tests into white-box ones. +Use them carefully and sparingly. [compiler debugging]: ../compiler-debugging.md#rustc_-test-attributes @@ -651,5 +651,4 @@ in-source lint level attributes as required. Note that the `rustfix` version will *not* have `-A unused` passed, meaning that you may have to `#[allow(unused)]` to suppress `unused` -lints on the rustfix'd file (because we might be testing rustfix -on `unused` lints themselves). +lints on the rustfix'd file (because we might be testing rustfix on `unused` lints themselves). From dd8ae230c7988165350502e01e45a999c1d89b0a Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 23:08:29 +0200 Subject: [PATCH 378/583] some improvements --- src/doc/rustc-dev-guide/src/tests/ui.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index bd058fbed02e..0b5a5b3ccbab 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -202,8 +202,8 @@ Finally, they ensure that no additional unexpected errors are generated. They have several forms, but generally are a comment with the diagnostic level (such as `ERROR`) and a substring of the expected error output. -You don't have to write out the entire message, just make sure to include the important part of -the message to make it self-documenting. +You don't have to write out the entire message, +but be sure to include the important part of the message to make it self-documenting. Most error annotations need to match with the line of the diagnostic. There are several ways to match the message with the line (see the examples below): @@ -469,15 +469,14 @@ Just add one of the following [directives](directives.md): - `//@ run-crash` — compilation should succeed, but running the resulting binary should fail with a crash. Crashing is defined as "not exiting with a code in the range `0..=127`". - Example on Linux: Termination by `SIGABRT` or `SIGSEGV`. - Example on Windows: Exiting with the code for `STATUS_ILLEGAL_INSTRUCTION` (`0xC000001D`). + - Example on Linux: Termination by `SIGABRT` or `SIGSEGV`. + - Example on Windows: Exiting with the code for `STATUS_ILLEGAL_INSTRUCTION` (`0xC000001D`). - `//@ run-fail-or-crash` — compilation should succeed, but running the resulting binary should either `run-fail` or `run-crash`. Useful if a test crashes on some targets but just fails on others. -For `run-pass`. -`run-fail`, `run-crash` and `run-fail-or-crash` tests, by -default the output of the program itself is not checked. +For `run-pass`, `run-fail`, `run-crash`, and `run-fail-or-crash` tests, +the output of the program itself is not checked by default. If you want to check the output of running the program, include the `check-run-results` directive. This will check for a `.run.stderr` and @@ -526,7 +525,7 @@ The test should still include other normal directives and stdout/stderr files. When deciding where to place a test file, please try to find a subdirectory that best matches what you are trying to exercise. Do your best to keep things organized. -Admittedly it can be difficult as some tests can overlap different +Admittedly, it can be difficult as some tests can overlap different categories, and the existing layout may not fit well. Name the test by a concise description of what the test is checking. @@ -621,7 +620,7 @@ The compiler defines several perma-unstable `#[rustc_*]` attributes gated behind the internal feature `rustc_attrs` that dump extra compiler-internal information. See the corresponding subsection in [compiler debugging] for more details. -They can be used in tests to more precisely, legibly and easily test internal +They can be used in tests to more precisely, legibly, and easily test internal compiler state in cases where it would otherwise be very hard to do the same with "user-facing" Rust alone. Indeed, one could say that this slightly abuses From dd4870d0cc7b75b644206d00fc229329f5c99dfe Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 23:13:39 +0200 Subject: [PATCH 379/583] fix sembr tool corner case --- src/doc/rustc-dev-guide/ci/sembr/src/main.rs | 4 +++- src/doc/rustc-dev-guide/src/tests/ui.md | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 1de0638deb97..0ae77af047e6 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -24,7 +24,7 @@ static REGEX_IGNORE_END: LazyLock = static REGEX_IGNORE_LINK_TARGETS: LazyLock = LazyLock::new(|| Regex::new(r"^\[.+\]: ").unwrap()); static REGEX_SPLIT: LazyLock = - LazyLock::new(|| Regex::new(r"([^\.\d\-\*]\.|[^r]\?|!)\s").unwrap()); + LazyLock::new(|| Regex::new(r"([^\.\d\-\*]\.|[^r\~]\?|!)\s").unwrap()); // list elements, numbered (1.) or not (- and *) static REGEX_LIST_ENTRY: LazyLock = LazyLock::new(|| Regex::new(r"^\s*(\d\.|\-|\*|\d\))\s+").unwrap()); @@ -205,6 +205,7 @@ git log main.. compiler o? whatever r? @reviewer r? @reviewer +~? diagnostic "; let expected = " # some. heading @@ -237,6 +238,7 @@ o? whatever r? @reviewer r? @reviewer +~? diagnostic "; assert_eq!(expected, comply(original)); } diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md index 0b5a5b3ccbab..7332d1fb3851 100644 --- a/src/doc/rustc-dev-guide/src/tests/ui.md +++ b/src/doc/rustc-dev-guide/src/tests/ui.md @@ -232,9 +232,8 @@ The space character between `//~` (or other variants) and the subsequent text is negligible (i.e. there is no semantic difference between `//~ ERROR` and `//~ERROR` although the former is more common in the codebase). -`~? -` (example being `~? -ERROR`) is used to match diagnostics _without_ line info at all, +`~? ` (example being `~? ERROR`) +is used to match diagnostics _without_ line info at all, or where the line info is outside the main test file[^main test file]. These annotations can be placed on any line in the test file. From 43fc5a40ee5611cee8a69fc8198c8d2f46358195 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 23:14:55 +0200 Subject: [PATCH 380/583] sembr src/walkthrough.md --- src/doc/rustc-dev-guide/src/walkthrough.md | 40 ++++++++++------------ 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/walkthrough.md b/src/doc/rustc-dev-guide/src/walkthrough.md index 77d0953c4025..212fb298fd0b 100644 --- a/src/doc/rustc-dev-guide/src/walkthrough.md +++ b/src/doc/rustc-dev-guide/src/walkthrough.md @@ -41,18 +41,15 @@ Here is a quick list. We will go through each of these in order below. As I mentioned before, not all of these are needed for every type of contribution. -- **Idea discussion/Pre-RFC** A Pre-RFC is an early draft or design discussion - of a feature. +- **Idea discussion/Pre-RFC** A Pre-RFC is an early draft or design discussion of a feature. This stage is intended to flesh out the design space a bit and get a grasp on the different merits and problems with an idea. - It's a great way to get early feedback on your idea before presenting it to the wider - audience. + It's a great way to get early feedback on your idea before presenting it to the wider audience. You can find the original discussion [here][prerfc]. -- **RFC** This is when you formally present your idea to the community for - consideration. +- **RFC** This is when you formally present your idea to the community for consideration. You can find the RFC [here][rfc]. -- **Implementation** Implement your idea unstably in the compiler. You can - find the original implementation [here][impl1]. +- **Implementation** Implement your idea unstably in the compiler. + You can find the original implementation [here][impl1]. - **Possibly iterate/refine** As the community gets experience with your feature on the nightly compiler and in `std`, there may be additional feedback about design choice that might be adjusted. @@ -114,8 +111,7 @@ In this case, the discussion converged pretty quickly, but for some ideas, a lot more discussion can happen (e.g. see [this RFC][nonascii] which received a whopping 684 comments!). If that happens, don't be discouraged; -it means the community is interested in your idea, but it perhaps needs some -adjustments. +it means the community is interested in your idea, but it perhaps needs some adjustments. [nonascii]: https://github.com/rust-lang/rfcs/pull/2457 @@ -138,10 +134,10 @@ last chance for people to bring up objections. When the FCP is over, the disposition is adopted. Here are the three possible dispositions: -- _Merge_: accept the feature. Here is the proposal to merge for our [`?` macro - feature][rfcmerge]. -- _Close_: this feature in its current form is not a good fit for rust. Don't - be discouraged if this happens to your RFC, and don't take it personally. +- _Merge_: accept the feature. + Here is the proposal to merge for our [`?` macro feature][rfcmerge]. +- _Close_: this feature in its current form is not a good fit for rust. + Don't be discouraged if this happens to your RFC, and don't take it personally. This is not a reflection on you, but rather a community decision that rust will go a different direction. - _Postpone_: there is interest in going this direction but not at the moment. @@ -164,10 +160,12 @@ Here is the tracking issue on for our [`?` macro feature][tracking]. An eRFC is a variant of the RFC process used for complex features where the high-level need is clear, but the design space is too large to settle on a detailed specification upfront. Instead of providing a final design, an eRFC outlines a high-level strategy to authorize -a period of active experimentation. This allows the team to implement the feature behind +a period of active experimentation. +This allows the team to implement the feature behind a feature gate and gather practical data, which then informs a subsequent formal RFC for stabilization. While this process was used for major features like coroutines ([see RFC 2033][rfc2033]), -the explicit "eRFC" label is rarely used today. The project now generally prefers approving a standard +the explicit "eRFC" label is rarely used today. +The project now generally prefers approving a standard RFC for an initial version and iterating on it through the nightly channel before final stabilization. [rfc2033]: https://github.com/rust-lang/rfcs/pull/2033#issuecomment-309057591 @@ -199,8 +197,8 @@ When a new feature is implemented, it goes behind a _feature gate_, which means you have to use `#![feature(my_feature_name)]` to use the feature. The feature gate is removed when the feature is stabilized. -**Most bug fixes and improvements** don't require a feature gate. You can just -make your changes/improvements. +**Most bug fixes and improvements** don't require a feature gate. +You can just make your changes/improvements. When you open a PR on the [rust-lang/rust], a bot will assign your PR to a reviewer. If there is a particular Rust team member you are working with, you can @@ -217,8 +215,7 @@ When you finished iterating on the changes, you can mark the PR as `S-waiting-on-author` label and add the `S-waiting-on-review` label. Feel free to ask questions or discuss things you don't understand or disagree with. -However, recognize that the PR won't be merged unless someone on the Rust team approves -it. +However, recognize that the PR won't be merged unless someone on the Rust team approves it. If a reviewer leave a comment like `r=me after fixing ...`, that means they approve the PR and you can merge it with comment with `@bors r=reviewer-github-id`(e.g. `@bors r=eddyb`) to merge it after fixing trivial issues. @@ -235,8 +232,7 @@ If all tests pass, the PR is merged and becomes part of the next nightly compile There are a couple of things that may happen for some PRs during the review process -- If the change is substantial enough, the reviewer may request an FCP on - the PR. +- If the change is substantial enough, the reviewer may request an FCP on the PR. This gives all members of the appropriate team a chance to review the changes. - If the change may cause breakage, the reviewer may request a [crater] run. This compiles the compiler with your changes and then attempts to compile all From 7d8b5324ebc8eb98b46a0dd749eb3dcb84c18ae9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 23:27:41 +0200 Subject: [PATCH 381/583] sembr src/debuginfo/lldb-visualizers.md --- .../src/debuginfo/lldb-visualizers.md | 234 +++++++++++------- 1 file changed, 144 insertions(+), 90 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md index 40ab9dce375c..c329e23d6006 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md @@ -28,7 +28,8 @@ LLDB provides 3 mechanisms for customizing output: ## Formats -The official documentation is [here](https://lldb.llvm.org/use/variable.html#type-format). In short, +The official documentation is [here](https://lldb.llvm.org/use/variable.html#type-format). +In short, formats allow one to set the default print format for primitive types (e.g. print `25u8` as decimal `25`, hex `0x19`, or binary `00011001`). @@ -47,13 +48,15 @@ plugins and CLI. [sbvalue]: https://lldb.llvm.org/python_api/lldb.SBValue.html A Synthetic Provider is a Python class, written with a specific interface, that is associated with -one or more Rust types. The Synthetic Provider wraps `SBValue` objects and LLDB will call our +one or more Rust types. +The Synthetic Provider wraps `SBValue` objects and LLDB will call our class's functions when inspecting the variable. The wrapped value is still an `SBValue`, but when calling e.g. `SBValue.GetChildAtIndex`, it will -internally call `SyntheticProvider.get_child_at_index`. You can check if a value has a synthetic -provider via `SBValue.IsSynthetic()`, and which synthetic it is via `SBValue.GetTypeSynthetic()`. If -you want to interact with the underlying non-synthetic value, you can call +internally call `SyntheticProvider.get_child_at_index`. +You can check if a value has a synthetic +provider via `SBValue.IsSynthetic()`, and which synthetic it is via `SBValue.GetTypeSynthetic()`. +If you want to interact with the underlying non-synthetic value, you can call `SBValue.GetNonSyntheticValue()`. @@ -83,18 +86,20 @@ class SyntheticProvider: def get_value(self) -> SBValue: ... ``` -Below are explanations of the methods, their quirks, and how they should generally be used. If a -method overrides an `SBValue` method, that method will be listed. +Below are explanations of the methods, their quirks, and how they should generally be used. +If a method overrides an `SBValue` method, that method will be listed. ### `__init__` This function is called once per object, and must store the `valobj` in the python class so that it -is accessible elsewhere. Very little else should be done here. +is accessible elsewhere. +Very little else should be done here. ### (optional) `update` -This function is called prior to LLDB interacting with a variable, but after `__init__`. LLDB tracks -whether `update` has already been called. If it has been, and if it is not possible for the variable +This function is called prior to LLDB interacting with a variable, but after `__init__`. +LLDB tracks whether `update` has already been called. +If it has been, and if it is not possible for the variable to have changed (e.g. inspecting the same variable a second time without stepping), it will omit the call to `update`. @@ -107,15 +112,18 @@ Typical operations include storing the heap pointer, length, capacity, and eleme determining an enum variable's variant, or checking which slots of a `HashMap` are occupied. The bool returned from this function is somewhat complicated, see: -[`update` caching](#update-caching) below for more info. When in doubt, return `False`/`None`. +[`update` caching](#update-caching) below for more info. +When in doubt, return `False`/`None`. Currently (Nov 2025), none of the visualizers return `True`, but that may change as the debug info test suite is improved. #### `update` caching -LLDB attempts to cache values when possible, including child values. This cache is effectively the +LLDB attempts to cache values when possible, including child values. +This cache is effectively the number of child objects, and the addresses of the underlying debugee memory that the child object -represents. By returning `True`, you indicate to LLDB that the number of children and the addresses +represents. +By returning `True`, you indicate to LLDB that the number of children and the addresses of those children have not changed since the last time `update` was run, meaning it can reuse the cached children. @@ -123,19 +131,23 @@ cached children. information**. Returning `False` indicates that there have been changes, the cache will be flushed, and the -children will be fetched from scratch. It is the safer option if you are unsure. +children will be fetched from scratch. +It is the safer option if you are unsure. -The only relationship that matters is parent-to-child. Grandchildren depend on the `update` function -of their direct parent, not that of the grandparent. +The only relationship that matters is parent-to-child. +Grandchildren depend on the `update` function of their direct parent, not that of the grandparent. -It is important to view the child cache as pointers-to-memory. For example, if a slice's `data_ptr` -value and `length` have not changed, returning `True` is appropriate. Even if the slice is mutable +It is important to view the child cache as pointers-to-memory. +For example, if a slice's `data_ptr` +value and `length` have not changed, returning `True` is appropriate. +Even if the slice is mutable and elements of it are overwritten (e.g. `slice[0] = 15`), because the child cache consists of *pointers*, they will reflect the new data at that memory location. Conversely, if `data_ptr` has changed, that means it is pointing to a new location in memory, the -child pointers are invalid, and the cache must be flushed. If the `length` has changed, we need to -flush the cache to reflect the new number of children. If `length` has changed but `data_ptr` has +child pointers are invalid, and the cache must be flushed. +If the `length` has changed, we need to flush the cache to reflect the new number of children. +If `length` has changed but `data_ptr` has not, it is possible to store the old children in the `SyntheticProvider` itself (e.g. `list[SBValue]`) and dole those out rather than generating them from scratch, only creating new children if they do not already exist in the `SyntheticProvider`'s list. @@ -159,19 +171,21 @@ Often, this will be a one-liner of `return True`/`return False` or > Overrides `SBValue.GetNumChildren` -Returns the total number of children that LLDB should try to access when printing the type. This -number **does not** need to match to total number of synthetic children. +Returns the total number of children that LLDB should try to access when printing the type. +This number **does not** need to match to total number of synthetic children. The `max_children` argument can be returned if calculating the number of children can be expensive (e.g. linked list). If this is not a consideration, `max_children` can be omitted from the function signature. Additionally, fields can be intentionally "hidden" from LLDB while still being accessible to the -user. For example, one might want a `vec![1, 2, 3]` to display only its elements, but still have the -`len` and `capacity` values accessible on request. By returning `3` from `num_children`, one can +user. +For example, one might want a `vec![1, 2, 3]` to display only its elements, but still have the +`len` and `capacity` values accessible on request. +By returning `3` from `num_children`, one can restrict LLDB to only displaying `[1, 2, 3]`, while users can still directly access `v.len` and -`v.capacity`. See: [Example Provider: Vec\](#example-provider-vect) to see an implementation of -this. +`v.capacity`. +See: [Example Provider: Vec\](#example-provider-vect) to see an implementation of this. ### `get_child_index` @@ -179,12 +193,14 @@ this. > > Affects `SBValue.GetChildMemberWithName` -Given a name, returns the index that the child should be accessed at. It is expected that the return -value of this function is passed directly to `get_child_at_index`. As with `num_children`, the +Given a name, returns the index that the child should be accessed at. +It is expected that the return value of this function is passed directly to `get_child_at_index`. +As with `num_children`, the values returned here *can* be arbitrary, so long as they are properly coordinated with `get_child_at_index`. -One special value is `$$dereference$$`. Accounting for this pseudo-field will allow LLDB to use the +One special value is `$$dereference$$`. +Accounting for this pseudo-field will allow LLDB to use the `SBValue` returned from `get_child_at_index` as the result of a dereference via LLDB's expression parser (e.g. `*val` and `val->field`) @@ -192,24 +208,28 @@ parser (e.g. `*val` and `val->field`) > Overrides `SBValue.GetChildAtIndex` -Given an index, returns a child `SBValue`. Often these are generated via +Given an index, returns a child `SBValue`. +Often these are generated via `SBValue.CreateValueFromAddress`, but less commonly `SBValue.CreateChildAtOffset`, -`SBValue.CreateValueFromExpression`, and `SBValue.CreateValueFromData`. These functions can be a +`SBValue.CreateValueFromExpression`, and `SBValue.CreateValueFromData`. +These functions can be a little finicky, so you may need to fiddle with them to get the output you want. -In some cases, `SBValue.Clone` is appropriate. It creates a new child that is an exact copy of an -existing child, but with a new name. This is useful for cases like tuples, which have field names of +In some cases, `SBValue.Clone` is appropriate. +It creates a new child that is an exact copy of an existing child, but with a new name. +This is useful for cases like tuples, which have field names of the style `__0`, `__1`, ... when we would prefer they were named `0`, `1`, ... -Small alterations can be made to the resulting child before it is returned. This is useful for -`&str`/`String`, where we would prefer if the children were displayed as +Small alterations can be made to the resulting child before it is returned. +This is useful for `&str`/`String`, where we would prefer if the children were displayed as `lldb.eFormatBytesWithASCII` rather than just as a decimal value. ### (optional) `get_type_name` > Overrides `SBValue.GetDisplayTypeName` -Overrides the displayed name of a type. For a synthetic `SBValue` whose type name is overridden, the +Overrides the displayed name of a type. +For a synthetic `SBValue` whose type name is overridden, the original type name can still be retrieved via `SBValue.GetTypeName()` and `SBValue.GetType().GetName()` @@ -240,32 +260,39 @@ Summary providers are python functions of the following form: def SummaryProvider(valobj: SBValue, _lldb_internal) -> str: ... ``` -Where the returned string is passed verbatim to the user. If the returned value isn't a string, it +Where the returned string is passed verbatim to the user. +If the returned value isn't a string, it is naively convered to a string (e.g. `return None` prints `"None"`, not an empty string). If the `SBValue` passed in is of a type that has a Synthetic Provider, `valobj.IsSynthetic()` will -return `True`, and the synthetic's corresponding functions will be used. If this is undesirable, the -original value can be retrieved via `valobj.GetNonSyntheticValue()`. This can be helpful in cases +return `True`, and the synthetic's corresponding functions will be used. +If this is undesirable, the original value can be retrieved via `valobj.GetNonSyntheticValue()`. +This can be helpful in cases like `String`, where individually calling `GetChildAtIndex` in a loop is much slower than accessing the heap pointer, reading the whole byte array directly from the debugee's memory, and using Python's `bytes.decode()`. ### Instance Summaries -Regular `SummaryProvider` functions take an opaque `SBValue`. That `SBValue` will reflect the type's +Regular `SummaryProvider` functions take an opaque `SBValue`. +That `SBValue` will reflect the type's `SyntheticProvider` if one exists, but we cannot access the `SyntheticProvider` instance itself, or -any of its internal implementation details. This is deterimental in cases where we need some of -those internal details to help complete the summary. Currently (Nov 2025), in the synthetic we just +any of its internal implementation details. +This is deterimental in cases where we need some of +those internal details to help complete the summary. +Currently (Nov 2025), in the synthetic we just run the non-synthetic value through the synthetic provider (`synth = SyntheticProvider(valobj.GetNonSyntheticValue(), _dict)`), but this is obviously suboptimal and there are plans to use the method outlined below. -Instead, we can leverage the Python module's state to allow for instance summaries. Prior art for +Instead, we can leverage the Python module's state to allow for instance summaries. +Prior art for this technique exists in the [old CodeLLDB Rust visualizer scripts](https://github.com/vadimcn/codelldb/blob/cf9574977b80e29c6de2c44d12f1071a53a54caf/formatters/rust.py#L110). In short: every Synthetic Provider's `__init__` function stores a unique ID and a weak reference to -`self` in a global dictionary. The Synthetic Provider class also implements a `get_summary` -function. The type's `SummaryProvider` is a function that looks up the unique ID in this dictionary, +`self` in a global dictionary. +The Synthetic Provider class also implements a `get_summary` function. +The type's `SummaryProvider` is a function that looks up the unique ID in this dictionary, then calls a `get_summary` on the instance it retrieves. ```python @@ -293,9 +320,11 @@ def InstanceSummaryProvider(valobj: SBValue, _dict) -> str: return SYNTH_BY_ID[valobj.GetNonSyntheticValue().GetID()].get_summary() ``` -For example, one might use this for the Enum synthetic provider. The summary would like to access +For example, one might use this for the Enum synthetic provider. +The summary would like to access the variant name, but there isn't a convenient way to reflect this via the type name or child-values -of the synthetic. By implementing an instance summary, we can retrieve the variant name via +of the synthetic. +By implementing an instance summary, we can retrieve the variant name via `self.variant.GetTypeName()` and some string manipulation. # Writing Visualizer Scripts @@ -304,18 +333,21 @@ of the synthetic. By implementing an instance summary, we can retrieve the varia >Visualizers must be written to account for both formats whenever possible. See: >[rust-codegen](./rust-codegen.md#dwarf-vs-pdb) for an overview of the differences -Scripts are injected into LLDB via the CLI command `command script import .py`. Once +Scripts are injected into LLDB via the CLI command `command script import .py`. +Once injected, classes and functions can be added to the synthetic/summary pool with `type synthetic add` -and `type summary add` respectively. The summaries and synthetics can be associated with a -"category", which is typically named after the language the providers are intended for. The category -we use will be called `Rust`. +and `type summary add` respectively. +The summaries and synthetics can be associated with a +"category", which is typically named after the language the providers are intended for. +The category we use will be called `Rust`. > TIP: all LLDB commands can be prefixed with `help` (e.g. `help type synthetic add`) for a brief description, list of arguments, and examples. Currently (Nov 2025) we use `command source ...`, which executes a series of CLI commands from the file [`lldb_commands`](https://github.com/rust-lang/rust/blob/main/src/etc/lldb_commands) to add -providers. This file is somewhat unwieldy, and will soon be supplanted by the Python API equivalent +providers. +This file is somewhat unwieldy, and will soon be supplanted by the Python API equivalent outlined below. ## `__lldb_init_module` @@ -327,16 +359,20 @@ def __lldb_init_module(debugger: SBDebugger, _lldb_internal) -> None: ... ``` This function is called at the end of `command script import ...`, but before control returns back -to the CLI. It allows the script to initialize its own state. +to the CLI. +It allows the script to initialize its own state. -Crucially, it is passed a reference to the debugger itself. This allows us to create the `Rust` -category and add providers to it. It can also allow us to conditionally change which providers we -use depending on what version of LLDB the script detects. This is vital for backwards compatibility +Crucially, it is passed a reference to the debugger itself. +This allows us to create the `Rust` category and add providers to it. +It can also allow us to conditionally change which providers we +use depending on what version of LLDB the script detects. +This is vital for backwards compatibility once we begin using recognizer functions, as recognizers were added in lldb 19.0. ## Visualizer Resolution -The order that visualizers resolve in is listed [here][formatters_101]. In short: +The order that visualizers resolve in is listed [here][formatters_101]. +In short: [formatters_101]: https://lldb.llvm.org/use/variable.html#finding-formatters-101 @@ -347,14 +383,17 @@ provider), use that * If none of the above work, iterate through the regex type matchers Within each of those steps, **iteration is done backwards** to allow new commands to "override" old -commands. This is important for cases like `Box` vs `Box`, were we want a specialized +commands. +This is important for cases like `Box` vs `Box`, were we want a specialized synthetic for the former, but a more generalized synthetic for the latter. ## Minutiae LLDB's API is very powerful, but there are some "gotchas" and unintuitive behavior, some of which -will be outlined below. The python implementation can be viewed at the path returned by the CLI -command `lldb -P` in `lldb\__init__.py`. In addition to the +will be outlined below. +The python implementation can be viewed at the path returned by the CLI +command `lldb -P` in `lldb\__init__.py`. +In addition to the [examples in the lldb repo][synth_examples], there are also [C++ visualizers][plugin_cpp] that can be used as a reference (e.g. [LibCxxVector, the equivalent to `Vec`][cxx_vector]). While C++'s visualizers are written in C++ and have access to LLDB's internals, the API and general practices @@ -370,19 +409,22 @@ are very similar. children of the pointed-to-object are its own children. * The non-function fields are typically [`property()`][property] fields that point directly to the function anyway (e.g. `SBValue.type = property(GetType, None)`). Accessing through these shorthands -is a bit slower to access than just calling the function directly, so they should be avoided. Some +is a bit slower to access than just calling the function directly, so they should be avoided. +Some of the properties return special objects with special properties (e.g. `SBValue.member` returns an -object that acts like `dict[str, SBValue]` to access children). Internally, many of these special +object that acts like `dict[str, SBValue]` to access children). +Internally, many of these special objects just allocate a new class instance and call the function on the `SBValue` anyway, resulting in additional performance loss (e.g. `SBValue.member` internally just implements `__getitem__` which is the one-liner `return self.valobj.GetChildMemberWithName(name)`) * `SBValue.GetID` returns a unique `int` for each value for the duration of the debug session. -Synthetic `SBValue`'s have a different ID than their underlying `SBValue`. The underlying ID can be -retrieved via `SBValue.GetNonSyntheticValue().GetID()`. +Synthetic `SBValue`'s have a different ID than their underlying `SBValue`. +The underlying ID can be retrieved via `SBValue.GetNonSyntheticValue().GetID()`. * When manually calculating an address, `SBValue.GetValueAsAddress` should be preferred over `SBValue.GetValueAsUnsigned` due to [target-specific behavior][get_address] * Getting a string representation of an `SBValue` can be tricky because `GetSummary` requires a -summary provider and `GetValue` requires the type be representable by a primitive. In almost all +summary provider and `GetValue` requires the type be representable by a primitive. +In almost all cases where neither of those conditions are met, the type is a user defined struct that can be passed through `StructSummaryProvider`. @@ -393,12 +435,14 @@ passed through `StructSummaryProvider`. * "Aggregate type" means a non-primitive struct/class/union * "Template" is equivalent to "Generic" -* Types can be looked up by their name via `SBTarget.FindFirstType(type_name)`. `SBTarget` can be -acquired via `SBValue.GetTarget` +* Types can be looked up by their name via `SBTarget.FindFirstType(type_name)`. + `SBTarget` can be acquired via `SBValue.GetTarget` * `SBType.template_args` returns `None` instead of an empty list if the type has no generics * It is sometimes necessary to transform a type into the type you want via functions like -`SBType.GetArrayType` and `SBType.GetPointerType`. These functions cannot fail. They ask the -underlying LLDB `TypeSystem` plugin for the type, bypassing the debug info completely. Even if the +`SBType.GetArrayType` and `SBType.GetPointerType`. +These functions cannot fail. +They ask the underlying LLDB `TypeSystem` plugin for the type, bypassing the debug info completely. +Even if the type does not exist in the debug info at all, these functions can create the appropriate type. * `SBType.GetCanonicalType` is effectively `SBType.GetTypedefedType` + `SBType.GetUnqualifiedType`. Unlike `SBType.GetTypedefedType`, it will always return a valid `SBType` regardless of whether or @@ -411,11 +455,13 @@ always possible since the static fields are otherwise completely inaccessible. ## SyntheticProvider -We start with the typical prelude, using `__slots__` since we have known fields. In addition to the +We start with the typical prelude, using `__slots__` since we have known fields. +In addition to the object itself, we also need to store the type of the elements because `Vec`'s heap pointer is a -`*mut u8`, not a `*mut T`. Rust is a statically typed language, so the type of `T` will never -change. That means we can store it during initialization. The heap pointer, length, and capacity -*can* change though, and thus are default initialized here. +`*mut u8`, not a `*mut T`. +Rust is a statically typed language, so the type of `T` will never change. +That means we can store it during initialization. +The heap pointer, length, and capacity *can* change though, and thus are default initialized here. ```python import lldb @@ -452,12 +498,15 @@ class VecSyntheticProvider: For the implementation of `get_template_args` and `resolve_msvc_template_arg`, please see: [`lldb_providers.py`](https://github.com/rust-lang/rust/blob/main/src/etc/lldb_providers.py#L136). -Next, the update function. We check if the pointer or length have changed. We can ommit checking the -capacity, as the number of children will remain the same unless `len` changes. If changing the -capacity resulted in a reallocation, `data_ptr`'s address would be different. +Next, the update function. +We check if the pointer or length have changed. +We can ommit checking the +capacity, as the number of children will remain the same unless `len` changes. +If changing the capacity resulted in a reallocation, `data_ptr`'s address would be different. If `data_ptr` and `length` haven't changed, we can take advantage of LLDB's caching and return -early. If they have changed, we store the new values and tell LLDB to flush the cache. +early. +If they have changed, we store the new values and tell LLDB to flush the cache. ```python def update(self): @@ -490,9 +539,10 @@ def num_children(self) -> int: When accessing elements, we expect values of the format `[0]`, `[1]`, etc. to mimic indexing. Additionally, we still want the user to be able to quickly access the length and capacity, as they -can be very useful when debugging. We assign these values `u32::MAX - 1` and `u32::MAX - 2` -respectively, as we can almost surely guarantee that they will not overlap with element values. Note -that we can account for both the full and shorthand `capacity` name. +can be very useful when debugging. +We assign these values `u32::MAX - 1` and `u32::MAX - 2` +respectively, as we can almost surely guarantee that they will not overlap with element values. +Note that we can account for both the full and shorthand `capacity` name. ```python def get_child_index(self, name: str) -> int: @@ -527,17 +577,18 @@ def get_child_at_index(self, index: int) -> SBValue: return self.valobj.CreateValueFromAddress(f"[{index}]", addr, self.element_type) ``` -For the type's display name, we can strip the path qualifier. User defined types named -`Vec` will end up fully qualified, so there shouldn't be any ambiguity. We can also remove the -allocator generic, as it's very very rarely useful. We use `get_template_args` instead of -`self.element_type.GetName()` for 3 reasons: +For the type's display name, we can strip the path qualifier. +User defined types named `Vec` will end up fully qualified, so there shouldn't be any ambiguity. +We can also remove the allocator generic, as it's very very rarely useful. +We use `get_template_args` instead of `self.element_type.GetName()` for 3 reasons: 1. If we fail to resolve the element type for any reason, `self.valobj`'s type name can still let the user know what the real type of the element is 2. Type names are not subject to the limitations of DWARF and PDB nodes, so the template type in the name will reflect things like `*const`/`*mut` and `&`/`&mut`. 3. We do not currently (Nov 2025) normalize MSVC type names, but once we do, we will need to work with the -string-names of types anyway. It's also much easier to cache a string-to-string conversion compared +string-names of types anyway. +It's also much easier to cache a string-to-string conversion compared to an `SBType`-to-string conversion. ```python @@ -550,11 +601,14 @@ the `get_value` function. ## SummaryProvider -The summary provider is very simple thanks to our synthetic provider. The only real hiccup is that -`GetSummary` only returns a value if the object's type has a `SummaryProvider`. If it doesn't, it -will return an empty string which is not ideal. In a full set of visualizer scripts, we can ensure +The summary provider is very simple thanks to our synthetic provider. +The only real hiccup is that +`GetSummary` only returns a value if the object's type has a `SummaryProvider`. +If it doesn't, it will return an empty string which is not ideal. +In a full set of visualizer scripts, we can ensure that every type that doesn't have a `GetSummary()` or a `GetValue()` is a struct, and then delegate -to a generic `StructSummaryProvider`. For this demonstration, I will gloss over that detail. +to a generic `StructSummaryProvider`. +For this demonstration, I will gloss over that detail. ```python def VecSummaryProvider(valobj: SBValue, _lldb_internal) -> str: @@ -659,4 +713,4 @@ We can also confirm that the "hidden" length and capacity are still accessible: (unsigned long long) vec_v.capacity = 5 (lldb) v vec_v.cap (unsigned long long) vec_v.cap = 5 -``` \ No newline at end of file +``` From d4764e3210e6c7a2767b1ac2ec69bc33d3685201 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 23:46:27 +0200 Subject: [PATCH 382/583] some improvements --- .../src/debuginfo/lldb-visualizers.md | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md index c329e23d6006..83e2b0d5794e 100644 --- a/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md +++ b/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md @@ -1,6 +1,6 @@ # LLDB - Python Providers -> NOTE: LLDB's C++<->Python FFI expects a version of python designated at the time LLDB was +> NOTE: LLDB's C++<->Python FFI expects a version of Python designated at the time LLDB was >compiled. LLDB is careful to correspond this version to the minimum in typical Linux and macOS >distributions, but on Windows there is no easy solution. If you receive an import error regarding >`_lldb` not existing, a mismatched Python version is likely the cause. @@ -11,14 +11,15 @@ [minimal_python_install]: https://discourse.llvm.org/t/a-minimal-python-install-for-lldb/88658 [issue_167001]: https://github.com/llvm/llvm-project/issues/167001 -> NOTE: Currently (Nov 2025), LLDB's minimum supported Python version is 3.8 with plans to update it to ->3.9 or 3.10 depending on several outside factors. Scripts should ideally be written with only the ->features available in the minimum supported Python version. Please see [this discussion][mrpv] for ->more info. +> NOTE: As of Nov 2025, +> LLDB's minimum supported Python version is 3.8, with plans to update it to +> 3.9 or 3.10, depending on several outside factors. Scripts should ideally be written with only the +> features available in the minimum supported Python version. Please see [this discussion][mrpv] for +> more info. [mrpv]: https://discourse.llvm.org/t/rfc-upgrading-llvm-s-minimum-required-python-version/88605/ -> NOTE: The path to LLDB's python package can be located via the CLI command `lldb -P` +> NOTE: The path to LLDB's Python package can be located via the CLI command `lldb -P` LLDB provides 3 mechanisms for customizing output: @@ -91,7 +92,7 @@ If a method overrides an `SBValue` method, that method will be listed. ### `__init__` -This function is called once per object, and must store the `valobj` in the python class so that it +This function is called once per object, and must store the `valobj` in the Python class so that it is accessible elsewhere. Very little else should be done here. @@ -114,7 +115,8 @@ determining an enum variable's variant, or checking which slots of a `HashMap` a The bool returned from this function is somewhat complicated, see: [`update` caching](#update-caching) below for more info. When in doubt, return `False`/`None`. -Currently (Nov 2025), none of the visualizers return `True`, but that may change as the debug info +As of Nov 2025, +none of the visualizers return `True`, but that may change as the debug info test suite is improved. #### `update` caching @@ -155,7 +157,7 @@ children if they do not already exist in the `SyntheticProvider`'s list. For further clarification, see [this discussion](https://discourse.llvm.org/t/when-is-it-safe-to-cache-syntheticprovider-update/88608) > NOTE: when testing the caching behavior, do not rely on LLDB's heuristic to persist variables when -> stepping. Instead, store the variable in a python object (e.g. `v = lldb.frame.var("var_name")`), +> stepping. Instead, store the variable in a Python object (e.g. `v = lldb.frame.var("var_name")`), > step forward, and then inspect the stored variable. ### (optional) `has_children` @@ -248,13 +250,14 @@ access the generic parameters of the type. The `SBValue` returned is expected to be a primitive type or pointer, and is treated as the value of the variable in expressions. -> IMPORTANT: The `SBValue` returned **must be stored in the `SyntheticProvider`**. There is ->currently (Nov 2025) a bug where if the `SBValue` is acquired within `get_value` and not stored ->anywhere, Python will segfault when LLDB attempts to access the value. +> IMPORTANT: The `SBValue` returned **must be stored in the `SyntheticProvider`**. +> As of Nov 2025, +> there is a bug where if the `SBValue` is acquired within `get_value` and not stored +> anywhere, Python will segfault when LLDB attempts to access the value. ## Summary Providers -Summary providers are python functions of the following form: +Summary providers are Python functions of the following form: ```python def SummaryProvider(valobj: SBValue, _lldb_internal) -> str: ... @@ -280,7 +283,7 @@ That `SBValue` will reflect the type's any of its internal implementation details. This is deterimental in cases where we need some of those internal details to help complete the summary. -Currently (Nov 2025), in the synthetic we just +As of Nov 2025, in the synthetic we just run the non-synthetic value through the synthetic provider (`synth = SyntheticProvider(valobj.GetNonSyntheticValue(), _dict)`), but this is obviously suboptimal and there are plans to use the method outlined below. @@ -344,7 +347,8 @@ The category we use will be called `Rust`. > TIP: all LLDB commands can be prefixed with `help` (e.g. `help type synthetic add`) for a brief description, list of arguments, and examples. -Currently (Nov 2025) we use `command source ...`, which executes a series of CLI commands from the +As of Nov 2025, +we use `command source ...`, which executes a series of CLI commands from the file [`lldb_commands`](https://github.com/rust-lang/rust/blob/main/src/etc/lldb_commands) to add providers. This file is somewhat unwieldy, and will soon be supplanted by the Python API equivalent @@ -391,7 +395,7 @@ synthetic for the former, but a more generalized synthetic for the latter. LLDB's API is very powerful, but there are some "gotchas" and unintuitive behavior, some of which will be outlined below. -The python implementation can be viewed at the path returned by the CLI +The Python implementation can be viewed at the path returned by the CLI command `lldb -P` in `lldb\__init__.py`. In addition to the [examples in the lldb repo][synth_examples], there are also [C++ visualizers][plugin_cpp] that can @@ -586,7 +590,8 @@ We use `get_template_args` instead of `self.element_type.GetName()` for 3 reason the user know what the real type of the element is 2. Type names are not subject to the limitations of DWARF and PDB nodes, so the template type in the name will reflect things like `*const`/`*mut` and `&`/`&mut`. -3. We do not currently (Nov 2025) normalize MSVC type names, but once we do, we will need to work with the +3. As of Nov 2025, +we don't normalize MSVC type names, but once we do, we will need to work with the string-names of types anyway. It's also much easier to cache a string-to-string conversion compared to an `SBType`-to-string conversion. From 2aea912db2236e39aba7f3f4dca41f7047dd912d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 29 Jan 2026 23:51:07 +0200 Subject: [PATCH 383/583] sembr src/building/suggested.md --- .../rustc-dev-guide/src/building/suggested.md | 214 +++++++++--------- 1 file changed, 113 insertions(+), 101 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index c87dc6b28d87..71921532244e 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -1,27 +1,29 @@ # Suggested workflows -The full bootstrapping process takes quite a while. Here are some suggestions to -make your life easier. +The full bootstrapping process takes quite a while. +Here are some suggestions to make your life easier. ## Installing a pre-push hook CI will automatically fail your build if it doesn't pass `tidy`, our internal -tool for ensuring code quality. If you'd like, you can install a [Git +tool for ensuring code quality. +If you'd like, you can install a [Git hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) that will -automatically run `./x test tidy` on each push, to ensure your code is up to -par. If the hook fails then run `./x test tidy --bless` and commit the changes. +automatically run `./x test tidy` on each push, to ensure your code is up to par. +If the hook fails then run `./x test tidy --bless` and commit the changes. If you decide later that the pre-push behavior is undesirable, you can delete the `pre-push` file in `.git/hooks`. -A prebuilt git hook lives at [`src/etc/pre-push.sh`]. It can be copied into -your `.git/hooks` folder as `pre-push` (without the `.sh` extension!). +A prebuilt git hook lives at [`src/etc/pre-push.sh`]. + It can be copied into your `.git/hooks` folder as `pre-push` (without the `.sh` extension!). You can also install the hook as a step of running `./x setup`! ## Config extensions When working on different tasks, you might need to switch between different bootstrap configurations. -Sometimes you may want to keep an old configuration for future use. But saving raw config values in +Sometimes you may want to keep an old configuration for future use. +But saving raw config values in random files and manually copying and pasting them can quickly become messy, especially if you have a long history of different configurations. @@ -51,9 +53,10 @@ include = ["cross.toml"] You can also include extensions within extensions recursively. -**Note:** In the `include` field, the overriding logic follows a right-to-left order. For example, -in `include = ["a.toml", "b.toml"]`, extension `b.toml` overrides `a.toml`. Also, parent extensions -always overrides the inner ones. +**Note:** In the `include` field, the overriding logic follows a right-to-left order. +For example, +in `include = ["a.toml", "b.toml"]`, extension `b.toml` overrides `a.toml`. +Also, parent extensions always overrides the inner ones. ## Configuring `rust-analyzer` for `rustc` @@ -61,34 +64,37 @@ always overrides the inner ones. Checking the "library" tree requires a stage1 compiler, which can be a heavy process on some computers. For this reason, bootstrap has a flag called `--skip-std-check-if-no-download-rustc` that skips checking the -"library" tree if `rust.download-rustc` isn't available. If you want to avoid putting a heavy load on your computer +"library" tree if `rust.download-rustc` isn't available. +If you want to avoid putting a heavy load on your computer with `rust-analyzer`, you can add the `--skip-std-check-if-no-download-rustc` flag to your `./x check` command in the `rust-analyzer` configuration. ### Project-local rust-analyzer setup -`rust-analyzer` can help you check and format your code whenever you save a -file. By default, `rust-analyzer` runs the `cargo check` and `rustfmt` commands, +`rust-analyzer` can help you check and format your code whenever you save a file. +By default, `rust-analyzer` runs the `cargo check` and `rustfmt` commands, but you can override these commands to use more adapted versions of these tools -when hacking on `rustc`. With custom setup, `rust-analyzer` can use `./x check` +when hacking on `rustc`. +With custom setup, `rust-analyzer` can use `./x check` to check the sources, and the stage 0 rustfmt to format them. The default `rust-analyzer.check.overrideCommand` command line will check all -the crates and tools in the repository. If you are working on a specific part, -you can override the command to only check the part you are working on to save -checking time. For example, if you are working on the compiler, you can override +the crates and tools in the repository. +If you are working on a specific part, +you can override the command to only check the part you are working on to save checking time. +For example, if you are working on the compiler, you can override the command to `x check compiler --json-output` to only check the compiler part. You can run `x check --help --verbose` to see the available parts. Running `./x setup editor` will prompt you to create a project-local LSP config -file for one of the supported editors. You can also create the config file as a -step of running `./x setup`. +file for one of the supported editors. +You can also create the config file as a step of running `./x setup`. ### Using a separate build directory for rust-analyzer By default, when rust-analyzer runs a check or format command, it will share -the same build directory as manual command-line builds. This can be inconvenient -for two reasons: +the same build directory as manual command-line builds. +This can be inconvenient for two reasons: - Each build will lock the build directory and force the other to wait, so it becomes impossible to run command-line builds while rust-analyzer is running commands in the background. @@ -111,12 +117,11 @@ requires extra disk space. ### Visual Studio Code Selecting `vscode` in `./x setup editor` will prompt you to create a -`.vscode/settings.json` file which will configure Visual Studio code. The -recommended `rust-analyzer` settings live at +`.vscode/settings.json` file which will configure Visual Studio code. +The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_settings.json`]. -If running `./x check` on save is inconvenient, in VS Code you can use a [Build -Task] instead: +If running `./x check` on save is inconvenient, in VS Code you can use a [Build Task] instead: ```JSON // .vscode/tasks.json @@ -140,27 +145,26 @@ Task] instead: ### Neovim -For Neovim users, there are a few options. The -easiest way is by using [neoconf.nvim](https://github.com/folke/neoconf.nvim/), -which allows for project-local configuration files with the native LSP. The -steps for how to use it are below. Note that they require rust-analyzer to -already be configured with Neovim. Steps for this can be [found -here](https://rust-analyzer.github.io/manual.html#nvim-lsp). +For Neovim users, there are a few options. +The easiest way is by using [neoconf.nvim](https://github.com/folke/neoconf.nvim/), +which allows for project-local configuration files with the native LSP. +The steps for how to use it are below. +Note that they require rust-analyzer to already be configured with Neovim. +Steps for this can be [found here](https://rust-analyzer.github.io/manual.html#nvim-lsp). -1. First install the plugin. This can be done by following the steps in the - README. -2. Run `./x setup editor`, and select `vscode` to create a - `.vscode/settings.json` file. `neoconf` is able to read and update - rust-analyzer settings automatically when the project is opened when this - file is detected. +1. First install the plugin. + This can be done by following the steps in the README. +2. Run `./x setup editor`, and select `vscode` to create a `.vscode/settings.json` file. + `neoconf` is able to read and update + rust-analyzer settings automatically when the project is opened when this file is detected. If you're using `coc.nvim`, you can run `./x setup editor` and select `vim` to -create a `.vim/coc-settings.json`. The settings can be edited with -`:CocLocalConfig`. The recommended settings live at -[`src/etc/rust_analyzer_settings.json`]. +create a `.vim/coc-settings.json`. +The settings can be edited with `:CocLocalConfig`. +The recommended settings live at [`src/etc/rust_analyzer_settings.json`]. -Another way is without a plugin, and creating your own logic in your -configuration. The following code will work for any checkout of rust-lang/rust (newer than February 2025): +Another way is without a plugin, and creating your own logic in your configuration. +The following code will work for any checkout of rust-lang/rust (newer than February 2025): ```lua local function expand_config_variables(option) @@ -216,8 +220,7 @@ lspconfig.rust_analyzer.setup { If you would like to use the build task that is described above, you may either make your own command in your config, or you can install a plugin such as -[overseer.nvim](https://github.com/stevearc/overseer.nvim) that can [read -VSCode's `task.json` +[overseer.nvim](https://github.com/stevearc/overseer.nvim) that can [read VSCode's `task.json` files](https://github.com/stevearc/overseer.nvim/blob/master/doc/guides.md#vs-code-tasks), and follow the same instructions as above. @@ -240,55 +243,58 @@ Helix comes with built-in LSP and rust-analyzer support. It can be configured through `languages.toml`, as described [here](https://docs.helix-editor.com/languages.html). You can run `./x setup editor` and select `helix`, which will prompt you to -create `languages.toml` with the recommended configuration for Helix. The -recommended settings live at [`src/etc/rust_analyzer_helix.toml`]. +create `languages.toml` with the recommended configuration for Helix. +The recommended settings live at [`src/etc/rust_analyzer_helix.toml`]. ### Zed Zed comes with built-in LSP and rust-analyzer support. It can be configured through `.zed/settings.json`, as described -[here](https://zed.dev/docs/configuring-languages). Selecting `zed` -in `./x setup editor` will prompt you to create a `.zed/settings.json` -file which will configure Zed with the recommended configuration. The -recommended `rust-analyzer` settings live +[here](https://zed.dev/docs/configuring-languages). +Selecting `zed` in `./x setup editor` will prompt you to create a `.zed/settings.json` +file which will configure Zed with the recommended configuration. +The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_zed.json`]. ## Check, check, and check again -When doing simple refactoring, it can be useful to run `./x check` -continuously. If you set up `rust-analyzer` as described above, this will be -done for you every time you save a file. Here you are just checking that the +When doing simple refactoring, it can be useful to run `./x check` continuously. +If you set up `rust-analyzer` as described above, this will be +done for you every time you save a file. +Here you are just checking that the compiler can **build**, but often that is all you need (e.g., when renaming a -method). You can then run `./x build` when you actually need to run tests. +method). +You can then run `./x build` when you actually need to run tests. -In fact, it is sometimes useful to put off tests even when you are not 100% sure -the code will work. You can then keep building up refactoring commits and only -run the tests at some later time. You can then use `git bisect` to track down -**precisely** which commit caused the problem. A nice side-effect of this style +In fact, it is sometimes useful to put off tests even when you are not 100% sure the code will work. +You can then keep building up refactoring commits and only run the tests at some later time. +You can then use `git bisect` to track down **precisely** which commit caused the problem. +A nice side-effect of this style is that you are left with a fairly fine-grained set of commits at the end, all -of which build and pass tests. This often helps reviewing. +of which build and pass tests. +This often helps reviewing. ## Configuring `rustup` to use nightly -Some parts of the bootstrap process uses pinned, nightly versions of tools like -rustfmt. To make things like `cargo fmt` work correctly in your repo, run +Some parts of the bootstrap process uses pinned, nightly versions of tools like rustfmt. +To make things like `cargo fmt` work correctly in your repo, run ```console cd rustup override set nightly ``` -after [installing a nightly toolchain] with `rustup`. Don't forget to do this -for all directories you have [setup a worktree for]. You may need to use the -pinned nightly version from `src/stage0`, but often the normal `nightly` channel -will work. +after [installing a nightly toolchain] with `rustup`. +Don't forget to do this for all directories you have [setup a worktree for]. +You may need to use the +pinned nightly version from `src/stage0`, but often the normal `nightly` channel will work. **Note** see [the section on vscode] for how to configure it with this real rustfmt `x` uses, and [the section on rustup] for how to setup `rustup` toolchain for your bootstrapped compiler -**Note** This does _not_ allow you to build `rustc` with cargo directly. You -still have to use `x` to work on the compiler or standard library, this just +**Note** This does _not_ allow you to build `rustc` with cargo directly. +You still have to use `x` to work on the compiler or standard library, this just lets you use `cargo fmt`. [installing a nightly toolchain]: https://rust-lang.github.io/rustup/concepts/channels.html?highlight=nightl#working-with-nightly-rust @@ -300,18 +306,22 @@ lets you use `cargo fmt`. If you are not working on the compiler, you often don't need to build the compiler tree. For example, you can skip building the compiler and only build the `library` tree or the -tools under `src/tools`. To achieve that, you have to enable this by setting the `download-rustc` -option in your configuration. This tells bootstrap to use the latest nightly compiler for `stage > 0` +tools under `src/tools`. +To achieve that, you have to enable this by setting the `download-rustc` +option in your configuration. +This tells bootstrap to use the latest nightly compiler for `stage > 0` steps, meaning it will have two precompiled compilers: stage0 compiler and `download-rustc` compiler -for `stage > 0` steps. This way, it will never need to build the in-tree compiler. As a result, your -build time will be significantly reduced by not building the in-tree compiler. +for `stage > 0` steps. +This way, it will never need to build the in-tree compiler. +As a result, your build time will be significantly reduced by not building the in-tree compiler. ## Faster rebuilds with `--keep-stage-std` -Sometimes just checking whether the compiler builds is not enough. A common -example is that you need to add a `debug!` statement to inspect the value of -some state or better understand the problem. In that case, you don't really need -a full build. By bypassing bootstrap's cache invalidation, you can often get +Sometimes just checking whether the compiler builds is not enough. +A common example is that you need to add a `debug!` statement to inspect the value of +some state or better understand the problem. +In that case, you don't really need a full build. +By bypassing bootstrap's cache invalidation, you can often get these builds to complete very fast (e.g., around 30 seconds). The only catch is this requires a bit of fudging and may produce compilers that don't work (but that is easily detected and fixed). @@ -323,53 +333,54 @@ The sequence of commands you want is as follows: - Note that we added the `--keep-stage-std=1` flag here As mentioned, the effect of `--keep-stage-std=1` is that we just _assume_ that the -old standard library can be re-used. If you are editing the compiler, this is -often true: you haven't changed the standard library, after all. But -sometimes, it's not true: for example, if you are editing the "metadata" part of +old standard library can be re-used. +If you are editing the compiler, this is +often true: you haven't changed the standard library, after all. +But sometimes, it's not true: for example, if you are editing the "metadata" part of the compiler, which controls how the compiler encodes types and other states into the `rlib` files, or if you are editing things that wind up in the metadata (such as the definition of the MIR). **The TL;DR is that you might get weird behavior from a compile when using `--keep-stage-std=1`** -- for example, strange [ICEs](../appendix/glossary.html#ice) -or other panics. In that case, you should simply remove the `--keep-stage-std=1` -from the command and rebuild. That ought to fix the problem. +or other panics. +In that case, you should simply remove the `--keep-stage-std=1` from the command and rebuild. +That ought to fix the problem. -You can also use `--keep-stage-std=1` when running tests. Something like this: +You can also use `--keep-stage-std=1` when running tests. +Something like this: - Initial test run: `./x test tests/ui` - Subsequent test run: `./x test tests/ui --keep-stage-std=1` ## Using incremental compilation -You can further enable the `--incremental` flag to save additional time in -subsequent rebuilds: +You can further enable the `--incremental` flag to save additional time in subsequent rebuilds: ```bash ./x test tests/ui --incremental --test-args issue-1234 ``` -If you don't want to include the flag with every command, you can enable it in -the `bootstrap.toml`: +If you don't want to include the flag with every command, you can enable it in the `bootstrap.toml`: ```toml [rust] incremental = true ``` -Note that incremental compilation will use more disk space than usual. If disk -space is a concern for you, you might want to check the size of the `build` +Note that incremental compilation will use more disk space than usual. +If disk space is a concern for you, you might want to check the size of the `build` directory from time to time. ## Fine-tuning optimizations -Setting `optimize = false` makes the compiler too slow for tests. However, to -improve the test cycle, you can disable optimizations selectively only for the +Setting `optimize = false` makes the compiler too slow for tests. +However, to improve the test cycle, you can disable optimizations selectively only for the crates you'll have to rebuild ([source](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/incremental.20compilation.20question/near/202712165)). For example, when working on `rustc_mir_build`, the `rustc_mir_build` and -`rustc_driver` crates take the most time to incrementally rebuild. You could -therefore set the following in the root `Cargo.toml`: +`rustc_driver` crates take the most time to incrementally rebuild. +You could therefore set the following in the root `Cargo.toml`: ```toml [profile.release.package.rustc_mir_build] @@ -382,22 +393,24 @@ opt-level = 0 Working on multiple branches in parallel can be a little annoying, since building the compiler on one branch will cause the old build and the incremental -compilation cache to be overwritten. One solution would be to have multiple +compilation cache to be overwritten. +One solution would be to have multiple clones of the repository, but that would mean storing the Git metadata multiple times, and having to update each clone individually. -Fortunately, Git has a better solution called [worktrees]. This lets you create -multiple "working trees", which all share the same Git database. Moreover, +Fortunately, Git has a better solution called [worktrees]. +This lets you create multiple "working trees", which all share the same Git database. +Moreover, because all of the worktrees share the same object database, if you update a branch (e.g. `main`) in any of them, you can use the new commits from any of the -worktrees. One caveat, though, is that submodules do not get shared. They will -still be cloned multiple times. +worktrees. +One caveat, though, is that submodules do not get shared. +They will still be cloned multiple times. [worktrees]: https://git-scm.com/docs/git-worktree Given you are inside the root directory for your Rust repository, you can create -a "linked working tree" in a new "rust2" directory by running the following -command: +a "linked working tree" in a new "rust2" directory by running the following command: ```bash git worktree add ../rust2 @@ -409,8 +422,7 @@ Creating a new worktree for a new branch based on `main` looks like: git worktree add -b my-feature ../rust2 main ``` -You can then use that rust2 folder as a separate workspace for modifying and -building `rustc`! +You can then use that rust2 folder as a separate workspace for modifying and building `rustc`! ## Working with nix From ffc3b8a3f163d8978b9f1512113c939e0aeed73e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:03:23 +0200 Subject: [PATCH 384/583] capitalise start of sentence --- src/doc/rustc-dev-guide/src/building/suggested.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 71921532244e..b5c9b9b4e3d1 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -284,7 +284,7 @@ cd rustup override set nightly ``` -after [installing a nightly toolchain] with `rustup`. +After [installing a nightly toolchain] with `rustup`. Don't forget to do this for all directories you have [setup a worktree for]. You may need to use the pinned nightly version from `src/stage0`, but often the normal `nightly` channel will work. From d7e88cf39b073d4b49d442117511977ec47c8dfd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:08:23 +0200 Subject: [PATCH 385/583] sembr src/git.md --- src/doc/rustc-dev-guide/src/git.md | 255 ++++++++++++++++------------- 1 file changed, 138 insertions(+), 117 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index abe72b29cb1e..d6665f6e17cc 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -1,20 +1,22 @@ # Using Git -The Rust project uses [Git] to manage its source code. In order to -contribute, you'll need some familiarity with its features so that your changes +The Rust project uses [Git] to manage its source code. +In order to contribute, you'll need some familiarity with its features so that your changes can be incorporated into the compiler. [Git]: https://git-scm.com The goal of this page is to cover some of the more common questions and -problems new contributors face. Although some Git basics will be covered here, +problems new contributors face. +Although some Git basics will be covered here, if you find that this is still a little too fast for you, it might make sense to first read some introductions to Git, such as the Beginner and Getting -started sections of [this tutorial from Atlassian][atlassian-git]. GitHub also -provides [documentation] and [guides] for beginners, or you can consult the +started sections of [this tutorial from Atlassian][atlassian-git]. +GitHub also provides [documentation] and [guides] for beginners, or you can consult the more in depth [book from Git]. -This guide is incomplete. If you run into trouble with git that this page doesn't help with, +This guide is incomplete. +If you run into trouble with git that this page doesn't help with, please [open an issue] so we can document how to fix it. [open an issue]: https://github.com/rust-lang/rustc-dev-guide/issues/new @@ -26,15 +28,15 @@ please [open an issue] so we can document how to fix it. ## Prerequisites We'll assume that you've installed Git, forked [rust-lang/rust], and cloned the -forked repo to your PC. We'll use the command line interface to interact +forked repo to your PC. +We'll use the command line interface to interact with Git; there are also a number of GUIs and IDE integrations that can generally do the same things. [rust-lang/rust]: https://github.com/rust-lang/rust -If you've cloned your fork, then you will be able to reference it with `origin` -in your local repo. It may be helpful to also set up a remote for the official -rust-lang/rust repo via +If you've cloned your fork, then you will be able to reference it with `origin` in your local repo. +It may be helpful to also set up a remote for the official rust-lang/rust repo via ```console git remote add upstream https://github.com/rust-lang/rust.git @@ -54,21 +56,19 @@ useful when contributing to other repositories in the Rust project. ## Standard Process -Below is the normal procedure that you're likely to use for most minor changes -and PRs: +Below is the normal procedure that you're likely to use for most minor changes and PRs: - 1. Ensure that you're making your changes on top of `main`: - `git checkout main`. + 1. Ensure that you're making your changes on top of `main`: `git checkout main`. 2. Get the latest changes from the Rust repo: `git pull upstream main --ff-only`. (see [No-Merge Policy][no-merge-policy] for more info about this). 3. Make a new branch for your change: `git checkout -b issue-12345-fix`. 4. Make some changes to the repo and test them. 5. Stage your changes via `git add src/changed/file.rs src/another/change.rs` - and then commit them with `git commit`. Of course, making intermediate commits - may be a good idea as well. Avoid `git add .`, as it makes it too easy to - unintentionally commit changes that should not be committed, such as submodule - updates. You can use `git status` to check if there are any files you forgot - to stage. + and then commit them with `git commit`. + Of course, making intermediate commits may be a good idea as well. + Avoid `git add .`, as it makes it too easy to + unintentionally commit changes that should not be committed, such as submodule updates. + You can use `git status` to check if there are any files you forgot to stage. 6. Push your changes to your fork: `git push --set-upstream origin issue-12345-fix` (After adding commits, you can use `git push` and after rebasing or pulling-and-rebasing, you can use `git push --force-with-lease`). @@ -100,14 +100,15 @@ Here are some common issues you might run into: ### I made a merge commit by accident. Git has two ways to update your branch with the newest changes: merging and rebasing. -Rust [uses rebasing][no-merge-policy]. If you make a merge commit, it's not too hard to fix: -`git rebase -i upstream/main`. +Rust [uses rebasing][no-merge-policy]. +If you make a merge commit, it's not too hard to fix: `git rebase -i upstream/main`. See [Rebasing](#rebasing) for more about rebasing. ### I deleted my fork on GitHub! -This is not a problem from git's perspective. If you run `git remote -v`, +This is not a problem from git's perspective. +If you run `git remote -v`, it will say something like this: ```console @@ -137,19 +138,21 @@ You might also notice conflicts in the web UI: ![conflict in src/tools/cargo](./img/submodule-conflicts.png) The most common cause is that you rebased after a change and ran `git add .` without first running -`x` to update the submodules. Alternatively, you might have run `cargo fmt` instead of `x fmt` +`x` to update the submodules. + Alternatively, you might have run `cargo fmt` instead of `x fmt` and modified files in a submodule, then committed the changes. To fix it, do the following things (if you changed a submodule other than cargo, replace `src/tools/cargo` with the path to that submodule): 1. See which commit has the accidental changes: `git log --stat -n1 src/tools/cargo` -2. Revert the changes to that commit: `git checkout ~ src/tools/cargo`. Type `~` - literally but replace `` with the output from step 1. +2. Revert the changes to that commit: `git checkout ~ src/tools/cargo`. + Type `~` literally but replace `` with the output from step 1. 3. Tell git to commit the changes: `git commit --fixup ` 4. Repeat steps 1-3 for all the submodules you modified. - If you modified the submodule in several different commits, you will need to repeat steps 1-3 - for each commit you modified. You'll know when to stop when the `git log` command shows a commit + for each commit you modified. + You'll know when to stop when the `git log` command shows a commit that's not authored by you. 5. Squash your changes into the existing commits: `git rebase --autosquash -i upstream/main` 6. [Push your changes](#standard-process). @@ -168,9 +171,11 @@ error: Please commit or stash them. (See for the difference between the two.) -This means you have made changes since the last time you made a commit. To be able to rebase, either +This means you have made changes since the last time you made a commit. +To be able to rebase, either commit your changes, or make a temporary commit called a "stash" to have them still not be committed -when you finish rebasing. You may want to configure git to make this "stash" automatically, which +when you finish rebasing. +You may want to configure git to make this "stash" automatically, which will prevent the "cannot rebase" error in nearly all cases: ```console @@ -191,8 +196,9 @@ rm -r src/stdarch ### I see `<<< HEAD`? -You were probably in the middle of a rebase or merge conflict. See -[Conflicts](#rebasing-and-conflicts) for how to fix the conflict. If you don't care about the changes +You were probably in the middle of a rebase or merge conflict. +See [Conflicts](#rebasing-and-conflicts) for how to fix the conflict. +If you don't care about the changes and just want to get a clean copy of the repository back, you can use `git reset`: ```console @@ -213,17 +219,19 @@ hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. ``` -The advice this gives is incorrect! Because of Rust's -["no-merge" policy](#no-merge-policy) the merge commit created by `git pull` -will not be allowed in the final PR, in addition to defeating the point of the -rebase! Use `git push --force-with-lease` instead. +The advice this gives is incorrect! +Because of Rust's ["no-merge" policy](#no-merge-policy) the merge commit created by `git pull` +will not be allowed in the final PR, in addition to defeating the point of the rebase! +Use `git push --force-with-lease` instead. ### Git is trying to rebase commits I didn't write? If you see many commits in your rebase list, or merge commits, or commits by other people that you -didn't write, it likely means you're trying to rebase over the wrong branch. For example, you may +didn't write, it likely means you're trying to rebase over the wrong branch. +For example, you may have a `rust-lang/rust` remote `upstream`, but ran `git rebase origin/main` instead of `git rebase -upstream/main`. The fix is to abort the rebase and use the correct branch instead: +upstream/main`. +The fix is to abort the rebase and use the correct branch instead: ```console git rebase --abort @@ -239,7 +247,8 @@ git rebase --interactive upstream/main ### Quick note about submodules When updating your local repository with `git pull`, you may notice that sometimes -Git says you have modified some files that you have never edited. For example, +Git says you have modified some files that you have never edited. +For example, running `git status` gives you something like (note the `new commits` mention): ```console @@ -263,17 +272,18 @@ git submodule update ``` Some submodules are not actually needed; for example, `src/llvm-project` doesn't need to be checked -out if you're using `download-ci-llvm`. To avoid having to keep fetching its history, you can use +out if you're using `download-ci-llvm`. + To avoid having to keep fetching its history, you can use `git submodule deinit -f src/llvm-project`, which will also avoid it showing as modified again. ## Rebasing and Conflicts When you edit your code locally, you are making changes to the version of -rust-lang/rust that existed when you created your feature branch. As such, when -you submit your PR it is possible that some of the changes that have been made +rust-lang/rust that existed when you created your feature branch. +As such, when you submit your PR it is possible that some of the changes that have been made to rust-lang/rust since then are in conflict with the changes you've made. -When this happens, you need to resolve the conflicts before your changes can be -merged. To do that, you need to rebase your work on top of rust-lang/rust. +When this happens, you need to resolve the conflicts before your changes can be merged. +To do that, you need to rebase your work on top of rust-lang/rust. ### Rebasing @@ -294,13 +304,14 @@ git pull --rebase https://github.com/rust-lang/rust.git main > have rebased and fixed all conflicts. When you rebase a branch on main, all the changes on your branch are -reapplied to the most recent version of `main`. In other words, Git tries to +reapplied to the most recent version of `main`. +In other words, Git tries to pretend that the changes you made to the old version of `main` were instead -made to the new version of `main`. During this process, you should expect to +made to the new version of `main`. +During this process, you should expect to encounter at least one "rebase conflict." This happens when Git's attempt to -reapply the changes fails because your changes conflicted with other changes -that have been made. You can tell that this happened because you'll see -lines in the output that look like +reapply the changes fails because your changes conflicted with other changes that have been made. +You can tell that this happened because you'll see lines in the output that look like ```console CONFLICT (content): Merge conflict in file.rs @@ -316,21 +327,23 @@ Your code >>>>>>> 8fbf656... Commit fixes 12345 ``` -This represents the lines in the file that Git could not figure out how to -rebase. The section between `<<<<<<< HEAD` and `=======` has the code from -`main`, while the other side has your version of the code. You'll need to -decide how to deal with the conflict. You may want to keep your changes, +This represents the lines in the file that Git could not figure out how to rebase. +The section between `<<<<<<< HEAD` and `=======` has the code from +`main`, while the other side has your version of the code. +You'll need to decide how to deal with the conflict. +You may want to keep your changes, keep the changes on `main`, or combine the two. -Generally, resolving the conflict consists of two steps: First, fix the -particular conflict. Edit the file to make the changes you want and remove the -`<<<<<<<`, `=======` and `>>>>>>>` lines in the process. Second, check the -surrounding code. If there was a conflict, its likely there are some logical -errors lying around too! It's a good idea to run `x check` here to make sure -there are no glaring errors. +Generally, resolving the conflict consists of two steps: First, fix the particular conflict. +Edit the file to make the changes you want and remove the +`<<<<<<<`, `=======` and `>>>>>>>` lines in the process. +Second, check the surrounding code. +If there was a conflict, its likely there are some logical errors lying around too! +It's a good idea to run `x check` here to make sure there are no glaring errors. Once you're all done fixing the conflicts, you need to stage the files that had -conflicts in them via `git add`. Afterwards, run `git rebase --continue` to let +conflicts in them via `git add`. +Afterwards, run `git rebase --continue` to let Git know that you've resolved the conflicts and it should finish the rebase. Once the rebase has succeeded, you'll want to update the associated branch on @@ -340,13 +353,11 @@ your fork with `git push --force-with-lease`. The [above section](#rebasing) is a specific guide on rebasing work and dealing with merge conflicts. -Here is some general advice about how to keep your local repo -up-to-date with upstream changes: +Here is some general advice about how to keep your local repo up-to-date with upstream changes: -Using `git pull upstream main` while on your local `main` branch regularly -will keep it up-to-date. You will also want to keep your feature branches -up-to-date as well. After pulling, you can checkout the feature branches -and rebase them: +Using `git pull upstream main` while on your local `main` branch regularly will keep it up-to-date. +You will also want to keep your feature branches up-to-date as well. +After pulling, you can checkout the feature branches and rebase them: ```console git checkout main @@ -367,21 +378,21 @@ feature branches are in sync with their state on the Github side. ### Squash your commits -"Squashing" commits into each other causes them to be merged into a single -commit. Both the upside and downside of this is that it simplifies the history. +"Squashing" commits into each other causes them to be merged into a single commit. +Both the upside and downside of this is that it simplifies the history. On the one hand, you lose track of the steps in which changes were made, but the history becomes easier to work with. If there are no conflicts and you are just squashing to clean up the history, -use `git rebase --interactive --keep-base main`. This keeps the fork point -of your PR the same, making it easier to review the diff of what happened +use `git rebase --interactive --keep-base main`. +This keeps the fork point of your PR the same, making it easier to review the diff of what happened across your rebases. Squashing can also be useful as part of conflict resolution. If your branch contains multiple consecutive rewrites of the same code, or if the rebase conflicts are extremely severe, you can use -`git rebase --interactive main` to gain more control over the process. This -allows you to choose to skip commits, edit the commits that you do not skip, +`git rebase --interactive main` to gain more control over the process. +This allows you to choose to skip commits, edit the commits that you do not skip, change the order in which they are applied, or "squash" them into each other. Alternatively, you can sacrifice the commit history like this: @@ -395,34 +406,35 @@ git rebase --continue ``` You also may want to squash just the last few commits together, possibly -because they only represent "fixups" and not real changes. For example, +because they only represent "fixups" and not real changes. +For example, `git rebase --interactive HEAD~2` will allow you to edit the two commits only. ### `git range-diff` After completing a rebase, and before pushing up your changes, you may want to -review the changes between your old branch and your new one. You can do that -with `git range-diff main @{upstream} HEAD`. +review the changes between your old branch and your new one. +You can do that with `git range-diff main @{upstream} HEAD`. The first argument to `range-diff`, `main` in this case, is the base revision -that you're comparing your old and new branch against. The second argument is +that you're comparing your old and new branch against. +The second argument is the old version of your branch; in this case, `@upstream` means the version that -you've pushed to GitHub, which is the same as what people will see in your pull -request. Finally, the third argument to `range-diff` is the *new* version of +you've pushed to GitHub, which is the same as what people will see in your pull request. +Finally, the third argument to `range-diff` is the *new* version of your branch; in this case, it is `HEAD`, which is the commit that is currently checked-out in your local repo. -Note that you can also use the equivalent, abbreviated form `git range-diff -main @{u} HEAD`. +Note that you can also use the equivalent, abbreviated form `git range-diff main @{u} HEAD`. Unlike in regular Git diffs, you'll see a `-` or `+` next to another `-` or `+` -in the range-diff output. The marker on the left indicates a change between the -old branch and the new branch, and the marker on the right indicates a change -you've committed. So, you can think of a range-diff as a "diff of diffs" since +in the range-diff output. +The marker on the left indicates a change between the +old branch and the new branch, and the marker on the right indicates a change you've committed. +So, you can think of a range-diff as a "diff of diffs" since it shows you the differences between your old diff and your new diff. -Here's an example of `git range-diff` output (taken from [Git's -docs][range-diff-example-docs]): +Here's an example of `git range-diff` output (taken from [Git's docs][range-diff-example-docs]): ```console -: ------- > 1: 0ddba11 Prepare for the inevitable! @@ -447,12 +459,13 @@ docs][range-diff-example-docs]): (Note that `git range-diff` output in your terminal will probably be easier to read than in this example because it will have colors.) -Another feature of `git range-diff` is that, unlike `git diff`, it will also -diff commit messages. This feature can be useful when amending several commit +Another feature of `git range-diff` is that, unlike `git diff`, it will also diff commit messages. +This feature can be useful when amending several commit messages so you can make sure you changed the right parts. `git range-diff` is a very useful command, but note that it can take some time -to get used to its output format. You may also find Git's documentation on the +to get used to its output format. +You may also find Git's documentation on the command useful, especially their ["Examples" section][range-diff-example-docs]. [range-diff-example-docs]: https://git-scm.com/docs/git-range-diff#_examples @@ -460,18 +473,19 @@ command useful, especially their ["Examples" section][range-diff-example-docs]. ## No-Merge Policy The rust-lang/rust repo uses what is known as a "rebase workflow." This means -that merge commits in PRs are not accepted. As a result, if you are running -`git merge` locally, chances are good that you should be rebasing instead. Of -course, this is not always true; if your merge will just be a fast-forward, +that merge commits in PRs are not accepted. +As a result, if you are running +`git merge` locally, chances are good that you should be rebasing instead. +Of course, this is not always true; if your merge will just be a fast-forward, like the merges that `git pull` usually performs, then no merge commit is -created and you have nothing to worry about. Running `git config merge.ff only` -(this will apply the config to the local repo) +created and you have nothing to worry about. +Running `git config merge.ff only` (this will apply the config to the local repo) once will ensure that all the merges you perform are of this type, so that you cannot make a mistake. -There are a number of reasons for this decision and like all others, it is a -tradeoff. The main advantage is the generally linear commit history. This -greatly simplifies bisecting and makes the history and commit log much easier +There are a number of reasons for this decision and like all others, it is a tradeoff. +The main advantage is the generally linear commit history. +This greatly simplifies bisecting and makes the history and commit log much easier to follow and understand. ## Tips for reviewing @@ -490,15 +504,17 @@ You can also use `git diff -w origin/main` to view changes locally. To checkout PRs locally, you can use `git fetch upstream pull/NNNNN/head && git checkout FETCH_HEAD`. -You can also use github's cli tool. Github shows a button on PRs where you can copy-paste the -command to check it out locally. See for more info. +You can also use github's cli tool. +Github shows a button on PRs where you can copy-paste the command to check it out locally. +See for more info. ![`gh` suggestion](./img/github-cli.png) ### Using GitHub dev As an alternative to the GitHub web UI, GitHub Dev provides a web-based editor for browsing -repository and PRs. It can be opened by replacing `github.com` with `github.dev` in the URL +repository and PRs. +It can be opened by replacing `github.com` with `github.dev` in the URL or by pressing `.` on a GitHub page. See [the docs for github.dev editor](https://docs.github.com/en/codespaces/the-githubdev-web-based-editor) for more details. @@ -506,8 +522,8 @@ for more details. ### Moving large sections of code Git and Github's default diff view for large moves *within* a file is quite poor; it will show each -line as deleted and each line as added, forcing you to compare each line yourself. Git has an option -to show moved lines in a different color: +line as deleted and each line as added, forcing you to compare each line yourself. +Git has an option to show moved lines in a different color: ```console git log -p --color-moved=dimmed-zebra --color-moved-ws=allow-indentation-change @@ -517,12 +533,14 @@ See [the docs for `--color-moved`](https://git-scm.com/docs/git-diff#Documentati ### range-diff -See [the relevant section for PR authors](#git-range-diff). This can be useful for comparing code +See [the relevant section for PR authors](#git-range-diff). +This can be useful for comparing code that was force-pushed to make sure there are no unexpected changes. ### Ignoring changes to specific files -Many large files in the repo are autogenerated. To view a diff that ignores changes to those files, +Many large files in the repo are autogenerated. +To view a diff that ignores changes to those files, you can use the following syntax (e.g. Cargo.lock): ```console @@ -535,11 +553,13 @@ Arbitrary patterns are supported (e.g. `:!compiler/*`). Patterns use the same sy ## Git submodules **NOTE**: submodules are a nice thing to know about, but it *isn't* an absolute -prerequisite to contribute to `rustc`. If you are using Git for the first time, +prerequisite to contribute to `rustc`. +If you are using Git for the first time, you might want to get used to the main concepts of Git before reading this section. The `rust-lang/rust` repository uses [Git submodules] as a way to use other -Rust projects from within the `rust` repo. Examples include Rust's fork of +Rust projects from within the `rust` repo. +Examples include Rust's fork of `llvm-project`, `cargo` and libraries like `stdarch` and `backtrace`. Those projects are developed and maintained in an separate Git (and GitHub) @@ -547,13 +567,14 @@ repository, and they have their own Git history/commits, issue tracker and PRs. Submodules allow us to create some sort of embedded sub-repository inside the `rust` repository and use them like they were directories in the `rust` repository. -Take `llvm-project` for example. `llvm-project` is maintained in the [`rust-lang/llvm-project`] -repository, but it is used in `rust-lang/rust` by the compiler for code generation and -optimization. We bring it in `rust` as a submodule, in the `src/llvm-project` folder. +Take `llvm-project` for example. +`llvm-project` is maintained in the [`rust-lang/llvm-project`] +repository, but it is used in `rust-lang/rust` by the compiler for code generation and optimization. +We bring it in `rust` as a submodule, in the `src/llvm-project` folder. The contents of submodules are ignored by Git: submodules are in some sense isolated -from the rest of the repository. However, if you try to `cd src/llvm-project` and then -run `git status`: +from the rest of the repository. +However, if you try to `cd src/llvm-project` and then run `git status`: ```console HEAD detached at 9567f08afc943 @@ -566,7 +587,8 @@ particular commit. This is because, like any dependency, we want to be able to control which version to use. Submodules allow us to do just that: every submodule is "pinned" to a certain -commit, which doesn't change unless modified manually. If you use `git checkout ` +commit, which doesn't change unless modified manually. +If you use `git checkout ` in the `llvm-project` directory and go back to the `rust` directory, you can stage this change like any other, e.g. by running `git add src/llvm-project`. (Note that if you *don't* stage the change to commit, then you run the risk that running @@ -576,10 +598,10 @@ it automatically "updates" the submodules.) This version selection is usually done by the maintainers of the project, and looks like [this][llvm-update]. -Git submodules take some time to get used to, so don't worry if it isn't perfectly -clear yet. You will rarely have to use them directly and, again, you don't need -to know everything about submodules to contribute to Rust. Just know that they -exist and that they correspond to some sort of embedded subrepository dependency +Git submodules take some time to get used to, so don't worry if it isn't perfectly clear yet. +You will rarely have to use them directly and, again, you don't need +to know everything about submodules to contribute to Rust. +Just know that they exist and that they correspond to some sort of embedded subrepository dependency that Git can nicely and fairly conveniently handle for us. ### Hard-resetting submodules @@ -639,13 +661,12 @@ src/gcc` in this example, you need to: 2. `rm -rf .git/modules//config` 3. `rm -rf .gitconfig.lock` if somehow the `.gitconfig` lock is orphaned. -Then do something like `./x fmt` to have bootstrap manage the submodule -checkouts for you. +Then do something like `./x fmt` to have bootstrap manage the submodule checkouts for you. ## Ignoring commits during `git blame` -Some commits contain large reformatting changes that don't otherwise change functionality. They can -be instructed to be ignored by `git blame` through +Some commits contain large reformatting changes that don't otherwise change functionality. +They can be instructed to be ignored by `git blame` through [`.git-blame-ignore-revs`](https://github.com/rust-lang/rust/blob/HEAD/.git-blame-ignore-revs): 1. Configure `git blame` to use `.git-blame-ignore-revs` as the list of commits to ignore: `git From d34695f50338f899732994ecc8e11865decc571d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:21:42 +0200 Subject: [PATCH 386/583] some improvements --- src/doc/rustc-dev-guide/src/git.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index d6665f6e17cc..c0b449e8fb28 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -220,7 +220,7 @@ hint: See the 'Note about fast-forwards' in 'git push --help' for details. ``` The advice this gives is incorrect! -Because of Rust's ["no-merge" policy](#no-merge-policy) the merge commit created by `git pull` +Because of Rust's ["no-merge" policy](#no-merge-policy), the merge commit created by `git pull` will not be allowed in the final PR, in addition to defeating the point of the rebase! Use `git push --force-with-lease` instead. @@ -280,7 +280,7 @@ out if you're using `download-ci-llvm`. When you edit your code locally, you are making changes to the version of rust-lang/rust that existed when you created your feature branch. -As such, when you submit your PR it is possible that some of the changes that have been made +As such, when you submit your PR, it is possible that some of the changes that have been made to rust-lang/rust since then are in conflict with the changes you've made. When this happens, you need to resolve the conflicts before your changes can be merged. To do that, you need to rebase your work on top of rust-lang/rust. @@ -309,7 +309,7 @@ In other words, Git tries to pretend that the changes you made to the old version of `main` were instead made to the new version of `main`. During this process, you should expect to -encounter at least one "rebase conflict." This happens when Git's attempt to +encounter at least one "rebase conflict". This happens when Git's attempt to reapply the changes fails because your changes conflicted with other changes that have been made. You can tell that this happened because you'll see lines in the output that look like @@ -472,7 +472,7 @@ command useful, especially their ["Examples" section][range-diff-example-docs]. ## No-Merge Policy -The rust-lang/rust repo uses what is known as a "rebase workflow." This means +The rust-lang/rust repo uses what is known as a "rebase workflow". This means that merge commits in PRs are not accepted. As a result, if you are running `git merge` locally, chances are good that you should be rebasing instead. @@ -483,7 +483,7 @@ Running `git config merge.ff only` (this will apply the config to the local repo once will ensure that all the merges you perform are of this type, so that you cannot make a mistake. -There are a number of reasons for this decision and like all others, it is a tradeoff. +There are a number of reasons for this decision, and like all others, it is a tradeoff. The main advantage is the generally linear commit history. This greatly simplifies bisecting and makes the history and commit log much easier to follow and understand. @@ -560,7 +560,7 @@ you might want to get used to the main concepts of Git before reading this secti The `rust-lang/rust` repository uses [Git submodules] as a way to use other Rust projects from within the `rust` repo. Examples include Rust's fork of -`llvm-project`, `cargo` and libraries like `stdarch` and `backtrace`. +`llvm-project`, `cargo`, and libraries like `stdarch` and `backtrace`. Those projects are developed and maintained in an separate Git (and GitHub) repository, and they have their own Git history/commits, issue tracker and PRs. From 72338fafb2c607684d9c848ecdb1f0b314cd8588 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Thu, 29 Jan 2026 15:38:49 +0000 Subject: [PATCH 387/583] Port `rustc_layout` to attribute parser --- .../src/attributes/rustc_internal.rs | 59 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 3 +- .../rustc_hir/src/attrs/data_structures.rs | 12 ++++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/messages.ftl | 2 - compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_passes/src/errors.rs | 7 --- compiler/rustc_passes/src/layout_test.rs | 49 +++++++-------- tests/ui/layout/debug.rs | 8 +-- tests/ui/layout/debug.stderr | 28 +++++---- 10 files changed, 115 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 365cd55bdc38..6375927b01de 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,4 +1,5 @@ use rustc_ast::{LitIntType, LitKind, MetaItemLit}; +use rustc_hir::attrs::RustcLayoutType; use rustc_session::errors; use super::prelude::*; @@ -329,3 +330,61 @@ impl NoArgsAttributeParser for RustcOffloadKernelParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOffloadKernel; } + +pub(crate) struct RustcLayoutParser; + +impl CombineAttributeParser for RustcLayoutParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_layout]; + + type Item = RustcLayoutType; + + const CONVERT: ConvertFn = |items, _| AttributeKind::RustcLayout(items); + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::TyAlias), + ]); + + const TEMPLATE: AttributeTemplate = + template!(List: &["abi", "align", "size", "homogenous_aggregate", "debug"]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let ArgParser::List(items) = args else { + cx.expected_list(cx.attr_span, args); + return vec![]; + }; + + let mut result = Vec::new(); + for item in items.mixed() { + let Some(arg) = item.meta_item() else { + cx.unexpected_literal(item.span()); + continue; + }; + let Some(ident) = arg.ident() else { + cx.expected_identifier(arg.span()); + return vec![]; + }; + let ty = match ident.name { + sym::abi => RustcLayoutType::Abi, + sym::align => RustcLayoutType::Align, + sym::size => RustcLayoutType::Size, + sym::homogeneous_aggregate => RustcLayoutType::HomogenousAggregate, + sym::debug => RustcLayoutType::Debug, + _ => { + cx.expected_specific_argument( + ident.span, + &[sym::abi, sym::align, sym::size, sym::homogeneous_aggregate, sym::debug], + ); + continue; + } + }; + result.push(ty); + } + result + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 20bdfa45a013..a4d08de515ad 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -75,7 +75,7 @@ use crate::attributes::rustc_dump::{ RustcDumpVtable, }; use crate::attributes::rustc_internal::{ - RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, + RustcHasIncoherentInherentImplsParser, RustcLayoutParser, RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, RustcMustImplementOneOfParser, @@ -198,6 +198,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, // tidy-alphabetical-end diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index ef9ee4b6fb2d..bc58f1d8faa5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -690,6 +690,15 @@ impl IntoDiagArg for CrateType { } } +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum RustcLayoutType { + Abi, + Align, + Size, + HomogenousAggregate, + Debug, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -1048,6 +1057,9 @@ pub enum AttributeKind { /// Represents `#[rustc_has_incoherent_inherent_impls]` RustcHasIncoherentInherentImpls, + /// Represents `#[rustc_layout]` + RustcLayout(ThinVec), + /// Represents `#[rustc_layout_scalar_valid_range_end]`. RustcLayoutScalarValidRangeEnd(Box, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 631b07ff1da2..4fd0f5698a8b 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -111,6 +111,7 @@ impl AttributeKind { RustcDumpVtable(..) => No, RustcDynIncompatibleTrait(..) => No, RustcHasIncoherentInherentImpls => Yes, + RustcLayout(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcLegacyConstGenerics { .. } => Yes, diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 395d940ddae6..f8ff46189c05 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -302,8 +302,6 @@ passes_layout_align = align: {$align} passes_layout_homogeneous_aggregate = homogeneous_aggregate: {$homogeneous_aggregate} -passes_layout_invalid_attribute = - `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases passes_layout_of = layout_of({$normalized_ty}) = {$ty_layout} passes_layout_size = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 541870607b63..800965f81d3d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -299,6 +299,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcDumpVtable(..) | AttributeKind::RustcDynIncompatibleTrait(..) | AttributeKind::RustcHasIncoherentInherentImpls + | AttributeKind::RustcLayout(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) | AttributeKind::RustcLayoutScalarValidRangeStart(..) | AttributeKind::RustcLintOptDenyFieldAccess { .. } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index bf9b615546d8..29688dd4d537 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -519,13 +519,6 @@ pub(crate) struct LayoutOf<'tcx> { pub ty_layout: String, } -#[derive(Diagnostic)] -#[diag(passes_layout_invalid_attribute)] -pub(crate) struct LayoutInvalidAttribute { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_abi_of)] pub(crate) struct AbiOf { diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 4054cfa56330..354a98d6d0dc 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -1,20 +1,18 @@ use rustc_abi::{HasDataLayout, TargetDataLayout}; -use rustc_hir::Attribute; +use rustc_hir::attrs::{AttributeKind, RustcLayoutType}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::find_attr; use rustc_middle::span_bug; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers}; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt; use rustc_trait_selection::traits; -use crate::errors::{ - LayoutAbi, LayoutAlign, LayoutHomogeneousAggregate, LayoutInvalidAttribute, LayoutOf, - LayoutSize, UnrecognizedArgument, -}; +use crate::errors::{LayoutAbi, LayoutAlign, LayoutHomogeneousAggregate, LayoutOf, LayoutSize}; pub fn test_layout(tcx: TyCtxt<'_>) { if !tcx.features().rustc_attrs() { @@ -22,14 +20,14 @@ pub fn test_layout(tcx: TyCtxt<'_>) { return; } for id in tcx.hir_crate_items(()).definitions() { - for attr in tcx.get_attrs(id, sym::rustc_layout) { - match tcx.def_kind(id) { - DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union => { - dump_layout_of(tcx, id, attr); - } - _ => { - tcx.dcx().emit_err(LayoutInvalidAttribute { span: tcx.def_span(id) }); - } + let attrs = tcx.get_all_attrs(id); + if let Some(attrs) = find_attr!(attrs, AttributeKind::RustcLayout(attrs) => attrs) { + // Attribute parsing handles error reporting + if matches!( + tcx.def_kind(id), + DefKind::TyAlias | DefKind::Enum | DefKind::Struct | DefKind::Union + ) { + dump_layout_of(tcx, id, attrs); } } } @@ -66,7 +64,7 @@ pub fn ensure_wf<'tcx>( } } -fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { +fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attrs: &[RustcLayoutType]) { let typing_env = ty::TypingEnv::post_analysis(tcx, item_def_id); let ty = tcx.type_of(item_def_id).instantiate_identity(); let span = tcx.def_span(item_def_id.to_def_id()); @@ -75,32 +73,29 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { } match tcx.layout_of(typing_env.as_query_input(ty)) { Ok(ty_layout) => { - // Check out the `#[rustc_layout(..)]` attribute to tell what to dump. - // The `..` are the names of fields to dump. - let meta_items = attr.meta_item_list().unwrap_or_default(); - for meta_item in meta_items { - match meta_item.name() { + for attr in attrs { + match attr { // FIXME: this never was about ABI and now this dump arg is confusing - Some(sym::abi) => { + RustcLayoutType::Abi => { tcx.dcx().emit_err(LayoutAbi { span, abi: format!("{:?}", ty_layout.backend_repr), }); } - Some(sym::align) => { + RustcLayoutType::Align => { tcx.dcx().emit_err(LayoutAlign { span, align: format!("{:?}", ty_layout.align), }); } - Some(sym::size) => { + RustcLayoutType::Size => { tcx.dcx() .emit_err(LayoutSize { span, size: format!("{:?}", ty_layout.size) }); } - Some(sym::homogeneous_aggregate) => { + RustcLayoutType::HomogenousAggregate => { tcx.dcx().emit_err(LayoutHomogeneousAggregate { span, homogeneous_aggregate: format!( @@ -111,16 +106,12 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { }); } - Some(sym::debug) => { + RustcLayoutType::Debug => { let normalized_ty = tcx.normalize_erasing_regions(typing_env, ty); // FIXME: using the `Debug` impl here isn't ideal. let ty_layout = format!("{:#?}", *ty_layout); tcx.dcx().emit_err(LayoutOf { span, normalized_ty, ty_layout }); } - - _ => { - tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() }); - } } } } diff --git a/tests/ui/layout/debug.rs b/tests/ui/layout/debug.rs index 90e3c58dad71..60415911d38e 100644 --- a/tests/ui/layout/debug.rs +++ b/tests/ui/layout/debug.rs @@ -68,12 +68,12 @@ union P5 { zst: [u16; 0], byte: u8 } //~ ERROR: layout_of #[rustc_layout(debug)] type X = std::mem::MaybeUninit; //~ ERROR: layout_of -#[rustc_layout(debug)] -const C: () = (); //~ ERROR: can only be applied to +#[rustc_layout(debug)] //~ ERROR: cannot be used on constants +const C: () = (); impl S { - #[rustc_layout(debug)] - const C: () = (); //~ ERROR: can only be applied to + #[rustc_layout(debug)] //~ ERROR: cannot be used on associated consts + const C: () = (); } #[rustc_layout(debug)] diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index 79ce21eb532b..c92f876fa5a1 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -4,6 +4,22 @@ error: unions cannot have zero fields LL | union EmptyUnion {} | ^^^^^^^^^^^^^^^^^^^ +error: `#[rustc_layout]` attribute cannot be used on constants + --> $DIR/debug.rs:71:1 + | +LL | #[rustc_layout(debug)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_layout]` can be applied to data types and type aliases + +error: `#[rustc_layout]` attribute cannot be used on associated consts + --> $DIR/debug.rs:75:5 + | +LL | #[rustc_layout(debug)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_layout]` can be applied to data types and type aliases + error: layout_of(E) = Layout { size: Size(12 bytes), align: AbiAlign { @@ -577,12 +593,6 @@ error: layout_of(MaybeUninit) = Layout { LL | type X = std::mem::MaybeUninit; | ^^^^^^ -error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:72:1 - | -LL | const C: () = (); - | ^^^^^^^^^^^ - error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/debug.rs:80:19 | @@ -604,12 +614,6 @@ error: the type `T` does not have a fixed layout LL | type TooGeneric = T; | ^^^^^^^^^^^^^^^^^^ -error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:76:5 - | -LL | const C: () = (); - | ^^^^^^^^^^^ - error: aborting due to 20 previous errors For more information about this error, try `rustc --explain E0277`. From 654a5a3404952815c0aae2f3b2d231623c8f9f8b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:24:39 +0200 Subject: [PATCH 388/583] sembr src/compiler-team.md --- src/doc/rustc-dev-guide/src/compiler-team.md | 64 +++++++++----------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index 495bd22da4d8..6a02273f24e4 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -3,7 +3,8 @@ > NOTE: > There exists much detail about the team [on Forge], making most of the following obsolete. -rustc is maintained by the [Rust compiler team][team]. The people who belong to +rustc is maintained by the [Rust compiler team][team]. +The people who belong to this team collectively work to track regressions and implement new features. Members of the Rust compiler team are people who have made significant contributions to rustc and its design. @@ -34,26 +35,24 @@ who are reviewers of each part. ## Rust compiler meeting The compiler team has a weekly meeting where we do triage and try to -generally stay on top of new bugs, regressions, and discuss important -things in general. -They are held on [Zulip][zulip-meetings]. It works roughly as follows: +generally stay on top of new bugs, regressions, and discuss important things in general. +They are held on [Zulip][zulip-meetings]. +It works roughly as follows: - **Announcements, MCPs/FCPs, and WG-check-ins:** We share some - announcements with the rest of the team about important things we want - everyone to be aware of. We also share the status of MCPs and FCPs and we - use the opportunity to have a couple of WGs giving us an update about - their work. + announcements with the rest of the team about important things we want everyone to be aware of. + We also share the status of MCPs and FCPs and we + use the opportunity to have a couple of WGs giving us an update about their work. - **Check for beta and stable nominations:** These are nominations of things to backport to beta and stable respectively. - We then look for new cases where the compiler broke previously working - code in the wild. Regressions are important issues to fix, so it's + We then look for new cases where the compiler broke previously working code in the wild. + Regressions are important issues to fix, so it's likely that they are tagged as P-critical or P-high; the major exception would be bug fixes (though even there we often [aim to give warnings first][procedure]). - **Review P-critical and P-high bugs:** P-critical and P-high bugs are - those that are sufficiently important for us to actively track - progress. P-critical and P-high bugs should ideally always have an - assignee. + those that are sufficiently important for us to actively track progress. + P-critical and P-high bugs should ideally always have an assignee. - **Check `S-waiting-on-t-compiler` and `I-compiler-nominated` issues:** These are issues where feedback from the team is desired. - **Look over the performance triage report:** We check for PRs that made the @@ -61,8 +60,7 @@ They are held on [Zulip][zulip-meetings]. It works roughly as follows: the regression can be addressed in a future PR. The meeting currently takes place on Thursdays at 10am Boston time -(UTC-4 typically, but daylight savings time sometimes makes things -complicated). +(UTC-4 typically, but daylight savings time sometimes makes things complicated). [procedure]: ./bug-fix-procedure.md [zulip-t-compiler]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler @@ -72,17 +70,16 @@ complicated). ## Team membership Membership in the Rust team is typically offered when someone has been -making significant contributions to the compiler for some -time. Membership is both a recognition but also an obligation: +making significant contributions to the compiler for some time. +Membership is both a recognition but also an obligation: compiler team members are generally expected to help with upkeep as well as doing reviews and other work. If you are interested in becoming a compiler team member, the first -thing to do is to start fixing some bugs, or get involved in a working -group. One good way to find bugs is to look for +thing to do is to start fixing some bugs, or get involved in a working group. +One good way to find bugs is to look for [open issues tagged with E-easy](https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy) -or -[E-mentor](https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AE-mentor). +or [E-mentor](https://github.com/rust-lang/rust/issues?q=is%3Aopen+is%3Aissue+label%3AE-mentor). You can also dig through the graveyard of PRs that were [closed due to inactivity](https://github.com/rust-lang/rust/pulls?q=is%3Apr+label%3AS-inactive), @@ -92,18 +89,17 @@ for which the original author didn't have time. ### r+ rights -Once you have made a number of individual PRs to rustc, we will often -offer r+ privileges. This means that you have the right to instruct -"bors" (the robot that manages which PRs get landed into rustc) to -merge a PR +Once you have made a number of individual PRs to rustc, we will often offer r+ privileges. +This means that you have the right to instruct +"bors" (the robot that manages which PRs get landed into rustc) to merge a PR ([here are some instructions for how to talk to bors][bors-guide]). [bors-guide]: https://bors.rust-lang.org/ The guidelines for reviewers are as follows: -- You are always welcome to review any PR, regardless of who it is - assigned to. However, do not r+ PRs unless: +- You are always welcome to review any PR, regardless of who it is assigned to. + However, do not r+ PRs unless: - You are confident in that part of the code. - You are confident that nobody else wants to review it first. - For example, sometimes people will express a desire to review a @@ -119,18 +115,16 @@ The guidelines for reviewers are as follows: Once you have r+ rights, you can also be added to the [reviewer rotation]. [triagebot] is the bot that [automatically assigns] incoming PRs to reviewers. -If you are added, you will be randomly selected to review -PRs. If you find you are assigned a PR that you don't feel comfortable +If you are added, you will be randomly selected to review PRs. +If you find you are assigned a PR that you don't feel comfortable reviewing, you can also leave a comment like `r? @so-and-so` to assign to someone else — if you don't know who to request, just write `r? -@nikomatsakis for reassignment` and @nikomatsakis will pick someone -for you. +@nikomatsakis for reassignment` and @nikomatsakis will pick someone for you. [reviewer rotation]: https://github.com/rust-lang/rust/blob/36285c5de8915ecc00d91ae0baa79a87ed5858d5/triagebot.toml#L528-L577 [triagebot]: https://github.com/rust-lang/triagebot/ [automatically assigns]: https://forge.rust-lang.org/triagebot/pr-assignment.html -Getting on the reviewer rotation is much appreciated as it lowers the -review burden for all of us! However, if you don't have time to give -people timely feedback on their PRs, it may be better that you don't -get on the list. +Getting on the reviewer rotation is much appreciated as it lowers the review burden for all of us! +However, if you don't have time to give +people timely feedback on their PRs, it may be better that you don't get on the list. From b4ded124a73bb4399a8d32747af8904b16f43480 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:27:42 +0200 Subject: [PATCH 389/583] extraneous whitespace --- src/doc/rustc-dev-guide/src/compiler-team.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-team.md b/src/doc/rustc-dev-guide/src/compiler-team.md index 6a02273f24e4..ee058c805f0d 100644 --- a/src/doc/rustc-dev-guide/src/compiler-team.md +++ b/src/doc/rustc-dev-guide/src/compiler-team.md @@ -99,7 +99,7 @@ This means that you have the right to instruct The guidelines for reviewers are as follows: - You are always welcome to review any PR, regardless of who it is assigned to. - However, do not r+ PRs unless: + However, do not r+ PRs unless: - You are confident in that part of the code. - You are confident that nobody else wants to review it first. - For example, sometimes people will express a desire to review a From 84e994ebd45b78a9d4f5c94c1b76f9543912dad5 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:28:12 +0200 Subject: [PATCH 390/583] sembr src/tests/ci.md --- src/doc/rustc-dev-guide/src/tests/ci.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index ce80b07fe08d..723926f2241f 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -6,16 +6,15 @@ The primary goal of our CI system is to ensure that the `main` branch of From a high-level point of view, when you open a pull request at `rust-lang/rust`, the following will happen: -- A small [subset](#pull-request-builds) of tests and checks are run after each - push to the PR. +- A small [subset](#pull-request-builds) of tests and checks are run after each push to the PR. This should help catch common errors. - When the PR is approved, the [bors] bot enqueues the PR into a [merge queue]. - Once the PR gets to the front of the queue, bors will create a merge commit and run the [full test suite](#auto-builds) on it. The merge commit either contains only one specific PR or it can be a ["rollup"](#rollups) which combines multiple PRs together, to reduce CI costs and merge delays. -- Once the whole test suite finishes, two things can happen. Either CI fails - with an error that needs to be addressed by the developer, or CI succeeds and +- Once the whole test suite finishes, two things can happen. + Either CI fails with an error that needs to be addressed by the developer, or CI succeeds and the merge commit is then pushed to the `main` branch. If you want to modify what gets executed on CI, see [Modifying CI jobs](#modifying-ci-jobs). @@ -303,8 +302,7 @@ This is worth it because these release artifacts: - Allow perf testing even at a later date. - Allow bisection when bugs are discovered later. -- Ensure release quality since if we're always releasing, we can catch problems - early. +- Ensure release quality since if we're always releasing, we can catch problems early. ### Rollups @@ -449,8 +447,7 @@ If you want to determine which `bootstrap.toml` settings are used in CI for a particular job, it is probably easiest to just look at the build log. To do this: -1. Go to - +1. Go to to find the most recently successful build, and click on it. 2. Choose the job you are interested in on the left-hand side. 3. Click on the gear icon and choose "View raw logs" From c3e9f0007e08508f0e3778d0ffb873563b5a8bf1 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:31:17 +0200 Subject: [PATCH 391/583] sembr src/rustdoc-internals/rustdoc-json-test-suite.md --- .../src/rustdoc-internals/rustdoc-json-test-suite.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md index 7a846b711326..3208c84fe554 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md @@ -18,8 +18,7 @@ Each crate's json output is checked by 2 programs: [jsondoclint](#jsondocck) and This makes sure there are no dangling [`Id`]s. +Also, talk about how it works --> ## jsondocck @@ -35,7 +34,7 @@ It uses [JSONPath] as a query language, which takes a path, and returns a *list* - `//@ has `: Check `` exists, and at least 1 of the matches is equal to the given `` - `//@ !has `: Checks `` exists, but none of the matches equal the given ``. - `//@ is `: Check `` matches exactly one value, and it's equal to the given ``. -- `//@ is ...`: Check that `` matches to exactly every given ``. +- `//@ is ...`: Check that `` matches to exactly every given ``. Ordering doesn't matter here. - `//@ !is `: Check `` matches exactly one value, and that value is not equal to the given ``. - `//@ count `: Check that `` matches to `` of values. @@ -48,7 +47,8 @@ These are defined in [`directive.rs`]. Values can be either JSON values, or variables. - JSON values are JSON literals, e.g. `true`, `"string"`, `{"key": "value"}`. - These often need to be quoted using `'`, to be processed as 1 value. See [§Argument splitting](#argument-splitting) + These often need to be quoted using `'`, to be processed as 1 value. + See [§Argument splitting](#argument-splitting) - Variables can be used to store the value in one path, and use it in later queries. They are set with the `//@ set = ` directive, and accessed with `$` From 654945a736b2eba01c62191fffd47d0a124d0018 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Jan 2026 14:48:05 +1100 Subject: [PATCH 392/583] Simplify `QueryStackFrame::new`. Instead of passing a closure that computes a hash and immediately calling it, just compute the hash and pass the value. --- compiler/rustc_query_impl/src/plumbing.rs | 14 ++++++-------- compiler/rustc_query_system/src/query/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index c71352c3fd20..d1b1c3356cfd 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -373,14 +373,12 @@ pub(crate) fn create_query_frame< ) -> QueryStackFrame> { let def_id = key.key_as_def_id(); - let hash = || { - tcx.with_stable_hashing_context(|mut hcx| { - let mut hasher = StableHasher::new(); - kind.as_usize().hash_stable(&mut hcx, &mut hasher); - key.hash_stable(&mut hcx, &mut hasher); - hasher.finish::() - }) - }; + let hash = tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + kind.as_usize().hash_stable(&mut hcx, &mut hasher); + key.hash_stable(&mut hcx, &mut hasher); + hasher.finish::() + }); let def_id_for_ty_in_cycle = key.def_id_for_ty_in_cycle(); let info = diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 701253d50fcc..a689c3ac41b0 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -63,11 +63,11 @@ impl QueryStackFrame { pub fn new( info: I, dep_kind: DepKind, - hash: impl FnOnce() -> Hash64, + hash: Hash64, def_id: Option, def_id_for_ty_in_cycle: Option, ) -> Self { - Self { info, def_id, dep_kind, hash: hash(), def_id_for_ty_in_cycle } + Self { info, def_id, dep_kind, hash, def_id_for_ty_in_cycle } } fn lift>( From 637e2cb5e1d0eb7ecddd61999d54b7abd68ea1e9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Jan 2026 16:31:04 +1100 Subject: [PATCH 393/583] Rename `create_query_frame_extra`. It produces a `QueryStackFrameExtra`, so `stack_` should be in the name. --- compiler/rustc_query_impl/src/plumbing.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index d1b1c3356cfd..b3448a6dc864 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -322,7 +322,7 @@ macro_rules! should_ever_cache_on_disk { }; } -fn create_query_frame_extra<'tcx, K: Key + Copy + 'tcx>( +fn mk_query_stack_frame_extra<'tcx, K: Key + Copy + 'tcx>( (tcx, key, kind, name, do_describe): ( TyCtxt<'tcx>, K, @@ -382,7 +382,7 @@ pub(crate) fn create_query_frame< let def_id_for_ty_in_cycle = key.def_id_for_ty_in_cycle(); let info = - QueryStackDeferred::new((tcx, key, kind, name, do_describe), create_query_frame_extra); + QueryStackDeferred::new((tcx, key, kind, name, do_describe), mk_query_stack_frame_extra); QueryStackFrame::new(info, kind, hash, def_id, def_id_for_ty_in_cycle) } From 31eab45e4a979503a14db53a0a5311dc2cb30e1d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:58:42 +0200 Subject: [PATCH 394/583] fix sembr tool corner case --- src/doc/rustc-dev-guide/ci/sembr/src/main.rs | 13 ++++++++++++- .../rustdoc-internals/rustdoc-json-test-suite.md | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 0ae77af047e6..6720267e14f3 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -83,7 +83,8 @@ fn ignore(line: &str, in_code_block: bool) -> bool { || line.contains(" etc.") || line.contains("i.e.") || line.contains("et. al") - || line.contains("") || line.contains('|') || line.trim_start().starts_with('>') || line.starts_with('#') @@ -266,6 +267,11 @@ leave the text alone ``` + ignore +html comment closing + handle the indented well @@ -292,6 +298,11 @@ leave the text alone ``` + ignore +html comment closing + handle the indented well [a target]: https://example.com diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md index 3208c84fe554..2a4b89250f10 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md @@ -18,7 +18,8 @@ Each crate's json output is checked by 2 programs: [jsondoclint](#jsondocck) and This makes sure there are no dangling [`Id`]s. +Also, talk about how it works + --> ## jsondocck From 62dbe9fbab878473fb9deb765222ecbbe521fb8c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 30 Jan 2026 00:59:05 +0200 Subject: [PATCH 395/583] whitespace --- .../src/rustdoc-internals/rustdoc-json-test-suite.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md index 2a4b89250f10..f9d9d1b59a91 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md @@ -32,7 +32,7 @@ It uses [JSONPath] as a query language, which takes a path, and returns a *list* - `//@ has `: Checks `` exists, i.e. matches at least 1 value. - `//@ !has `: Checks `` doesn't exist, i.e. matches 0 values. -- `//@ has `: Check `` exists, and at least 1 of the matches is equal to the given `` +- `//@ has `: Check `` exists, and at least 1 of the matches is equal to the given `` - `//@ !has `: Checks `` exists, but none of the matches equal the given ``. - `//@ is `: Check `` matches exactly one value, and it's equal to the given ``. - `//@ is ...`: Check that `` matches to exactly every given ``. @@ -47,7 +47,7 @@ These are defined in [`directive.rs`]. Values can be either JSON values, or variables. -- JSON values are JSON literals, e.g. `true`, `"string"`, `{"key": "value"}`. +- JSON values are JSON literals, e.g. `true`, `"string"`, `{"key": "value"}`. These often need to be quoted using `'`, to be processed as 1 value. See [§Argument splitting](#argument-splitting) - Variables can be used to store the value in one path, and use it in later queries. From 1c3d9abbad36117265b8a4c85b6cff0f78fdeed5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 15 Jan 2026 19:09:07 +1100 Subject: [PATCH 396/583] Reduce generics use in the query system. `QueryStackFrame` is a generic type, and the `I` parameter leaks out to many other related types. However, it only has two instantiations in practice: - `QueryStackFrame` - `QueryStackFrame>` And most of the places that currently use `QueryStackFrame` are actually specific to one of the instantiations. This commit removes the unneeded genericity. The following types are only ever used with `QueryStackDeferred<'tcx>`: - QueryMap - QueryJobInfo - QueryJob - QueryWaiter - QueryLatchInfo - QueryLatch - QueryState - QueryResult and their `` parameter is changed to `<'tcx>`. Also, the `QueryContext::QueryInfo` associated type is removed. `CycleError` and `QueryInfo` are still generic over type, because they are used with both instantiations. This required also adding a `<'tcx>` parameter to the traits `QueryDispatcher` and `QueryContext`, which is annoying but I can't see how to avoid it. --- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_middle/src/query/plumbing.rs | 2 +- compiler/rustc_query_impl/src/lib.rs | 17 ++-- compiler/rustc_query_impl/src/plumbing.rs | 21 ++--- .../rustc_query_system/src/dep_graph/graph.rs | 20 ++-- .../src/query/dispatcher.rs | 23 ++--- compiler/rustc_query_system/src/query/job.rs | 94 +++++++++---------- compiler/rustc_query_system/src/query/mod.rs | 20 ++-- .../rustc_query_system/src/query/plumbing.rs | 94 +++++++++---------- 9 files changed, 140 insertions(+), 153 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index ad63251363e2..cea50f95df4b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -88,7 +88,7 @@ use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; use rustc_query_system::ich::StableHashingContext; -use rustc_query_system::query::{QueryMode, QueryStackDeferred, QueryState}; +use rustc_query_system::query::{QueryMode, QueryState}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{ diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 7b85dac41aef..2bda014a19fe 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -440,7 +440,7 @@ macro_rules! define_callbacks { #[derive(Default)] pub struct QueryStates<'tcx> { $( - pub $name: QueryState<$($K)*, QueryStackDeferred<'tcx>>, + pub $name: QueryState<'tcx, $($K)*>, )* } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 382b8750a1ce..8fa4fb3090db 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -21,7 +21,7 @@ use rustc_query_system::dep_graph::SerializedDepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ CycleError, CycleErrorHandling, HashResult, QueryCache, QueryDispatcher, QueryMap, QueryMode, - QueryStackDeferred, QueryState, get_query_incr, get_query_non_incr, + QueryState, get_query_incr, get_query_non_incr, }; use rustc_span::{ErrorGuaranteed, Span}; @@ -66,7 +66,7 @@ impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDA // This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`. impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> - QueryDispatcher for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> + QueryDispatcher<'tcx> for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> where for<'a> C::Key: HashStable>, { @@ -86,10 +86,7 @@ where } #[inline(always)] - fn query_state<'a>( - self, - qcx: QueryCtxt<'tcx>, - ) -> &'a QueryState> + fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState<'tcx, Self::Key> where QueryCtxt<'tcx>: 'a, { @@ -98,7 +95,7 @@ where unsafe { &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>) .byte_add(self.vtable.query_state) - .cast::>>() + .cast::>() } } @@ -211,13 +208,15 @@ where /// on the type `rustc_query_impl::query_impl::$name::QueryType`. trait QueryDispatcherUnerased<'tcx> { type UnerasedValue; - type Dispatcher: QueryDispatcher>; + type Dispatcher: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>; const NAME: &'static &'static str; fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher; - fn restore_val(value: ::Value) -> Self::UnerasedValue; + fn restore_val( + value: >::Value, + ) -> Self::UnerasedValue; } pub fn query_system<'a>( diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index b3448a6dc864..ef7a62351930 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -60,9 +60,7 @@ impl<'tcx> HasDepContext for QueryCtxt<'tcx> { } } -impl<'tcx> QueryContext for QueryCtxt<'tcx> { - type QueryInfo = QueryStackDeferred<'tcx>; - +impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { #[inline] fn jobserver_proxy(&self) -> &Proxy { &self.tcx.jobserver_proxy @@ -93,10 +91,7 @@ impl<'tcx> QueryContext for QueryCtxt<'tcx> { /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, /// especially when called from within a deadlock handler, unless a /// complete map is needed and no deadlock is possible at this call site. - fn collect_active_jobs( - self, - require_complete: bool, - ) -> Result>, QueryMap>> { + fn collect_active_jobs(self, require_complete: bool) -> Result, QueryMap<'tcx>> { let mut jobs = QueryMap::default(); let mut complete = true; @@ -415,7 +410,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>( } pub(crate) fn query_key_hash_verify<'tcx>( - query: impl QueryDispatcher>, + query: impl QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, qcx: QueryCtxt<'tcx>, ) { let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name()); @@ -443,7 +438,7 @@ pub(crate) fn query_key_hash_verify<'tcx>( fn try_load_from_on_disk_cache<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) where - Q: QueryDispatcher>, + Q: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, { debug_assert!(tcx.dep_graph.is_green(&dep_node)); @@ -489,7 +484,7 @@ where fn force_from_dep_node<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) -> bool where - Q: QueryDispatcher>, + Q: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>, { // We must avoid ever having to call `force_from_dep_node()` for a // `DepNode::codegen_unit`: @@ -732,14 +727,14 @@ macro_rules! define_queries { } #[inline(always)] - fn restore_val(value: ::Value) -> Self::UnerasedValue { + fn restore_val(value: >::Value) -> Self::UnerasedValue { erase::restore_val::>(value) } } pub(crate) fn collect_active_jobs<'tcx>( tcx: TyCtxt<'tcx>, - qmap: &mut QueryMap>, + qmap: &mut QueryMap<'tcx>, require_complete: bool, ) -> Option<()> { let make_query = |tcx, key| { @@ -823,7 +818,7 @@ macro_rules! define_queries { // These arrays are used for iteration and can't be indexed by `DepKind`. const COLLECT_ACTIVE_JOBS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap>, bool) -> Option<()> + for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<'tcx>, bool) -> Option<()> ] = &[$(query_impl::$name::collect_active_jobs),*]; diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index f0cc9636b75c..6d46d144d0f1 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -519,7 +519,11 @@ impl DepGraph { /// This encodes a diagnostic by creating a node with an unique index and associating /// `diagnostic` with it, for use in the next session. #[inline] - pub fn record_diagnostic(&self, qcx: Qcx, diagnostic: &DiagInner) { + pub fn record_diagnostic<'tcx, Qcx: QueryContext<'tcx>>( + &self, + qcx: Qcx, + diagnostic: &DiagInner, + ) { if let Some(ref data) = self.data { D::read_deps(|task_deps| match task_deps { TaskDepsRef::EvalAlways | TaskDepsRef::Ignore => return, @@ -532,7 +536,7 @@ impl DepGraph { /// This forces a diagnostic node green by running its side effect. `prev_index` would /// refer to a node created used `encode_diagnostic` in the previous session. #[inline] - pub fn force_diagnostic_node( + pub fn force_diagnostic_node<'tcx, Qcx: QueryContext<'tcx>>( &self, qcx: Qcx, prev_index: SerializedDepNodeIndex, @@ -669,7 +673,7 @@ impl DepGraphData { /// This encodes a diagnostic by creating a node with an unique index and associating /// `diagnostic` with it, for use in the next session. #[inline] - fn encode_diagnostic( + fn encode_diagnostic<'tcx, Qcx: QueryContext<'tcx>>( &self, qcx: Qcx, diagnostic: &DiagInner, @@ -693,7 +697,7 @@ impl DepGraphData { /// This forces a diagnostic node green by running its side effect. `prev_index` would /// refer to a node created used `encode_diagnostic` in the previous session. #[inline] - fn force_diagnostic_node( + fn force_diagnostic_node<'tcx, Qcx: QueryContext<'tcx>>( &self, qcx: Qcx, prev_index: SerializedDepNodeIndex, @@ -843,7 +847,7 @@ impl DepGraph { DepNodeColor::Unknown } - pub fn try_mark_green>( + pub fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, dep_node: &DepNode, @@ -858,7 +862,7 @@ impl DepGraphData { /// A node will have an index, when it's already been marked green, or when we can mark it /// green. This function will mark the current task as a reader of the specified node, when /// a node index can be found for that node. - pub(crate) fn try_mark_green>( + pub(crate) fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, dep_node: &DepNode, @@ -883,7 +887,7 @@ impl DepGraphData { } #[instrument(skip(self, qcx, parent_dep_node_index, frame), level = "debug")] - fn try_mark_parent_green>( + fn try_mark_parent_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, parent_dep_node_index: SerializedDepNodeIndex, @@ -973,7 +977,7 @@ impl DepGraphData { /// Try to mark a dep-node which existed in the previous compilation session as green. #[instrument(skip(self, qcx, prev_dep_node_index, frame), level = "debug")] - fn try_mark_previous_green>( + fn try_mark_previous_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>( &self, qcx: Qcx, prev_dep_node_index: SerializedDepNodeIndex, diff --git a/compiler/rustc_query_system/src/query/dispatcher.rs b/compiler/rustc_query_system/src/query/dispatcher.rs index 1ca76a70364c..d7dd6dd6464a 100644 --- a/compiler/rustc_query_system/src/query/dispatcher.rs +++ b/compiler/rustc_query_system/src/query/dispatcher.rs @@ -14,8 +14,8 @@ pub type HashResult = Option, &V) -> Fingerp /// Unambiguous shorthand for `::DepContext`. #[expect(type_alias_bounds)] -type DepContextOf = - <::Qcx as HasDepContext>::DepContext; +type DepContextOf<'tcx, This: QueryDispatcher<'tcx>> = + <>::Qcx as HasDepContext>::DepContext; /// Trait that can be used as a vtable for a single query, providing operations /// and metadata for that query. @@ -25,15 +25,15 @@ type DepContextOf = /// Those types are not visible from this `rustc_query_system` crate. /// /// "Dispatcher" should be understood as a near-synonym of "vtable". -pub trait QueryDispatcher: Copy { +pub trait QueryDispatcher<'tcx>: Copy { fn name(self) -> &'static str; /// Query context used by this dispatcher, i.e. `rustc_query_impl::QueryCtxt`. - type Qcx: QueryContext; + type Qcx: QueryContext<'tcx>; // `Key` and `Value` are `Copy` instead of `Clone` to ensure copying them stays cheap, // but it isn't necessary. - type Key: DepNodeParams> + Eq + Hash + Copy + Debug; + type Key: DepNodeParams> + Eq + Hash + Copy + Debug; type Value: Copy; type Cache: QueryCache; @@ -41,18 +41,15 @@ pub trait QueryDispatcher: Copy { fn format_value(self) -> fn(&Self::Value) -> String; // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_state<'a>( - self, - tcx: Self::Qcx, - ) -> &'a QueryState::QueryInfo>; + fn query_state<'a>(self, tcx: Self::Qcx) -> &'a QueryState<'tcx, Self::Key>; // Don't use this method to access query results, instead use the methods on TyCtxt fn query_cache<'a>(self, tcx: Self::Qcx) -> &'a Self::Cache; - fn cache_on_disk(self, tcx: DepContextOf, key: &Self::Key) -> bool; + fn cache_on_disk(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> bool; // Don't use this method to compute query results, instead use the methods on TyCtxt - fn execute_query(self, tcx: DepContextOf, k: Self::Key) -> Self::Value; + fn execute_query(self, tcx: DepContextOf<'tcx, Self>, k: Self::Key) -> Self::Value; fn compute(self, tcx: Self::Qcx, key: Self::Key) -> Self::Value; @@ -74,7 +71,7 @@ pub trait QueryDispatcher: Copy { /// Synthesize an error value to let compilation continue after a cycle. fn value_from_cycle_error( self, - tcx: DepContextOf, + tcx: DepContextOf<'tcx, Self>, cycle_error: &CycleError, guar: ErrorGuaranteed, ) -> Self::Value; @@ -89,7 +86,7 @@ pub trait QueryDispatcher: Copy { fn hash_result(self) -> HashResult; // Just here for convenience and checking that the key matches the kind, don't override this. - fn construct_dep_node(self, tcx: DepContextOf, key: &Self::Key) -> DepNode { + fn construct_dep_node(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> DepNode { DepNode::construct(tcx, self.dep_kind(), key) } } diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 5810ce0cbe66..177bcd63cbc6 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -12,7 +12,7 @@ use rustc_hir::def::DefKind; use rustc_session::Session; use rustc_span::{DUMMY_SP, Span}; -use super::QueryStackFrameExtra; +use super::{QueryStackDeferred, QueryStackFrameExtra}; use crate::dep_graph::DepContext; use crate::error::CycleStack; use crate::query::plumbing::CycleError; @@ -26,8 +26,8 @@ pub struct QueryInfo { pub query: QueryStackFrame, } -impl QueryInfo { - pub(crate) fn lift>( +impl<'tcx> QueryInfo> { + pub(crate) fn lift>( &self, qcx: Qcx, ) -> QueryInfo { @@ -35,39 +35,39 @@ impl QueryInfo { } } -pub type QueryMap = FxHashMap>; +pub type QueryMap<'tcx> = FxHashMap>; /// A value uniquely identifying an active query job. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct QueryJobId(pub NonZero); impl QueryJobId { - fn query(self, map: &QueryMap) -> QueryStackFrame { + fn query<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame> { map.get(&self).unwrap().query.clone() } - fn span(self, map: &QueryMap) -> Span { + fn span<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Span { map.get(&self).unwrap().job.span } - fn parent(self, map: &QueryMap) -> Option { + fn parent<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option { map.get(&self).unwrap().job.parent } - fn latch(self, map: &QueryMap) -> Option<&QueryLatch> { + fn latch<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Option<&'a QueryLatch<'tcx>> { map.get(&self).unwrap().job.latch.as_ref() } } #[derive(Clone, Debug)] -pub struct QueryJobInfo { - pub query: QueryStackFrame, - pub job: QueryJob, +pub struct QueryJobInfo<'tcx> { + pub query: QueryStackFrame>, + pub job: QueryJob<'tcx>, } /// Represents an active query job. #[derive(Debug)] -pub struct QueryJob { +pub struct QueryJob<'tcx> { pub id: QueryJobId, /// The span corresponding to the reason for which this query was required. @@ -77,23 +77,23 @@ pub struct QueryJob { pub parent: Option, /// The latch that is used to wait on this job. - latch: Option>, + latch: Option>, } -impl Clone for QueryJob { +impl<'tcx> Clone for QueryJob<'tcx> { fn clone(&self) -> Self { Self { id: self.id, span: self.span, parent: self.parent, latch: self.latch.clone() } } } -impl QueryJob { +impl<'tcx> QueryJob<'tcx> { /// Creates a new query job. #[inline] pub fn new(id: QueryJobId, span: Span, parent: Option) -> Self { QueryJob { id, span, parent, latch: None } } - pub(super) fn latch(&mut self) -> QueryLatch { + pub(super) fn latch(&mut self) -> QueryLatch<'tcx> { if self.latch.is_none() { self.latch = Some(QueryLatch::new()); } @@ -113,12 +113,12 @@ impl QueryJob { } impl QueryJobId { - pub(super) fn find_cycle_in_stack( + pub(super) fn find_cycle_in_stack<'tcx>( &self, - query_map: QueryMap, + query_map: QueryMap<'tcx>, current_job: &Option, span: Span, - ) -> CycleError { + ) -> CycleError> { // Find the waitee amongst `current_job` parents let mut cycle = Vec::new(); let mut current_job = Option::clone(current_job); @@ -152,7 +152,10 @@ impl QueryJobId { #[cold] #[inline(never)] - pub fn find_dep_kind_root(&self, query_map: QueryMap) -> (QueryJobInfo, usize) { + pub fn find_dep_kind_root<'tcx>( + &self, + query_map: QueryMap<'tcx>, + ) -> (QueryJobInfo<'tcx>, usize) { let mut depth = 1; let info = query_map.get(&self).unwrap(); let dep_kind = info.query.dep_kind; @@ -172,31 +175,31 @@ impl QueryJobId { } #[derive(Debug)] -struct QueryWaiter { +struct QueryWaiter<'tcx> { query: Option, condvar: Condvar, span: Span, - cycle: Mutex>>, + cycle: Mutex>>>, } #[derive(Debug)] -struct QueryLatchInfo { +struct QueryLatchInfo<'tcx> { complete: bool, - waiters: Vec>>, + waiters: Vec>>, } #[derive(Debug)] -pub(super) struct QueryLatch { - info: Arc>>, +pub(super) struct QueryLatch<'tcx> { + info: Arc>>, } -impl Clone for QueryLatch { +impl<'tcx> Clone for QueryLatch<'tcx> { fn clone(&self) -> Self { Self { info: Arc::clone(&self.info) } } } -impl QueryLatch { +impl<'tcx> QueryLatch<'tcx> { fn new() -> Self { QueryLatch { info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), @@ -206,10 +209,10 @@ impl QueryLatch { /// Awaits for the query job to complete. pub(super) fn wait_on( &self, - qcx: impl QueryContext, + qcx: impl QueryContext<'tcx>, query: Option, span: Span, - ) -> Result<(), CycleError> { + ) -> Result<(), CycleError>> { let waiter = Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() }); self.wait_on_inner(qcx, &waiter); @@ -224,7 +227,7 @@ impl QueryLatch { } /// Awaits the caller on this latch by blocking the current thread. - fn wait_on_inner(&self, qcx: impl QueryContext, waiter: &Arc>) { + fn wait_on_inner(&self, qcx: impl QueryContext<'tcx>, waiter: &Arc>) { let mut info = self.info.lock(); if !info.complete { // We push the waiter on to the `waiters` list. It can be accessed inside @@ -260,7 +263,7 @@ impl QueryLatch { /// Removes a single waiter from the list of waiters. /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Arc> { + fn extract_waiter(&self, waiter: usize) -> Arc> { let mut info = self.info.lock(); debug_assert!(!info.complete); // Remove the waiter from the list of waiters @@ -280,8 +283,8 @@ type Waiter = (QueryJobId, usize); /// For visits of resumable waiters it returns Some(Some(Waiter)) which has the /// required information to resume the waiter. /// If all `visit` calls returns None, this function also returns None. -fn visit_waiters( - query_map: &QueryMap, +fn visit_waiters<'tcx, F>( + query_map: &QueryMap<'tcx>, query: QueryJobId, mut visit: F, ) -> Option> @@ -314,8 +317,8 @@ where /// `span` is the reason for the `query` to execute. This is initially DUMMY_SP. /// If a cycle is detected, this initial value is replaced with the span causing /// the cycle. -fn cycle_check( - query_map: &QueryMap, +fn cycle_check<'tcx>( + query_map: &QueryMap<'tcx>, query: QueryJobId, span: Span, stack: &mut Vec<(Span, QueryJobId)>, @@ -354,8 +357,8 @@ fn cycle_check( /// Finds out if there's a path to the compiler root (aka. code which isn't in a query) /// from `query` without going through any of the queries in `visited`. /// This is achieved with a depth first search. -fn connected_to_root( - query_map: &QueryMap, +fn connected_to_root<'tcx>( + query_map: &QueryMap<'tcx>, query: QueryJobId, visited: &mut FxHashSet, ) -> bool { @@ -376,7 +379,7 @@ fn connected_to_root( } // Deterministically pick an query from a list -fn pick_query<'a, I: Clone, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T +fn pick_query<'a, 'tcx, T, F>(query_map: &QueryMap<'tcx>, queries: &'a [T], f: F) -> &'a T where F: Fn(&T) -> (Span, QueryJobId), { @@ -401,10 +404,10 @@ where /// the function return true. /// If a cycle was not found, the starting query is removed from `jobs` and /// the function returns false. -fn remove_cycle( - query_map: &QueryMap, +fn remove_cycle<'tcx>( + query_map: &QueryMap<'tcx>, jobs: &mut Vec, - wakelist: &mut Vec>>, + wakelist: &mut Vec>>, ) -> bool { let mut visited = FxHashSet::default(); let mut stack = Vec::new(); @@ -505,10 +508,7 @@ fn remove_cycle( /// uses a query latch and then resuming that waiter. /// There may be multiple cycles involved in a deadlock, so this searches /// all active queries for cycles before finally resuming all the waiters at once. -pub fn break_query_cycles( - query_map: QueryMap, - registry: &rustc_thread_pool::Registry, -) { +pub fn break_query_cycles<'tcx>(query_map: QueryMap<'tcx>, registry: &rustc_thread_pool::Registry) { let mut wakelist = Vec::new(); // It is OK per the comments: // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 @@ -602,7 +602,7 @@ pub fn report_cycle<'a>( sess.dcx().create_err(cycle_diag) } -pub fn print_query_stack( +pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( qcx: Qcx, mut current_query: Option, dcx: DiagCtxtHandle<'_>, diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index a689c3ac41b0..63202429679d 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -58,10 +58,10 @@ pub struct QueryStackFrame { pub def_id_for_ty_in_cycle: Option, } -impl QueryStackFrame { +impl<'tcx> QueryStackFrame> { #[inline] pub fn new( - info: I, + info: QueryStackDeferred<'tcx>, dep_kind: DepKind, hash: Hash64, def_id: Option, @@ -70,10 +70,7 @@ impl QueryStackFrame { Self { info, def_id, dep_kind, hash, def_id_for_ty_in_cycle } } - fn lift>( - &self, - qcx: Qcx, - ) -> QueryStackFrame { + fn lift>(&self, qcx: Qcx) -> QueryStackFrame { QueryStackFrame { info: qcx.lift_query_info(&self.info), dep_kind: self.dep_kind, @@ -159,9 +156,7 @@ pub enum QuerySideEffect { Diagnostic(DiagInner), } -pub trait QueryContext: HasDepContext { - type QueryInfo: Clone; - +pub trait QueryContext<'tcx>: HasDepContext { /// Gets a jobserver reference which is used to release then acquire /// a token while waiting on a query. fn jobserver_proxy(&self) -> &Proxy; @@ -171,12 +166,9 @@ pub trait QueryContext: HasDepContext { /// Get the query information from the TLS context. fn current_query_job(self) -> Option; - fn collect_active_jobs( - self, - require_complete: bool, - ) -> Result, QueryMap>; + fn collect_active_jobs(self, require_complete: bool) -> Result, QueryMap<'tcx>>; - fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra; + fn lift_query_info(self, info: &QueryStackDeferred<'tcx>) -> QueryStackFrameExtra; /// Load a side effect associated to the node in the previous session. fn load_side_effect( diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 7e9f83e8fe82..c4431ff870d0 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -18,7 +18,7 @@ use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; use tracing::instrument; -use super::{QueryDispatcher, QueryStackFrameExtra}; +use super::{QueryDispatcher, QueryStackDeferred, QueryStackFrameExtra}; use crate::dep_graph::{ DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams, HasDepContext, }; @@ -34,23 +34,23 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { move |x| x.0 == *k } -pub struct QueryState { - active: Sharded)>>, +pub struct QueryState<'tcx, K> { + active: Sharded)>>, } /// Indicates the state of a query for a given key in a query map. -enum QueryResult { +enum QueryResult<'tcx> { /// An already executing query. The query job can be used to await for its completion. - Started(QueryJob), + Started(QueryJob<'tcx>), /// The query panicked. Queries trying to wait on this will raise a fatal error which will /// silently panic. Poisoned, } -impl QueryResult { +impl<'tcx> QueryResult<'tcx> { /// Unwraps the query job expecting that it has started. - fn expect_job(self) -> QueryJob { + fn expect_job(self) -> QueryJob<'tcx> { match self { Self::Started(job) => job, Self::Poisoned => { @@ -60,7 +60,7 @@ impl QueryResult { } } -impl QueryState +impl<'tcx, K> QueryState<'tcx, K> where K: Eq + Hash + Copy + Debug, { @@ -71,13 +71,13 @@ where pub fn collect_active_jobs( &self, qcx: Qcx, - make_query: fn(Qcx, K) -> QueryStackFrame, - jobs: &mut QueryMap, + make_query: fn(Qcx, K) -> QueryStackFrame>, + jobs: &mut QueryMap<'tcx>, require_complete: bool, ) -> Option<()> { let mut active = Vec::new(); - let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult)>>| { + let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult<'tcx>)>>| { for (k, v) in iter.iter() { if let QueryResult::Started(ref job) = *v { active.push((*k, job.clone())); @@ -108,40 +108,40 @@ where } } -impl Default for QueryState { - fn default() -> QueryState { +impl<'tcx, K> Default for QueryState<'tcx, K> { + fn default() -> QueryState<'tcx, K> { QueryState { active: Default::default() } } } /// A type representing the responsibility to execute the job in the `job` field. /// This will poison the relevant query if dropped. -struct JobOwner<'tcx, K, I> +struct JobOwner<'a, 'tcx, K> where K: Eq + Hash + Copy, { - state: &'tcx QueryState, + state: &'a QueryState<'tcx, K>, key: K, } #[cold] #[inline(never)] -fn mk_cycle(query: Q, qcx: Q::Qcx, cycle_error: CycleError) -> Q::Value +fn mk_cycle<'tcx, Q>(query: Q, qcx: Q::Qcx, cycle_error: CycleError) -> Q::Value where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { let error = report_cycle(qcx.dep_context().sess(), &cycle_error); handle_cycle_error(query, qcx, &cycle_error, error) } -fn handle_cycle_error( +fn handle_cycle_error<'tcx, Q>( query: Q, qcx: Q::Qcx, cycle_error: &CycleError, error: Diag<'_>, ) -> Q::Value where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { match query.cycle_error_handling() { CycleErrorHandling::Error => { @@ -170,7 +170,7 @@ where } } -impl<'tcx, K, I> JobOwner<'tcx, K, I> +impl<'a, 'tcx, K> JobOwner<'a, 'tcx, K> where K: Eq + Hash + Copy, { @@ -207,7 +207,7 @@ where } } -impl<'tcx, K, I> Drop for JobOwner<'tcx, K, I> +impl<'a, 'tcx, K> Drop for JobOwner<'a, 'tcx, K> where K: Eq + Hash + Copy, { @@ -241,8 +241,8 @@ pub struct CycleError { pub cycle: Vec>, } -impl CycleError { - fn lift>(&self, qcx: Qcx) -> CycleError { +impl<'tcx> CycleError> { + fn lift>(&self, qcx: Qcx) -> CycleError { CycleError { usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift(qcx))), cycle: self.cycle.iter().map(|info| info.lift(qcx)).collect(), @@ -272,14 +272,14 @@ where #[cold] #[inline(never)] -fn cycle_error( +fn cycle_error<'tcx, Q>( query: Q, qcx: Q::Qcx, try_execute: QueryJobId, span: Span, ) -> (Q::Value, Option) where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. @@ -290,16 +290,16 @@ where } #[inline(always)] -fn wait_for_query( +fn wait_for_query<'tcx, Q>( query: Q, qcx: Q::Qcx, span: Span, key: Q::Key, - latch: QueryLatch<::QueryInfo>, + latch: QueryLatch<'tcx>, current: Option, ) -> (Q::Value, Option) where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { // For parallel queries, we'll block and wait until the query running // in another thread has completed. Record how long we wait in the @@ -339,7 +339,7 @@ where } #[inline(never)] -fn try_execute_query( +fn try_execute_query<'tcx, Q, const INCR: bool>( query: Q, qcx: Q::Qcx, span: Span, @@ -347,7 +347,7 @@ fn try_execute_query( dep_node: Option, ) -> (Q::Value, Option) where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { let state = query.query_state(qcx); let key_hash = sharded::make_hash(&key); @@ -408,17 +408,17 @@ where } #[inline(always)] -fn execute_job( +fn execute_job<'tcx, Q, const INCR: bool>( query: Q, qcx: Q::Qcx, - state: &QueryState::QueryInfo>, + state: &QueryState<'tcx, Q::Key>, key: Q::Key, key_hash: u64, id: QueryJobId, dep_node: Option, ) -> (Q::Value, Option) where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { // Use `JobOwner` so the query will be poisoned if executing it panics. let job_owner = JobOwner { state, key }; @@ -480,14 +480,14 @@ where // Fast path for when incr. comp. is off. #[inline(always)] -fn execute_job_non_incr( +fn execute_job_non_incr<'tcx, Q>( query: Q, qcx: Q::Qcx, key: Q::Key, job_id: QueryJobId, ) -> (Q::Value, DepNodeIndex) where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled()); @@ -516,7 +516,7 @@ where } #[inline(always)] -fn execute_job_incr( +fn execute_job_incr<'tcx, Q>( query: Q, qcx: Q::Qcx, dep_graph_data: &DepGraphData<::Deps>, @@ -525,7 +525,7 @@ fn execute_job_incr( job_id: QueryJobId, ) -> (Q::Value, DepNodeIndex) where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { if !query.anon() && !query.eval_always() { // `to_dep_node` is expensive for some `DepKind`s. @@ -571,7 +571,7 @@ where } #[inline(always)] -fn try_load_from_disk_and_cache_in_memory( +fn try_load_from_disk_and_cache_in_memory<'tcx, Q>( query: Q, dep_graph_data: &DepGraphData<::Deps>, qcx: Q::Qcx, @@ -579,7 +579,7 @@ fn try_load_from_disk_and_cache_in_memory( dep_node: &DepNode, ) -> Option<(Q::Value, DepNodeIndex)> where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { // Note this function can be called concurrently from the same query // We must ensure that this is handled correctly. @@ -757,14 +757,14 @@ fn incremental_verify_ich_failed( /// /// Note: The optimization is only available during incr. comp. #[inline(never)] -fn ensure_must_run( +fn ensure_must_run<'tcx, Q>( query: Q, qcx: Q::Qcx, key: &Q::Key, check_cache: bool, ) -> (bool, Option) where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { if query.eval_always() { return (true, None); @@ -809,9 +809,9 @@ pub enum QueryMode { } #[inline(always)] -pub fn get_query_non_incr(query: Q, qcx: Q::Qcx, span: Span, key: Q::Key) -> Q::Value +pub fn get_query_non_incr<'tcx, Q>(query: Q, qcx: Q::Qcx, span: Span, key: Q::Key) -> Q::Value where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled()); @@ -819,7 +819,7 @@ where } #[inline(always)] -pub fn get_query_incr( +pub fn get_query_incr<'tcx, Q>( query: Q, qcx: Q::Qcx, span: Span, @@ -827,7 +827,7 @@ pub fn get_query_incr( mode: QueryMode, ) -> Option where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { debug_assert!(qcx.dep_context().dep_graph().is_fully_enabled()); @@ -849,9 +849,9 @@ where Some(result) } -pub fn force_query(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode) +pub fn force_query<'tcx, Q>(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode) where - Q: QueryDispatcher, + Q: QueryDispatcher<'tcx>, { // We may be concurrently trying both execute and force a query. // Ensure that only one of them runs the query. From d2aa64fa2d9c23f43253946a17086fd8cfdff539 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 20 Jan 2026 01:07:54 +0800 Subject: [PATCH 397/583] Fix performance issue in liveness checking --- compiler/rustc_mir_transform/src/liveness.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index a5a2fcd71a9a..f52ddd54e014 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1086,11 +1086,6 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let Some((name, decl_span)) = self.checked_places.names[index] else { continue }; - // By convention, underscore-prefixed bindings are explicitly allowed to be unused. - if name.as_str().starts_with('_') { - continue; - } - let is_maybe_drop_guard = maybe_drop_guard( tcx, self.typing_env, @@ -1118,6 +1113,11 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { continue; }; + // By convention, underscore-prefixed bindings are allowed to be unused explicitly + if name.as_str().starts_with('_') { + break; + } + match kind { AccessKind::Assign => { let suggestion = annotate_mut_binding_to_immutable_binding( From be833ca74c1ed8dc1504dc5d3ae23896189341b0 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 30 Jan 2026 07:30:41 +0530 Subject: [PATCH 398/583] remove postcard dep from proc-macro-srv-cli --- src/tools/rust-analyzer/Cargo.lock | 1 - src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index a2a18cf8eeea..2cf3e37a43bc 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1882,7 +1882,6 @@ dependencies = [ "expect-test", "intern", "paths", - "postcard", "proc-macro-api", "proc-macro-srv", "proc-macro-test", diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index a25e3b64ad42..f586fe7644d7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -16,7 +16,6 @@ doctest = false [dependencies] proc-macro-srv.workspace = true proc-macro-api.workspace = true -postcard.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} [dev-dependencies] From 617ac8c0d444bf6aa7f4574b7018b6ea2d5480a1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 30 Jan 2026 07:47:40 +0530 Subject: [PATCH 399/583] correct visibility of transport layer --- .../rust-analyzer/crates/proc-macro-api/src/transport.rs | 4 ++-- .../crates/proc-macro-api/src/transport/json.rs | 8 ++++---- .../crates/proc-macro-api/src/transport/postcard.rs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs index e31d6a589d94..f383edb0cbbb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs @@ -1,3 +1,3 @@ //! Contains construct for transport of messages. -pub mod json; -pub mod postcard; +pub(crate) mod json; +pub(crate) mod postcard; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs index da79dc5309b5..f433bb7de033 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs @@ -3,7 +3,7 @@ use std::io::{self, BufRead, Write}; use serde::{Serialize, de::DeserializeOwned}; -pub fn read<'a, R: BufRead + ?Sized>( +pub(crate) fn read<'a, R: BufRead + ?Sized>( inp: &mut R, buf: &'a mut String, ) -> io::Result> { @@ -28,18 +28,18 @@ pub fn read<'a, R: BufRead + ?Sized>( } } -pub fn write(out: &mut W, buf: &String) -> io::Result<()> { +pub(crate) fn write(out: &mut W, buf: &String) -> io::Result<()> { tracing::debug!("> {}", buf); out.write_all(buf.as_bytes())?; out.write_all(b"\n")?; out.flush() } -pub fn encode(msg: &T) -> io::Result { +pub(crate) fn encode(msg: &T) -> io::Result { Ok(serde_json::to_string(msg)?) } -pub fn decode(buf: &mut str) -> io::Result { +pub(crate) fn decode(buf: &mut str) -> io::Result { let mut deserializer = serde_json::Deserializer::from_str(buf); // Note that some proc-macro generate very deep syntax tree // We have to disable the current limit of serde here diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs index ddd5f405d5dc..75aa90e4c480 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs @@ -4,7 +4,7 @@ use std::io::{self, BufRead, Write}; use serde::{Serialize, de::DeserializeOwned}; -pub fn read<'a, R: BufRead + ?Sized>( +pub(crate) fn read<'a, R: BufRead + ?Sized>( inp: &mut R, buf: &'a mut Vec, ) -> io::Result>> { @@ -16,15 +16,15 @@ pub fn read<'a, R: BufRead + ?Sized>( Ok(Some(buf)) } -pub fn write(out: &mut W, buf: &[u8]) -> io::Result<()> { +pub(crate) fn write(out: &mut W, buf: &[u8]) -> io::Result<()> { out.write_all(buf)?; out.flush() } -pub fn encode(msg: &T) -> io::Result> { +pub(crate) fn encode(msg: &T) -> io::Result> { postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } -pub fn decode(buf: &mut [u8]) -> io::Result { +pub(crate) fn decode(buf: &mut [u8]) -> io::Result { postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } From cf6796a2b06e5d07250cc7c50b0ecb9d6f008793 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 30 Jan 2026 15:00:20 +1100 Subject: [PATCH 400/583] Remove redundant `IntoQueryParam` calls from query plumbing In each of these contexts, the key must have already been converted by the caller, since otherwise it wouldn't have type `Cache::Key`. --- compiler/rustc_middle/src/query/inner.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index e41d23f82f12..80e64e6a78ed 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -10,7 +10,6 @@ use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::dep_graph; -use crate::query::IntoQueryParam; use crate::query::erase::{self, Erasable, Erased}; use crate::ty::TyCtxt; @@ -27,7 +26,6 @@ pub(crate) fn query_get_at<'tcx, Cache>( where Cache: QueryCache, { - let key = key.into_query_param(); match try_get_cached(tcx, query_cache, &key) { Some(value) => value, None => execute_query(tcx, span, key, QueryMode::Get).unwrap(), @@ -46,7 +44,6 @@ pub(crate) fn query_ensure<'tcx, Cache>( ) where Cache: QueryCache, { - let key = key.into_query_param(); if try_get_cached(tcx, query_cache, &key).is_none() { execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache }); } @@ -66,7 +63,6 @@ where Cache: QueryCache>>, Result: Erasable, { - let key = key.into_query_param(); if let Some(res) = try_get_cached(tcx, query_cache, &key) { erase::restore_val(res).map(drop) } else { From 4ae5b43b0f8c7bb4b579ea8e5e34dd928ffe87a2 Mon Sep 17 00:00:00 2001 From: Frank King Date: Fri, 30 Jan 2026 11:04:04 +0800 Subject: [PATCH 401/583] refactor: remove `Ty::pinned_ref` in favor of `Ty::maybe_pinned_ref` Also returns the `Region` of the reference type. --- compiler/rustc_hir_typeck/src/pat.rs | 2 +- compiler/rustc_middle/src/ty/sty.rs | 22 +++++++------------- compiler/rustc_pattern_analysis/src/rustc.rs | 8 +++---- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index b56ab6dcb4ab..f054145dc7e9 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2841,7 +2841,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the bad interactions of the given hack detailed in (note_1). debug!("check_pat_ref: expected={:?}", expected); match expected.maybe_pinned_ref() { - Some((r_ty, r_pinned, r_mutbl)) + Some((r_ty, r_pinned, r_mutbl, _)) if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) || r_mutbl == pat_mutbl) && pat_pinned == r_pinned => diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 4c5bd85e6b72..4be30d8b6c91 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1336,25 +1336,17 @@ impl<'tcx> Ty<'tcx> { } } - pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> { - if let Adt(def, args) = self.kind() - && def.is_pin() - && let &ty::Ref(_, ty, mutbl) = args.type_at(0).kind() - { - return Some((ty, mutbl)); - } - None - } - - pub fn maybe_pinned_ref(self) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability)> { - match *self.kind() { + pub fn maybe_pinned_ref( + self, + ) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability, Region<'tcx>)> { + match self.kind() { Adt(def, args) if def.is_pin() - && let ty::Ref(_, ty, mutbl) = *args.type_at(0).kind() => + && let &ty::Ref(region, ty, mutbl) = args.type_at(0).kind() => { - Some((ty, ty::Pinnedness::Pinned, mutbl)) + Some((ty, ty::Pinnedness::Pinned, mutbl, region)) } - ty::Ref(_, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl)), + &Ref(region, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl, region)), _ => None, } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 3e97842b7f39..dc38f2d8bc70 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -4,8 +4,8 @@ use std::iter::once; use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx}; use rustc_arena::DroplessArena; +use rustc_hir::HirId; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, HirId}; use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary}; @@ -471,9 +471,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { PatKind::Deref { pin, subpattern } => { fields = vec![self.lower_pat(subpattern).at_index(0)]; arity = 1; - ctor = match pin { - hir::Pinnedness::Not if ty.is_ref() => Ref, - hir::Pinnedness::Pinned if let Some((inner_ty, _)) = ty.pinned_ref() => { + ctor = match (pin, ty.maybe_pinned_ref()) { + (ty::Pinnedness::Not, Some((_, ty::Pinnedness::Not, _, _))) => Ref, + (ty::Pinnedness::Pinned, Some((inner_ty, ty::Pinnedness::Pinned, _, _))) => { self.internal_state.has_lowered_deref_pat.set(true); DerefPattern(RevealedTy(inner_ty)) } From 9c135ad1e0ee2f79d16612ec682fa7c764e4a3c6 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 30 Jan 2026 07:20:28 +0000 Subject: [PATCH 402/583] Show break type expectation cause for let-else --- compiler/rustc_hir_typeck/src/demand.rs | 8 +++++++ .../let-else-break-help-issue-142602.rs | 23 +++++++++++++++++++ .../let-else-break-help-issue-142602.stderr | 21 +++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 tests/ui/let-else/let-else-break-help-issue-142602.rs create mode 100644 tests/ui/let-else/let-else-break-help-issue-142602.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 3f214b4d2fcc..84663ff884b4 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -605,6 +605,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { parent_id = self.tcx.parent_hir_id(*hir_id); parent } + hir::Node::Stmt(hir::Stmt { hir_id, kind: hir::StmtKind::Let(_), .. }) => { + parent_id = self.tcx.parent_hir_id(*hir_id); + parent + } + hir::Node::LetStmt(hir::LetStmt { hir_id, .. }) => { + parent_id = self.tcx.parent_hir_id(*hir_id); + parent + } hir::Node::Block(_) => { parent_id = self.tcx.parent_hir_id(parent_id); parent diff --git a/tests/ui/let-else/let-else-break-help-issue-142602.rs b/tests/ui/let-else/let-else-break-help-issue-142602.rs new file mode 100644 index 000000000000..b57c7f757150 --- /dev/null +++ b/tests/ui/let-else/let-else-break-help-issue-142602.rs @@ -0,0 +1,23 @@ +// testcase from https://github.com/rust-lang/rust/issues/142602 + +pub fn main() { + // Case 1: break before let-else + let _a = loop { + if true { + break; + } + let Some(_) = Some(5) else { + break 3; //~ ERROR mismatched types + }; + }; + + // Case 2: two let-else statements + let _b = loop { + let Some(_) = Some(5) else { + break; + }; + let Some(_) = Some(4) else { + break 3; //~ ERROR mismatched types + }; + }; +} diff --git a/tests/ui/let-else/let-else-break-help-issue-142602.stderr b/tests/ui/let-else/let-else-break-help-issue-142602.stderr new file mode 100644 index 000000000000..aafbaa158ab1 --- /dev/null +++ b/tests/ui/let-else/let-else-break-help-issue-142602.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/let-else-break-help-issue-142602.rs:10:19 + | +LL | break; + | ----- expected because of this `break` +... +LL | break 3; + | ^ expected `()`, found integer + +error[E0308]: mismatched types + --> $DIR/let-else-break-help-issue-142602.rs:20:19 + | +LL | break; + | ----- expected because of this `break` +... +LL | break 3; + | ^ expected `()`, found integer + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From 91feb76d941592145238eda59e73be25fd191af3 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 30 Jan 2026 08:56:56 +0000 Subject: [PATCH 403/583] Skip unused_allocation lint when method takes &Box --- compiler/rustc_lint/src/unused.rs | 8 ++- .../unused-allocation-box-ref-issue-151846.rs | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 5516298c5e85..8b630d0d7385 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1782,7 +1782,7 @@ declare_lint! { declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]); impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { - fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &hir::Expr<'_>) { match e.kind { hir::ExprKind::Call(path_expr, [_]) if let hir::ExprKind::Path(qpath) = &path_expr.kind @@ -1793,6 +1793,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { for adj in cx.typeck_results().expr_adjustments(e) { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind { + if let ty::Ref(_, inner_ty, _) = adj.target.kind() + && inner_ty.is_box() + { + // If the target type is `&Box` or `&mut Box`, the allocation is necessary + continue; + } match m { adjustment::AutoBorrowMutability::Not => { cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag); diff --git a/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs b/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs new file mode 100644 index 000000000000..57eefc3891fa --- /dev/null +++ b/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs @@ -0,0 +1,54 @@ +//@ check-pass +// Test for issue #151846: unused_allocation warning should ignore +// allocations to pass Box to things taking self: &Box + +#![deny(unused_allocation)] + +struct MyStruct; + +trait TraitTakesBoxRef { + fn trait_takes_box_ref(&self); +} + +impl TraitTakesBoxRef for Box { + fn trait_takes_box_ref(&self) {} +} + +impl MyStruct { + fn inherent_takes_box_ref(self: &Box) {} +} + +fn takes_box_ref(_: &Box) {} + +trait TraitTakesBoxVal { + fn trait_takes_box_val(self); +} + +impl TraitTakesBoxVal for Box { + fn trait_takes_box_val(self) {} +} + +impl MyStruct { + fn inherent_takes_box_val(self: Box) {} +} + +fn takes_box_val(_: Box) {} + +pub fn foo() { + // These should NOT warn - the allocation is necessary because + // the method takes &Box + Box::new(MyStruct).trait_takes_box_ref(); + Box::new(MyStruct).inherent_takes_box_ref(); + takes_box_ref(&Box::new(MyStruct)); + + // These already don't warn - the allocation is necessary + Box::new(MyStruct).trait_takes_box_val(); + Box::new(MyStruct).inherent_takes_box_val(); + takes_box_val(Box::new(MyStruct)); + + // Fully-qualified syntax also does not warn: + as TraitTakesBoxRef>::trait_takes_box_ref(&Box::new(MyStruct)); + MyStruct::inherent_takes_box_ref(&Box::new(MyStruct)); +} + +fn main() {} From d5e80aa7141283d5a29bf6f2dabf4e9d4a108efa Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 27 Jan 2026 18:13:05 +1100 Subject: [PATCH 404/583] Make `try_load_from_disk_fn` optional in query vtables --- compiler/rustc_middle/src/query/plumbing.rs | 15 ++++++----- compiler/rustc_query_impl/src/lib.rs | 7 ++--- compiler/rustc_query_impl/src/plumbing.rs | 29 +++++++++------------ 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 2bda014a19fe..86eea8f3748e 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -18,6 +18,13 @@ use crate::query::{ }; use crate::ty::TyCtxt; +pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( + tcx: TyCtxt<'tcx>, + key: &Key, + prev_index: SerializedDepNodeIndex, + index: DepNodeIndex, +) -> Option; + /// Stores function pointers and other metadata for a particular query. /// /// Used indirectly by query plumbing in `rustc_query_system`, via a trait. @@ -34,13 +41,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub cache_on_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool, pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, pub compute: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, - pub can_load_from_disk: bool, - pub try_load_from_disk: fn( - tcx: TyCtxt<'tcx>, - key: &C::Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, - ) -> Option, + pub try_load_from_disk_fn: Option>, pub loadable_from_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool, pub hash_result: HashResult, diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 8fa4fb3090db..21daa020407c 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -128,11 +128,8 @@ where prev_index: SerializedDepNodeIndex, index: DepNodeIndex, ) -> Option { - if self.vtable.can_load_from_disk { - (self.vtable.try_load_from_disk)(qcx.tcx, key, prev_index, index) - } else { - None - } + // `?` will return None immediately for queries that never cache to disk. + self.vtable.try_load_from_disk_fn?(qcx.tcx, key, prev_index, index) } #[inline] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index ef7a62351930..0b7a2d0aa6a3 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -666,24 +666,21 @@ macro_rules! define_queries { ) ) }, - can_load_from_disk: should_ever_cache_on_disk!([$($modifiers)*] true false), - try_load_from_disk: should_ever_cache_on_disk!([$($modifiers)*] { - |tcx, key, prev_index, index| { - if ::rustc_middle::query::cached::$name(tcx, key) { - let value = $crate::plumbing::try_load_from_disk::< - queries::$name::ProvidedValue<'tcx> - >( - tcx, - prev_index, - index, - ); - value.map(|value| queries::$name::provided_to_erased(tcx, value)) - } else { - None + try_load_from_disk_fn: should_ever_cache_on_disk!([$($modifiers)*] { + Some(|tcx, key, prev_index, index| { + // Check the `cache_on_disk_if` condition for this key. + if !::rustc_middle::query::cached::$name(tcx, key) { + return None; } - } + + let value: queries::$name::ProvidedValue<'tcx> = + $crate::plumbing::try_load_from_disk(tcx, prev_index, index)?; + + // Arena-alloc the value if appropriate, and erase it. + Some(queries::$name::provided_to_erased(tcx, value)) + }) } { - |_tcx, _key, _prev_index, _index| None + None }), value_from_cycle_error: |tcx, cycle, guar| { let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar); From d39609b7198328d0f97381883c5b5e1e39e04f42 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 27 Jan 2026 18:19:04 +1100 Subject: [PATCH 405/583] Make `is_loadable_from_disk_fn` optional in query vtables --- compiler/rustc_middle/src/query/plumbing.rs | 6 ++++-- compiler/rustc_query_impl/src/lib.rs | 4 ++-- compiler/rustc_query_impl/src/plumbing.rs | 16 ++++++++-------- .../rustc_query_system/src/query/dispatcher.rs | 2 +- .../rustc_query_system/src/query/plumbing.rs | 4 ++-- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 86eea8f3748e..b280cc821bc2 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -25,6 +25,9 @@ pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( index: DepNodeIndex, ) -> Option; +pub type IsLoadableFromDiskFn<'tcx, Key> = + fn(tcx: TyCtxt<'tcx>, key: &Key, index: SerializedDepNodeIndex) -> bool; + /// Stores function pointers and other metadata for a particular query. /// /// Used indirectly by query plumbing in `rustc_query_system`, via a trait. @@ -42,8 +45,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, pub compute: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, pub try_load_from_disk_fn: Option>, - pub loadable_from_disk: - fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool, + pub is_loadable_from_disk_fn: Option>, pub hash_result: HashResult, pub value_from_cycle_error: fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value, diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 21daa020407c..912c461f643c 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -133,13 +133,13 @@ where } #[inline] - fn loadable_from_disk( + fn is_loadable_from_disk( self, qcx: QueryCtxt<'tcx>, key: &Self::Key, index: SerializedDepNodeIndex, ) -> bool { - (self.vtable.loadable_from_disk)(qcx.tcx, key, index) + self.vtable.is_loadable_from_disk_fn.map_or(false, |f| f(qcx.tcx, key, index)) } fn value_from_cycle_error( diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 0b7a2d0aa6a3..7fffce051c4d 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -682,18 +682,18 @@ macro_rules! define_queries { } { None }), + is_loadable_from_disk_fn: should_ever_cache_on_disk!([$($modifiers)*] { + Some(|tcx, key, index| -> bool { + ::rustc_middle::query::cached::$name(tcx, key) && + $crate::plumbing::loadable_from_disk(tcx, index) + }) + } { + None + }), value_from_cycle_error: |tcx, cycle, guar| { let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar); erase::erase_val(result) }, - loadable_from_disk: |_tcx, _key, _index| { - should_ever_cache_on_disk!([$($modifiers)*] { - ::rustc_middle::query::cached::$name(_tcx, _key) && - $crate::plumbing::loadable_from_disk(_tcx, _index) - } { - false - }) - }, hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]), format_value: |value| format!("{:?}", erase::restore_val::>(*value)), } diff --git a/compiler/rustc_query_system/src/query/dispatcher.rs b/compiler/rustc_query_system/src/query/dispatcher.rs index d7dd6dd6464a..dfc06e4c3ef0 100644 --- a/compiler/rustc_query_system/src/query/dispatcher.rs +++ b/compiler/rustc_query_system/src/query/dispatcher.rs @@ -61,7 +61,7 @@ pub trait QueryDispatcher<'tcx>: Copy { index: DepNodeIndex, ) -> Option; - fn loadable_from_disk( + fn is_loadable_from_disk( self, qcx: Self::Qcx, key: &Self::Key, diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index c4431ff870d0..d386f3e70690 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -631,7 +631,7 @@ where // Sanity check for the logic in `ensure`: if the node is green and the result loadable, // we should actually be able to load it. debug_assert!( - !query.loadable_from_disk(qcx, key, prev_dep_node_index), + !query.is_loadable_from_disk(qcx, key, prev_dep_node_index), "missing on-disk cache entry for loadable {dep_node:?}" ); @@ -798,7 +798,7 @@ where return (false, None); } - let loadable = query.loadable_from_disk(qcx, key, serialized_dep_node_index); + let loadable = query.is_loadable_from_disk(qcx, key, serialized_dep_node_index); (!loadable, Some(dep_node)) } From e044220de53b2e9c8a8191ff9da5df4b469eb3ea Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 27 Jan 2026 22:53:22 +1100 Subject: [PATCH 406/583] Make `will_cache_on_disk_for_key_fn` optional in query vtables --- compiler/rustc_macros/src/query.rs | 24 ++++--------------- compiler/rustc_middle/src/query/plumbing.rs | 4 +++- compiler/rustc_query_impl/src/lib.rs | 4 ++-- compiler/rustc_query_impl/src/plumbing.rs | 10 +++++--- .../src/query/dispatcher.rs | 2 +- .../rustc_query_system/src/query/plumbing.rs | 2 +- 6 files changed, 19 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 1ad9bbc3b4b3..0cac699e5b62 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -280,31 +280,21 @@ fn add_query_desc_cached_impl( let crate::query::Providers { #name: _, .. }; }; - // Find out if we should cache the query on disk - let cache = if let Some((args, expr)) = modifiers.cache.as_ref() { + // Generate a function to check whether we should cache the query to disk, for some key. + if let Some((args, expr)) = modifiers.cache.as_ref() { let tcx = args.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ }); // expr is a `Block`, meaning that `{ #expr }` gets expanded // to `{ { stmts... } }`, which triggers the `unused_braces` lint. // we're taking `key` by reference, but some rustc types usually prefer being passed by value - quote! { + cached.extend(quote! { #[allow(unused_variables, unused_braces, rustc::pass_by_value)] #[inline] pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::query::queries::#name::Key<'tcx>) -> bool { #ra_hint #expr } - } - } else { - quote! { - // we're taking `key` by reference, but some rustc types usually prefer being passed by value - #[allow(rustc::pass_by_value)] - #[inline] - pub fn #name<'tcx>(_: TyCtxt<'tcx>, _: &crate::query::queries::#name::Key<'tcx>) -> bool { - #ra_hint - false - } - } - }; + }); + } let (tcx, desc) = &modifiers.desc; let tcx = tcx.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t }); @@ -322,10 +312,6 @@ fn add_query_desc_cached_impl( descs.extend(quote! { #desc }); - - cached.extend(quote! { - #cache - }); } pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index b280cc821bc2..b21db6eeeea0 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -18,6 +18,8 @@ use crate::query::{ }; use crate::ty::TyCtxt; +pub type WillCacheOnDiskForKeyFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key) -> bool; + pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( tcx: TyCtxt<'tcx>, key: &Key, @@ -41,7 +43,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub query_state: usize, // Offset of this query's cache field in the QueryCaches struct pub query_cache: usize, - pub cache_on_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool, + pub will_cache_on_disk_for_key_fn: Option>, pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, pub compute: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, pub try_load_from_disk_fn: Option>, diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 912c461f643c..4e79d0842da2 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -81,8 +81,8 @@ where } #[inline(always)] - fn cache_on_disk(self, tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool { - (self.vtable.cache_on_disk)(tcx, key) + fn will_cache_on_disk_for_key(self, tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool { + self.vtable.will_cache_on_disk_for_key_fn.map_or(false, |f| f(tcx, key)) } #[inline(always)] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 7fffce051c4d..2d4e10a0380c 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -396,7 +396,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>( assert!(query.query_state(qcx).all_inactive()); let cache = query.query_cache(qcx); cache.iter(&mut |key, value, dep_node| { - if query.cache_on_disk(qcx.tcx, key) { + if query.will_cache_on_disk_for_key(qcx.tcx, key) { let dep_node = SerializedDepNodeIndex::new(dep_node.index()); // Record position of the cache entry. @@ -445,7 +445,7 @@ where let key = Q::Key::recover(tcx, &dep_node).unwrap_or_else(|| { panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash) }); - if query.cache_on_disk(tcx, &key) { + if query.will_cache_on_disk_for_key(tcx, &key) { let _ = query.execute_query(tcx, key); } } @@ -648,7 +648,11 @@ macro_rules! define_queries { cycle_error_handling: cycle_error_handling!([$($modifiers)*]), query_state: std::mem::offset_of!(QueryStates<'tcx>, $name), query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name), - cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key), + will_cache_on_disk_for_key_fn: should_ever_cache_on_disk!([$($modifiers)*] { + Some(::rustc_middle::query::cached::$name) + } { + None + }), execute_query: |tcx, key| erase::erase_val(tcx.$name(key)), compute: |tcx, key| { #[cfg(debug_assertions)] diff --git a/compiler/rustc_query_system/src/query/dispatcher.rs b/compiler/rustc_query_system/src/query/dispatcher.rs index dfc06e4c3ef0..ac6c38dd7db5 100644 --- a/compiler/rustc_query_system/src/query/dispatcher.rs +++ b/compiler/rustc_query_system/src/query/dispatcher.rs @@ -46,7 +46,7 @@ pub trait QueryDispatcher<'tcx>: Copy { // Don't use this method to access query results, instead use the methods on TyCtxt fn query_cache<'a>(self, tcx: Self::Qcx) -> &'a Self::Cache; - fn cache_on_disk(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> bool; + fn will_cache_on_disk_for_key(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> bool; // Don't use this method to compute query results, instead use the methods on TyCtxt fn execute_query(self, tcx: DepContextOf<'tcx, Self>, k: Self::Key) -> Self::Value; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index d386f3e70690..04902d0ab10c 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -623,7 +623,7 @@ where // We always expect to find a cached result for things that // can be forced from `DepNode`. debug_assert!( - !query.cache_on_disk(*qcx.dep_context(), key) + !query.will_cache_on_disk_for_key(*qcx.dep_context(), key) || !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), "missing on-disk cache entry for {dep_node:?}" ); From 09117c9b706f5b8a061fe52d5d6029b50de398a1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 30 Jan 2026 17:17:46 +1100 Subject: [PATCH 407/583] Use the query vtable in `query_feed` plumbing --- compiler/rustc_middle/src/query/inner.rs | 50 +++++++++++-------- compiler/rustc_middle/src/query/mod.rs | 1 - compiler/rustc_middle/src/query/plumbing.rs | 22 ++------ .../rustc_query_system/src/dep_graph/graph.rs | 5 +- 4 files changed, 37 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index e41d23f82f12..b25ae06479b9 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -1,17 +1,14 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. -use std::fmt::Debug; - -use rustc_data_structures::fingerprint::Fingerprint; use rustc_query_system::dep_graph::{DepKind, DepNodeParams}; -use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::dep_graph; use crate::query::IntoQueryParam; use crate::query::erase::{self, Erasable, Erased}; +use crate::query::plumbing::QueryVTable; use crate::ty::TyCtxt; /// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` @@ -84,35 +81,38 @@ where } /// Common implementation of query feeding, used by `define_feedable!`. -pub(crate) fn query_feed<'tcx, Cache, Value>( +pub(crate) fn query_feed<'tcx, Cache>( tcx: TyCtxt<'tcx>, dep_kind: DepKind, - hasher: Option, &Value) -> Fingerprint>, + query_vtable: &QueryVTable<'tcx, Cache>, cache: &Cache, key: Cache::Key, - erased: Erased, + value: Cache::Value, ) where - Cache: QueryCache>, + Cache: QueryCache, Cache::Key: DepNodeParams>, - Value: Erasable + Debug, { - let value = erase::restore_val::(erased); + let format_value = query_vtable.format_value; + // Check whether the in-memory cache already has a value for this key. match try_get_cached(tcx, cache, &key) { Some(old) => { - let old = erase::restore_val::(old); - if let Some(hasher) = hasher { - let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx - .with_stable_hashing_context(|mut hcx| { - (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) - }); + // The query already has a cached value for this key. + // That's OK if both values are the same, i.e. they have the same hash, + // so now we check their hashes. + if let Some(hasher_fn) = query_vtable.hash_result { + let (old_hash, value_hash) = tcx.with_stable_hashing_context(|ref mut hcx| { + (hasher_fn(hcx, &old), hasher_fn(hcx, &value)) + }); if old_hash != value_hash { // We have an inconsistency. This can happen if one of the two // results is tainted by errors. In this case, delay a bug to // ensure compilation is doomed, and keep the `old` value. tcx.dcx().delayed_bug(format!( "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", + old value: {old}\nnew value: {value}", + old = format_value(&old), + value = format_value(&value), )); } } else { @@ -121,14 +121,24 @@ pub(crate) fn query_feed<'tcx, Cache, Value>( // the query should not be marked `no_hash`. bug!( "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", + old value: {old}\nnew value: {value}", + old = format_value(&old), + value = format_value(&value), ) } } None => { + // There is no cached value for this key, so feed the query by + // adding the provided value to the cache. let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); - let dep_node_index = tcx.dep_graph.with_feed_task(dep_node, tcx, &value, hasher); - cache.complete(key, erased, dep_node_index); + let dep_node_index = tcx.dep_graph.with_feed_task( + dep_node, + tcx, + &value, + query_vtable.hash_result, + query_vtable.format_value, + ); + cache.complete(key, value, dep_node_index); } } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index cea50f95df4b..c9291d89be8a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -87,7 +87,6 @@ use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, Trait use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; -use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryMode, QueryState}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 2bda014a19fe..0f08fa77cbc7 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -492,18 +492,6 @@ macro_rules! define_callbacks { }; } -macro_rules! hash_result { - ([]) => {{ - Some(dep_graph::hash_result) - }}; - ([(no_hash) $($rest:tt)*]) => {{ - None - }}; - ([$other:tt $($modifiers:tt)*]) => { - hash_result!([$($modifiers)*]) - }; -} - macro_rules! define_feedable { ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { @@ -513,19 +501,17 @@ macro_rules! define_feedable { let key = self.key().into_query_param(); let tcx = self.tcx; - let erased = queries::$name::provided_to_erased(tcx, value); - let cache = &tcx.query_system.caches.$name; + let erased_value = queries::$name::provided_to_erased(tcx, value); let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; - let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); $crate::query::inner::query_feed( tcx, dep_kind, - hasher, - cache, + &tcx.query_system.query_vtables.$name, + &tcx.query_system.caches.$name, key, - erased, + erased_value, ); } })* diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 6d46d144d0f1..9b9b73d73202 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -561,12 +561,13 @@ impl DepGraph { /// FIXME: If the code is changed enough for this node to be marked before requiring the /// caller's node, we suppose that those changes will be enough to mark this node red and /// force a recomputation using the "normal" way. - pub fn with_feed_task, R: Debug>( + pub fn with_feed_task, R>( &self, node: DepNode, cx: Ctxt, result: &R, hash_result: Option, &R) -> Fingerprint>, + format_value_fn: fn(&R) -> String, ) -> DepNodeIndex { if let Some(data) = self.data.as_ref() { // The caller query has more dependencies than the node we are creating. We may @@ -584,7 +585,7 @@ impl DepGraph { result, prev_index, hash_result, - |value| format!("{value:?}"), + format_value_fn, ); #[cfg(debug_assertions)] From 9e5820379d60942604547640a83480fc57466915 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 29 Jan 2026 17:56:04 +0100 Subject: [PATCH 408/583] Fix flakyness issue with `tests/rustdoc-gui/globals.goml` test --- tests/rustdoc-gui/globals.goml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml index 89f57add8161..4f12175f6ab2 100644 --- a/tests/rustdoc-gui/globals.goml +++ b/tests/rustdoc-gui/globals.goml @@ -4,14 +4,14 @@ include: "utils.goml" // URL query -go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds" +go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa" wait-for: "#search-tabs" assert-window-property-false: {"searchIndex": null} // Form input go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" call-function: ("perform-search", {"query": "Foo"}) -assert-window-property-false: {"searchIndex": null} +wait-for-window-property-false: {"searchIndex": null} // source sidebar go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" From acb5ee2f843789c4e8d903a894edcf01dbfd0c23 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 30 Jan 2026 13:01:22 +0100 Subject: [PATCH 409/583] Disable append-elements.rs test with debug assertions The IR is a bit different (in particular wrt naming) if debug-assertions-std is enabled. Peculiarly, the issue goes away if overflow-check-std is also enabled, which is why CI did not catch this. --- tests/codegen-llvm/lib-optimizations/append-elements.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/codegen-llvm/lib-optimizations/append-elements.rs b/tests/codegen-llvm/lib-optimizations/append-elements.rs index b8657104d665..5d7d746dbb6c 100644 --- a/tests/codegen-llvm/lib-optimizations/append-elements.rs +++ b/tests/codegen-llvm/lib-optimizations/append-elements.rs @@ -1,6 +1,7 @@ //@ compile-flags: -O -Zmerge-functions=disabled //@ needs-deterministic-layouts //@ min-llvm-version: 21 +//@ ignore-std-debug-assertions (causes different value naming) #![crate_type = "lib"] //! Check that a temporary intermediate allocations can eliminated and replaced From e12acb9251eb9d5a967aeafbe7ff3b3e97ae401f Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 24 Nov 2025 15:18:59 +0800 Subject: [PATCH 410/583] Forbid manual `Unpin` impls for structurally pinned types --- compiler/rustc_hir_analysis/messages.ftl | 4 + .../src/coherence/builtin.rs | 36 +++++++++ compiler/rustc_hir_analysis/src/errors.rs | 11 +++ tests/ui/pin-ergonomics/impl-unpin.adt.stderr | 14 ++++ .../ui/pin-ergonomics/impl-unpin.assoc.stderr | 15 ++++ tests/ui/pin-ergonomics/impl-unpin.rs | 74 +++++++++++++++++++ .../ui/pin-ergonomics/impl-unpin.tait.stderr | 27 +++++++ .../pin-ergonomics/impl-unpin.ty_alias.stderr | 14 ++++ 8 files changed, 195 insertions(+) create mode 100644 tests/ui/pin-ergonomics/impl-unpin.adt.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.assoc.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.rs create mode 100644 tests/ui/pin-ergonomics/impl-unpin.tait.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d9f8eba65c4a..33b8d3c82965 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -224,6 +224,10 @@ hir_analysis_impl_not_marked_default = `{$ident}` specializes an item from a par hir_analysis_impl_not_marked_default_err = `{$ident}` specializes an item from a parent `impl`, but that item is not marked `default` .note = parent implementation is in crate `{$cname}` +hir_analysis_impl_unpin_for_pin_projected_type = explicit impls for the `Unpin` trait are not permitted for structurally pinned types + .label = impl of `Unpin` not allowed + .help = `{$adt_name}` is structurally pinned because it is marked as `#[pin_v2]` + hir_analysis_inherent_dyn = cannot define inherent `impl` for a dyn auto trait .label = impl requires at least one non-auto trait .note = define and implement a new trait or type instead diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 61562cc1e4f3..f81913a9c86f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -38,6 +38,7 @@ pub(super) fn check_trait<'tcx>( checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; + checker.check(lang_items.unpin_trait(), visit_implementation_of_unpin)?; checker.check(lang_items.const_param_ty_trait(), |checker| { visit_implementation_of_const_param_ty(checker) })?; @@ -134,6 +135,41 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } } +fn visit_implementation_of_unpin(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_header = checker.impl_header; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_unpin: impl_did={:?}", impl_did); + + let self_type = impl_header.trait_ref.instantiate_identity().self_ty(); + debug!("visit_implementation_of_unpin: self_type={:?}", self_type); + + let span = tcx.def_span(impl_did); + + if tcx.features().pin_ergonomics() { + match self_type.kind() { + // Soundness concerns: a type `T` annotated with `#[pin_v2]` is allowed to project + // `Pin<&mut T>` to its field `Pin<&mut U>` safely (even if `U: !Unpin`). + // If `T` is allowed to impl `Unpin` manually (note that `Unpin` is a safe trait, + // which cannot carry safety properties), then `&mut U` could be obtained from + // `&mut T` that dereferenced by `Pin<&mut T>`, which breaks the safety contract of + // `Pin<&mut U>` for `U: !Unpin`. + ty::Adt(adt, _) if adt.is_pin_project() => { + return Err(tcx.dcx().emit_err(crate::errors::ImplUnpinForPinProjectedType { + span, + adt_span: tcx.def_span(adt.did()), + adt_name: tcx.item_name(adt.did()), + })); + } + ty::Adt(_, _) => {} + _ => { + return Err(tcx.dcx().span_delayed_bug(span, "impl of `Unpin` for a non-adt type")); + } + }; + } + Ok(()) +} + fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let header = checker.impl_header; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2a77d0b997e2..f0c9023c522c 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1690,3 +1690,14 @@ pub(crate) struct EiiWithGenerics { pub eii_name: Symbol, pub impl_name: Symbol, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_impl_unpin_for_pin_projected_type)] +pub(crate) struct ImplUnpinForPinProjectedType { + #[primary_span] + #[label] + pub span: Span, + #[help] + pub adt_span: Span, + pub adt_name: Symbol, +} diff --git a/tests/ui/pin-ergonomics/impl-unpin.adt.stderr b/tests/ui/pin-ergonomics/impl-unpin.adt.stderr new file mode 100644 index 000000000000..56fac42d4e32 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.adt.stderr @@ -0,0 +1,14 @@ +error: explicit impls for the `Unpin` trait are not permitted for structurally pinned types + --> $DIR/impl-unpin.rs:14:5 + | +LL | impl Unpin for Foo {} + | ^^^^^^^^^^^^^^^^^^ impl of `Unpin` not allowed + | +help: `Foo` is structurally pinned because it is marked as `#[pin_v2]` + --> $DIR/impl-unpin.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr b/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr new file mode 100644 index 000000000000..7f0ee1ddd898 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr @@ -0,0 +1,15 @@ +error[E0321]: cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + --> $DIR/impl-unpin.rs:68:5 + | +LL | impl Unpin for ::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error[E0321]: cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + --> $DIR/impl-unpin.rs:70:5 + | +LL | impl Unpin for ::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0321`. diff --git a/tests/ui/pin-ergonomics/impl-unpin.rs b/tests/ui/pin-ergonomics/impl-unpin.rs new file mode 100644 index 000000000000..ded8b4774dd9 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.rs @@ -0,0 +1,74 @@ +//@ revisions: adt tait ty_alias assoc +#![feature(pin_ergonomics)] +#![cfg_attr(tait, feature(type_alias_impl_trait))] +#![allow(incomplete_features)] + +#[pin_v2] +struct Foo; +struct Bar; + +#[cfg(adt)] +mod adt { + use super::*; + + impl Unpin for Foo {} + //[adt]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types + impl Unpin for Bar {} // ok +} + +#[cfg(ty_alias)] +mod ty_alias { + use super::*; + + type Identity = T; + + impl Unpin for Identity {} + //[ty_alias]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types + impl Unpin for Identity {} // ok +} + +#[cfg(tait)] +mod tait { + use super::*; + + trait Identity {} + + impl Identity for T {} + + type FooAlias = impl Identity; + type BarAlias = impl Identity; + + #[define_opaque(FooAlias)] + fn foo_alias() -> FooAlias { + Foo + } + #[define_opaque(BarAlias)] + fn bar_alias() -> BarAlias { + Bar + } + + impl Unpin for FooAlias {} + //[tait]~^ ERROR only traits defined in the current crate can be implemented for arbitrary types + impl Unpin for BarAlias {} + //[tait]~^ ERROR only traits defined in the current crate can be implemented for arbitrary types +} + +#[cfg(assoc)] +mod assoc { + use super::*; + + trait Identity { + type Assoc; + } + + impl Identity for T { + type Assoc = T; + } + + impl Unpin for ::Assoc {} + //[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + impl Unpin for ::Assoc {} + //[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/impl-unpin.tait.stderr b/tests/ui/pin-ergonomics/impl-unpin.tait.stderr new file mode 100644 index 000000000000..5d9392745df3 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.tait.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-unpin.rs:50:5 + | +LL | impl Unpin for FooAlias {} + | ^^^^^^^^^^^^^^^-------- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-unpin.rs:52:5 + | +LL | impl Unpin for BarAlias {} + | ^^^^^^^^^^^^^^^-------- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr b/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr new file mode 100644 index 000000000000..82c1a7d29ddf --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr @@ -0,0 +1,14 @@ +error: explicit impls for the `Unpin` trait are not permitted for structurally pinned types + --> $DIR/impl-unpin.rs:25:5 + | +LL | impl Unpin for Identity {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Unpin` not allowed + | +help: `Foo` is structurally pinned because it is marked as `#[pin_v2]` + --> $DIR/impl-unpin.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + From 2a9de6ba853ff78b9b34b655b87313f0737417c5 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 27 Jan 2026 21:35:59 +0100 Subject: [PATCH 411/583] CI: rfl: move to temporary commit to pass `-Zunstable-options` for custom target specs This also takes the chance to move forward the job to Linux v6.19-rc7, thus the previous workaround is not needed anymore. Link: https://github.com/rust-lang/rust/pull/151534 Signed-off-by: Miguel Ojeda --- src/ci/docker/scripts/rfl-build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index e7f86e10f55a..535250557740 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,8 +2,8 @@ set -euo pipefail -# https://github.com/rust-lang/rust/pull/145974 -LINUX_VERSION=842cfd8e5aff3157cb25481b2900b49c188d628a +# https://github.com/rust-lang/rust/pull/151534 +LINUX_VERSION=eb268c7972f65fa0b11b051c5ef2b92747bb2b62 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt From bae7a199f1cd87052396f65b59b51946eae29db1 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Fri, 30 Jan 2026 09:55:34 -0500 Subject: [PATCH 412/583] Address review comments and fix tests --- library/alloc/src/collections/binary_heap/mod.rs | 7 ++++--- library/alloctests/lib.rs | 1 - library/alloctests/tests/collections/binary_heap.rs | 2 +- library/alloctests/tests/lib.rs | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 65c8d5213f16..97aafbc7b699 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -656,14 +656,15 @@ impl BinaryHeap { /// # Examples /// /// ``` + /// #![feature(binary_heap_pop_if)] /// use std::collections::BinaryHeap; /// let mut heap = BinaryHeap::from([1, 2]); /// let pred = |x: &i32| *x % 2 == 0; /// /// assert_eq!(heap.pop_if(pred), Some(2)); - /// assert_eq!(heap, BinaryHeap::from([1])); + /// assert_eq!(heap.as_slice(), [1]); /// assert_eq!(heap.pop_if(pred), None); - /// assert_eq!(heap, BinaryHeap::from([1])); + /// assert_eq!(heap.as_slice(), [1]); /// ``` /// /// # Time complexity @@ -671,7 +672,7 @@ impl BinaryHeap { /// The worst case cost of `pop_if` on a heap containing *n* elements is *O*(log(*n*)). #[unstable(feature = "binary_heap_pop_if", issue = "151828")] pub fn pop_if(&mut self, predicate: impl FnOnce(&T) -> bool) -> Option { - let first = self.data.first()?; + let first = self.peek()?; if predicate(first) { self.pop() } else { None } } diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index 9806c59ce0e1..fe14480102e3 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -17,7 +17,6 @@ #![feature(allocator_api)] #![feature(array_into_iter_constructors)] #![feature(assert_matches)] -#![feature(binary_heap_pop_if)] #![feature(box_vec_non_null)] #![feature(char_internals)] #![feature(const_alloc_error)] diff --git a/library/alloctests/tests/collections/binary_heap.rs b/library/alloctests/tests/collections/binary_heap.rs index 1b6afec7f355..e1484c32a4f8 100644 --- a/library/alloctests/tests/collections/binary_heap.rs +++ b/library/alloctests/tests/collections/binary_heap.rs @@ -145,7 +145,7 @@ fn test_pop_if() { while let Some(popped) = heap.pop_if(|x| *x > 2) { assert_eq!(popped, sorted.pop().unwrap()); } - assert_eq!(heap.into_sorted_vec(), vec![1, 2]); + assert_eq!(heap.into_sorted_vec(), vec![0, 1, 2]); } #[test] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 2926248edbf5..e15c86496cf1 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -1,4 +1,5 @@ #![feature(allocator_api)] +#![feature(binary_heap_pop_if)] #![feature(const_heap)] #![feature(deque_extend_front)] #![feature(iter_array_chunks)] From 85f2923dc53811317f460fdac5c20d602dcb23d8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 30 Jan 2026 16:00:31 +0100 Subject: [PATCH 413/583] fix: Fix diagnostics being leaked when diagnostics panic --- .../crates/rust-analyzer/src/diagnostics.rs | 58 ++++++++++--------- .../crates/rust-analyzer/src/main_loop.rs | 18 +++--- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 712960f13d7e..8d0f52433e02 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -289,34 +289,40 @@ pub(crate) fn fetch_native_diagnostics( let mut diagnostics = subscriptions[slice] .iter() .copied() - .filter_map(|file_id| { - let line_index = snapshot.file_line_index(file_id).ok()?; - let source_root = snapshot.analysis.source_root_id(file_id).ok()?; + .map(|file_id| { + let diagnostics = (|| { + let line_index = snapshot.file_line_index(file_id).ok()?; + let source_root = snapshot.analysis.source_root_id(file_id).ok()?; - let config = &snapshot.config.diagnostics(Some(source_root)); - let diagnostics = match kind { - NativeDiagnosticsFetchKind::Syntax => { - snapshot.analysis.syntax_diagnostics(config, file_id).ok()? - } - - NativeDiagnosticsFetchKind::Semantic if config.enabled => snapshot - .analysis - .semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id) - .ok()?, - NativeDiagnosticsFetchKind::Semantic => return None, - }; - let diagnostics = diagnostics - .into_iter() - .filter_map(|d| { - if d.range.file_id == file_id { - Some(convert_diagnostic(&line_index, d)) - } else { - odd_ones.push(d); - None + let config = &snapshot.config.diagnostics(Some(source_root)); + let diagnostics = match kind { + NativeDiagnosticsFetchKind::Syntax => { + snapshot.analysis.syntax_diagnostics(config, file_id).ok()? } - }) - .collect::>(); - Some((file_id, diagnostics)) + + NativeDiagnosticsFetchKind::Semantic if config.enabled => snapshot + .analysis + .semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id) + .ok()?, + NativeDiagnosticsFetchKind::Semantic => return None, + }; + Some( + diagnostics + .into_iter() + .filter_map(|d| { + if d.range.file_id == file_id { + Some(convert_diagnostic(&line_index, d)) + } else { + odd_ones.push(d); + None + } + }) + .collect::>(), + ) + })() + .unwrap_or_default(); + + (file_id, diagnostics) }) .collect::>(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index f5cead5d8f9d..64decc9e0db6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -666,31 +666,33 @@ impl GlobalState { move |sender| { // We aren't observing the semantics token cache here let snapshot = AssertUnwindSafe(&snapshot); - let Ok(diags) = std::panic::catch_unwind(|| { + let diags = std::panic::catch_unwind(|| { fetch_native_diagnostics( &snapshot, subscriptions.clone(), slice.clone(), NativeDiagnosticsFetchKind::Syntax, ) - }) else { - return; - }; + }) + .unwrap_or_else(|_| { + subscriptions.iter().map(|&id| (id, Vec::new())).collect::>() + }); sender .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags))) .unwrap(); if fetch_semantic { - let Ok(diags) = std::panic::catch_unwind(|| { + let diags = std::panic::catch_unwind(|| { fetch_native_diagnostics( &snapshot, subscriptions.clone(), slice.clone(), NativeDiagnosticsFetchKind::Semantic, ) - }) else { - return; - }; + }) + .unwrap_or_else(|_| { + subscriptions.iter().map(|&id| (id, Vec::new())).collect::>() + }); sender .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic( generation, diags, From 38ee80d48efa8c1be83227be36676a9ccfba55ad Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 17 Jan 2026 21:40:27 -0500 Subject: [PATCH 414/583] add `#[rustc_non_const_trait_method]` --- .../src/attributes/rustc_internal.rs | 12 ++++++++++ compiler/rustc_attr_parsing/src/context.rs | 7 +++--- .../src/check_consts/check.rs | 4 ++-- .../src/const_eval/fn_queries.rs | 11 ++++++++-- compiler/rustc_feature/src/builtin_attrs.rs | 6 +++++ .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_middle/src/ty/mod.rs | 9 ++++++-- compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + .../traits/const-traits/partial/attr-gate.rs | 7 ++++++ .../const-traits/partial/attr-gate.stderr | 13 +++++++++++ .../const-traits/partial/no-const-callers.rs | 22 +++++++++++++++++++ .../partial/no-const-callers.stderr | 19 ++++++++++++++++ 14 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 tests/ui/traits/const-traits/partial/attr-gate.rs create mode 100644 tests/ui/traits/const-traits/partial/attr-gate.stderr create mode 100644 tests/ui/traits/const-traits/partial/no-const-callers.rs create mode 100644 tests/ui/traits/const-traits/partial/no-const-callers.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 6375927b01de..38f728fa9f55 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -388,3 +388,15 @@ impl CombineAttributeParser for RustcLayoutParser { result } } + +pub(crate) struct RustcNonConstTraitMethodParser; + +impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { + const PATH: &'static [Symbol] = &[sym::rustc_non_const_trait_method]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::Trait { body: false })), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNonConstTraitMethod; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index a4d08de515ad..0cabc0895053 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -79,9 +79,9 @@ use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, RustcMustImplementOneOfParser, - RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, RustcNounwindParser, - RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, - RustcSimdMonomorphizeLaneLimitParser, + RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, + RustcNonConstTraitMethodParser, RustcNounwindParser, RustcObjectLifetimeDefaultParser, + RustcOffloadKernelParser, RustcScalableVectorParser, RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -305,6 +305,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 95dbf42d4d44..2abbf75984a4 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -778,12 +778,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // than usual. trace!("attempting to call a trait method"); - let trait_is_const = tcx.is_const_trait(trait_did); + let is_const = tcx.constness(callee) == hir::Constness::Const; // Only consider a trait to be const if the const conditions hold. // Otherwise, it's really misleading to call something "conditionally" // const when it's very obviously not conditionally const. - if trait_is_const && has_const_conditions == Some(ConstConditionsHold::Yes) { + if is_const && has_const_conditions == Some(ConstConditionsHold::Yes) { // Trait calls are always conditionally-const. self.check_op(ops::ConditionallyConstCall { callee, diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index cdf0dcff381f..46cdca53ba8c 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -1,7 +1,8 @@ +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{ Constness, ExprKind, ForeignItemKind, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, - Node, TraitItem, TraitItemKind, VariantData, + Node, TraitItem, TraitItemKind, VariantData, find_attr, }; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; @@ -36,7 +37,13 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Constness { Constness::NotConst => tcx.constness(tcx.local_parent(def_id)), } } - Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(..), .. }) => tcx.trait_def(tcx.local_parent(def_id)).constness, + Node::TraitItem(ti @ TraitItem { kind: TraitItemKind::Fn(..), .. }) => { + if find_attr!(tcx.hir_attrs(ti.hir_id()), AttributeKind::RustcNonConstTraitMethod) { + Constness::NotConst + } else { + tcx.trait_def(tcx.local_parent(def_id)).constness + } + } _ => { tcx.dcx().span_bug( tcx.def_span(def_id), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index ded8a5a4ae51..8f447e1477b3 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1329,6 +1329,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \ the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`" ), + rustc_attr!( + rustc_non_const_trait_method, AttributeType::Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::No, + "`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods \ + as non-const to allow large traits to easier transition to const" + ), BuiltinAttribute { name: sym::rustc_diagnostic_item, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index bc58f1d8faa5..92dda79b0920 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1096,6 +1096,9 @@ pub enum AttributeKind { /// Represents `#[rustc_no_implicit_autorefs]` RustcNoImplicitAutorefs, + /// Represents `#[rustc_non_const_trait_method]`. + RustcNonConstTraitMethod, + /// Represents `#[rustc_nounwind]` RustcNounwind, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 4fd0f5698a8b..816ed07c1dc4 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -124,6 +124,7 @@ impl AttributeKind { RustcMustImplementOneOf { .. } => No, RustcNeverReturnsNullPointer => Yes, RustcNoImplicitAutorefs => Yes, + RustcNonConstTraitMethod => No, // should be reported via other queries like `constness` RustcNounwind => No, RustcObjcClass { .. } => No, RustcObjcSelector { .. } => No, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3e3c9e27186a..044c99ffeb21 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2086,8 +2086,13 @@ impl<'tcx> TyCtxt<'tcx> { DefKind::Impl { of_trait: false } => { self.constness(def_id) == hir::Constness::Const } - DefKind::Impl { of_trait: true } | DefKind::Trait => { - self.is_conditionally_const(parent_def_id) + DefKind::Impl { of_trait: true } => { + self.constness(self.trait_item_of(def_id).unwrap()) == hir::Constness::Const + && self.is_conditionally_const(parent_def_id) + } + DefKind::Trait => { + self.constness(def_id) == hir::Constness::Const + && self.is_conditionally_const(parent_def_id) } _ => bug!("unexpected parent item of associated fn: {parent_def_id:?}"), } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 800965f81d3d..8cf68b280850 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -310,6 +310,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcMain | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcNoImplicitAutorefs + | AttributeKind::RustcNonConstTraitMethod | AttributeKind::RustcNounwind | AttributeKind::RustcObjcClass { .. } | AttributeKind::RustcObjcSelector { .. } @@ -335,7 +336,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Used { .. } | AttributeKind::WindowsSubsystem(..) // tidy-alphabetical-end - ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b80d624bc496..b0ef95d10ffa 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1995,6 +1995,7 @@ symbols! { rustc_no_implicit_autorefs, rustc_no_implicit_bounds, rustc_no_mir_inline, + rustc_non_const_trait_method, rustc_nonnull_optimization_guaranteed, rustc_nounwind, rustc_objc_class, diff --git a/tests/ui/traits/const-traits/partial/attr-gate.rs b/tests/ui/traits/const-traits/partial/attr-gate.rs new file mode 100644 index 000000000000..c59a03de2153 --- /dev/null +++ b/tests/ui/traits/const-traits/partial/attr-gate.rs @@ -0,0 +1,7 @@ +trait A { + #[rustc_non_const_trait_method] + //~^ ERROR: use of an internal attribute + fn a(); +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/partial/attr-gate.stderr b/tests/ui/traits/const-traits/partial/attr-gate.stderr new file mode 100644 index 000000000000..bc86cbe4edf7 --- /dev/null +++ b/tests/ui/traits/const-traits/partial/attr-gate.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of an internal attribute + --> $DIR/attr-gate.rs:2:5 + | +LL | #[rustc_non_const_trait_method] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: the `#[rustc_non_const_trait_method]` attribute is an internal implementation detail that will never be stable + = note: `#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods as non-const to allow large traits to easier transition to const + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/partial/no-const-callers.rs b/tests/ui/traits/const-traits/partial/no-const-callers.rs new file mode 100644 index 000000000000..efcdea36f1ca --- /dev/null +++ b/tests/ui/traits/const-traits/partial/no-const-callers.rs @@ -0,0 +1,22 @@ +#![feature(const_trait_impl, rustc_attrs)] + +const trait A { + fn a(); + #[rustc_non_const_trait_method] + fn b() { println!("hi"); } +} + +impl const A for () { + fn a() {} +} + +const fn foo() { + T::a(); + T::b(); + //~^ ERROR: cannot call non-const associated function + <()>::a(); + <()>::b(); + //~^ ERROR: cannot call non-const associated function +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/partial/no-const-callers.stderr b/tests/ui/traits/const-traits/partial/no-const-callers.stderr new file mode 100644 index 000000000000..31179161c5cd --- /dev/null +++ b/tests/ui/traits/const-traits/partial/no-const-callers.stderr @@ -0,0 +1,19 @@ +error[E0015]: cannot call non-const associated function `::b` in constant functions + --> $DIR/no-const-callers.rs:15:5 + | +LL | T::b(); + | ^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const associated function `<() as A>::b` in constant functions + --> $DIR/no-const-callers.rs:18:5 + | +LL | <()>::b(); + | ^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0015`. From 6147a3fc88f9920a2569adf7b6f4e8b63cd887c1 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 17 Jan 2026 23:03:57 -0500 Subject: [PATCH 415/583] constify `Iterator` --- library/core/src/iter/traits/collect.rs | 6 +- library/core/src/iter/traits/iterator.rs | 77 +++++++++++++++++++++++- library/core/src/option.rs | 11 ++-- library/coretests/tests/iter/mod.rs | 15 +++++ library/coretests/tests/lib.rs | 1 + 5 files changed, 103 insertions(+), 7 deletions(-) diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index c3b9a0f0b7a4..9c3edfd4192d 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -279,7 +279,8 @@ pub trait FromIterator: Sized { )] #[rustc_skip_during_method_dispatch(array, boxed_slice)] #[stable(feature = "rust1", since = "1.0.0")] -pub trait IntoIterator { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +pub const trait IntoIterator { /// The type of the elements being iterated over. #[rustc_diagnostic_item = "IntoIteratorItem"] #[stable(feature = "rust1", since = "1.0.0")] @@ -312,7 +313,8 @@ pub trait IntoIterator { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for I { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const IntoIterator for I { type Item = I::Item; type IntoIter = I; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index dc484e2a27f6..d919230d094d 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -37,7 +37,8 @@ fn _assert_is_dyn_compatible(_: &dyn Iterator) {} #[lang = "iterator"] #[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub trait Iterator { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +pub const trait Iterator { /// The type of the elements being iterated over. #[rustc_diagnostic_item = "IteratorItem"] #[stable(feature = "rust1", since = "1.0.0")] @@ -107,6 +108,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_next_chunk", issue = "98326")] + #[rustc_non_const_trait_method] fn next_chunk( &mut self, ) -> Result<[Self::Item; N], array::IntoIter> @@ -219,6 +221,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn count(self) -> usize where Self: Sized, @@ -251,6 +254,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn last(self) -> Option where Self: Sized, @@ -298,6 +302,7 @@ pub trait Iterator { /// ``` #[inline] #[unstable(feature = "iter_advance_by", issue = "77404")] + #[rustc_non_const_trait_method] fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { /// Helper trait to specialize `advance_by` via `try_fold` for `Sized` iterators. trait SpecAdvanceBy { @@ -375,6 +380,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn nth(&mut self, n: usize) -> Option { self.advance_by(n).ok()?; self.next() @@ -425,6 +431,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_step_by", since = "1.28.0")] + #[rustc_non_const_trait_method] fn step_by(self, step: usize) -> StepBy where Self: Sized, @@ -496,6 +503,7 @@ pub trait Iterator { /// [`OsStr`]: ../../std/ffi/struct.OsStr.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn chain(self, other: U) -> Chain where Self: Sized, @@ -614,6 +622,7 @@ pub trait Iterator { /// [`zip`]: crate::iter::zip #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn zip(self, other: U) -> Zip where Self: Sized, @@ -657,6 +666,7 @@ pub trait Iterator { /// [`intersperse_with`]: Iterator::intersperse_with #[inline] #[unstable(feature = "iter_intersperse", issue = "79524")] + #[rustc_non_const_trait_method] fn intersperse(self, separator: Self::Item) -> Intersperse where Self: Sized, @@ -715,6 +725,7 @@ pub trait Iterator { /// [`intersperse`]: Iterator::intersperse #[inline] #[unstable(feature = "iter_intersperse", issue = "79524")] + #[rustc_non_const_trait_method] fn intersperse_with(self, separator: G) -> IntersperseWith where Self: Sized, @@ -774,6 +785,7 @@ pub trait Iterator { #[rustc_diagnostic_item = "IteratorMap"] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn map(self, f: F) -> Map where Self: Sized, @@ -819,6 +831,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_for_each", since = "1.21.0")] + #[rustc_non_const_trait_method] fn for_each(self, f: F) where Self: Sized, @@ -894,6 +907,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "iter_filter"] + #[rustc_non_const_trait_method] fn filter