From 19c60651fe11f17087a0fd659142d1e65ba0912e Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Sat, 18 Jul 2026 20:23:30 +0100 Subject: [PATCH 01/37] Optimize slice::contains for bytewise types --- library/core/src/slice/cmp.rs | 10 ++++-- library/coretests/tests/slice.rs | 33 +++++++++++++++++ .../lib-optimizations/slice-contains.rs | 36 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 tests/codegen-llvm/lib-optimizations/slice-contains.rs diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 10a0088477162..3a62e7f61b2b4 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -391,10 +391,16 @@ where } } -impl SliceContains for u8 { +impl SliceContains for T { #[inline] fn slice_contains(&self, x: &[Self]) -> bool { - memchr::memchr(*self, x).is_some() + // SAFETY: `UnsignedBytewiseOrd` guarantees that `Self` has the same + // layout as `u8` and is initialized, so both the value and slice can + // be read as bytes. + let (byte, bytes) = unsafe { + (*(self as *const Self).cast::(), from_raw_parts(x.as_ptr().cast::(), x.len())) + }; + memchr::memchr(byte, bytes).is_some() } } diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index a4db7304fff90..22b1ba7738af9 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -5,6 +5,39 @@ use core::num::NonZero; use core::ops::{Range, RangeInclusive}; use core::slice; +#[test] +fn test_contains_bytewise_types() { + let mut bools = [false; 64]; + assert!(bools.contains(&false)); + assert!(!bools.contains(&true)); + bools[31] = true; + assert!(bools.contains(&true)); + + let one = NonZero::new(1_u8).unwrap(); + let two = NonZero::new(2_u8).unwrap(); + let three = NonZero::new(3_u8).unwrap(); + let mut nonzeros = [one; 64]; + nonzeros[31] = two; + assert!(nonzeros.contains(&one)); + assert!(nonzeros.contains(&two)); + assert!(!nonzeros.contains(&three)); + + let mut optional_nonzeros = [Some(one); 64]; + optional_nonzeros[31] = None; + assert!(optional_nonzeros.contains(&Some(one))); + assert!(optional_nonzeros.contains(&None)); + assert!(!optional_nonzeros.contains(&Some(two))); + + let a = core::ascii::Char::CapitalA; + let q = core::ascii::Char::CapitalQ; + let z = core::ascii::Char::CapitalZ; + let mut ascii = [a; 64]; + ascii[31] = z; + assert!(ascii.contains(&a)); + assert!(ascii.contains(&z)); + assert!(!ascii.contains(&q)); +} + #[test] fn test_position() { let b = [1, 2, 3, 5, 5]; diff --git a/tests/codegen-llvm/lib-optimizations/slice-contains.rs b/tests/codegen-llvm/lib-optimizations/slice-contains.rs new file mode 100644 index 0000000000000..ecca007875148 --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/slice-contains.rs @@ -0,0 +1,36 @@ +// Ensure one-byte slice `contains` specializations use the optimized byte search. +//@ compile-flags: -Copt-level=3 -Zinline-mir=false + +#![crate_type = "lib"] +#![feature(ascii_char)] + +use std::ascii::Char as AsciiChar; +use std::num::NonZeroU8; + +// CHECK-LABEL: @contains_bool +#[no_mangle] +pub fn contains_bool(x: bool, data: &[bool]) -> bool { + // CHECK: call core::slice::memchr + data.contains(&x) +} + +// CHECK-LABEL: @contains_nonzero_u8 +#[no_mangle] +pub fn contains_nonzero_u8(x: NonZeroU8, data: &[NonZeroU8]) -> bool { + // CHECK: call core::slice::memchr + data.contains(&x) +} + +// CHECK-LABEL: @contains_option_nonzero_u8 +#[no_mangle] +pub fn contains_option_nonzero_u8(x: Option, data: &[Option]) -> bool { + // CHECK: call core::slice::memchr + data.contains(&x) +} + +// CHECK-LABEL: @contains_ascii_char +#[no_mangle] +pub fn contains_ascii_char(x: AsciiChar, data: &[AsciiChar]) -> bool { + // CHECK: call core::slice::memchr + data.contains(&x) +} From 40d419f33247bfcd91ebab2b1ba8089786bcc031 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 17 Jul 2026 10:48:59 +0800 Subject: [PATCH 02/37] ci: loongarch64-linux: Bump binutils to 2.46.1 and gcc to 16.1 --- src/ci/docker/README.md | 8 ++--- .../dist-loongarch64-linux/Dockerfile | 4 +-- .../loongarch64-unknown-linux-gnu.defconfig | 4 +-- .../dist-loongarch64-musl/Dockerfile | 4 +-- .../loongarch64-unknown-linux-musl.defconfig | 4 +-- src/ci/docker/scripts/crosstool-ng-git.sh | 30 +++++++++++++++++++ 6 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 src/ci/docker/scripts/crosstool-ng-git.sh diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 6e5a38a3c515a..f8c5b3dac16f8 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -261,9 +261,9 @@ For targets: `loongarch64-unknown-linux-gnu` - Target options > Bitness = 64-bit - Operating System > Target OS = linux - Operating System > Linux kernel version = 5.19.16 -- Binary utilities > Version of binutils = 2.45 +- Binary utilities > Version of binutils = 2.46.1 - C-library > glibc version = 2.36 -- C compiler > gcc version = 15.2.0 +- C compiler > gcc version = 16.1.0 - C compiler > C++ = ENABLE -- to cross compile LLVM ### `loongarch64-unknown-linux-musl.defconfig` @@ -277,9 +277,9 @@ For targets: `loongarch64-unknown-linux-musl` - Target options > Bitness = 64-bit - Operating System > Target OS = linux - Operating System > Linux kernel version = 5.19.16 -- Binary utilities > Version of binutils = 2.45 +- Binary utilities > Version of binutils = 2.46.1 - C-library > musl version = 1.2.5 -- C compiler > gcc version = 15.2.0 +- C compiler > gcc version = 16.1.0 - C compiler > C++ = ENABLE -- to cross compile LLVM ### `mips-linux-gnu.defconfig` 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 c4f8351bd9466..218b711f66fc0 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -3,9 +3,9 @@ FROM ubuntu:22.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh -COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-git.sh /scripts/ COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ -RUN sh /scripts/crosstool-ng.sh +RUN sh /scripts/crosstool-ng-git.sh COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig index 60c9cc7ef7252..5b3f1a270edfa 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/loongarch64-unknown-linux-gnu.defconfig @@ -14,7 +14,7 @@ CT_KERNEL_LINUX=y CT_LINUX_V_5_19=y CT_LINUX_VERSION="5.19.16" CT_GLIBC_V_2_36=y -CT_BINUTILS_V_2_45=y -CT_GCC_V_15=y +CT_BINUTILS_V_2_46=y +CT_GCC_V_16=y CT_CC_GCC_ENABLE_DEFAULT_PIE=y CT_CC_LANG_CXX=y 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 61b28e4189a4a..2ff6da0d3814b 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile @@ -3,9 +3,9 @@ FROM ubuntu:22.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh -COPY scripts/crosstool-ng.sh /scripts/ +COPY scripts/crosstool-ng-git.sh /scripts/ COPY scripts/crosstool-ng-sha256-20260705.diff /scripts/ -RUN sh /scripts/crosstool-ng.sh +RUN sh /scripts/crosstool-ng-git.sh COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig b/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig index 73e29d7aca725..07fed33600f29 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/loongarch64-unknown-linux-musl.defconfig @@ -15,8 +15,8 @@ CT_LINUX_V_5_19=y CT_LINUX_VERSION="5.19.16" CT_LIBC_MUSL=y CT_MUSL_V_1_2_5=y -CT_BINUTILS_V_2_45=y -CT_GCC_V_15=y +CT_BINUTILS_V_2_46=y +CT_GCC_V_16=y CT_CC_GCC_ENABLE_DEFAULT_PIE=y CT_CC_LANG_CXX=y CT_GETTEXT_NEEDED=y diff --git a/src/ci/docker/scripts/crosstool-ng-git.sh b/src/ci/docker/scripts/crosstool-ng-git.sh new file mode 100644 index 0000000000000..faccd7dc9bbf5 --- /dev/null +++ b/src/ci/docker/scripts/crosstool-ng-git.sh @@ -0,0 +1,30 @@ +#!/bin/sh +set -ex + +# ignore-tidy-file-linelength + +URL=https://github.com/crosstool-ng/crosstool-ng +REV=27cd8380e72bb1cf3e7cf4a06a9cdbdc57df6f72 + +mkdir crosstool-ng +cd crosstool-ng +git init +git fetch --depth=1 ${URL} ${REV} +git reset --hard FETCH_HEAD + +# https://github.com/crosstool-ng/crosstool-ng/issues/1832 +# "download source of zlib is invalid now" +sed -e "s|zlib.net/'|zlib.net/fossils'|" -i packages/zlib/package.desc + +# FIXME(#158718): patch crosstools-ng known-good kernel artifact SHA256 +# checksums to the artifacts we mirror in `ci-mirrors`. +# See +# . +patch -p1 Date: Mon, 20 Jul 2026 16:50:37 +0800 Subject: [PATCH 03/37] Promote loongarch32-unknown-none* to Tier 2 MCP: https://github.com/rust-lang/compiler-team/issues/968 --- .../src/spec/targets/loongarch32_unknown_none.rs | 2 +- .../targets/loongarch32_unknown_none_softfloat.rs | 2 +- .../host-x86_64/dist-loongarch64-linux/Dockerfile | 14 +++++++++++++- src/doc/rustc/src/platform-support.md | 4 ++-- .../rustc/src/platform-support/loongarch-none.md | 4 ++-- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none.rs b/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none.rs index 7e074b73919f3..cbf8cec1e6865 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { llvm_target: "loongarch32-unknown-none".into(), metadata: TargetMetadata { description: Some("Freestanding/bare-metal LoongArch32".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(false), }, diff --git a/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none_softfloat.rs index 4e6807012e891..3fd12e50d0ae3 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none_softfloat.rs @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { llvm_target: "loongarch32-unknown-none".into(), metadata: TargetMetadata { description: Some("Freestanding/bare-metal LoongArch32 softfloat".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(false), }, 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 218b711f66fc0..17206c911aabc 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -39,12 +39,24 @@ ENV CC_loongarch64_unknown_none=loongarch64-unknown-linux-gnu-gcc \ AR_loongarch64_unknown_none_softfloat=loongarch64-unknown-linux-gnu-ar \ CXX_loongarch64_unknown_none_softfloat=loongarch64-unknown-linux-gnu-g++ \ CFLAGS_loongarch64_unknown_none_softfloat="-ffreestanding -mabi=lp64s -mfpu=none" \ - CXXFLAGS_loongarch64_unknown_none_softfloat="-ffreestanding -mabi=lp64s -mfpu=none" + CXXFLAGS_loongarch64_unknown_none_softfloat="-ffreestanding -mabi=lp64s -mfpu=none" \ + CC_loongarch32_unknown_none=loongarch64-unknown-linux-gnu-gcc \ + AR_loongarch32_unknown_none=loongarch64-unknown-linux-gnu-ar \ + CXX_loongarch32_unknown_none=loongarch64-unknown-linux-gnu-g++ \ + CFLAGS_loongarch32_unknown_none="-ffreestanding -march=la32rv1.0 -mabi=ilp32d" \ + CXXFLAGS_loongarch32_unknown_none="-ffreestanding -march=la32rv1.0 -mabi=ilp32d" \ + CC_loongarch32_unknown_none_softfloat=loongarch64-unknown-linux-gnu-gcc \ + AR_loongarch32_unknown_none_softfloat=loongarch64-unknown-linux-gnu-ar \ + CXX_loongarch32_unknown_none_softfloat=loongarch64-unknown-linux-gnu-g++ \ + CFLAGS_loongarch32_unknown_none_softfloat="-ffreestanding -march=la32rv1.0 -mabi=ilp32s -mfpu=none" \ + CXXFLAGS_loongarch32_unknown_none_softfloat="-ffreestanding -march=la32rv1.0 -mabi=ilp32s -mfpu=none" ENV HOSTS=loongarch64-unknown-linux-gnu ENV TARGETS=$HOSTS ENV TARGETS=$TARGETS,loongarch64-unknown-none ENV TARGETS=$TARGETS,loongarch64-unknown-none-softfloat +ENV TARGETS=$TARGETS,loongarch32-unknown-none +ENV TARGETS=$TARGETS,loongarch32-unknown-none-softfloat ENV RUST_CONFIGURE_ARGS="--enable-extended \ --enable-full-tools \ diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index ce4d55c2d2b00..f360637e216bf 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -187,6 +187,8 @@ target | std | notes [`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.5 (Pentium 4) [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [^win32-msvc-alignment] +[`loongarch32-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch32 Bare-metal (ILP32D ABI) +[`loongarch32-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch32 Bare-metal (ILP32S ABI) [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] @@ -352,8 +354,6 @@ target | std | host | notes [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [^win32-msvc-alignment] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony -[`loongarch32-unknown-none`](platform-support/loongarch-none.md) | * | | LoongArch32 Bare-metal (ILP32D ABI) -[`loongarch32-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | | LoongArch32 Bare-metal (ILP32S ABI) [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux [`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) [^snan-inverted] diff --git a/src/doc/rustc/src/platform-support/loongarch-none.md b/src/doc/rustc/src/platform-support/loongarch-none.md index 7c3a7e6c8bc57..bf8db72e71646 100644 --- a/src/doc/rustc/src/platform-support/loongarch-none.md +++ b/src/doc/rustc/src/platform-support/loongarch-none.md @@ -4,8 +4,8 @@ Freestanding/bare-metal LoongArch binaries in ELF format: firmware, kernels, etc | Target | Description | Tier | |--------|-------------|------| -| `loongarch32-unknown-none` | LoongArch 32-bit, ILP32D ABI (freestanding, hard-float) | Tier 3 | -| `loongarch32-unknown-none-softfloat` | LoongArch 32-bit, ILP32S ABI (freestanding, soft-float) | Tier 3 | +| `loongarch32-unknown-none` | LoongArch 32-bit, ILP32D ABI (freestanding, hard-float) | Tier 2 | +| `loongarch32-unknown-none-softfloat` | LoongArch 32-bit, ILP32S ABI (freestanding, soft-float) | Tier 2 | | `loongarch64-unknown-none` | LoongArch 64-bit, LP64D ABI (freestanding, hard-float) | Tier 2 | | `loongarch64-unknown-none-softfloat` | LoongArch 64-bit, LP64S ABI (freestanding, soft-float) | Tier 2 | From ad3ce248b6afcc6d255e6ea08f67ddc0b7f76600 Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Sun, 26 Jul 2026 21:29:02 +0330 Subject: [PATCH 04/37] test: add regression test for bool indexing codegen Signed-off-by: Amirhossein Akhlaghpour --- ...ng-with-bools-no-redundant-instructions.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/assembly-llvm/indexing-with-bools-no-redundant-instructions.rs diff --git a/tests/assembly-llvm/indexing-with-bools-no-redundant-instructions.rs b/tests/assembly-llvm/indexing-with-bools-no-redundant-instructions.rs new file mode 100644 index 0000000000000..628f4faab6d06 --- /dev/null +++ b/tests/assembly-llvm/indexing-with-bools-no-redundant-instructions.rs @@ -0,0 +1,35 @@ +//@ assembly-output: emit-asm +//@ only-x86_64 +//@ ignore-sgx Test incompatible with LVI mitigations +//@ compile-flags: -Copt-level=3 + +//! Regression test for https://github.com/rust-lang/rust/issues/123216. +//! Indexing with a `bool` should not generate redundant `jmp` or `and` +//! instructions. + +#![crate_type = "lib"] + +#[no_mangle] +pub fn bool_index(a: u32, b: bool, c: bool, d: &mut [u128; 2]) { + // CHECK-LABEL: bool_index: + // CHECK: testl %esi, %esi + // CHECK: je + // CHECK: xorb %dl, %dil + // CHECK: orb $1, (%rcx) + // CHECK-NOT: jmp + // CHECK-NOT: andb $1, %dil + // CHECK: movzbl %dil, %eax + // CHECK: andl $1, %eax + // CHECK: shll $4, %eax + // CHECK: orb $1, (%rcx,%rax) + // CHECK: retq + + let mut a = a & 1 != 0; + + if b { + a ^= c; + d[0] |= 1; + } + + d[a as usize] |= 1; +} From 9e11f3ccdc10ef49e7e937c030386e036bad1ba0 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Fri, 24 Jul 2026 13:13:50 +0300 Subject: [PATCH 05/37] add tests for loading injected crates --- .../cargo-issue-7359-load-panic/Cargo.toml | 11 +++ .../cargo-issue-7359-load-panic/main.rs | 1 + .../cargo-issue-7359-load-panic/rmake.rs | 27 ++++++++ .../compiler_builtins.rs | 9 +++ .../run-make/locate-compiler-builtins/core.rs | 20 ++++++ .../run-make/locate-compiler-builtins/lib.rs | 6 ++ .../locate-compiler-builtins/rmake.rs | 68 +++++++++++++++++++ tests/run-make/locate-panic-runtime/core.rs | 21 ++++++ tests/run-make/locate-panic-runtime/lib.rs | 11 +++ .../locate-panic-runtime/panic_abort.rs | 9 +++ tests/run-make/locate-panic-runtime/rmake.rs | 62 +++++++++++++++++ tests/run-make/locate-panic-runtime/std.rs | 11 +++ 12 files changed, 256 insertions(+) create mode 100644 tests/run-make-cargo/cargo-issue-7359-load-panic/Cargo.toml create mode 100644 tests/run-make-cargo/cargo-issue-7359-load-panic/main.rs create mode 100644 tests/run-make-cargo/cargo-issue-7359-load-panic/rmake.rs create mode 100644 tests/run-make/locate-compiler-builtins/compiler_builtins.rs create mode 100644 tests/run-make/locate-compiler-builtins/core.rs create mode 100644 tests/run-make/locate-compiler-builtins/lib.rs create mode 100644 tests/run-make/locate-compiler-builtins/rmake.rs create mode 100644 tests/run-make/locate-panic-runtime/core.rs create mode 100644 tests/run-make/locate-panic-runtime/lib.rs create mode 100644 tests/run-make/locate-panic-runtime/panic_abort.rs create mode 100644 tests/run-make/locate-panic-runtime/rmake.rs create mode 100644 tests/run-make/locate-panic-runtime/std.rs diff --git a/tests/run-make-cargo/cargo-issue-7359-load-panic/Cargo.toml b/tests/run-make-cargo/cargo-issue-7359-load-panic/Cargo.toml new file mode 100644 index 0000000000000..3ab3cf5353220 --- /dev/null +++ b/tests/run-make-cargo/cargo-issue-7359-load-panic/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "foo" +version = "0.1.0" +edition = "2024" + +[[bin]] +name = "foo" +path = "main.rs" + +[profile.release] +panic = "abort" diff --git a/tests/run-make-cargo/cargo-issue-7359-load-panic/main.rs b/tests/run-make-cargo/cargo-issue-7359-load-panic/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make-cargo/cargo-issue-7359-load-panic/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make-cargo/cargo-issue-7359-load-panic/rmake.rs b/tests/run-make-cargo/cargo-issue-7359-load-panic/rmake.rs new file mode 100644 index 0000000000000..cd03a87cc6644 --- /dev/null +++ b/tests/run-make-cargo/cargo-issue-7359-load-panic/rmake.rs @@ -0,0 +1,27 @@ +// This is a regression test to ensure that rustc doesn't load `panic_abort` +// from the sysroot. See rust-lang/cargo#7359 +// +//@ needs-target-std + +use run_make_support::{cargo, path, target}; + +fn main() { + let target_dir = path("target"); + + cargo() + .args(&[ + "build", + "--release", + "--manifest-path", + "Cargo.toml", + "-Zbuild-std=std", + "--target", + &target(), + ]) + .env("RUSTC_BOOTSTRAP", "1") + // Visual Studio 2022 requires that the LIB env var be set so it can + // find the Windows SDK. + .env("LIB", std::env::var("LIB").unwrap_or_default()) + .run_fail() + .assert_stderr_contains("duplicate lang item in crate `core`: `sized`"); +} diff --git a/tests/run-make/locate-compiler-builtins/compiler_builtins.rs b/tests/run-make/locate-compiler-builtins/compiler_builtins.rs new file mode 100644 index 0000000000000..104052a138fc8 --- /dev/null +++ b/tests/run-make/locate-compiler-builtins/compiler_builtins.rs @@ -0,0 +1,9 @@ +// We are `compiler_builtins`. +#![feature(lang_items, no_core, compiler_builtins)] +#![allow(internal_features)] +#![compiler_builtins] +#![no_std] +#![no_core] +#![crate_type = "rlib"] + +extern crate core; diff --git a/tests/run-make/locate-compiler-builtins/core.rs b/tests/run-make/locate-compiler-builtins/core.rs new file mode 100644 index 0000000000000..7d75b528bb268 --- /dev/null +++ b/tests/run-make/locate-compiler-builtins/core.rs @@ -0,0 +1,20 @@ +// We are core. +#![feature(lang_items, no_core)] +#![allow(internal_features)] +#![no_std] +#![no_core] +#![crate_type = "rlib"] + +// Hack: this is needed for the import prelude resolution. +pub mod prelude { + pub mod rust_2024 {} +} + +#[lang = "pointee_sized"] +pub trait PointeeSized {} + +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} + +#[lang = "sized"] +pub trait Sized: MetaSized {} diff --git a/tests/run-make/locate-compiler-builtins/lib.rs b/tests/run-make/locate-compiler-builtins/lib.rs new file mode 100644 index 0000000000000..78151c433e4c2 --- /dev/null +++ b/tests/run-make/locate-compiler-builtins/lib.rs @@ -0,0 +1,6 @@ +#![no_std] +#![no_implicit_prelude] +#![crate_type = "dylib"] +// Hack: `compiler_builtins` is not injected in crates with `#![no_core]`. +// However, we still resolve to the local core since sysroot is +// disabled in this test. diff --git a/tests/run-make/locate-compiler-builtins/rmake.rs b/tests/run-make/locate-compiler-builtins/rmake.rs new file mode 100644 index 0000000000000..e2c981e587c5a --- /dev/null +++ b/tests/run-make/locate-compiler-builtins/rmake.rs @@ -0,0 +1,68 @@ +// This test makes sure that the injected `compiler_builtins` can be loaded both from +// `-L crate=` and `-L dependency=` paths (apart from sysroot). +// +// The need for `-L dependency=` paths comes from RFC 3874 (build-std=always). +// +// Note: We have two possible `compiler_builtins` crates: the built one and the one from +// the sysroot. Sysroot lookup is disabled via the `--sysroot=` override to verify +// that we can load the correct one. + +use run_make_support::{path, rfs, rust_lib_name, rustc}; + +fn main() { + rfs::create_dir("compiler_builtins"); + + // Compile `core`. + rustc() + .input("core.rs") + .arg("-Zunstable-options") + .panic("immediate-abort") + .arg("--edition=2024") + .sysroot("./no_exists") + .run(); + + // Compile `compiler_builtins` into a separate directory to prevent it from being + // found via `-L .`. + rustc() + .input("compiler_builtins.rs") + .arg("-Zunstable-options") + .panic("immediate-abort") + .arg("--edition=2024") + .out_dir("compiler_builtins") + .sysroot("./no_exists") + .run(); + + // Compile the final artifact. `compiler_builtins` cannot be located without the + // `-L{crate/dependency}=` option. + rustc() + .input("lib.rs") + .arg("-Zunstable-options") + .panic("immediate-abort") + .arg("--edition=2024") + .sysroot("./no_exists") + .run_fail() + .assert_stderr_contains("can't find crate for `compiler_builtins`"); + + // Compile the final artifact. `compiler_builtins` can be located via + // `-Lcrate=` paths. + rustc() + .input("lib.rs") + .arg("-Zunstable-options") + .panic("immediate-abort") + .arg("--edition=2024") + .sysroot("./sysroot") + .library_search_path(format!("crate={}", path("compiler_builtins").display())) + .run(); + + // Compile the final artifact. The `compiler_builtins` can be located via + // `-Ldependency=` paths. + rustc() + .input("lib.rs") + .arg("-Zunstable-options") + .panic("immediate-abort") + .arg("--edition=2024") + .sysroot("./no_exists") + .library_search_path(format!("dependency={}", path("compiler_builtins").display())) + .run_fail() + .assert_stderr_contains("can't find crate for `compiler_builtins`"); +} diff --git a/tests/run-make/locate-panic-runtime/core.rs b/tests/run-make/locate-panic-runtime/core.rs new file mode 100644 index 0000000000000..ed65cbecc0560 --- /dev/null +++ b/tests/run-make/locate-panic-runtime/core.rs @@ -0,0 +1,21 @@ +// We are core. +#![feature(lang_items, no_core)] +#![allow(internal_features)] +#![no_std] +#![no_core] +#![crate_type = "rlib"] + +#[lang = "panic_info"] +pub struct PanicInfo {} + +#[lang = "copy"] +pub trait Copy: Sized {} + +#[lang = "pointee_sized"] +pub trait PointeeSized {} + +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} + +#[lang = "sized"] +pub trait Sized: MetaSized {} diff --git a/tests/run-make/locate-panic-runtime/lib.rs b/tests/run-make/locate-panic-runtime/lib.rs new file mode 100644 index 0000000000000..e85363beaf408 --- /dev/null +++ b/tests/run-make/locate-panic-runtime/lib.rs @@ -0,0 +1,11 @@ +#![feature(no_core)] +#![no_std] +#![no_core] +#![crate_type = "dylib"] + +extern crate std; + +#[panic_handler] +fn panic(_: &std::PanicInfo) -> ! { + loop {} +} diff --git a/tests/run-make/locate-panic-runtime/panic_abort.rs b/tests/run-make/locate-panic-runtime/panic_abort.rs new file mode 100644 index 0000000000000..f681b3386853a --- /dev/null +++ b/tests/run-make/locate-panic-runtime/panic_abort.rs @@ -0,0 +1,9 @@ +// We are panic runtime. +#![feature(panic_runtime, no_core)] +#![allow(internal_features)] +#![no_std] +#![no_core] +#![panic_runtime] +#![crate_type = "rlib"] + +extern crate core; diff --git a/tests/run-make/locate-panic-runtime/rmake.rs b/tests/run-make/locate-panic-runtime/rmake.rs new file mode 100644 index 0000000000000..7cc3b04b23420 --- /dev/null +++ b/tests/run-make/locate-panic-runtime/rmake.rs @@ -0,0 +1,62 @@ +// This test makes sure that the injected panic runtime can be loaded both from +// `-L crate=` and `-L dependency=` paths. +// +// The need for `-L dependency=` paths comes from RFC 3874 (build-std=always). +// +// Note: We have two possible panic runtime crates: the built one and the one from +// the sysroot. Sysroot lookup is disabled via the `--sysroot=` override to verify +// that we can load the correct one. + +use run_make_support::{path, rfs, rust_lib_name, rustc}; + +fn main() { + rfs::create_dir("panic_abort"); + + // Compile `core`. + rustc().input("core.rs").panic("abort").sysroot("./no_exists").run(); + + // Compile `panic_abort` into a separate directory to prevent it from being + // found via `-L .`. + rustc() + .input("panic_abort.rs") + .panic("abort") + .out_dir("panic_abort") + .sysroot("./no_exists") + .run(); + + // Compile `std`. + rustc() + .input("std.rs") + .extern_("panic_abort", &path("panic_abort").join(rust_lib_name("panic_abort"))) + .panic("abort") + .sysroot("./no_exists") + .run(); + + // Compile the final artifact. The panic runtime cannot be located without the + // `-L{crate/dependency}=` option. + rustc() + .input("lib.rs") + .arg("-Cpanic=abort") + .sysroot("./no_exists") + .run_fail() + .assert_stderr_contains("can't find crate for `panic_abort`"); + + // Compile the final artifact. The panic runtime can be located via + // `-Lcrate=` paths. + rustc() + .input("lib.rs") + .arg("-Cpanic=abort") + .sysroot("./no_exists") + .library_search_path(format!("crate={}", path("panic_abort").display())) + .run(); + + // Compile the final artifact. The panic runtime can be located via + // `-Ldependency=` paths. + rustc() + .input("lib.rs") + .arg("-Cpanic=abort") + .sysroot("./no_exists") + .library_search_path(format!("dependency={}", path("panic_abort").display())) + .run_fail() + .assert_stderr_contains("can't find crate for `panic_abort`"); +} diff --git a/tests/run-make/locate-panic-runtime/std.rs b/tests/run-make/locate-panic-runtime/std.rs new file mode 100644 index 0000000000000..63eb75eca50a2 --- /dev/null +++ b/tests/run-make/locate-panic-runtime/std.rs @@ -0,0 +1,11 @@ +// We are std. +#![feature(needs_panic_runtime, no_core)] +#![allow(internal_features)] +#![no_std] +#![no_core] +// Tell rustc to inject panic runtime. +#![needs_panic_runtime] +#![crate_type = "rlib"] + +extern crate core; +pub use core::*; From 87e2c0b041f21819bb5edc3825481dd1dc7cc2d2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 27 Jul 2026 15:05:40 +0200 Subject: [PATCH 06/37] remove InterpError::map_err_info --- .../src/interpret/eval_context.rs | 18 +++------------ .../rustc_const_eval/src/interpret/mod.rs | 2 +- .../src/interpret/validity.rs | 6 ++--- .../rustc_middle/src/mir/interpret/error.rs | 23 +++++++++++-------- .../src/known_panics_lint.rs | 9 +++----- src/tools/miri/src/diagnostics.rs | 6 ++--- 6 files changed, 25 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index b6e3f9c3009c6..45d57f77a4079 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -21,9 +21,9 @@ use rustc_target::callconv::FnAbi; use tracing::{debug, trace}; use super::{ - Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine, - MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, - err_inval, interp_ok, throw_inval, throw_ub, throw_ub_format, + Frame, FrameInfo, GlobalId, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, + Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, interp_ok, + throw_inval, throw_ub, throw_ub_format, }; use crate::{enter_trace_span, util}; @@ -239,18 +239,6 @@ pub(super) fn from_known_layout<'tcx>( } } -/// Turn the given error into a human-readable string. Expects the string to be printed, so if -/// `RUSTC_CTFE_BACKTRACE` is set this will show a backtrace of the rustc internals that -/// triggered the error. -/// -/// This is NOT the preferred way to render an error; use `report` from `const_eval` instead. -/// However, this is useful when error messages appear in ICEs. -pub fn format_interp_error<'tcx>(e: InterpErrorInfo<'tcx>) -> String { - let (e, backtrace) = e.into_parts(); - backtrace.print_backtrace(); - e.to_string() -} - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn new( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index d52c1f9306444..a02dd509b507e 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -23,7 +23,7 @@ mod visitor; pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here pub use self::call::FnArg; -pub use self::eval_context::{InterpCx, format_interp_error}; +pub use self::eval_context::InterpCx; use self::eval_context::{from_known_layout, mir_assign_valid_types}; pub use self::intern::{ HasStaticRootDefId, InternError, InternKind, intern_const_alloc_for_constprop, diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 75a6753fe3d0e..312fc3c5e3f11 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -32,7 +32,6 @@ use super::machine::AllocMap; use super::{ AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, PlaceTy, Pointer, Projectable, Scalar, ValueVisitor, err_ub, - format_interp_error, }; use crate::enter_trace_span; @@ -1604,7 +1603,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { v.reset_padding(val)?; interp_ok(()) }) - .map_err_info(|err| { + .inspect_err_info(|err| { if !matches!( err.kind(), InterpErrorKind::UndefinedBehavior(ValidationError { .. }) @@ -1614,9 +1613,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // during validation. | InterpErrorKind::MachineStop(_) ) { - bug!("Unexpected error during validation: {}", format_interp_error(err)); + bug!("Unexpected error during validation: {}", err.to_string()); } - err }) } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index e2c67b29943d6..3a1954065a829 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -218,6 +218,17 @@ impl<'tcx> InterpErrorInfo<'tcx> { pub fn kind(&self) -> &InterpErrorKind<'tcx> { &self.0.kind } + + /// Turn the given error into a human-readable string. Expects the string to be printed, so if + /// `RUSTC_CTFE_BACKTRACE` is set this will show a backtrace of the rustc internals that + /// triggered the error. + /// + /// This is NOT the preferred way to render an error; use `report` from `const_eval` instead. + /// However, this is useful when error messages appear in ICEs. + pub fn to_string(&self) -> String { + self.0.backtrace.print_backtrace(); + self.0.kind.to_string() + } } fn print_backtrace(backtrace: &Backtrace) { @@ -1043,14 +1054,6 @@ impl<'tcx, T> InterpResult<'tcx, T> { InterpResult::new(self.disarm().map(f)) } - #[inline] - pub fn map_err_info( - self, - f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>, - ) -> InterpResult<'tcx, T> { - InterpResult::new(self.disarm().map_err(f)) - } - #[inline] pub fn map_err_kind( self, @@ -1063,8 +1066,8 @@ impl<'tcx, T> InterpResult<'tcx, T> { } #[inline] - pub fn inspect_err_kind(self, f: impl FnOnce(&InterpErrorKind<'tcx>)) -> InterpResult<'tcx, T> { - InterpResult::new(self.disarm().inspect_err(|e| f(&e.0.kind))) + pub fn inspect_err_info(self, f: impl FnOnce(&InterpErrorInfo<'tcx>)) -> InterpResult<'tcx, T> { + InterpResult::new(self.disarm().inspect_err(f)) } #[inline] diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index e762821724231..b865323e50b6c 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -6,9 +6,7 @@ use std::fmt::Debug; use rustc_abi::{BackendRepr, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx}; use rustc_const_eval::const_eval::DummyMachine; -use rustc_const_eval::interpret::{ - ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, interp_ok, -}; +use rustc_const_eval::interpret::{ImmTy, InterpCx, InterpResult, Projectable, Scalar, interp_ok}; use rustc_data_structures::fx::FxHashSet; use rustc_hir::HirId; use rustc_hir::def::DefKind; @@ -233,7 +231,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { f(self) - .map_err_info(|err| { + .inspect_err_info(|err| { trace!("InterpCx operation failed: {:?}", err); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, @@ -241,9 +239,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { assert!( !err.kind().formatted_string(), "known panics lint encountered formatting error: {}", - format_interp_error(err), + err.to_string(), ); - err }) .discard_err() } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 4f853d5ca02ff..d4fe89d4f0258 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -374,7 +374,7 @@ pub fn report_result<'tcx>( ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) bug!( "This validation error should be impossible in Miri: {}", - format_interp_error(res) + res.to_string() ); } UndefinedBehavior(_) => "Undefined Behavior", @@ -391,7 +391,7 @@ pub fn report_result<'tcx>( ) => "post-monomorphization error", _ => { ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) - bug!("This error should be impossible in Miri: {}", format_interp_error(res)); + bug!("This error should be impossible in Miri: {}", res.to_string()); } }; #[rustfmt::skip] @@ -468,7 +468,7 @@ pub fn report_result<'tcx>( if let Some(title) = title { write!(primary_msg, "{title}: ").unwrap(); } - write!(primary_msg, "{}", format_interp_error(res)).unwrap(); + write!(primary_msg, "{}", res.to_string()).unwrap(); if labels.is_empty() { labels.push(format!( From a61412069d0fbddffa223faef5212e3c796d47ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 11 Mar 2026 23:37:14 +0000 Subject: [PATCH 07/37] Account for ownership mismatch on argument that doesn't meet bound ``` error[E0277]: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied --> $DIR/ownership-mismatch-on-arg.rs:42:13 | LL | foo(hi, hi, hi); | ^^^ -- -- `needs_borrow::Hello` doesn't satisfy the trait bound | | | | | `needs_borrow::Hello` doesn't satisfy the trait bound | unsatisfied trait bound | help: the trait `needs_borrow::Tr` is not implemented for `needs_borrow::Hello` --> $DIR/ownership-mismatch-on-arg.rs:27:5 | LL | struct Hello; | ^^^^^^^^^^^^ help: the trait `needs_borrow::Tr` is implemented for `&needs_borrow::Hello` --> $DIR/ownership-mismatch-on-arg.rs:30:5 | LL | impl Tr for &Hello {} | ^^^^^^^^^^^^^^^^^^ note: required by a bound in `needs_borrow::foo` --> $DIR/ownership-mismatch-on-arg.rs:32:15 | LL | fn foo(_v: T, _w: T, _k: K) {} | ^^ required by this bound in `foo` help: consider borrowing these argument | LL | foo(&hi, &hi, hi); | + + ``` --- .../src/error_reporting/traits/suggestions.rs | 256 +++++++++++++----- .../self-mapping-arguments-errors.stderr | 15 +- .../as_expression.current.stderr | 4 +- ...d-intrinsic-monomorphization-bounds.stderr | 5 +- .../trait-bounds/ownership-mismatch-on-arg.rs | 47 ++++ .../ownership-mismatch-on-arg.stderr | 96 +++++++ 6 files changed, 353 insertions(+), 70 deletions(-) create mode 100644 tests/ui/trait-bounds/ownership-mismatch-on-arg.rs create mode 100644 tests/ui/trait-bounds/ownership-mismatch-on-arg.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 8c72f0d90bb58..7301f29b0fd7f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1643,85 +1643,215 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.predicate_must_hold_modulo_regions(&obligation) }; + let trait_pred_and_imm_ref = poly_trait_pred.map_bound(|p| { + (p, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty())) + }); + let trait_pred_and_mut_ref = poly_trait_pred.map_bound(|p| { + (p, Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty())) + }); + + let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); + let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); + let code = match obligation.cause.code() { ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code, // FIXME(compiler-errors): This is kind of a mess, but required for obligations // that come from a path expr to affect the *call* expr. - c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _) + c @ ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) if self.tcx.hir_span(*hir_id).lo() == span.lo() => { // `hir_id` corresponds to the HIR node that introduced a `where`-clause obligation. - // If that obligation comes from a type in an associated method call, we need - // special handling here. - if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id) - && let hir::ExprKind::Call(base, _) = expr.kind - && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, segment)) = base.kind - && let hir::Node::Expr(outer) = self.tcx.parent_hir_node(expr.hir_id) - && let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mtbl, _) = outer.kind - && ty.span == span - { - // We've encountered something like `&str::from("")`, where the intended code - // was likely `<&str>::from("")`. The former is interpreted as "call method - // `from` on `str` and borrow the result", while the latter means "call method - // `from` on `&str`". - - let trait_pred_and_imm_ref = poly_trait_pred.map_bound(|p| { - (p, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty())) - }); - let trait_pred_and_mut_ref = poly_trait_pred.map_bound(|p| { - (p, Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty())) - }); + if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id) { + // If that obligation comes from a type in an associated method call, we need + // special handling here. + if let hir::ExprKind::Call(base, _) = expr.kind + && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, segment)) = + base.kind + && let hir::Node::Expr(outer) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mtbl, _) = outer.kind + && ty.span == span + { + // We've encountered something like `&str::from("")`, where the intended code + // was likely `<&str>::from("")`. The former is interpreted as "call method + // `from` on `str` and borrow the result", while the latter means "call method + // `from` on `&str`". - let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); - let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); - let sugg_msg = |pre: &str| { - format!( - "you likely meant to call the associated function `{FN}` for type \ - `&{pre}{TY}`, but the code as written calls associated function `{FN}` on \ - type `{TY}`", - FN = segment.ident, - TY = poly_trait_pred.self_ty(), - ) - }; - match (imm_ref_self_ty_satisfies_pred, mut_ref_self_ty_satisfies_pred, mtbl) { - (true, _, hir::Mutability::Not) | (_, true, hir::Mutability::Mut) => { - err.multipart_suggestion( - sugg_msg(mtbl.prefix_str()), - vec![ - (outer.span.shrink_to_lo(), "<".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, - ); + let sugg_msg = |pre: &str| { + format!( + "you likely meant to call the associated function `{FN}` for type \ + `&{pre}{TY}`, but the code as written calls associated function `{FN}` on \ + type `{TY}`", + FN = segment.ident, + TY = poly_trait_pred.self_ty(), + ) + }; + match (imm_ref_self_ty_satisfies_pred, mut_ref_self_ty_satisfies_pred, mtbl) + { + (true, _, hir::Mutability::Not) | (_, true, hir::Mutability::Mut) => { + err.multipart_suggestion( + sugg_msg(mtbl.prefix_str()), + vec![ + (outer.span.shrink_to_lo(), "<".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } + (true, _, hir::Mutability::Mut) => { + // There's an associated function found on the immutable borrow of the + err.multipart_suggestion( + sugg_msg("mut "), + vec![ + (outer.span.shrink_to_lo().until(span), "<&".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } + (_, true, hir::Mutability::Not) => { + err.multipart_suggestion( + sugg_msg(""), + vec![ + ( + outer.span.shrink_to_lo().until(span), + "<&mut ".to_string(), + ), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } + _ => {} } - (true, _, hir::Mutability::Mut) => { - // There's an associated function found on the immutable borrow of the - err.multipart_suggestion( - sugg_msg("mut "), - vec![ - (outer.span.shrink_to_lo().until(span), "<&".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, + // If we didn't return early here, we would instead suggest `&&str::from("")`. + return false; + } else if let hir::ExprKind::Call(_, args) = expr.kind { + if let Some(typeck_results) = &self.typeck_results + && let Some(pred) = self + .tcx + .predicates_of(*def_id) + .instantiate_identity(self.tcx) + .predicates + .into_iter() + .nth(*idx) + && let Some(pred) = pred.as_trait_clause() + // This feature allows for `for T: Trait`, which fails + // `instantiate_bound_regions_with_erased`. Avoid suggesting for now. + && !self.tcx.features().non_lifetime_binders() + { + let pred_ty = self.tcx.instantiate_bound_regions_with_erased( + pred.self_ty().skip_norm_wip(), ); - } - (_, true, hir::Mutability::Not) => { - err.multipart_suggestion( - sugg_msg(""), - vec![ - (outer.span.shrink_to_lo().until(span), "<&mut ".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, + let fn_sig = self.tcx.instantiate_bound_regions_with_erased( + self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(), ); + + let mut spans = vec![]; + for (arg, input) in args.into_iter().zip(fn_sig.inputs()) { + if let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(arg) { + let ty = self.tcx.instantiate_bound_regions_with_erased( + poly_trait_pred.self_ty(), + ); + let pred_has_arg_type = + self.infcx.can_eq(param_env, arg_ty, ty); + let arg_is_type_param = + self.infcx.can_eq(param_env, pred_ty, *input); + if pred_has_arg_type && arg_is_type_param { + err.span_label( + arg.span, + format!("`{arg_ty}` doesn't satisfy the trait bound"), + ); + spans.push(arg.span); + } + } + } + let this = pluralize!("this", spans.len()); + if !spans.is_empty() { + if imm_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider borrowing {this} argument"), + spans + .iter() + .map(|sp| (sp.shrink_to_lo(), "&".into())) + .collect(), + Applicability::MaybeIncorrect, + ); + } + if mut_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider mutably borrowing {this} argument"), + spans + .iter() + .map(|sp| (sp.shrink_to_lo(), "&mut ".into())) + .collect(), + Applicability::MaybeIncorrect, + ); + } + return false; + } } - _ => {} } - // If we didn't return early here, we would instead suggest `&&str::from("")`. - return false; } c } + ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) + if let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) + && let hir::ExprKind::MethodCall(_segment, rcvr, args, ..) = expr.kind + && let Some(typeck_results) = &self.typeck_results + && let Some(pred) = self + .tcx + .predicates_of(*def_id) + .instantiate_identity(self.tcx) + .predicates + .into_iter() + .nth(*idx) + && let Some(pred) = pred.as_trait_clause() + // This feature allows for `for T: Trait`, which fails + // `instantiate_bound_regions_with_erased`. Avoid suggesting for now. + && !self.tcx.features().non_lifetime_binders() => + { + // We've got a method call where likely one of the arguments didn't meet a bound. + let pred_ty = + self.tcx.instantiate_bound_regions_with_erased(pred.self_ty().skip_norm_wip()); + let fn_sig = self.tcx.instantiate_bound_regions_with_erased( + self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(), + ); + + let mut spans = vec![]; + for (arg, input) in [rcvr].into_iter().chain(args.into_iter()).zip(fn_sig.inputs()) + { + let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(arg) else { continue }; + let ty = + self.tcx.instantiate_bound_regions_with_erased(poly_trait_pred.self_ty()); + let pred_has_arg_type = self.infcx.can_eq(param_env, arg_ty, ty); + let arg_is_type_param = self.infcx.can_eq(param_env, pred_ty, *input); + if pred_has_arg_type && arg_is_type_param { + err.span_label( + arg.span, + format!("`{arg_ty}` doesn't satisfy the trait bound"), + ); + spans.push(arg.span); + } + } + let this = pluralize!("this", spans.len()); + if !spans.is_empty() { + if imm_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider borrowing {this} argument"), + spans.iter().map(|sp| (sp.shrink_to_lo(), "&".into())).collect(), + Applicability::MaybeIncorrect, + ); + } + if mut_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider mutably borrowing {this} argument"), + spans.iter().map(|sp| (sp.shrink_to_lo(), "&mut ".into())).collect(), + Applicability::MaybeIncorrect, + ); + } + } + return false; + } c if matches!( span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(DesugaringKind::ForLoop) diff --git a/tests/ui/delegation/self-mapping-arguments-errors.stderr b/tests/ui/delegation/self-mapping-arguments-errors.stderr index a508721e68640..cd38ad84aeabf 100644 --- a/tests/ui/delegation/self-mapping-arguments-errors.stderr +++ b/tests/ui/delegation/self-mapping-arguments-errors.stderr @@ -22,11 +22,16 @@ LL | | } error[E0277]: the trait bound `(): target_expr_doesnt_relower_when_defs_inside::MyAdd` is not satisfied --> $DIR/self-mapping-arguments-errors.rs:14:5 | -LL | / reuse impl MyAdd for W { -... | -LL | | self.0 -LL | | } - | |_____^ the trait `target_expr_doesnt_relower_when_defs_inside::MyAdd` is not implemented for `()` +LL | reuse impl MyAdd for W { + | _____^ - + | |____________________________| +... || +LL | || self.0 +LL | || } + | || ^ + | ||_____| + | |_____`{type error}` doesn't satisfy the trait bound + | the trait `target_expr_doesnt_relower_when_defs_inside::MyAdd` is not implemented for `()` | help: the following other types implement trait `target_expr_doesnt_relower_when_defs_inside::MyAdd` --> $DIR/self-mapping-arguments-errors.rs:8:5 diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr index 0cb117d3fc4c3..fb5a3e2172da5 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr @@ -16,7 +16,9 @@ error[E0277]: the trait bound `X: A` is not satisfied --> $DIR/as_expression.rs:60:15 | LL | X.start().foo().finish(); - | ^^^ unsatisfied trait bound + | --------- ^^^ unsatisfied trait bound + | | + | `X` doesn't satisfy the trait bound | help: the trait `A` is not implemented for `X` --> $DIR/as_expression.rs:70:1 diff --git a/tests/ui/intrinsics/bad-intrinsic-monomorphization-bounds.stderr b/tests/ui/intrinsics/bad-intrinsic-monomorphization-bounds.stderr index db2a222c8f2c4..148e6ea1e6aca 100644 --- a/tests/ui/intrinsics/bad-intrinsic-monomorphization-bounds.stderr +++ b/tests/ui/intrinsics/bad-intrinsic-monomorphization-bounds.stderr @@ -2,7 +2,10 @@ error[E0277]: the trait bound `Foo: intrinsics::bounds::FloatPrimitive` is not s --> $DIR/bad-intrinsic-monomorphization-bounds.rs:16:5 | LL | intrinsics::fadd_fast(a, b) - | ^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | ^^^^^^^^^^^^^^^^^^^^^ - - `Foo` doesn't satisfy the trait bound + | | | + | | `Foo` doesn't satisfy the trait bound + | unsatisfied trait bound | help: the nightly-only, unstable trait `intrinsics::bounds::FloatPrimitive` is not implemented for `Foo` --> $DIR/bad-intrinsic-monomorphization-bounds.rs:13:1 diff --git a/tests/ui/trait-bounds/ownership-mismatch-on-arg.rs b/tests/ui/trait-bounds/ownership-mismatch-on-arg.rs new file mode 100644 index 0000000000000..a3d3b8a9a0826 --- /dev/null +++ b/tests/ui/trait-bounds/ownership-mismatch-on-arg.rs @@ -0,0 +1,47 @@ +// #134805 +mod needs_deref { + #[derive(Clone, Copy, Debug)] + struct Hello; + + trait Tr: Clone + Copy {} + impl Tr for Hello {} + + fn foo(_v: T, _w: T, _k: K) {} + + struct S; + impl S { + fn foo(&self, _v: T, _w: T, _k: K) {} + } + + fn bar() { + let hellos = [Hello; 3]; + for hi in hellos.iter() { + foo(hi, hi, hi); //~ ERROR: the trait bound `&needs_deref::Hello: needs_deref::Tr` is not satisfied + S.foo(hi, hi, hi); //~ ERROR: the trait bound `&needs_deref::Hello: needs_deref::Tr` is not satisfied + } + } +} + +mod needs_borrow { + #[derive(Clone, Copy, Debug)] + struct Hello; + + trait Tr: Clone + Copy {} + impl Tr for &Hello {} + + fn foo(_v: T, _w: T, _k: K) {} + + struct S; + impl S { + fn foo(&self, _v: T, _w: T, _k: K) {} + } + + fn bar() { + let hellos = [Hello; 3]; + for hi in hellos { + foo(hi, hi, hi); //~ ERROR: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied + S.foo(hi, hi, hi); //~ ERROR: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied + } + } +} +fn main() {} diff --git a/tests/ui/trait-bounds/ownership-mismatch-on-arg.stderr b/tests/ui/trait-bounds/ownership-mismatch-on-arg.stderr new file mode 100644 index 0000000000000..0d1f944dacfe8 --- /dev/null +++ b/tests/ui/trait-bounds/ownership-mismatch-on-arg.stderr @@ -0,0 +1,96 @@ +error[E0277]: the trait bound `&needs_deref::Hello: needs_deref::Tr` is not satisfied + --> $DIR/ownership-mismatch-on-arg.rs:19:13 + | +LL | foo(hi, hi, hi); + | ^^^ -- -- `&needs_deref::Hello` doesn't satisfy the trait bound + | | | + | | `&needs_deref::Hello` doesn't satisfy the trait bound + | the trait `needs_deref::Tr` is not implemented for `&needs_deref::Hello` + | +note: required by a bound in `needs_deref::foo` + --> $DIR/ownership-mismatch-on-arg.rs:9:15 + | +LL | fn foo(_v: T, _w: T, _k: K) {} + | ^^ required by this bound in `foo` + +error[E0277]: the trait bound `&needs_deref::Hello: needs_deref::Tr` is not satisfied + --> $DIR/ownership-mismatch-on-arg.rs:20:15 + | +LL | S.foo(hi, hi, hi); + | ^^^ -- -- `&needs_deref::Hello` doesn't satisfy the trait bound + | | | + | | `&needs_deref::Hello` doesn't satisfy the trait bound + | the trait `needs_deref::Tr` is not implemented for `&needs_deref::Hello` + | +help: the trait `needs_deref::Tr` is implemented for `needs_deref::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:7:5 + | +LL | impl Tr for Hello {} + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `needs_deref::S::foo` + --> $DIR/ownership-mismatch-on-arg.rs:13:39 + | +LL | fn foo(&self, _v: T, _w: T, _k: K) {} + | ^^ required by this bound in `S::foo` + +error[E0277]: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied + --> $DIR/ownership-mismatch-on-arg.rs:42:13 + | +LL | foo(hi, hi, hi); + | ^^^ -- -- `needs_borrow::Hello` doesn't satisfy the trait bound + | | | + | | `needs_borrow::Hello` doesn't satisfy the trait bound + | unsatisfied trait bound + | +help: the trait `needs_borrow::Tr` is not implemented for `needs_borrow::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:27:5 + | +LL | struct Hello; + | ^^^^^^^^^^^^ +help: the trait `needs_borrow::Tr` is implemented for `&needs_borrow::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:30:5 + | +LL | impl Tr for &Hello {} + | ^^^^^^^^^^^^^^^^^^ +note: required by a bound in `needs_borrow::foo` + --> $DIR/ownership-mismatch-on-arg.rs:32:15 + | +LL | fn foo(_v: T, _w: T, _k: K) {} + | ^^ required by this bound in `foo` +help: consider borrowing these argument + | +LL | foo(&hi, &hi, hi); + | + + + +error[E0277]: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied + --> $DIR/ownership-mismatch-on-arg.rs:43:15 + | +LL | S.foo(hi, hi, hi); + | ^^^ -- -- `needs_borrow::Hello` doesn't satisfy the trait bound + | | | + | | `needs_borrow::Hello` doesn't satisfy the trait bound + | unsatisfied trait bound + | +help: the trait `needs_borrow::Tr` is not implemented for `needs_borrow::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:27:5 + | +LL | struct Hello; + | ^^^^^^^^^^^^ +help: the trait `needs_borrow::Tr` is implemented for `&needs_borrow::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:30:5 + | +LL | impl Tr for &Hello {} + | ^^^^^^^^^^^^^^^^^^ +note: required by a bound in `needs_borrow::S::foo` + --> $DIR/ownership-mismatch-on-arg.rs:36:19 + | +LL | fn foo(&self, _v: T, _w: T, _k: K) {} + | ^^ required by this bound in `S::foo` +help: consider borrowing these argument + | +LL | S.foo(&hi, &hi, hi); + | + + + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. From 1e1a6990917f1f0292e35387659a3260e99f09d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 29 Jul 2026 08:47:59 +0000 Subject: [PATCH 08/37] deduplicate method/function call "point at arg" logic --- .../src/error_reporting/traits/suggestions.rs | 152 ++++++++---------- 1 file changed, 65 insertions(+), 87 deletions(-) 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 7301f29b0fd7f..75937ff5531b5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1653,6 +1653,44 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); + let mut point_at_relevant_args = + |pred_ty: Ty<'tcx>, args_and_inputs: Vec<(hir::Expr<'_>, Ty<'tcx>)>| { + let Some(typeck_results) = &self.typeck_results else { return false }; + + let erased_self_ty = + self.tcx.instantiate_bound_regions_with_erased(poly_trait_pred.self_ty()); + let mut spans = vec![]; + for (arg, input) in args_and_inputs { + let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(&arg) else { continue }; + let pred_has_arg_type = self.infcx.can_eq(param_env, arg_ty, erased_self_ty); + let arg_is_type_param = self.infcx.can_eq(param_env, pred_ty, input); + if pred_has_arg_type && arg_is_type_param { + err.span_label( + arg.span, + format!("`{arg_ty}` doesn't satisfy the trait bound"), + ); + spans.push(arg.span); + } + } + let this = pluralize!("this", spans.len()); + if !spans.is_empty() { + if imm_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider borrowing {this} argument"), + spans.iter().map(|sp| (sp.shrink_to_lo(), "&".into())).collect(), + Applicability::MaybeIncorrect, + ); + } + if mut_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider mutably borrowing {this} argument"), + spans.iter().map(|sp| (sp.shrink_to_lo(), "&mut ".into())).collect(), + Applicability::MaybeIncorrect, + ); + } + } + !spans.is_empty() + }; let code = match obligation.cause.code() { ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code, // FIXME(compiler-errors): This is kind of a mess, but required for obligations @@ -1726,12 +1764,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // If we didn't return early here, we would instead suggest `&&str::from("")`. return false; } else if let hir::ExprKind::Call(_, args) = expr.kind { - if let Some(typeck_results) = &self.typeck_results - && let Some(pred) = self + if let Some(pred) = self .tcx - .predicates_of(*def_id) + .clauses_of(*def_id) .instantiate_identity(self.tcx) - .predicates + .clauses .into_iter() .nth(*idx) && let Some(pred) = pred.as_trait_clause() @@ -1745,48 +1782,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let fn_sig = self.tcx.instantiate_bound_regions_with_erased( self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(), ); - - let mut spans = vec![]; - for (arg, input) in args.into_iter().zip(fn_sig.inputs()) { - if let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(arg) { - let ty = self.tcx.instantiate_bound_regions_with_erased( - poly_trait_pred.self_ty(), - ); - let pred_has_arg_type = - self.infcx.can_eq(param_env, arg_ty, ty); - let arg_is_type_param = - self.infcx.can_eq(param_env, pred_ty, *input); - if pred_has_arg_type && arg_is_type_param { - err.span_label( - arg.span, - format!("`{arg_ty}` doesn't satisfy the trait bound"), - ); - spans.push(arg.span); - } - } - } - let this = pluralize!("this", spans.len()); - if !spans.is_empty() { - if imm_ref_self_ty_satisfies_pred { - err.multipart_suggestion( - format!("consider borrowing {this} argument"), - spans - .iter() - .map(|sp| (sp.shrink_to_lo(), "&".into())) - .collect(), - Applicability::MaybeIncorrect, - ); - } - if mut_ref_self_ty_satisfies_pred { - err.multipart_suggestion( - format!("consider mutably borrowing {this} argument"), - spans - .iter() - .map(|sp| (sp.shrink_to_lo(), "&mut ".into())) - .collect(), - Applicability::MaybeIncorrect, - ); - } + if point_at_relevant_args( + pred_ty, + args.into_iter() + .zip(fn_sig.inputs()) + .map(|(e, t)| (*e, *t)) + .collect(), + ) { return false; } } @@ -1794,15 +1796,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } c } - ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) + c @ ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) if let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) && let hir::ExprKind::MethodCall(_segment, rcvr, args, ..) = expr.kind - && let Some(typeck_results) = &self.typeck_results && let Some(pred) = self .tcx - .predicates_of(*def_id) + .clauses_of(*def_id) .instantiate_identity(self.tcx) - .predicates + .clauses .into_iter() .nth(*idx) && let Some(pred) = pred.as_trait_clause() @@ -1810,47 +1811,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // `instantiate_bound_regions_with_erased`. Avoid suggesting for now. && !self.tcx.features().non_lifetime_binders() => { - // We've got a method call where likely one of the arguments didn't meet a bound. - let pred_ty = - self.tcx.instantiate_bound_regions_with_erased(pred.self_ty().skip_norm_wip()); let fn_sig = self.tcx.instantiate_bound_regions_with_erased( self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(), ); - - let mut spans = vec![]; - for (arg, input) in [rcvr].into_iter().chain(args.into_iter()).zip(fn_sig.inputs()) - { - let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(arg) else { continue }; - let ty = - self.tcx.instantiate_bound_regions_with_erased(poly_trait_pred.self_ty()); - let pred_has_arg_type = self.infcx.can_eq(param_env, arg_ty, ty); - let arg_is_type_param = self.infcx.can_eq(param_env, pred_ty, *input); - if pred_has_arg_type && arg_is_type_param { - err.span_label( - arg.span, - format!("`{arg_ty}` doesn't satisfy the trait bound"), - ); - spans.push(arg.span); - } - } - let this = pluralize!("this", spans.len()); - if !spans.is_empty() { - if imm_ref_self_ty_satisfies_pred { - err.multipart_suggestion( - format!("consider borrowing {this} argument"), - spans.iter().map(|sp| (sp.shrink_to_lo(), "&".into())).collect(), - Applicability::MaybeIncorrect, - ); - } - if mut_ref_self_ty_satisfies_pred { - err.multipart_suggestion( - format!("consider mutably borrowing {this} argument"), - spans.iter().map(|sp| (sp.shrink_to_lo(), "&mut ".into())).collect(), - Applicability::MaybeIncorrect, - ); - } + // We've got a method call where likely one of the arguments didn't meet a bound. + let pred_ty = + self.tcx.instantiate_bound_regions_with_erased(pred.self_ty().skip_norm_wip()); + if point_at_relevant_args( + pred_ty, + [rcvr] + .into_iter() + .chain(args.into_iter()) + .zip(fn_sig.inputs()) + .map(|(e, t)| (*e, *t)) + .collect(), + ) { + return false; } - return false; + c } c if matches!( span.ctxt().outer_expn_data().kind, From 1d77f8a61e245f45c13a8fe863cc7521dc59b818 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 29 Jul 2026 18:21:29 +0200 Subject: [PATCH 09/37] reject varargs without pattern post-expansion --- .../rustc_ast_passes/src/ast_validation.rs | 7 + compiler/rustc_ast_passes/src/diagnostics.rs | 12 ++ ...-varargs-without-pattern-post-expansion.rs | 55 +++++ ...args-without-pattern-post-expansion.stderr | 191 ++++++++++++++++++ tests/ui/thir-print/c-variadic.rs | 4 +- tests/ui/thir-print/c-variadic.stderr | 12 -- tests/ui/thir-print/c-variadic.stdout | 14 +- 7 files changed, 273 insertions(+), 22 deletions(-) create mode 100644 tests/ui/c-variadic/reject-varargs-without-pattern-post-expansion.rs create mode 100644 tests/ui/c-variadic/reject-varargs-without-pattern-post-expansion.stderr delete mode 100644 tests/ui/thir-print/c-variadic.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0e47424ba1aa4..587f950a67720 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -929,6 +929,13 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, FnCtxt::Free | FnCtxt::Assoc(_) => { + // Reject `...` without a pattern post-expansion. The varargs_without_pattern + // FCW is already triggered pre-expansion. + if let PatKind::Missing = variadic_param.pat.kind { + self.dcx() + .emit_err(diagnostics::VarargsWithoutPattern { span: variadic_param.span }); + } + match self.sess.target.supports_c_variadic_definitions() { CVariadicStatus::NotSupported => { self.dcx().emit_err(diagnostics::CVariadicNotSupported { diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index af22ead1332d1..0ed0c2095808c 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -1243,3 +1243,15 @@ pub(crate) enum DeprecatedWhereClauseLocationSugg { span: Span, }, } + +#[derive(Diagnostic)] +#[diag("missing pattern for `...` argument")] +pub(crate) struct VarargsWithoutPattern { + #[suggestion( + "add a pattern for this argument", + applicability = "machine-applicable", + code = "_: ..." + )] + #[primary_span] + pub span: Span, +} diff --git a/tests/ui/c-variadic/reject-varargs-without-pattern-post-expansion.rs b/tests/ui/c-variadic/reject-varargs-without-pattern-post-expansion.rs new file mode 100644 index 0000000000000..d68cf940290bf --- /dev/null +++ b/tests/ui/c-variadic/reject-varargs-without-pattern-post-expansion.rs @@ -0,0 +1,55 @@ +#![crate_type = "lib"] +#![warn(varargs_without_pattern)] + +// Test that we reject a bare `...` without a pattern post-expansion in function definitons and +// trait method declarations. On foreign function declarations it is allowed. +// +// We have the `varargs_without_pattern` FCW for this idiom, with the intent to eventually also +// reject this idiom pre-expansion. + +// Bare `...` is allowed in extern blocks. +extern "C" { + fn g(...); +} + +// When the `...` argument does not make it past expansion, that only lints. +macro_rules! discard_item { + ($item:item) => {}; +} + +discard_item! { + unsafe extern "C" fn f(...) -> i32 { + //~^ WARN missing pattern for `...` argument + //~| WARN this was previously accepted by the compiler but is being phased out + 0 + } +} + +// But when it does make it post-expansion, that is a hard error. +macro_rules! identity_item { + ($item:item) => { + $item + }; +} + +identity_item! { + unsafe extern "C" fn f(...) {} + //~^ ERROR missing pattern for `...` argument + //~| WARN missing pattern for `...` argument + //~| WARN this was previously accepted by the compiler but is being phased out + //~| WARN missing pattern for `...` argument + //~| WARN this was previously accepted by the compiler but is being phased out +} + +trait T { + identity_item! { + unsafe extern "C" fn f(...); + //~^ ERROR missing pattern for `...` argument + //~| WARN missing pattern for `...` argument + //~| WARN this was previously accepted by the compiler but is being phased out + //~| WARN missing pattern for `...` argument + //~| WARN this was previously accepted by the compiler but is being phased out + //~| WARN anonymous_parameters + //~| WARN this is accepted in the current edition (Rust 2015) + } +} diff --git a/tests/ui/c-variadic/reject-varargs-without-pattern-post-expansion.stderr b/tests/ui/c-variadic/reject-varargs-without-pattern-post-expansion.stderr new file mode 100644 index 0000000000000..f9243c97f2522 --- /dev/null +++ b/tests/ui/c-variadic/reject-varargs-without-pattern-post-expansion.stderr @@ -0,0 +1,191 @@ +error: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:36:28 + | +LL | unsafe extern "C" fn f(...) {} + | ^^^ help: add a pattern for this argument: `_: ...` + +error: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:46:32 + | +LL | unsafe extern "C" fn f(...); + | ^^^ help: add a pattern for this argument: `_: ...` + +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:21:28 + | +LL | unsafe extern "C" fn f(...) -> i32 { + | ^^^ + | + = 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 #145544 +note: the lint level is defined here + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:2:9 + | +LL | #![warn(varargs_without_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...) -> i32 { + | ++ + +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:36:28 + | +LL | unsafe extern "C" fn f(...) {} + | ^^^ + | + = 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 #145544 +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...) {} + | ++ + +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:36:28 + | +LL | unsafe extern "C" fn f(...) {} + | ^^^ + | + = 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 #145544 + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...) {} + | ++ + +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:46:32 + | +LL | unsafe extern "C" fn f(...); + | ^^^ + | + = 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 #145544 +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...); + | ++ + +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:46:32 + | +LL | unsafe extern "C" fn f(...); + | ^^^ + | + = 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 #145544 + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...); + | ++ + +warning: anonymous parameters are deprecated and will be removed in the next edition + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:46:32 + | +LL | unsafe extern "C" fn f(...); + | ^^^ help: try naming the parameter or explicitly ignoring it: `_: ...` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see + = note: `#[warn(anonymous_parameters)]` (part of `#[warn(rust_2018_compatibility)]`) on by default + +error: aborting due to 2 previous errors; 6 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:21:28 + | +LL | unsafe extern "C" fn f(...) -> i32 { + | ^^^ + | + = 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 #145544 +note: the lint level is defined here + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:2:9 + | +LL | #![warn(varargs_without_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...) -> i32 { + | ++ + +Future breakage diagnostic: +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:36:28 + | +LL | unsafe extern "C" fn f(...) {} + | ^^^ + | + = 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 #145544 +note: the lint level is defined here + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:2:9 + | +LL | #![warn(varargs_without_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...) {} + | ++ + +Future breakage diagnostic: +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:36:28 + | +LL | unsafe extern "C" fn f(...) {} + | ^^^ + | + = 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 #145544 +note: the lint level is defined here + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:2:9 + | +LL | #![warn(varargs_without_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...) {} + | ++ + +Future breakage diagnostic: +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:46:32 + | +LL | unsafe extern "C" fn f(...); + | ^^^ + | + = 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 #145544 +note: the lint level is defined here + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:2:9 + | +LL | #![warn(varargs_without_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...); + | ++ + +Future breakage diagnostic: +warning: missing pattern for `...` argument + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:46:32 + | +LL | unsafe extern "C" fn f(...); + | ^^^ + | + = 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 #145544 +note: the lint level is defined here + --> $DIR/reject-varargs-without-pattern-post-expansion.rs:2:9 + | +LL | #![warn(varargs_without_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: name the argument, or use `_` to continue ignoring it + | +LL | unsafe extern "C" fn f(_: ...); + | ++ + diff --git a/tests/ui/thir-print/c-variadic.rs b/tests/ui/thir-print/c-variadic.rs index b07c422ea3cd4..2dbf2d5179190 100644 --- a/tests/ui/thir-print/c-variadic.rs +++ b/tests/ui/thir-print/c-variadic.rs @@ -1,6 +1,4 @@ //@ compile-flags: -Zunpretty=thir-tree --crate-type=lib //@ check-pass -#![expect(varargs_without_pattern)] -// The `...` argument uses `PatKind::Missing`. -unsafe extern "C" fn foo(_: i32, ...) {} +unsafe extern "C" fn foo(_: i32, _: ...) {} diff --git a/tests/ui/thir-print/c-variadic.stderr b/tests/ui/thir-print/c-variadic.stderr deleted file mode 100644 index e05e50a93f57d..0000000000000 --- a/tests/ui/thir-print/c-variadic.stderr +++ /dev/null @@ -1,12 +0,0 @@ -Future incompatibility report: Future breakage diagnostic: -warning: missing pattern for `...` argument - --> $DIR/c-variadic.rs:6:34 - | -LL | unsafe extern "C" fn foo(_: i32, ...) {} - | ^^^ - | -help: name the argument, or use `_` to continue ignoring it - | -LL | unsafe extern "C" fn foo(_: i32, _: ...) {} - | ++ - diff --git a/tests/ui/thir-print/c-variadic.stdout b/tests/ui/thir-print/c-variadic.stdout index ad6dacb4753b3..466825e4dc116 100644 --- a/tests/ui/thir-print/c-variadic.stdout +++ b/tests/ui/thir-print/c-variadic.stdout @@ -2,13 +2,13 @@ DefId(0:3 ~ c_variadic[a5de]::foo): params: [ Param { ty: i32 - ty_span: Some($DIR/c-variadic.rs:6:29: 6:32 (#0)) + ty_span: Some($DIR/c-variadic.rs:4:29: 4:32 (#0)) self_kind: None hir_id: Some(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).1)) param: Some( Pat { ty: i32 - span: $DIR/c-variadic.rs:6:26: 6:27 (#0) + span: $DIR/c-variadic.rs:4:26: 4:27 (#0) kind: PatKind { Wild } @@ -23,9 +23,9 @@ params: [ param: Some( Pat { ty: std::ffi::VaList<'{erased}> - span: $DIR/c-variadic.rs:6:34: 6:37 (#0) + span: $DIR/c-variadic.rs:4:34: 4:35 (#0) kind: PatKind { - Missing + Wild } } ) @@ -35,7 +35,7 @@ body: Expr { ty: () temp_scope_id: 6 - span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + span: $DIR/c-variadic.rs:4:42: 4:44 (#0) kind: Scope { region_scope: Node(6) @@ -44,11 +44,11 @@ body: Expr { ty: () temp_scope_id: 6 - span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + span: $DIR/c-variadic.rs:4:42: 4:44 (#0) kind: Block { targeted_by_break: false - span: $DIR/c-variadic.rs:6:39: 6:41 (#0) + span: $DIR/c-variadic.rs:4:42: 4:44 (#0) region_scope: Node(5) safety_mode: Safe stmts: [] From b0811a03bedc2cc80b21595e58dfe72ffb2b7d3b Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Wed, 29 Jul 2026 18:42:13 +0300 Subject: [PATCH 10/37] support `-Ldependency` search paths for panic runtimes --- compiler/rustc_metadata/src/creader.rs | 107 ++++++++---------- .../rustc_metadata/src/dependency_format.rs | 57 +++++----- compiler/rustc_metadata/src/rmeta/decoder.rs | 7 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 1 - compiler/rustc_metadata/src/rmeta/mod.rs | 1 - .../cargo-issue-7359-load-panic/rmake.rs | 3 +- tests/run-make/locate-panic-runtime/rmake.rs | 21 ++-- .../abort-link-to-unwind-dylib.rs | 1 + .../abort-link-to-unwind-dylib.stderr | 4 +- 9 files changed, 92 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index bb91d855feaeb..a3d85f906c538 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -57,7 +57,6 @@ pub struct CStore { metadata_loader: Box, metas: IndexVec>>, - injected_panic_runtime: Option, /// This crate needs an allocator and either provides it itself, or finds it in a dependency. /// If the above is true, then this field denotes the kind of the found allocator. allocator_kind: Option, @@ -291,10 +290,6 @@ impl CStore { deps } - pub(crate) fn injected_panic_runtime(&self) -> Option { - self.injected_panic_runtime - } - pub(crate) fn allocator_kind(&self) -> Option { self.allocator_kind } @@ -541,7 +536,6 @@ impl CStore { // corresponding `CrateNum`. This first entry will always remain // `None`. metas: IndexVec::from_iter(iter::once(None)), - injected_panic_runtime: None, allocator_kind: None, alloc_error_handler_kind: None, has_global_allocator: false, @@ -954,74 +948,69 @@ impl CStore { } fn inject_panic_runtime(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) { - // If we're only compiling an rlib, then there's no need to select a - // panic runtime, so we just skip this section entirely. - let only_rlib = tcx.crate_types().iter().all(|ct| *ct == CrateType::Rlib); - if only_rlib { - info!("panic runtime injection skipped, only generating rlib"); - return; - } - - // If we need a panic runtime, we try to find an existing one here. At - // the same time we perform some general validation of the DAG we've got - // going such as ensuring everything has a compatible panic strategy. - let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime); - for (_cnum, data) in self.iter_crate_data() { - needs_panic_runtime |= data.needs_panic_runtime(); - } - - // If we just don't need a panic runtime at all, then we're done here - // and there's nothing else to do. - if !needs_panic_runtime { + // Panic runtimes are only injected when building std. We don't want to + // inject them as direct dependencies, because Cargo relies on loading + // panic runtimes via `-Ldependency` search paths, as per RFC 3874 + // (build-std=always). + if !attr::contains_name(&krate.attrs, sym::needs_panic_runtime) { return; } - // By this point we know that we need a panic runtime. Here we just load - // an appropriate default runtime for our panic strategy. + // Here we just load `panic_unwind` and `panic_abort`. // // We may resolve to an already loaded crate (as the crate may not have // been explicitly linked prior to this), but this is fine. // // Also note that we have yet to perform validation of the crate graph // in terms of everyone has a compatible panic runtime format, that's - // performed later as part of the `dependency_format` module. - let desired_strategy = tcx.sess.panic_strategy(); - let name = match desired_strategy { - PanicStrategy::Unwind => sym::panic_unwind, - PanicStrategy::Abort => sym::panic_abort, - PanicStrategy::ImmediateAbort => { - // Immediate-aborting panics don't use a runtime. + // performed later as part of the `dependency_format` module along with + // the activation of only one runtime for the desired strategy. + let mut resolve_panic_runtime = |desired_strategy: PanicStrategy| { + let name = match desired_strategy { + PanicStrategy::Unwind => sym::panic_unwind, + PanicStrategy::Abort => sym::panic_abort, + PanicStrategy::ImmediateAbort => unreachable!(), + }; + + info!("loading panic runtime, name = `{}`", name); + + // This has to be conditional as both `panic_unwind` and `panic_abort` may be present in the + // crate graph at the same time. One of them will later be activated in dependency_formats. + let Some(cnum) = self.resolve_crate( + tcx, + name, + DUMMY_SP, + CrateDepKind::Conditional, + CrateOrigin::Injected, + ) else { return; + }; + let cdata = self.get_crate_data(cnum); + + // Sanity check the loaded crate to ensure it is indeed a panic runtime. + if !cdata.is_panic_runtime() { + tcx.dcx().emit_err(diagnostics::CrateNotPanicRuntime { crate_name: name }); } - }; - info!("panic runtime not found -- loading {}", name); - // This has to be conditional as both panic_unwind and panic_abort may be present in the - // crate graph at the same time. One of them will later be activated in dependency_formats. - let Some(cnum) = self.resolve_crate( - tcx, - name, - DUMMY_SP, - CrateDepKind::Conditional, - CrateOrigin::Injected, - ) else { - return; + // Sanity check the panic strategy is indeed what we thought it was. + // Note: Both `panic_unwind` and `panic_abort` might be compiler with + // `ImmediateAbort` strategy. + if cdata.required_panic_strategy() != Some(PanicStrategy::ImmediateAbort) + && cdata.required_panic_strategy() != Some(desired_strategy) + { + tcx.dcx().emit_err(diagnostics::NoPanicStrategy { + crate_name: name, + strategy: desired_strategy, + }); + } }; - let cdata = self.get_crate_data(cnum); - // Sanity check the loaded crate to ensure it is indeed a panic runtime - // and the panic strategy is indeed what we thought it was. - if !cdata.is_panic_runtime() { - tcx.dcx().emit_err(diagnostics::CrateNotPanicRuntime { crate_name: name }); - } - if cdata.required_panic_strategy() != Some(desired_strategy) { - tcx.dcx().emit_err(diagnostics::NoPanicStrategy { - crate_name: name, - strategy: desired_strategy, - }); + // Always resolve `panic_abort` as it is a non-optional dependency of `std`. + resolve_panic_runtime(PanicStrategy::Abort); + // Conditionally resolve `panic_unwind` as it is an optional dependency of `std`. + if tcx.sess.panic_strategy() == PanicStrategy::Unwind { + resolve_panic_runtime(PanicStrategy::Unwind); } - - self.injected_panic_runtime = Some(cnum); } fn inject_profiler_runtime(&mut self, tcx: TyCtxt<'_>) { diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 257b2a9b03884..53bb2ab7234d1 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -64,7 +64,6 @@ use rustc_span::sym; use rustc_target::spec::PanicStrategy; use tracing::info; -use crate::creader::CStore; use crate::diagnostics::{ BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, IncompatibleWithImmediateAbort, IncompatibleWithImmediateAbortCore, LibRequired, @@ -250,14 +249,8 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { } } - // We've gotten this far because we're emitting some form of a final - // artifact which means that we may need to inject dependencies of some - // form. - // - // Things like panic runtimes may not have been activated quite yet, so do so here. - activate_injected_dep(CStore::from_tcx(tcx).injected_panic_runtime(), &mut ret, &|cnum| { - tcx.is_panic_runtime(cnum) - }); + // Panic runtimes may not have been activated quite yet, so do so here. + activate_panic_runtime(tcx, &mut ret); // When dylib B links to dylib A, then when using B we must also link to A. // It could be the case, however, that the rlib for A is present (hence we @@ -364,39 +357,45 @@ fn attempt_static(tcx: TyCtxt<'_>, unavailable: &mut Vec) -> Option, - list: &mut DependencyList, - replaces_injected: &dyn Fn(CrateNum) -> bool, -) { +/// Both `panic_unwind` and `panic_abort` were injected during the `std` build. +/// Here we need to activate only one, based on the desired strategy. +fn activate_panic_runtime(tcx: TyCtxt<'_>, list: &mut DependencyList) { + let desired_strategy = tcx.sess.panic_strategy(); + let desired_name = match desired_strategy { + PanicStrategy::Unwind => sym::panic_unwind, + PanicStrategy::Abort => sym::panic_abort, + PanicStrategy::ImmediateAbort => { + // Immediate-aborting panics don't use a runtime. + return; + } + }; + let mut activated = None; for (cnum, slot) in list.iter_enumerated() { - if !replaces_injected(cnum) { + if !tcx.is_panic_runtime(cnum) { continue; } if *slot != Linkage::NotLinked { return; } + if tcx.crate_name(cnum) == desired_name { + activated = Some(cnum); + break; + } } - if let Some(injected) = injected { - assert_eq!(list[injected], Linkage::NotLinked); - list[injected] = Linkage::Static; - } + if let Some(activated) = activated { + assert_eq!(list[activated], Linkage::NotLinked); + info!("panic runtime activated (cnum = {:?}) (name = {})", activated, desired_name); + list[activated] = Linkage::Static; + }; } /// After the linkage for a crate has been determined we need to verify that diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 91252ae6727ce..5088e180a1e72 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -788,10 +788,9 @@ impl MetadataBlob { )?; writeln!( out, - "compiler_builtins {} needs_allocator {} needs_panic_runtime {} no_builtins {} panic_runtime {} profiler_runtime {}", + "compiler_builtins {} needs_allocator {} no_builtins {} panic_runtime {} profiler_runtime {}", root.compiler_builtins, root.needs_allocator, - root.needs_panic_runtime, root.no_builtins, root.panic_runtime, root.profiler_runtime @@ -2027,10 +2026,6 @@ impl CrateMetadata { self.root.required_panic_strategy } - pub(crate) fn needs_panic_runtime(&self) -> bool { - self.root.needs_panic_runtime - } - pub(crate) fn is_private_dep(&self) -> bool { self.private_dep } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 602c5ee0201dd..b5f73dc29af93 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -747,7 +747,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debugger_visualizers, compiler_builtins: find_attr!(attrs, CompilerBuiltins), needs_allocator: find_attr!(attrs, NeedsAllocator), - needs_panic_runtime: find_attr!(attrs, NeedsPanicRuntime), no_builtins: find_attr!(attrs, NoBuiltins), panic_runtime: find_attr!(attrs, PanicRuntime), profiler_runtime: find_attr!(attrs, ProfilerRuntime), diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 237672878bb4c..8f2920b08d683 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -298,7 +298,6 @@ pub(crate) struct CrateRoot { compiler_builtins: bool, needs_allocator: bool, - needs_panic_runtime: bool, no_builtins: bool, panic_runtime: bool, profiler_runtime: bool, diff --git a/tests/run-make-cargo/cargo-issue-7359-load-panic/rmake.rs b/tests/run-make-cargo/cargo-issue-7359-load-panic/rmake.rs index cd03a87cc6644..670dff012331d 100644 --- a/tests/run-make-cargo/cargo-issue-7359-load-panic/rmake.rs +++ b/tests/run-make-cargo/cargo-issue-7359-load-panic/rmake.rs @@ -22,6 +22,5 @@ fn main() { // Visual Studio 2022 requires that the LIB env var be set so it can // find the Windows SDK. .env("LIB", std::env::var("LIB").unwrap_or_default()) - .run_fail() - .assert_stderr_contains("duplicate lang item in crate `core`: `sized`"); + .run(); } diff --git a/tests/run-make/locate-panic-runtime/rmake.rs b/tests/run-make/locate-panic-runtime/rmake.rs index 7cc3b04b23420..9126a72b490ef 100644 --- a/tests/run-make/locate-panic-runtime/rmake.rs +++ b/tests/run-make/locate-panic-runtime/rmake.rs @@ -1,7 +1,5 @@ -// This test makes sure that the injected panic runtime can be loaded both from -// `-L crate=` and `-L dependency=` paths. -// -// The need for `-L dependency=` paths comes from RFC 3874 (build-std=always). +// This test makes sure that the injected panic runtime can be loaded from +// `-L dependency=` paths as per RFC 3874 (build-std=always). // // Note: We have two possible panic runtime crates: the built one and the one from // the sysroot. Sysroot lookup is disabled via the `--sysroot=` override to verify @@ -16,7 +14,7 @@ fn main() { rustc().input("core.rs").panic("abort").sysroot("./no_exists").run(); // Compile `panic_abort` into a separate directory to prevent it from being - // found via `-L .`. + // found via `-L .` rustc() .input("panic_abort.rs") .panic("abort") @@ -33,7 +31,7 @@ fn main() { .run(); // Compile the final artifact. The panic runtime cannot be located without the - // `-L{crate/dependency}=` option. + // `-Ldependency=` option. rustc() .input("lib.rs") .arg("-Cpanic=abort") @@ -41,14 +39,16 @@ fn main() { .run_fail() .assert_stderr_contains("can't find crate for `panic_abort`"); - // Compile the final artifact. The panic runtime can be located via - // `-Lcrate=` paths. + // Compile the final artifact. The panic runtime cannot be located via + // `-Lcrate=` paths (This means that the panic runtime is not direct + // dependency). rustc() .input("lib.rs") .arg("-Cpanic=abort") .sysroot("./no_exists") .library_search_path(format!("crate={}", path("panic_abort").display())) - .run(); + .run_fail() + .assert_stderr_contains("can't find crate for `panic_abort`"); // Compile the final artifact. The panic runtime can be located via // `-Ldependency=` paths. @@ -57,6 +57,5 @@ fn main() { .arg("-Cpanic=abort") .sysroot("./no_exists") .library_search_path(format!("dependency={}", path("panic_abort").display())) - .run_fail() - .assert_stderr_contains("can't find crate for `panic_abort`"); + .run(); } diff --git a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs index a691ceb566b0c..0a1a8196d8a23 100644 --- a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs +++ b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs @@ -15,3 +15,4 @@ fn main() { } //~? ERROR the linked panic runtime `panic_unwind` is not compiled with this crate's panic strategy `abort` +//~? ERROR cannot link together two panic runtimes: panic_abort and panic_unwind diff --git a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr index bea1172f0e31b..0599052190b54 100644 --- a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr +++ b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr @@ -1,4 +1,6 @@ +error: cannot link together two panic runtimes: panic_abort and panic_unwind + error: the linked panic runtime `panic_unwind` is not compiled with this crate's panic strategy `abort` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors From d1e25b4ac8952835a30f66f232858d6fc8e5cec0 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Wed, 29 Jul 2026 21:43:53 +0300 Subject: [PATCH 11/37] tests: remove test for compiler_builtins --- .../compiler_builtins.rs | 9 --- .../run-make/locate-compiler-builtins/core.rs | 20 ------ .../run-make/locate-compiler-builtins/lib.rs | 6 -- .../locate-compiler-builtins/rmake.rs | 68 ------------------- 4 files changed, 103 deletions(-) delete mode 100644 tests/run-make/locate-compiler-builtins/compiler_builtins.rs delete mode 100644 tests/run-make/locate-compiler-builtins/core.rs delete mode 100644 tests/run-make/locate-compiler-builtins/lib.rs delete mode 100644 tests/run-make/locate-compiler-builtins/rmake.rs diff --git a/tests/run-make/locate-compiler-builtins/compiler_builtins.rs b/tests/run-make/locate-compiler-builtins/compiler_builtins.rs deleted file mode 100644 index 104052a138fc8..0000000000000 --- a/tests/run-make/locate-compiler-builtins/compiler_builtins.rs +++ /dev/null @@ -1,9 +0,0 @@ -// We are `compiler_builtins`. -#![feature(lang_items, no_core, compiler_builtins)] -#![allow(internal_features)] -#![compiler_builtins] -#![no_std] -#![no_core] -#![crate_type = "rlib"] - -extern crate core; diff --git a/tests/run-make/locate-compiler-builtins/core.rs b/tests/run-make/locate-compiler-builtins/core.rs deleted file mode 100644 index 7d75b528bb268..0000000000000 --- a/tests/run-make/locate-compiler-builtins/core.rs +++ /dev/null @@ -1,20 +0,0 @@ -// We are core. -#![feature(lang_items, no_core)] -#![allow(internal_features)] -#![no_std] -#![no_core] -#![crate_type = "rlib"] - -// Hack: this is needed for the import prelude resolution. -pub mod prelude { - pub mod rust_2024 {} -} - -#[lang = "pointee_sized"] -pub trait PointeeSized {} - -#[lang = "meta_sized"] -pub trait MetaSized: PointeeSized {} - -#[lang = "sized"] -pub trait Sized: MetaSized {} diff --git a/tests/run-make/locate-compiler-builtins/lib.rs b/tests/run-make/locate-compiler-builtins/lib.rs deleted file mode 100644 index 78151c433e4c2..0000000000000 --- a/tests/run-make/locate-compiler-builtins/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![no_std] -#![no_implicit_prelude] -#![crate_type = "dylib"] -// Hack: `compiler_builtins` is not injected in crates with `#![no_core]`. -// However, we still resolve to the local core since sysroot is -// disabled in this test. diff --git a/tests/run-make/locate-compiler-builtins/rmake.rs b/tests/run-make/locate-compiler-builtins/rmake.rs deleted file mode 100644 index e2c981e587c5a..0000000000000 --- a/tests/run-make/locate-compiler-builtins/rmake.rs +++ /dev/null @@ -1,68 +0,0 @@ -// This test makes sure that the injected `compiler_builtins` can be loaded both from -// `-L crate=` and `-L dependency=` paths (apart from sysroot). -// -// The need for `-L dependency=` paths comes from RFC 3874 (build-std=always). -// -// Note: We have two possible `compiler_builtins` crates: the built one and the one from -// the sysroot. Sysroot lookup is disabled via the `--sysroot=` override to verify -// that we can load the correct one. - -use run_make_support::{path, rfs, rust_lib_name, rustc}; - -fn main() { - rfs::create_dir("compiler_builtins"); - - // Compile `core`. - rustc() - .input("core.rs") - .arg("-Zunstable-options") - .panic("immediate-abort") - .arg("--edition=2024") - .sysroot("./no_exists") - .run(); - - // Compile `compiler_builtins` into a separate directory to prevent it from being - // found via `-L .`. - rustc() - .input("compiler_builtins.rs") - .arg("-Zunstable-options") - .panic("immediate-abort") - .arg("--edition=2024") - .out_dir("compiler_builtins") - .sysroot("./no_exists") - .run(); - - // Compile the final artifact. `compiler_builtins` cannot be located without the - // `-L{crate/dependency}=` option. - rustc() - .input("lib.rs") - .arg("-Zunstable-options") - .panic("immediate-abort") - .arg("--edition=2024") - .sysroot("./no_exists") - .run_fail() - .assert_stderr_contains("can't find crate for `compiler_builtins`"); - - // Compile the final artifact. `compiler_builtins` can be located via - // `-Lcrate=` paths. - rustc() - .input("lib.rs") - .arg("-Zunstable-options") - .panic("immediate-abort") - .arg("--edition=2024") - .sysroot("./sysroot") - .library_search_path(format!("crate={}", path("compiler_builtins").display())) - .run(); - - // Compile the final artifact. The `compiler_builtins` can be located via - // `-Ldependency=` paths. - rustc() - .input("lib.rs") - .arg("-Zunstable-options") - .panic("immediate-abort") - .arg("--edition=2024") - .sysroot("./no_exists") - .library_search_path(format!("dependency={}", path("compiler_builtins").display())) - .run_fail() - .assert_stderr_contains("can't find crate for `compiler_builtins`"); -} From 8d309d2eed9092a967b7b37c200063c91b48d44f Mon Sep 17 00:00:00 2001 From: zakrad <49591476+zakrad@users.noreply.github.com> Date: Thu, 30 Jul 2026 01:11:23 +0330 Subject: [PATCH 12/37] Add regression test for supertrait projection normalization through dyn --- ...lize-supertrait-projection-in-dyn-61083.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/ui/associated-types/normalize-supertrait-projection-in-dyn-61083.rs diff --git a/tests/ui/associated-types/normalize-supertrait-projection-in-dyn-61083.rs b/tests/ui/associated-types/normalize-supertrait-projection-in-dyn-61083.rs new file mode 100644 index 0000000000000..fb93a97328bf4 --- /dev/null +++ b/tests/ui/associated-types/normalize-supertrait-projection-in-dyn-61083.rs @@ -0,0 +1,28 @@ +//! Regression test for . +//! +//! An associated type projection in a supertrait bound (`Bar: Foo`) +//! failed to normalize when the `Bar` bound was reached through a trait object, +//! so passing the object to a function expecting `Foo` was rejected. + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +trait Foo {} + +trait Bar: Foo {} + +fn a(_x: &(impl Foo + ?Sized)) {} + +// The `dyn` form is the one that used to fail to normalize `T::Item` to `u32`. +fn b(y: &dyn Bar>) { + a(y) +} + +// The equivalent `impl Trait` form always compiled; keep it so both paths stay pinned. +fn c(y: &(impl Bar> + ?Sized)) { + a(y) +} + +fn main() {} From 3874adb16be9a05a9191538c7c0533753a8f892d Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Wed, 29 Jul 2026 16:39:43 -0500 Subject: [PATCH 13/37] Add -Zinstrument-mcount={fentry-nop-record,fentry-record} The linux kernel still uses fentry for x86 and s390x arches. s390x depends entirely on the compiler to record and nop these sections. This facilitates support for inserting nop's and/or recording the location of each mcount call in a special section named `__mcount_loc`. These attributes are currently only supported with fentry on the s390x target, otherwise they are quietly ignored (except on s390x). --- compiler/rustc_codegen_llvm/src/attributes.rs | 18 +++++++--- compiler/rustc_interface/src/tests.rs | 12 +++---- compiler/rustc_session/src/config.rs | 24 +++++++++---- compiler/rustc_session/src/options.rs | 35 ++++++++++++++----- compiler/rustc_session/src/session.rs | 13 ++++--- .../src/compiler-flags/instrument-mcount.md | 24 +++++++------ tests/codegen-llvm/instrument-mcount-opts.rs | 26 ++++++++++++++ 7 files changed, 111 insertions(+), 41 deletions(-) create mode 100644 tests/codegen-llvm/instrument-mcount-opts.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 1f00e47a89927..cdadc6cc73386 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -8,7 +8,8 @@ use rustc_middle::middle::codegen_fn_attrs::{ }; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::{ - BranchProtection, FunctionReturn, InstrumentMcount, OptLevel, PAuthKey, PacRet, + BranchProtection, FunctionReturn, InstrumentMcount, InstrumentMcountOpts, OptLevel, PAuthKey, + PacRet, }; use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; @@ -201,7 +202,7 @@ pub(crate) fn frame_pointer(sess: &Session) -> FramePointer { let opts = &sess.opts; // "mcount" function relies on stack pointer. // See . - if opts.unstable_opts.instrument_mcount == InstrumentMcount::Mcount { + if let InstrumentMcount::Mcount(_) = opts.unstable_opts.instrument_mcount { fp.ratchet(FramePointer::Always); } fp.ratchet(opts.cg.force_frame_pointers); @@ -248,8 +249,9 @@ fn instrument_function_attr<'ll>( }; if instrument_entry { + let mut opts = InstrumentMcountOpts::default(); match sess.opts.unstable_opts.instrument_mcount { - InstrumentMcount::Mcount => { + InstrumentMcount::Mcount(mopts) => { // The function name varies on platforms. // See test/CodeGen/mcount.c in clang. let mcount_name = match &sess.target.llvm_mcount_intrinsic { @@ -262,12 +264,20 @@ fn instrument_function_attr<'ll>( "instrument-function-entry-inlined", mcount_name, )); + opts = mopts; } - InstrumentMcount::Fentry => { + InstrumentMcount::Fentry(fopts) => { attrs.push(llvm::CreateAttrStringValue(cx.llcx, "fentry-call", "true")); + opts = fopts; } InstrumentMcount::Disabled => {} } + if opts.no_call { + attrs.push(llvm::CreateAttrString(cx.llcx, "mnop-mcount")); + } + if opts.record { + attrs.push(llvm::CreateAttrString(cx.llcx, "mrecord-mcount")); + } } } if let Some(options) = &sess.opts.unstable_opts.instrument_xray { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 22b643e74e582..67a820388cbec 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -13,11 +13,11 @@ use rustc_session::config::{ AnnotateMoves, AutoDiff, BranchProtection, CFGuard, Cfg, CodegenRetagOptions, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, FmtDebug, FunctionReturn, IncrementalStateAssertion, InliningThreshold, Input, - InstrumentCoverage, InstrumentMcount, InstrumentXRay, LinkSelfContained, LinkerPluginLto, - LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, OutFileName, - OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius, - ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, - build_configuration, build_session_options, rustc_optgroups, + InstrumentCoverage, InstrumentMcount, InstrumentMcountOpts, InstrumentXRay, LinkSelfContained, + LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, + OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, + Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, + WasiExecModel, build_configuration, build_session_options, rustc_optgroups, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -833,7 +833,7 @@ fn test_unstable_options_tracking_hash() { tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); - tracked!(instrument_mcount, InstrumentMcount::Mcount); + tracked!(instrument_mcount, InstrumentMcount::Mcount(InstrumentMcountOpts::default())); tracked!(instrument_xray, Some(InstrumentXRay::default())); tracked!(link_directives, false); tracked!(link_only, true); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 37488ebbf1e8f..2ac1db7ddb89e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -258,15 +258,23 @@ pub enum AnnotateMoves { Enabled(Option), } +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub struct InstrumentMcountOpts { + // Insert a nop which could be replaced by an mcount call. + pub no_call: bool, + // Record the location of the call instrument in a special linker section. + pub record: bool, +} + /// The different settings that the `-Z Instrument-mcount` flag can have. #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum InstrumentMcount { /// `-Z instrument-mcount=no` Disabled, /// `-Z instrument-mcount=yes` - Mcount, + Mcount(InstrumentMcountOpts), /// `-Z instrument-mcount=fentry` - Fentry, + Fentry(InstrumentMcountOpts), } /// Settings for `-Z instrument-xray` flag. @@ -3141,11 +3149,12 @@ pub(crate) mod dep_tracking { use super::{ AnnotateMoves, AutoDiff, BranchProtection, CFGuard, CFProtection, CodegenRetagOptions, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, - FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentMcount, InstrumentXRay, - LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, - OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, PointerAuthOption, - Polonius, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, - SymbolManglingVersion, WasiExecModel, + FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentMcount, + InstrumentMcountOpts, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, + MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, + OutputTypes, PatchableFunctionEntry, PointerAuthOption, Polonius, ResolveDocLinks, + SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, + WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3208,6 +3217,7 @@ pub(crate) mod dep_tracking { InstrumentCoverage, CoverageOptions, InstrumentMcount, + InstrumentMcountOpts, InstrumentXRay, CrateType, MergeFunctions, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5b71c0435185a..deb70ccf7cd2b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -852,8 +852,7 @@ mod desc { pub(crate) const parse_coverage_options: &str = "`block` | `branch` | `condition`"; pub(crate) const parse_codegen_retag_options: &str = "either no value or a comma-separated list of settings: `no-precise-im`, `no-precise-pin`"; - pub(crate) const parse_instrument_mcount: &str = - "either a boolean (`yes`, `no`, `on`, `off`, etc), or `fentry` on supported targets."; + pub(crate) const parse_instrument_mcount: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `fentry`, `fentry-record`, `fentry-nop-record` on supported targets"; pub(crate) const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub(crate) const parse_unpretty: &str = "`string` or `string=string`"; pub(crate) const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; @@ -1670,15 +1669,33 @@ pub mod parse { pub(crate) fn parse_instrument_mcount(slot: &mut InstrumentMcount, v: Option<&str>) -> bool { let mut use_mcount = false; + let mut opts = InstrumentMcountOpts::default(); if parse_bool(&mut use_mcount, v) { - *slot = if use_mcount { InstrumentMcount::Mcount } else { InstrumentMcount::Disabled }; - true - } else if let Some("fentry") = v { - *slot = InstrumentMcount::Fentry; - true - } else { - false + *slot = if use_mcount { + InstrumentMcount::Mcount(opts) + } else { + InstrumentMcount::Disabled + }; + return true; } + match v { + Some("fentry") => { + *slot = InstrumentMcount::Fentry(opts); + } + Some("fentry-record") => { + opts.record = true; + *slot = InstrumentMcount::Fentry(opts); + } + Some("fentry-nop-record") => { + opts.record = true; + opts.no_call = true; + *slot = InstrumentMcount::Fentry(opts); + } + _ => { + return false; + } + } + true } pub(crate) fn parse_instrument_xray( diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index eebead6fc1f47..da20c7ef088a4 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1612,10 +1612,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } - if sess.opts.unstable_opts.instrument_mcount == InstrumentMcount::Fentry - && !sess.target.options.supports_fentry - { - sess.dcx().emit_err(diagnostics::InstrumentationNotSupported { us: "fentry".to_string() }); + if let InstrumentMcount::Fentry(opts) = sess.opts.unstable_opts.instrument_mcount { + if !sess.target.options.supports_fentry { + sess.dcx() + .emit_err(diagnostics::InstrumentationNotSupported { us: "fentry".to_string() }); + } + if (opts.no_call || opts.record) && sess.target.arch != Arch::S390x { + sess.dcx() + .emit_err(diagnostics::InstrumentationNotSupported { us: "fentry-*".to_string() }); + } } if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray { diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-mcount.md b/src/doc/unstable-book/src/compiler-flags/instrument-mcount.md index 376b509048dab..07af4435bc7c1 100644 --- a/src/doc/unstable-book/src/compiler-flags/instrument-mcount.md +++ b/src/doc/unstable-book/src/compiler-flags/instrument-mcount.md @@ -12,17 +12,19 @@ Supported options: - `no`, `n`, `off`: Do no enable instrumentation. The default option. This requires, and enables frame pointer generation. - `yes`, `y`, `on`: Enable mcount based function instrumentation. - `fentry`: Enable fentry based function instrument, where supported. The calling conventions for this are different than mcount, with less overhead, and no frame pointer requirements. This counting function is always named `__fentry__`. This is only available on x86 and s390x targets. - -|target |mcount function|supports fentry|ABI notes| -|--- |--- |--- |--- | -|aarch64-apple-darwin | `\u{1}mcount` | | | -|aarch64-pc-windows-msvc | `mcount` | | | -|aarch64-unknown-linux-gnu| `_mcount` | | | -|i686-pc-windows-msvc | `mcount` | x| | -|i686-unknown-linux-gnu | `mcount` | x| | -|x86_64-pc-windows-gnu | `_mcount` | x| | -|x86_64-pc-windows-msvc | `mcount` | x| | -|x86_64-unknown-linux-gnu | `mcount` | x| 1| + - `fentry-nop-record` and `fentry-record`: These options extend the `fentry` option by recording each call site into a section named `__mcount_loc` in the output object file, and optionally replacing the call to `__fentry__` with a nop. These options are not implemented for all targets. Support is noted the supports fentry recording column below. + +|target |mcount function|supports fentry|supports fentry recording|ABI notes| +|--- |--- |--- |---- |-- | +|aarch64-apple-darwin | `\u{1}mcount` | | | | +|aarch64-pc-windows-msvc | `mcount` | | | | +|aarch64-unknown-linux-gnu| `_mcount` | | | | +|i686-pc-windows-msvc | `mcount` | x| | | +|i686-unknown-linux-gnu | `mcount` | x| | | +|x86_64-pc-windows-gnu | `_mcount` | x| | | +|x86_64-pc-windows-msvc | `mcount` | x| | | +|x86_64-unknown-linux-gnu | `mcount` | x| | 1| +|s390x-unknown-linux-gnu | `mcount` | x| x| | On arm eabi targets, the mcount function is usually named `__gnu_mcount_nc`, though some targets may use different names. Implementers of counting function should consult the target specific documentation for quirks of each ABI function. diff --git a/tests/codegen-llvm/instrument-mcount-opts.rs b/tests/codegen-llvm/instrument-mcount-opts.rs new file mode 100644 index 0000000000000..c2dc72614cc64 --- /dev/null +++ b/tests/codegen-llvm/instrument-mcount-opts.rs @@ -0,0 +1,26 @@ +//@ revisions: ncyr ycyr ycnr +//@ add-minicore +//@ needs-llvm-components: systemz +//@ compile-flags: -Copt-level=0 --target=s390x-unknown-linux-gnu +//@[ncyr] compile-flags: -Zinstrument-mcount=fentry-nop-record +//@[ycyr] compile-flags: -Zinstrument-mcount=fentry-record +//@[ycnr] compile-flags: -Zinstrument-mcount=fentry +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +// ncyr: attributes #{{.*}} {{.*}} "fentry-call"="true" "mnop-mcount" "mrecord-mcount" +// +// ncnr: attributes #{{.*}} {{.*}} "fentry-call"="true" "mnop-mcount" +// ncnr-NOT: attributes #{{.*}} {{.*}} "mrecord-mcount" +// +// ycnr: attributes #{{.*}} {{.*}} "fentry-call"="true" +// ycnr-NOT: attributes #{{.*}} {{.*}} "mnop-mcount" +// ycnr-NOT: attributes #{{.*}} {{.*}} "mrecord-mcount" +// +// ycyr: attributes #{{.*}} {{.*}} "fentry-call"="true" "mrecord-mcount" +// ycyr-NOT: attributes #{{.*}} {{.*}} "mnop-mcount" +pub fn foo() {} From ab78e6a167b574d151f34f496ece2922987491f7 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Fri, 31 Jul 2026 13:16:02 +0300 Subject: [PATCH 14/37] fix typo and do not emit extra error --- compiler/rustc_metadata/src/creader.rs | 2 +- compiler/rustc_metadata/src/dependency_format.rs | 1 - tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs | 1 - tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr | 4 +--- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index a3d85f906c538..28879fc78c2ae 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -993,7 +993,7 @@ impl CStore { } // Sanity check the panic strategy is indeed what we thought it was. - // Note: Both `panic_unwind` and `panic_abort` might be compiler with + // Note: Both `panic_unwind` and `panic_abort` might be compiled with // `ImmediateAbort` strategy. if cdata.required_panic_strategy() != Some(PanicStrategy::ImmediateAbort) && cdata.required_panic_strategy() != Some(desired_strategy) diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 53bb2ab7234d1..d42c190660289 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -388,7 +388,6 @@ fn activate_panic_runtime(tcx: TyCtxt<'_>, list: &mut DependencyList) { } if tcx.crate_name(cnum) == desired_name { activated = Some(cnum); - break; } } if let Some(activated) = activated { diff --git a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs index 0a1a8196d8a23..a691ceb566b0c 100644 --- a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs +++ b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.rs @@ -15,4 +15,3 @@ fn main() { } //~? ERROR the linked panic runtime `panic_unwind` is not compiled with this crate's panic strategy `abort` -//~? ERROR cannot link together two panic runtimes: panic_abort and panic_unwind diff --git a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr index 0599052190b54..bea1172f0e31b 100644 --- a/tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr +++ b/tests/ui/panic-runtime/abort-link-to-unwind-dylib.stderr @@ -1,6 +1,4 @@ -error: cannot link together two panic runtimes: panic_abort and panic_unwind - error: the linked panic runtime `panic_unwind` is not compiled with this crate's panic strategy `abort` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error From 3396d863737f1a33c8e40ee2d918b38145e44933 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Fri, 31 Jul 2026 19:09:30 +0300 Subject: [PATCH 15/37] tests: emit llvm-ir to avoid running the linker --- tests/run-make/locate-panic-runtime/rmake.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/run-make/locate-panic-runtime/rmake.rs b/tests/run-make/locate-panic-runtime/rmake.rs index 9126a72b490ef..3a53e4051b6cf 100644 --- a/tests/run-make/locate-panic-runtime/rmake.rs +++ b/tests/run-make/locate-panic-runtime/rmake.rs @@ -4,6 +4,8 @@ // Note: We have two possible panic runtime crates: the built one and the one from // the sysroot. Sysroot lookup is disabled via the `--sysroot=` override to verify // that we can load the correct one. +// +// `--emit=llvm-ir` is used to avoid running the linker. use run_make_support::{path, rfs, rust_lib_name, rustc}; @@ -36,6 +38,7 @@ fn main() { .input("lib.rs") .arg("-Cpanic=abort") .sysroot("./no_exists") + .emit("llvm-ir") .run_fail() .assert_stderr_contains("can't find crate for `panic_abort`"); @@ -47,6 +50,7 @@ fn main() { .arg("-Cpanic=abort") .sysroot("./no_exists") .library_search_path(format!("crate={}", path("panic_abort").display())) + .emit("llvm-ir") .run_fail() .assert_stderr_contains("can't find crate for `panic_abort`"); @@ -57,5 +61,6 @@ fn main() { .arg("-Cpanic=abort") .sysroot("./no_exists") .library_search_path(format!("dependency={}", path("panic_abort").display())) + .emit("llvm-ir") .run(); } From f48274b37f6fad7575b9c86fefa0e1405a7b9a72 Mon Sep 17 00:00:00 2001 From: beetrees Date: Fri, 31 Jul 2026 22:49:21 +0100 Subject: [PATCH 16/37] Linkify C-SKY targets in `platform-support.md` --- src/doc/rustc/src/platform-support.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index c527911bc96a2..2da705492f149 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -330,8 +330,8 @@ target | std | host | notes [`avr-none`](platform-support/avr-none.md) | * | | AVR; requires `-Zbuild-std=core` and `-Ctarget-cpu=...` `bpfeb-unknown-none` | * | | BPF (big endian) `bpfel-unknown-none` | * | | BPF (little endian) -`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian) -`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) +[`csky-unknown-linux-gnuabiv2`](platform-support/csky-unknown-linux-gnuabiv2.md) | ✓ | | C-SKY abiv2 Linux (little endian) +[`csky-unknown-linux-gnuabiv2hf`](platform-support/csky-unknown-linux-gnuabiv2.md) | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) [`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.5 [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) [`hexagon-unknown-qurt`](platform-support/hexagon-unknown-qurt.md)| * | | Hexagon QuRT From bacfb80bfda8e0fc22c1f7cd82643e4cd38100db Mon Sep 17 00:00:00 2001 From: Cole Kauder-McMurrich Date: Fri, 31 Jul 2026 13:16:55 -0400 Subject: [PATCH 17/37] Fix rustdoc ICE when checking if a generic arg can be elided --- src/librustdoc/clean/utils.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 9290555f2ef39..597543c957d13 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -117,18 +117,19 @@ pub(crate) fn clean_middle_generic_args<'tcx>( }; let mut elision_has_failed_once_before = false; + let index_offset = generics.count() - args.len(); let clean_arg = |(index, &arg): (usize, &ty::GenericArg<'tcx>)| { // Elide the self type. if has_self && index == 0 { return None; } - let param = generics.param_at(index, cx.tcx); + let param = generics.param_at(index + index_offset, cx.tcx); let arg = ty::Binder::bind_with_vars(arg, bound_vars); // Elide arguments that coincide with their default. if !elision_has_failed_once_before && let Some(default) = param.default_value(cx.tcx) { - let default = default.instantiate(cx.tcx, args.as_ref()).skip_norm_wip(); + let default = default.instantiate(cx.tcx, args.as_ref()).skip_normalization(); if can_elide_generic_arg(arg, arg.rebind(default)) { return None; } From ce8f41367bf4c2b73e5daafb38bce21b82d97345 Mon Sep 17 00:00:00 2001 From: Cole Kauder-McMurrich Date: Fri, 31 Jul 2026 17:11:07 -0400 Subject: [PATCH 18/37] Add regression test for rustdoc ICE when checking if a generic arg can be elided --- .../ice-clean-generic-args-133637.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/rustdoc-ui/ice-clean-generic-args-133637.rs diff --git a/tests/rustdoc-ui/ice-clean-generic-args-133637.rs b/tests/rustdoc-ui/ice-clean-generic-args-133637.rs new file mode 100644 index 0000000000000..b90ff547b9cfa --- /dev/null +++ b/tests/rustdoc-ui/ice-clean-generic-args-133637.rs @@ -0,0 +1,18 @@ +//@ check-pass +// https://github.com/rust-lang/rust/issues/133637 +#![crate_name="foo"] + +// Regression test for issue #133637. Previously we would index into the flattened generics list +// with the children generic indexes. This resulted in an ICE when debug assertions were on. + +struct SomeDefault; + +trait SomeTrait { + type Type<'a, 'b>; +} + +impl SomeTrait for T { + type Type<'a, 'b> = (&'a u8, &'b u8); +} + +type SomeType<'a, 'b, T, Gen = SomeDefault> = >::Type<'a, 'b>; From 6e2de73e9ca4e89f74ddad4bb688d92e85031d72 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sat, 1 Aug 2026 13:12:37 +0600 Subject: [PATCH 19/37] point at trait definition when it is used as a derive macro fixes https://github.com/rust-lang/rust/issues/159483 --- .../rustc_resolve/src/diagnostics/impls.rs | 35 +++++++++++++++- tests/ui/macros/derive-of-trait.rs | 32 +++++++++++++++ tests/ui/macros/derive-of-trait.stderr | 41 +++++++++++++++++++ tests/ui/macros/issue-88206.rs | 5 +-- tests/ui/macros/issue-88206.stderr | 31 +++++++------- tests/ui/macros/issue-88228.rs | 2 + tests/ui/macros/issue-88228.stderr | 7 +++- 7 files changed, 131 insertions(+), 22 deletions(-) create mode 100644 tests/ui/macros/derive-of-trait.rs create mode 100644 tests/ui/macros/derive-of-trait.stderr diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 212629395c98b..680f87d43e4e9 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -2034,8 +2034,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Don't confuse the user with tool modules or open modules. continue; } - Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { - "only a trait, without a derive macro".to_string() + Res::Def(DefKind::Trait, trait_def_id) if macro_kind == MacroKind::Derive => { + if let crate::DeclKind::Import { import, .. } = binding.kind + && !import.span.is_dummy() + { + self.record_use(ident, binding, Used::Other); + } + let trait_span = self.def_span(trait_def_id); + err.span_note(trait_span, format!("`{ident}` is a trait, not a derive macro")); + err.help(format!("consider implementing `{ident}` for your type manually")); + return; } res => format!( "{} {}, not {} {}", @@ -2067,6 +2075,29 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; } + // Not in scope: check if the name refers to a trait importable from elsewhere. + if macro_kind == MacroKind::Derive { + let trait_candidates = + self.lookup_import_candidates(ident, TypeNS, parent_scope, |res| { + matches!(res, Res::Def(DefKind::Trait, _)) + }); + let mut seen = FxHashSet::default(); + for candidate in &trait_candidates { + if let Some(def_id) = candidate.did + && seen.insert(def_id) + { + err.span_note( + self.def_span(def_id), + format!("`{ident}` is a trait, not a derive macro"), + ); + } + } + if !seen.is_empty() { + err.help(format!("consider implementing `{ident}` for your type manually")); + return; + } + } + if self.macro_names.contains(&IdentKey::new(ident)) { err.subdiagnostic(AddedMacroUse); return; diff --git a/tests/ui/macros/derive-of-trait.rs b/tests/ui/macros/derive-of-trait.rs new file mode 100644 index 0000000000000..ebabb01f3d587 --- /dev/null +++ b/tests/ui/macros/derive-of-trait.rs @@ -0,0 +1,32 @@ +//@ compile-flags: -Z deduplicate-diagnostics=yes + +// Trait used as a derive target should point at the trait definition and +// suggest a manual implementation — both when the trait is already in scope +// (via import or local definition) and when it is only importable. + +mod inner { + pub trait MyTrait {} //~ NOTE `MyTrait` is a trait, not a derive macro + pub trait OuterTrait {} //~ NOTE `OuterTrait` is a trait, not a derive macro +} + +use inner::MyTrait; + +trait LocalTrait {} +//~^ NOTE `LocalTrait` is a trait, not a derive macro + +// in-scope: locally defined +#[derive(LocalTrait)] +//~^ ERROR cannot find derive macro `LocalTrait` in this scope +struct A; + +// in-scope: imported +#[derive(MyTrait)] +//~^ ERROR cannot find derive macro `MyTrait` in this scope +struct B; + +// out-of-scope: importable but not imported +#[derive(OuterTrait)] +//~^ ERROR cannot find derive macro `OuterTrait` in this scope +struct C; + +fn main() {} diff --git a/tests/ui/macros/derive-of-trait.stderr b/tests/ui/macros/derive-of-trait.stderr new file mode 100644 index 0000000000000..6e40f13d9645f --- /dev/null +++ b/tests/ui/macros/derive-of-trait.stderr @@ -0,0 +1,41 @@ +error: cannot find derive macro `OuterTrait` in this scope + --> $DIR/derive-of-trait.rs:28:10 + | +LL | #[derive(OuterTrait)] + | ^^^^^^^^^^ + | +note: `OuterTrait` is a trait, not a derive macro + --> $DIR/derive-of-trait.rs:9:5 + | +LL | pub trait OuterTrait {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider implementing `OuterTrait` for your type manually + +error: cannot find derive macro `MyTrait` in this scope + --> $DIR/derive-of-trait.rs:23:10 + | +LL | #[derive(MyTrait)] + | ^^^^^^^ + | +note: `MyTrait` is a trait, not a derive macro + --> $DIR/derive-of-trait.rs:8:5 + | +LL | pub trait MyTrait {} + | ^^^^^^^^^^^^^^^^^^^^ + = help: consider implementing `MyTrait` for your type manually + +error: cannot find derive macro `LocalTrait` in this scope + --> $DIR/derive-of-trait.rs:18:10 + | +LL | #[derive(LocalTrait)] + | ^^^^^^^^^^ + | +note: `LocalTrait` is a trait, not a derive macro + --> $DIR/derive-of-trait.rs:14:1 + | +LL | trait LocalTrait {} + | ^^^^^^^^^^^^^^^^^^^ + = help: consider implementing `LocalTrait` for your type manually + +error: aborting due to 3 previous errors + diff --git a/tests/ui/macros/issue-88206.rs b/tests/ui/macros/issue-88206.rs index abf58fdcbc815..b78a2d48e0b62 100644 --- a/tests/ui/macros/issue-88206.rs +++ b/tests/ui/macros/issue-88206.rs @@ -8,15 +8,14 @@ use std::str::*; //~| NOTE `from_utf8_unchecked` is imported here, but it is a function mod hey { - pub trait Serialize {} + pub trait Serialize {} //~ NOTE `Serialize` is a trait, not a derive macro pub trait Deserialize {} pub struct X(i32); } use hey::{Serialize, Deserialize, X}; -//~^ NOTE `Serialize` is imported here, but it is only a trait, without a derive macro -//~| NOTE `Deserialize` is imported here, but it is a trait +//~^ NOTE `Deserialize` is imported here, but it is a trait //~| NOTE `X` is imported here, but it is a struct #[derive(Serialize)] diff --git a/tests/ui/macros/issue-88206.stderr b/tests/ui/macros/issue-88206.stderr index f7f5b56488007..93be644650f20 100644 --- a/tests/ui/macros/issue-88206.stderr +++ b/tests/ui/macros/issue-88206.stderr @@ -1,5 +1,5 @@ error: cannot find macro `X` in this scope - --> $DIR/issue-88206.rs:64:5 + --> $DIR/issue-88206.rs:63:5 | LL | X!(); | ^ @@ -11,7 +11,7 @@ LL | use hey::{Serialize, Deserialize, X}; | ^ error: cannot find macro `test` in this scope - --> $DIR/issue-88206.rs:60:5 + --> $DIR/issue-88206.rs:59:5 | LL | test!(); | ^^^^ @@ -19,7 +19,7 @@ LL | test!(); = note: `test` is in scope, but it is an attribute: `#[test]` error: cannot find macro `Copy` in this scope - --> $DIR/issue-88206.rs:56:5 + --> $DIR/issue-88206.rs:55:5 | LL | Copy!(); | ^^^^ @@ -27,7 +27,7 @@ LL | Copy!(); = note: `Copy` is in scope, but it is a derive macro: `#[derive(Copy)]` error: cannot find macro `Box` in this scope - --> $DIR/issue-88206.rs:52:5 + --> $DIR/issue-88206.rs:51:5 | LL | Box!(); | ^^^ @@ -35,7 +35,7 @@ LL | Box!(); = note: `Box` is in scope, but it is a struct, not a macro error: cannot find macro `from_utf8` in this scope - --> $DIR/issue-88206.rs:49:5 + --> $DIR/issue-88206.rs:48:5 | LL | from_utf8!(); | ^^^^^^^^^ @@ -47,7 +47,7 @@ LL | use std::str::*; | ^^^^^^^^^^^ error: cannot find attribute `println` in this scope - --> $DIR/issue-88206.rs:43:3 + --> $DIR/issue-88206.rs:42:3 | LL | #[println] | ^^^^^^^ @@ -55,7 +55,7 @@ LL | #[println] = note: `println` is in scope, but it is a function-like macro error: cannot find attribute `from_utf8_unchecked` in this scope - --> $DIR/issue-88206.rs:39:3 + --> $DIR/issue-88206.rs:38:3 | LL | #[from_utf8_unchecked] | ^^^^^^^^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | use std::str::*; | ^^^^^^^^^^^ error: cannot find attribute `Deserialize` in this scope - --> $DIR/issue-88206.rs:35:3 + --> $DIR/issue-88206.rs:34:3 | LL | #[Deserialize] | ^^^^^^^^^^^ @@ -79,7 +79,7 @@ LL | use hey::{Serialize, Deserialize, X}; | ^^^^^^^^^^^ error: cannot find derive macro `println` in this scope - --> $DIR/issue-88206.rs:30:10 + --> $DIR/issue-88206.rs:29:10 | LL | #[derive(println)] | ^^^^^^^ @@ -87,7 +87,7 @@ LL | #[derive(println)] = note: `println` is in scope, but it is a function-like macro error: cannot find derive macro `from_utf8_mut` in this scope - --> $DIR/issue-88206.rs:26:10 + --> $DIR/issue-88206.rs:25:10 | LL | #[derive(from_utf8_mut)] | ^^^^^^^^^^^^^ @@ -99,16 +99,17 @@ LL | use std::str::*; | ^^^^^^^^^^^ error: cannot find derive macro `Serialize` in this scope - --> $DIR/issue-88206.rs:22:10 + --> $DIR/issue-88206.rs:21:10 | LL | #[derive(Serialize)] | ^^^^^^^^^ | -note: `Serialize` is imported here, but it is only a trait, without a derive macro - --> $DIR/issue-88206.rs:17:11 +note: `Serialize` is a trait, not a derive macro + --> $DIR/issue-88206.rs:11:5 | -LL | use hey::{Serialize, Deserialize, X}; - | ^^^^^^^^^ +LL | pub trait Serialize {} + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: consider implementing `Serialize` for your type manually error: aborting due to 11 previous errors diff --git a/tests/ui/macros/issue-88228.rs b/tests/ui/macros/issue-88228.rs index b4195a92557ed..e58a90d08cdda 100644 --- a/tests/ui/macros/issue-88228.rs +++ b/tests/ui/macros/issue-88228.rs @@ -9,6 +9,8 @@ mod hey { //~ HELP consider importing this derive macro #[derive(Bla)] //~^ ERROR cannot find derive macro `Bla` +//~| NOTE `Bla` is a trait, not a derive macro +//~| HELP consider implementing `Bla` for your type manually struct A; #[derive(println)] diff --git a/tests/ui/macros/issue-88228.stderr b/tests/ui/macros/issue-88228.stderr index f9d0ac95da756..164af4e07bddf 100644 --- a/tests/ui/macros/issue-88228.stderr +++ b/tests/ui/macros/issue-88228.stderr @@ -1,5 +1,5 @@ error: cannot find macro `bla` in this scope - --> $DIR/issue-88228.rs:20:5 + --> $DIR/issue-88228.rs:22:5 | LL | bla!(); | ^^^ @@ -10,7 +10,7 @@ LL + use crate::hey::bla; | error: cannot find derive macro `println` in this scope - --> $DIR/issue-88228.rs:14:10 + --> $DIR/issue-88228.rs:16:10 | LL | #[derive(println)] | ^^^^^^^ @@ -23,6 +23,9 @@ error: cannot find derive macro `Bla` in this scope LL | #[derive(Bla)] | ^^^ | +note: `Bla` is a trait, not a derive macro + --> $SRC_DIR/core/src/marker.rs:LL:COL + = help: consider implementing `Bla` for your type manually help: consider importing this derive macro through its public re-export | LL + use crate::hey::Bla; From 2ffc259743df2aa6594a825d4964f56f0d31a1ad Mon Sep 17 00:00:00 2001 From: Yukang Date: Sat, 1 Aug 2026 21:44:44 +0800 Subject: [PATCH 20/37] Add regression test for unused_parens on contract clauses --- ...tract-clause-unused-parens-issue-143754.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/ui/contracts/contract-clause-unused-parens-issue-143754.rs diff --git a/tests/ui/contracts/contract-clause-unused-parens-issue-143754.rs b/tests/ui/contracts/contract-clause-unused-parens-issue-143754.rs new file mode 100644 index 0000000000000..576d99665f124 --- /dev/null +++ b/tests/ui/contracts/contract-clause-unused-parens-issue-143754.rs @@ -0,0 +1,22 @@ +//@ check-pass +// Regression test for . +// The contract macros wrap the clause in braces rather than parentheses, so `unused_parens` +// must not fire on a contract attribute (and must not emit the attribute-eating suggestion). + +#![expect(incomplete_features)] +#![feature(contracts)] +#![deny(unused_parens)] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn nest(x: Baz) -> i32 { + loop { + return x.baz + 50; + } +} + +struct Baz { + baz: i32, +} + +fn main() {} From d443d457c8884df8881b36b8ffc300ff12892a65 Mon Sep 17 00:00:00 2001 From: im-lunex Date: Sat, 1 Aug 2026 19:12:00 +0000 Subject: [PATCH 21/37] fix borrowck ICE for consts with fn pointer type * fix borrowck ICE for consts with fn pointer type annotate_argument_and_return_for_borrow called tcx.fn_sig on the item being checked whenever its type was FnDef or FnPtr. for a const whose type is a fn pointer that's not a function item, so we ICE'd with "unexpected sort of node in fn_sig". take the signature from the fn ptr type instead and skip the annotation when there's no fn decl. * run rustfmt and refmt * fix borrowck ICE for consts with fn pointer type annotate_argument_and_return_for_borrow called tcx.fn_sig on the item being checked whenever its type was FnDef or FnPtr. for a const whose type is a fn pointer that's not a function item, so we ICE'd with "unexpected sort of node in fn_sig". take the signature from the fn ptr type instead and skip the annotation when there's no fn decl. * edit comment * use ty.fn_sig per review --- .../src/diagnostics/conflict_errors.rs | 7 +++++-- .../borrowck/const-fn-ptr-borrow-annotation.rs | 16 ++++++++++++++++ .../const-fn-ptr-borrow-annotation.stderr | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 tests/ui/borrowck/const-fn-ptr-borrow-annotation.rs create mode 100644 tests/ui/borrowck/const-fn-ptr-borrow-annotation.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 8d1f9cea853f9..f2a55194cf0f0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -4277,7 +4277,8 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { ) -> Option> { // Define a fallback for when we can't match a closure. let fallback = || { - let is_closure = self.infcx.tcx.is_closure_like(self.mir_def_id().to_def_id()); + let tcx = self.infcx.tcx; + let is_closure = tcx.is_closure_like(self.mir_def_id().to_def_id()); if is_closure { None } else { @@ -4288,7 +4289,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { .instantiate_identity() .skip_norm_wip(); match ty.kind() { - ty::FnDef(_, _) | ty::FnPtr(..) => self.annotate_fn_sig( + ty::FnDef(_, _) => self.annotate_fn_sig( self.mir_def_id(), self.infcx .tcx @@ -4296,6 +4297,8 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { .instantiate_identity() .skip_norm_wip(), ), + // a const/static can have a fn ptr type, take the sig from the type instead. + ty::FnPtr(_, _) => self.annotate_fn_sig(self.mir_def_id(), ty.fn_sig(tcx)), _ => None, } } diff --git a/tests/ui/borrowck/const-fn-ptr-borrow-annotation.rs b/tests/ui/borrowck/const-fn-ptr-borrow-annotation.rs new file mode 100644 index 0000000000000..7d98d5a739797 --- /dev/null +++ b/tests/ui/borrowck/const-fn-ptr-borrow-annotation.rs @@ -0,0 +1,16 @@ +// Regression test for https://github.com/rust-lang/rust/issues/160255. + +use std::mem; + +const A: fn() = unsafe { + mem::transmute({ + fn fun() {} + let _ = fun as fn(); + { + let s = [0; 10]; + &s //~ ERROR: `s` does not live long enough [E0597] + } + }) +}; + +fn main() {} diff --git a/tests/ui/borrowck/const-fn-ptr-borrow-annotation.stderr b/tests/ui/borrowck/const-fn-ptr-borrow-annotation.stderr new file mode 100644 index 0000000000000..33ab95e4029b4 --- /dev/null +++ b/tests/ui/borrowck/const-fn-ptr-borrow-annotation.stderr @@ -0,0 +1,16 @@ +error[E0597]: `s` does not live long enough + --> $DIR/const-fn-ptr-borrow-annotation.rs:11:13 + | +LL | mem::transmute({ + | -------------- borrow later used by call +... +LL | let s = [0; 10]; + | - binding `s` declared here +LL | &s + | ^^ borrowed value does not live long enough +LL | } + | - `s` dropped here while still borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. From 2cee42105a5cc121030724e6ac806e3dd509c6e6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 1 Aug 2026 09:43:26 +0200 Subject: [PATCH 22/37] ElaborateBoxDeref: remove unnecessary projection --- compiler/rustc_middle/src/mir/statement.rs | 2 +- .../src/elaborate_box_derefs.rs | 34 ++++++------------- .../transmute.unreachable_box.GVN.32bit.diff | 4 +-- .../transmute.unreachable_box.GVN.64bit.diff | 4 +-- ...reachable_box.DataflowConstProp.32bit.diff | 2 +- ...reachable_box.DataflowConstProp.64bit.diff | 2 +- ...ng_operand.test.GVN.32bit.panic-abort.diff | 2 +- ...g_operand.test.GVN.32bit.panic-unwind.diff | 2 +- ...ng_operand.test.GVN.64bit.panic-abort.diff | 2 +- ...g_operand.test.GVN.64bit.panic-unwind.diff | 2 +- ...ric_rust_call.call.Inline.panic-abort.diff | 4 +-- ...ic_rust_call.call.Inline.panic-unwind.diff | 4 +-- ...inline_box_fn.call.Inline.panic-abort.diff | 4 +-- ...nline_box_fn.call.Inline.panic-unwind.diff | 4 +-- ...67_inline_as_ref_as_mut.b.Inline.after.mir | 4 +-- ...67_inline_as_ref_as_mut.d.Inline.after.mir | 4 +-- .../unsized_argument.caller.Inline.diff | 2 +- ...inhabited.LowerIntrinsics.panic-abort.diff | 2 +- ...nhabited.LowerIntrinsics.panic-unwind.diff | 2 +- 19 files changed, 37 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 17eeb7c3c12aa..e0c8789a8d8b3 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -442,7 +442,7 @@ impl<'tcx> Place<'tcx> { pub fn project_to_field( self, idx: FieldIdx, - local_decls: &impl HasLocalDecls<'tcx>, + local_decls: &(impl HasLocalDecls<'tcx> + ?Sized), tcx: TyCtxt<'tcx>, ) -> Self { let ty = self.ty(local_decls, tcx).ty; diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index 5c925b9ecaa42..6ce39aac6a0db 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -26,18 +26,8 @@ fn build_ptr_tys<'tcx>( (unique_ty, nonnull_ty, ptr_ty) } -/// Constructs the projection needed to access a Box's pointer -pub(super) fn build_projection<'tcx>( - unique_ty: Ty<'tcx>, - nonnull_ty: Ty<'tcx>, -) -> [PlaceElem<'tcx>; 2] { - [PlaceElem::Field(FieldIdx::ZERO, unique_ty), PlaceElem::Field(FieldIdx::ZERO, nonnull_ty)] -} - struct ElaborateBoxDerefVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, - unique_def: ty::AdtDef<'tcx>, - nonnull_def: ty::AdtDef<'tcx>, local_decls: &'a mut LocalDecls<'tcx>, patch: MirPatch<'tcx>, } @@ -63,22 +53,18 @@ impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> { { let source_info = self.local_decls[place.local].source_info; - let (unique_ty, nonnull_ty, ptr_ty) = - build_ptr_tys(tcx, boxed_ty, self.unique_def, self.nonnull_def); + let ptr_ty = Ty::new_imm_ptr(tcx, boxed_ty); let ptr_local = self.patch.new_temp(ptr_ty, source_info.span); + // Project to the first field (a `Unique`), then transmute that. We could project one + // further but in the end we'd hit a pattern type so we'd always have to transmute. + let field_place = + Place::from(place.local).project_to_field(FieldIdx::ZERO, &*self.local_decls, tcx); self.patch.add_assign( location, Place::from(ptr_local), - Rvalue::Cast( - CastKind::BoxDerefTransmute, - Operand::Copy( - Place::from(place.local) - .project_deeper(&build_projection(unique_ty, nonnull_ty), tcx), - ), - ptr_ty, - ), + Rvalue::Cast(CastKind::BoxDerefTransmute, Operand::Copy(field_place), ptr_ty), ); place.local = ptr_local; @@ -115,8 +101,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { let local_decls = &mut body.local_decls; - let mut visitor = - ElaborateBoxDerefVisitor { tcx, unique_def, nonnull_def, local_decls, patch }; + let mut visitor = ElaborateBoxDerefVisitor { tcx, local_decls, patch }; for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() { visitor.visit_basic_block_data(block, data); @@ -141,7 +126,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { let (unique_ty, nonnull_ty, ptr_ty) = build_ptr_tys(tcx, boxed_ty, unique_def, nonnull_def); - new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty)); + new_projections.extend_from_slice(&[ + PlaceElem::Field(FieldIdx::ZERO, unique_ty), + PlaceElem::Field(FieldIdx::ZERO, nonnull_ty), + ]); // While we can't project into a pattern type in a basic block, // this is debug info where it's fine. let pat_ty = Ty::new_pat(tcx, ptr_ty, tcx.mk_pat(PatternKind::NotNull)); diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff index a6756ba0245c7..f87e33bd69789 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.32bit.diff @@ -12,9 +12,9 @@ bb0: { StorageLive(_1); - _1 = const 1_usize as std::boxed::Box (Transmute); -- _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); +- _2 = copy (_1.0: std::ptr::Unique) as *const Never (BoxDerefTransmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData:: }}, std::alloc::Global); -+ _2 = const std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} is !null }} as *const Never (BoxDerefTransmute); ++ _2 = const std::ptr::Unique:: {{ pointer: std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: std::marker::PhantomData:: }} as *const Never (BoxDerefTransmute); unreachable; } } diff --git a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff index a6756ba0245c7..f87e33bd69789 100644 --- a/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.unreachable_box.GVN.64bit.diff @@ -12,9 +12,9 @@ bb0: { StorageLive(_1); - _1 = const 1_usize as std::boxed::Box (Transmute); -- _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); +- _2 = copy (_1.0: std::ptr::Unique) as *const Never (BoxDerefTransmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData:: }}, std::alloc::Global); -+ _2 = const std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} is !null }} as *const Never (BoxDerefTransmute); ++ _2 = const std::ptr::Unique:: {{ pointer: std::ptr::NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: std::marker::PhantomData:: }} as *const Never (BoxDerefTransmute); unreachable; } } diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff index 352d9345eef84..aaa0655d3c60e 100644 --- a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff +++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff @@ -13,7 +13,7 @@ StorageLive(_1); - _1 = const 1_usize as std::boxed::Box (Transmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData:: }}, std::alloc::Global); - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); + _2 = copy (_1.0: std::ptr::Unique) as *const Never (BoxDerefTransmute); unreachable; } } diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff index 352d9345eef84..aaa0655d3c60e 100644 --- a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff +++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff @@ -13,7 +13,7 @@ StorageLive(_1); - _1 = const 1_usize as std::boxed::Box (Transmute); + _1 = const Box::(std::ptr::Unique:: {{ pointer: NonNull:: {{ pointer: {0x1 as *const Never} is !null }}, _marker: PhantomData:: }}, std::alloc::Global); - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); + _2 = copy (_1.0: std::ptr::Unique) as *const Never (BoxDerefTransmute); unreachable; } } 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 8b5ad1519d27c..451d639ca2aa4 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 @@ -73,7 +73,7 @@ StorageDead(_2); StorageLive(_5); _10 = no_retag copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (BoxDerefTransmute); + _11 = copy (_10.0: std::ptr::Unique<()>) as *const () (BoxDerefTransmute); _5 = &raw const (*_11); StorageLive(_6); StorageLive(_7); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff index 14943534b98be..75b00c8885cd0 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff @@ -53,7 +53,7 @@ StorageDead(_2); StorageLive(_5); _10 = no_retag copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (BoxDerefTransmute); + _11 = copy (_10.0: std::ptr::Unique<()>) as *const () (BoxDerefTransmute); _5 = &raw const (*_11); StorageLive(_6); StorageLive(_7); 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 f8d47dcae5b27..3473ceb21a0ab 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 @@ -73,7 +73,7 @@ StorageDead(_2); StorageLive(_5); _10 = no_retag copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (BoxDerefTransmute); + _11 = copy (_10.0: std::ptr::Unique<()>) as *const () (BoxDerefTransmute); _5 = &raw const (*_11); StorageLive(_6); StorageLive(_7); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff index 14943534b98be..75b00c8885cd0 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff @@ -53,7 +53,7 @@ StorageDead(_2); StorageLive(_5); _10 = no_retag copy (*_1); - _11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (BoxDerefTransmute); + _11 = copy (_10.0: std::ptr::Unique<()>) as *const () (BoxDerefTransmute); _5 = &raw const (*_11); StorageLive(_6); StorageLive(_7); diff --git a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff index ceacf606f3553..92060c211330e 100644 --- a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-abort.diff @@ -10,7 +10,7 @@ + scope 1 (inlined > as FnMut>::call_mut) { + let mut _5: &mut dyn std::ops::FnMut; + let mut _6: *const dyn std::ops::FnMut; -+ let mut _7: std::ptr::NonNull>; ++ let mut _7: std::ptr::Unique>; + } bb0: { @@ -22,7 +22,7 @@ + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); -+ _7 = no_retag copy (((*_3).0: std::ptr::Unique>).0: std::ptr::NonNull>); ++ _7 = no_retag copy ((*_3).0: std::ptr::Unique>); + _6 = copy _7 as *const dyn std::ops::FnMut (BoxDerefTransmute); + _5 = &mut (*_6); + _0 = as FnMut>::call_mut(move _5, move _4) -> [return: bb2, unwind unreachable]; diff --git a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff index 862174fd94bff..085fa453ade13 100644 --- a/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/dont_ice_on_generic_rust_call.call.Inline.panic-unwind.diff @@ -10,7 +10,7 @@ + scope 1 (inlined > as FnMut>::call_mut) { + let mut _5: &mut dyn std::ops::FnMut; + let mut _6: *const dyn std::ops::FnMut; -+ let mut _7: std::ptr::NonNull>; ++ let mut _7: std::ptr::Unique>; + } bb0: { @@ -22,7 +22,7 @@ + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); -+ _7 = no_retag copy (((*_3).0: std::ptr::Unique>).0: std::ptr::NonNull>); ++ _7 = no_retag copy ((*_3).0: std::ptr::Unique>); + _6 = copy _7 as *const dyn std::ops::FnMut (BoxDerefTransmute); + _5 = &mut (*_6); + _0 = as FnMut>::call_mut(move _5, move _4) -> [return: bb4, unwind: bb2]; diff --git a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff index 0dc8adb257423..d04dc8f5ff5b2 100644 --- a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-abort.diff @@ -10,7 +10,7 @@ + scope 1 (inlined as Fn<(i32,)>>::call) { + let mut _5: &dyn std::ops::Fn(i32); + let mut _6: *const dyn std::ops::Fn(i32); -+ let mut _7: std::ptr::NonNull; ++ let mut _7: std::ptr::Unique; + } bb0: { @@ -23,7 +23,7 @@ + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); -+ _7 = no_retag copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); ++ _7 = no_retag copy ((*_3).0: std::ptr::Unique); + _6 = copy _7 as *const dyn std::ops::Fn(i32) (BoxDerefTransmute); + _5 = &(*_6); + _2 = >::call(move _5, move _4) -> [return: bb2, unwind unreachable]; diff --git a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff index 1b320f9200405..f2fc8c7388f7d 100644 --- a/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_box_fn.call.Inline.panic-unwind.diff @@ -10,7 +10,7 @@ + scope 1 (inlined as Fn<(i32,)>>::call) { + let mut _5: &dyn std::ops::Fn(i32); + let mut _6: *const dyn std::ops::Fn(i32); -+ let mut _7: std::ptr::NonNull; ++ let mut _7: std::ptr::Unique; + } bb0: { @@ -23,7 +23,7 @@ + StorageLive(_6); + StorageLive(_7); + StorageLive(_5); -+ _7 = no_retag copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); ++ _7 = no_retag copy ((*_3).0: std::ptr::Unique); + _6 = copy _7 as *const dyn std::ops::Fn(i32) (BoxDerefTransmute); + _5 = &(*_6); + _2 = >::call(move _5, move _4) -> [return: bb4, unwind: bb2]; diff --git a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir index f4972c7d1437e..1d56fa0860654 100644 --- a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir +++ b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.b.Inline.after.mir @@ -9,7 +9,7 @@ fn b(_1: &mut Box) -> &mut T { scope 1 (inlined as AsMut>::as_mut) { debug self => _4; let mut _5: *const T; - let mut _6: std::ptr::NonNull; + let mut _6: std::ptr::Unique; } bb0: { @@ -19,7 +19,7 @@ fn b(_1: &mut Box) -> &mut T { _4 = no_retag copy _1; StorageLive(_5); StorageLive(_6); - _6 = no_retag copy (((*_4).0: std::ptr::Unique).0: std::ptr::NonNull); + _6 = no_retag copy ((*_4).0: std::ptr::Unique); _5 = copy _6 as *const T (BoxDerefTransmute); _3 = &mut (*_5); StorageDead(_6); diff --git a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir index d5a0450af828e..a74065283737b 100644 --- a/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir +++ b/tests/mir-opt/inline/issue_58867_inline_as_ref_as_mut.d.Inline.after.mir @@ -8,7 +8,7 @@ fn d(_1: &Box) -> &T { scope 1 (inlined as AsRef>::as_ref) { debug self => _3; let mut _4: *const T; - let mut _5: std::ptr::NonNull; + let mut _5: std::ptr::Unique; } bb0: { @@ -17,7 +17,7 @@ fn d(_1: &Box) -> &T { _3 = copy _1; StorageLive(_4); StorageLive(_5); - _5 = no_retag copy (((*_3).0: std::ptr::Unique).0: std::ptr::NonNull); + _5 = no_retag copy ((*_3).0: std::ptr::Unique); _4 = copy _5 as *const T (BoxDerefTransmute); _2 = &(*_4); StorageDead(_5); diff --git a/tests/mir-opt/inline/unsized_argument.caller.Inline.diff b/tests/mir-opt/inline/unsized_argument.caller.Inline.diff index 8ca4ca123c829..6865766499a6a 100644 --- a/tests/mir-opt/inline/unsized_argument.caller.Inline.diff +++ b/tests/mir-opt/inline/unsized_argument.caller.Inline.diff @@ -12,7 +12,7 @@ StorageLive(_2); StorageLive(_3); _3 = move _1; - _4 = copy ((_3.0: std::ptr::Unique<[i32]>).0: std::ptr::NonNull<[i32]>) as *const [i32] (BoxDerefTransmute); + _4 = copy (_3.0: std::ptr::Unique<[i32]>) as *const [i32] (BoxDerefTransmute); _2 = callee(move (*_4)) -> [return: bb1, unwind: bb3]; } diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff index adf61031b3699..3a6a8e137aadc 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-abort.diff @@ -17,7 +17,7 @@ } bb1: { - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); + _2 = copy (_1.0: std::ptr::Unique) as *const Never (BoxDerefTransmute); PlaceMention((*_2)); unreachable; } diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff index adf61031b3699..3a6a8e137aadc 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.panic-unwind.diff @@ -17,7 +17,7 @@ } bb1: { - _2 = copy ((_1.0: std::ptr::Unique).0: std::ptr::NonNull) as *const Never (BoxDerefTransmute); + _2 = copy (_1.0: std::ptr::Unique) as *const Never (BoxDerefTransmute); PlaceMention((*_2)); unreachable; } From 5ab7f5081214ab5376e61d974735ab1d1016beca Mon Sep 17 00:00:00 2001 From: Cole Kauder-McMurrich Date: Sat, 1 Aug 2026 20:50:39 -0400 Subject: [PATCH 23/37] Comment fix for rustdoc ICE when checking if a generic arg can be elided --- src/librustdoc/clean/utils.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 597543c957d13..20a466fd3dee9 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -117,6 +117,8 @@ pub(crate) fn clean_middle_generic_args<'tcx>( }; let mut elision_has_failed_once_before = false; + + // Calculates where the parent trait's generic parameters end let index_offset = generics.count() - args.len(); let clean_arg = |(index, &arg): (usize, &ty::GenericArg<'tcx>)| { // Elide the self type. @@ -124,6 +126,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( return None; } + // Skips over the parent trait's generic parameters let param = generics.param_at(index + index_offset, cx.tcx); let arg = ty::Binder::bind_with_vars(arg, bound_vars); From 569e0bc92bdb5beb88c88a9b3cf49e447b6e0463 Mon Sep 17 00:00:00 2001 From: Cole Kauder-McMurrich Date: Sat, 1 Aug 2026 21:13:46 -0400 Subject: [PATCH 24/37] Further minimize regression test for rustdoc ICE --- tests/rustdoc-ui/ice-clean-generic-args-133637.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/rustdoc-ui/ice-clean-generic-args-133637.rs b/tests/rustdoc-ui/ice-clean-generic-args-133637.rs index b90ff547b9cfa..9b2b5d9dae4af 100644 --- a/tests/rustdoc-ui/ice-clean-generic-args-133637.rs +++ b/tests/rustdoc-ui/ice-clean-generic-args-133637.rs @@ -5,14 +5,8 @@ // Regression test for issue #133637. Previously we would index into the flattened generics list // with the children generic indexes. This resulted in an ICE when debug assertions were on. -struct SomeDefault; - -trait SomeTrait { +trait Trait { type Type<'a, 'b>; } -impl SomeTrait for T { - type Type<'a, 'b> = (&'a u8, &'b u8); -} - -type SomeType<'a, 'b, T, Gen = SomeDefault> = >::Type<'a, 'b>; +type Type = ::Type<'static, 'static>; From 5ba0b9f70c68aaadf47724a931f38c8ae37d431f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 1 Aug 2026 23:15:07 -0700 Subject: [PATCH 25/37] Add (failing) test to check for an exact alias before a similar name --- .../suggest-exact-alias-before-similar-name.rs | 15 +++++++++++++++ ...gest-exact-alias-before-similar-name.stderr | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/ui/suggestions/suggest-exact-alias-before-similar-name.rs create mode 100644 tests/ui/suggestions/suggest-exact-alias-before-similar-name.stderr diff --git a/tests/ui/suggestions/suggest-exact-alias-before-similar-name.rs b/tests/ui/suggestions/suggest-exact-alias-before-similar-name.rs new file mode 100644 index 0000000000000..2279dc87ed646 --- /dev/null +++ b/tests/ui/suggestions/suggest-exact-alias-before-similar-name.rs @@ -0,0 +1,15 @@ +struct Reader; +//~^ NOTE method `read_exact_buf` not found for this struct + +impl Reader { + fn read_exact(&self) {} + + #[doc(alias("read_exact_buf"))] + fn read_buf_exact(&self) {} +} + +fn main() { + Reader.read_exact_buf(); + //~^ ERROR no method named `read_exact_buf` found for struct `Reader` in the current scope + //~^^ HELP there is a method `read_exact` with a similar name +} diff --git a/tests/ui/suggestions/suggest-exact-alias-before-similar-name.stderr b/tests/ui/suggestions/suggest-exact-alias-before-similar-name.stderr new file mode 100644 index 0000000000000..422c9e50ac5e7 --- /dev/null +++ b/tests/ui/suggestions/suggest-exact-alias-before-similar-name.stderr @@ -0,0 +1,18 @@ +error[E0599]: no method named `read_exact_buf` found for struct `Reader` in the current scope + --> $DIR/suggest-exact-alias-before-similar-name.rs:12:12 + | +LL | struct Reader; + | ------------- method `read_exact_buf` not found for this struct +... +LL | Reader.read_exact_buf(); + | ^^^^^^^^^^^^^^ + | +help: there is a method `read_exact` with a similar name + | +LL - Reader.read_exact_buf(); +LL + Reader.read_exact(); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. From f7596e8603a367e899afd16b96197b07b97bc513 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 1 Aug 2026 23:56:43 -0700 Subject: [PATCH 26/37] Add doc aliases for transpositions `read_exact_buf` and `read_exact_buf_at` When fingers really want to type `read_exact`, that naturally leads to the typo `read_exact_buf` instead of `read_buf_exact`, and `read_exact_buf_at` instead of `read_buf_exact_at`. Add doc aliases. These will help people find them in rustdoc, and once https://github.com/rust-lang/rust/pull/160369 goes in, it'll also help people find them as rustfix suggestions. --- library/alloc/src/io/read.rs | 1 + library/std/src/os/unix/fs.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index 490a55f9d10dc..c9e639fb92e86 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -429,6 +429,7 @@ pub trait Read { /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted /// [`ErrorKind::UnexpectedEof`]: crate::io::ErrorKind::UnexpectedEof #[unstable(feature = "read_buf", issue = "78485")] + #[doc(alias("read_exact_buf"))] fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_, u8>) -> Result<()> { default_read_buf_exact(self, cursor) } diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index c119912c3b022..9b08f0cb3829f 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -198,6 +198,7 @@ pub trait FileExt { /// } /// ``` #[unstable(feature = "read_buf_at", issue = "140771")] + #[doc(alias("read_exact_buf_at"))] fn read_buf_exact_at( &self, mut buf: BorrowedCursor<'_, u8>, From 32c2ca0099cc13f39b96e684722b0bfd37c0cfeb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 1 Aug 2026 23:42:38 -0700 Subject: [PATCH 27/37] When suggesting method names, prefer *exact* doc aliases over similar names We currently prefer candidates from a similarity search over doc aliases or `rustc_confusables`, even though the latter requires an *exact* match. Reverse this order, so that an exactly matching doc alias or `rustc_confusables` entry will take precedence. The net result of this is that `read_exact_buf` will now produce a suggestion for `read_buf_exact`, not `read_exact`, if both exist and the former has a doc alias for `read_exact_buf`. --- compiler/rustc_hir_typeck/src/method/probe.rs | 32 +++++++++---------- .../attributes/rustc_confusables_std_cases.rs | 1 - .../rustc_confusables_std_cases.stderr | 10 +++--- ...suggest-exact-alias-before-similar-name.rs | 2 +- ...est-exact-alias-before-similar-name.stderr | 4 +-- 5 files changed, 22 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index eab4e1990455c..51b7d1b0c3e49 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2526,23 +2526,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if applicable_close_candidates.is_empty() { Ok(None) } else { - let best_name = { - let names = applicable_close_candidates - .iter() - .map(|cand| cand.name()) - .collect::>(); - find_best_match_for_name_with_substrings( - &names, - self.method_name.unwrap().name, - None, - ) - } - .or_else(|| { - applicable_close_candidates - .iter() - .find(|cand| self.matches_by_doc_alias(cand.def_id)) - .map(|cand| cand.name()) - }); + let best_name = applicable_close_candidates + .iter() + .find(|cand| self.matches_by_doc_alias(cand.def_id)) + .map(|cand| cand.name()) + .or_else(|| { + let names = applicable_close_candidates + .iter() + .map(|cand| cand.name()) + .collect::>(); + find_best_match_for_name_with_substrings( + &names, + self.method_name.unwrap().name, + None, + ) + }); Ok(best_name.and_then(|best_name| { applicable_close_candidates .into_iter() diff --git a/tests/ui/attributes/rustc_confusables_std_cases.rs b/tests/ui/attributes/rustc_confusables_std_cases.rs index 4f6baea26dfd7..5e5b806d517b6 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.rs +++ b/tests/ui/attributes/rustc_confusables_std_cases.rs @@ -16,7 +16,6 @@ fn main() { //~^ HELP you might have meant to use `len` x.size(); //~ ERROR E0599 //~^ HELP you might have meant to use `len` - //~| HELP there is a method `resize` with a similar name x.append(42); //~ ERROR E0308 //~^ HELP you might have meant to use `push` String::new().push(""); //~ ERROR E0308 diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index f58950f3cc618..d9bf05d71f122 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -59,8 +59,6 @@ error[E0599]: no method named `size` found for struct `Vec<{integer}>` in the cu LL | x.size(); | ^^^^ | -help: there is a method `resize` with a similar name, but with different arguments - --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: you might have meant to use `len` | LL - x.size(); @@ -68,7 +66,7 @@ LL + x.len(); | error[E0308]: mismatched types - --> $DIR/rustc_confusables_std_cases.rs:20:14 + --> $DIR/rustc_confusables_std_cases.rs:19:14 | LL | x.append(42); | ------ ^^ expected `&mut Vec<{integer}>`, found integer @@ -86,7 +84,7 @@ LL + x.push(42); | error[E0308]: mismatched types - --> $DIR/rustc_confusables_std_cases.rs:22:24 + --> $DIR/rustc_confusables_std_cases.rs:21:24 | LL | String::new().push(""); | ---- ^^ expected `char`, found `&str` @@ -101,7 +99,7 @@ LL | String::new().push_str(""); | ++++ error[E0599]: no method named `append` found for struct `String` in the current scope - --> $DIR/rustc_confusables_std_cases.rs:24:19 + --> $DIR/rustc_confusables_std_cases.rs:23:19 | LL | String::new().append(""); | ^^^^^^ @@ -113,7 +111,7 @@ LL + String::new().push_str(""); | error[E0599]: no method named `get_line` found for struct `Stdin` in the current scope - --> $DIR/rustc_confusables_std_cases.rs:28:11 + --> $DIR/rustc_confusables_std_cases.rs:27:11 | LL | stdin.get_line(&mut buffer).unwrap(); | ^^^^^^^^ diff --git a/tests/ui/suggestions/suggest-exact-alias-before-similar-name.rs b/tests/ui/suggestions/suggest-exact-alias-before-similar-name.rs index 2279dc87ed646..6478a1c328ef4 100644 --- a/tests/ui/suggestions/suggest-exact-alias-before-similar-name.rs +++ b/tests/ui/suggestions/suggest-exact-alias-before-similar-name.rs @@ -11,5 +11,5 @@ impl Reader { fn main() { Reader.read_exact_buf(); //~^ ERROR no method named `read_exact_buf` found for struct `Reader` in the current scope - //~^^ HELP there is a method `read_exact` with a similar name + //~^^ HELP there is a method `read_buf_exact` with a similar name } diff --git a/tests/ui/suggestions/suggest-exact-alias-before-similar-name.stderr b/tests/ui/suggestions/suggest-exact-alias-before-similar-name.stderr index 422c9e50ac5e7..ba18e84a78868 100644 --- a/tests/ui/suggestions/suggest-exact-alias-before-similar-name.stderr +++ b/tests/ui/suggestions/suggest-exact-alias-before-similar-name.stderr @@ -7,10 +7,10 @@ LL | struct Reader; LL | Reader.read_exact_buf(); | ^^^^^^^^^^^^^^ | -help: there is a method `read_exact` with a similar name +help: there is a method `read_buf_exact` with a similar name | LL - Reader.read_exact_buf(); -LL + Reader.read_exact(); +LL + Reader.read_buf_exact(); | error: aborting due to 1 previous error From ad2ca88d8df5d9ba52182c70b29ea1c2ac22476d Mon Sep 17 00:00:00 2001 From: Amirhossein Akhlaghpour Date: Sun, 2 Aug 2026 20:34:25 +0330 Subject: [PATCH 28/37] fix: restrict bool indexing assembly test from Windows Signed-off-by: Amirhossein Akhlaghpour --- .../indexing-with-bools-no-redundant-instructions.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/assembly-llvm/indexing-with-bools-no-redundant-instructions.rs b/tests/assembly-llvm/indexing-with-bools-no-redundant-instructions.rs index 628f4faab6d06..7397f2ec673b0 100644 --- a/tests/assembly-llvm/indexing-with-bools-no-redundant-instructions.rs +++ b/tests/assembly-llvm/indexing-with-bools-no-redundant-instructions.rs @@ -1,5 +1,6 @@ //@ assembly-output: emit-asm //@ only-x86_64 +//@ ignore-windows CHECK patterns use the SysV x86-64 calling convention //@ ignore-sgx Test incompatible with LVI mitigations //@ compile-flags: -Copt-level=3 From 910ec5a8d0e8af22eebcf9f1664826f20844783c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 2 Aug 2026 20:05:43 +1000 Subject: [PATCH 29/37] Simplify `DepKind` constants This commit changes `DEP_KIND_NUM_VARIANTS` so it's an associated const (renamed as `DepKind::NUM_VARIANTS`) obtained via `std::mem::variant_count`, removing the paranoid consecutiveness check. The commit also replaces multiple `DepKind::MAX as usize + 1` occurrences with `DepKind::NUM_VARIANTS`. --- .../rustc_middle/src/dep_graph/dep_node.rs | 33 +++++++------------ .../rustc_middle/src/dep_graph/serialized.rs | 8 ++--- compiler/rustc_middle/src/lib.rs | 1 + 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index b6fda22775c2a..c59d9620b5e53 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -69,7 +69,8 @@ impl DepKind { if u > Self::MAX { panic!("Invalid DepKind {u}"); } - // SAFETY: See comment on DEP_KIND_NUM_VARIANTS + // SAFETY: `DepKind` is `repr(u16)`, its variants are `0..=MAX`, and `u` was checked + // against `MAX` above. unsafe { std::mem::transmute(u) } } @@ -83,9 +84,16 @@ impl DepKind { *self as usize } + /// The number of dep kind variants. + pub(crate) const NUM_VARIANTS: usize = std::mem::variant_count::(); + /// This is the highest value a `DepKind` can have. It's used during encoding to - /// pack information into the unused bits. - pub(crate) const MAX: u16 = DEP_KIND_NUM_VARIANTS - 1; + /// pack information into the unused bits. u16 matches the `repr(u16)` on `DepKind`. + pub(crate) const MAX: u16 = { + let max = Self::NUM_VARIANTS - 1; + assert!(max < u16::MAX as usize); + max as u16 + }; } /// Combination of a [`DepKind`] and a key fingerprint that uniquely identifies @@ -279,25 +287,6 @@ macro_rules! define_dep_nodes { $( $(#[$q_attr])* $q_name, )* } - // This computes the number of dep kind variants. Along the way, it sanity-checks that the - // discriminants of the variants have been assigned consecutively from 0 so that they can - // be used as a dense index, and that all discriminants fit in a `u16`. - pub(crate) const DEP_KIND_NUM_VARIANTS: u16 = { - let deps = &[ - $(DepKind::$nq_name,)* - $(DepKind::$q_name,)* - ]; - let mut i = 0; - while i < deps.len() { - if i != deps[i].as_usize() { - panic!(); - } - i += 1; - } - assert!(deps.len() <= u16::MAX as usize); - deps.len() as u16 - }; - pub(super) fn dep_kind_from_label_string(label: &str) -> Result { match label { $( stringify!($nq_name) => Ok(self::DepKind::$nq_name), )* diff --git a/compiler/rustc_middle/src/dep_graph/serialized.rs b/compiler/rustc_middle/src/dep_graph/serialized.rs index daebc887055ac..1c476fc91697e 100644 --- a/compiler/rustc_middle/src/dep_graph/serialized.rs +++ b/compiler/rustc_middle/src/dep_graph/serialized.rs @@ -387,9 +387,9 @@ impl SerializedDepGraph { // Read the number of nodes of each dep kind, and perform // counting sort for `LazyNodeIndex`. - let mut kinds = Vec::with_capacity(DepKind::MAX as usize + 1); + let mut kinds = Vec::with_capacity(DepKind::NUM_VARIANTS); let mut offset = 0u32; - for _ in 0..(DepKind::MAX + 1) { + for _ in 0..(DepKind::NUM_VARIANTS) { let len = d.read_u32(); kinds.push(LazyKindIndex { start: offset, len, map: OnceLock::new() }); offset += len; @@ -654,7 +654,7 @@ impl EncoderState { edge_count: 0, node_count: 0, encoder: MemEncoder::new(), - kind_stats: iter::repeat_n(0, DepKind::MAX as usize + 1).collect(), + kind_stats: iter::repeat_n(0, DepKind::NUM_VARIANTS).collect(), }) }), } @@ -792,7 +792,7 @@ impl EncoderState { let mut encoder = self.file.lock().take().unwrap(); - let mut kind_stats: Vec = iter::repeat_n(0, DepKind::MAX as usize + 1).collect(); + let mut kind_stats: Vec = iter::repeat_n(0, DepKind::NUM_VARIANTS).collect(); let mut node_max = 0; let mut node_count = 0; diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 69c2e099080c9..ed1a2f7a831b1 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -56,6 +56,7 @@ #![feature(try_trait_v2_residual)] #![feature(try_trait_v2_yeet)] #![feature(type_alias_impl_trait)] +#![feature(variant_count)] #![feature(yeet_expr)] #![recursion_limit = "256"] // tidy-alphabetical-end From c4e9932cacd4508b4fde4e84bc76e177bfd0229d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 2 Aug 2026 20:39:31 +1000 Subject: [PATCH 30/37] Remove `DepKind::label_strs` It contains pre-stringified versions of all the `DepKind` variants. But we can just stringify on demand using `format!("{:?}")`. --- .../rustc_incremental/src/persist/clean.rs | 59 +++++++++---------- .../rustc_middle/src/dep_graph/dep_node.rs | 8 --- compiler/rustc_middle/src/dep_graph/mod.rs | 4 +- 3 files changed, 29 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_incremental/src/persist/clean.rs b/compiler/rustc_incremental/src/persist/clean.rs index d3a04ab5946b7..a311832e62d96 100644 --- a/compiler/rustc_incremental/src/persist/clean.rs +++ b/compiler/rustc_incremental/src/persist/clean.rs @@ -27,7 +27,7 @@ use rustc_hir::{ Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, find_attr, intravisit, }; -use rustc_middle::dep_graph::{DepNode, dep_kind_from_label, label_strs}; +use rustc_middle::dep_graph::{DepKind, DepNode, dep_kind_from_label}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::{Span, Symbol}; @@ -38,81 +38,78 @@ use crate::diagnostics; // Base and Extra labels to build up the labels /// For typedef, constants, and statics -const BASE_CONST: &[&str] = &[label_strs::type_of]; +const BASE_CONST: &[DepKind] = &[DepKind::type_of]; /// DepNodes for functions + methods -const BASE_FN: &[&str] = &[ +const BASE_FN: &[DepKind] = &[ // Callers will depend on the signature of these items, so we better test - label_strs::fn_sig, - label_strs::generics_of, - label_strs::clauses_of, - label_strs::type_of, + DepKind::fn_sig, + DepKind::generics_of, + DepKind::clauses_of, + DepKind::type_of, // And a big part of compilation (that we eventually want to cache) is type inference // information: - label_strs::typeck_root, + DepKind::typeck_root, ]; /// DepNodes for Hir, which is pretty much everything -const BASE_HIR: &[&str] = &[ +const BASE_HIR: &[DepKind] = &[ // hir_owner should be computed for all nodes - label_strs::hir_owner, + DepKind::hir_owner, ]; /// `impl` implementation of struct/trait -const BASE_IMPL: &[&str] = - &[label_strs::associated_item_def_ids, label_strs::generics_of, label_strs::impl_trait_header]; +const BASE_IMPL: &[DepKind] = + &[DepKind::associated_item_def_ids, DepKind::generics_of, DepKind::impl_trait_header]; /// DepNodes for exported mir bodies, which is relevant in "executable" /// code, i.e., functions+methods -const BASE_MIR: &[&str] = &[label_strs::optimized_mir, label_strs::promoted_mir]; +const BASE_MIR: &[DepKind] = &[DepKind::optimized_mir, DepKind::promoted_mir]; /// Struct, Enum and Union DepNodes /// /// Note that changing the type of a field does not change the type of the struct or enum, but /// adding/removing fields or changing a fields name or visibility does. -const BASE_STRUCT: &[&str] = - &[label_strs::generics_of, label_strs::clauses_of, label_strs::type_of]; +const BASE_STRUCT: &[DepKind] = &[DepKind::generics_of, DepKind::clauses_of, DepKind::type_of]; /// Trait definition `DepNode`s. /// Extra `DepNode`s for functions and methods. -const EXTRA_ASSOCIATED: &[&str] = &[label_strs::associated_item]; +const EXTRA_ASSOCIATED: &[DepKind] = &[DepKind::associated_item]; -const EXTRA_TRAIT: &[&str] = &[]; +const EXTRA_TRAIT: &[DepKind] = &[]; // Fully Built Labels -const LABELS_CONST: &[&[&str]] = &[BASE_HIR, BASE_CONST]; +const LABELS_CONST: &[&[DepKind]] = &[BASE_HIR, BASE_CONST]; /// Constant/Typedef in an impl -const LABELS_CONST_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED]; +const LABELS_CONST_IN_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED]; /// Trait-Const/Typedef DepNodes -const LABELS_CONST_IN_TRAIT: &[&[&str]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT]; +const LABELS_CONST_IN_TRAIT: &[&[DepKind]] = &[BASE_HIR, BASE_CONST, EXTRA_ASSOCIATED, EXTRA_TRAIT]; /// Function `DepNode`s. -const LABELS_FN: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN]; +const LABELS_FN: &[&[DepKind]] = &[BASE_HIR, BASE_MIR, BASE_FN]; /// Method `DepNode`s. -const LABELS_FN_IN_IMPL: &[&[&str]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED]; +const LABELS_FN_IN_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED]; /// Trait method `DepNode`s. -const LABELS_FN_IN_TRAIT: &[&[&str]] = +const LABELS_FN_IN_TRAIT: &[&[DepKind]] = &[BASE_HIR, BASE_MIR, BASE_FN, EXTRA_ASSOCIATED, EXTRA_TRAIT]; /// For generic cases like inline-assembly, modules, etc. -const LABELS_HIR_ONLY: &[&[&str]] = &[BASE_HIR]; +const LABELS_HIR_ONLY: &[&[DepKind]] = &[BASE_HIR]; /// Impl `DepNode`s. -const LABELS_TRAIT: &[&[&str]] = &[ - BASE_HIR, - &[label_strs::associated_item_def_ids, label_strs::clauses_of, label_strs::generics_of], -]; +const LABELS_TRAIT: &[&[DepKind]] = + &[BASE_HIR, &[DepKind::associated_item_def_ids, DepKind::clauses_of, DepKind::generics_of]]; /// Impl `DepNode`s. -const LABELS_IMPL: &[&[&str]] = &[BASE_HIR, BASE_IMPL]; +const LABELS_IMPL: &[&[DepKind]] = &[BASE_HIR, BASE_IMPL]; /// Abstract data type (struct, enum, union) `DepNode`s. -const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT]; +const LABELS_ADT: &[&[DepKind]] = &[BASE_HIR, BASE_STRUCT]; // FIXME: Struct/Enum/Unions Fields (there is currently no way to attach these) // @@ -289,7 +286,7 @@ impl<'tcx> CleanVisitor<'tcx> { .emit_fatal(diagnostics::UndefinedCleanDirty { span, kind: format!("{node:?}") }), }; let labels = - Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string()))); + Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| format!("{l:?}")))); (name, labels) } diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index c59d9620b5e53..e2a2bb8f2a552 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -294,14 +294,6 @@ macro_rules! define_dep_nodes { _ => Err(()), } } - - /// Contains variant => str representations for constructing - /// DepNode groups for tests. - #[expect(non_upper_case_globals)] - pub mod label_strs { - $( pub const $nq_name: &str = stringify!($nq_name); )* - $( pub const $q_name: &str = stringify!($q_name); )* - } }; } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 4f9cb03ff663e..3389c3ec91a5a 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -2,9 +2,7 @@ use std::panic; use tracing::instrument; -pub use self::dep_node::{ - DepKind, DepKindVTable, DepNode, WorkProductId, dep_kind_from_label, label_strs, -}; +pub use self::dep_node::{DepKind, DepKindVTable, DepNode, WorkProductId, dep_kind_from_label}; pub use self::dep_node_key::DepNodeKey; pub use self::graph::{ DepGraph, DepGraphData, DepNodeIndex, QuerySideEffect, TaskDepsRef, WorkProduct, From a707cbff5b5089982d7aaf471c636b2f6de5166c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 3 Aug 2026 07:23:11 +1000 Subject: [PATCH 31/37] Document `dep_kind_from_label_string` --- compiler/rustc_middle/src/dep_graph/dep_node.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index e2a2bb8f2a552..6abec9a4ff465 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -287,7 +287,9 @@ macro_rules! define_dep_nodes { $( $(#[$q_attr])* $q_name, )* } - pub(super) fn dep_kind_from_label_string(label: &str) -> Result { + /// Converts a string to a `DepKind`. Used for handling attributes like `rustc_clean` that + /// name dep kinds. + fn dep_kind_from_label_string(label: &str) -> Result { match label { $( stringify!($nq_name) => Ok(self::DepKind::$nq_name), )* $( stringify!($q_name) => Ok(self::DepKind::$q_name), )* From 3c80f1745f03da76b64b55783199cbd308226119 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 15 Jul 2026 04:47:45 +0900 Subject: [PATCH 32/37] add regression test for direct inline const generic defaults --- .../direct-inline-const-generic-default.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/direct-inline-const-generic-default.rs diff --git a/tests/ui/const-generics/generic_const_exprs/direct-inline-const-generic-default.rs b/tests/ui/const-generics/generic_const_exprs/direct-inline-const-generic-default.rs new file mode 100644 index 0000000000000..358d0d997cae8 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/direct-inline-const-generic-default.rs @@ -0,0 +1,10 @@ +//@ check-pass + +// Regression test for https://github.com/rust-lang/rust/issues/159063. + +#![feature(generic_const_exprs)] +#![feature(min_generic_const_args)] + +struct S; + +fn main() {} From a0ac21b64256b99ab2ef5fed2545010538e4cb0b Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 15 Jul 2026 04:47:57 +0900 Subject: [PATCH 33/37] fix generics lookup for direct inline const defaults --- compiler/rustc_hir_analysis/src/collect/generics_of.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index a986ae3964e26..dcc5579e14339 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -140,7 +140,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // // This has some implications for how we get the clauses available to the anon const // see `explicit_clauses_of` for more information on this - let generics = tcx.generics_of(parent_did); + let parent_def_id = tcx.local_parent(param_id); + let generics = tcx.generics_of(parent_def_id); let param_def_idx = generics.param_def_id_to_index[¶m_id.to_def_id()]; // In the above example this would be .params[..N#0] let own_params = generics.params_to(param_def_idx as usize, tcx).to_owned(); From fd3c89da8e3ff4bc0a7f43d35433d26a9108a43f Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:53:21 +0200 Subject: [PATCH 34/37] Add PR body notes for Cargo lock file maintenance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Beránek --- .github/renovate.json5 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 1725fb3426564..1827901fc041e 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -35,11 +35,16 @@ "dependencyDashboardApproval": false }, { - // Update all Cargo.lock files except library/Cargo.lock in one PR. + // Set defaults for all Cargo.lock files. + // library/Cargo.lock is grouped into a dedicated PR by the more + // specific rule below. "matchManagers": ["cargo"], "matchUpdateTypes": ["lockFileMaintenance"], "groupName": "Cargo lock file maintenance", - "commitMessageAction": "Cargo lock file maintenance" + "commitMessageAction": "Compiler and tools lock file update", + // Renovate merges all matching rules, so the lockfiles rules below + // also inherits this note and asks Triagebot for a dep-bumps reviewer. + "prBodyNotes": ["r? dep-bumps"] }, { // Update library/Cargo.lock in a dedicated PR. @@ -47,7 +52,7 @@ "matchUpdateTypes": ["lockFileMaintenance"], "matchFileNames": ["library/Cargo.lock"], "groupName": "library lock file maintenance", - "commitMessageAction": "Library lock file maintenance" + "commitMessageAction": "Library lock file update" }, { // These packages don't have a committed Cargo.lock file. @@ -63,7 +68,7 @@ "matchManagers": ["npm"], "matchUpdateTypes": ["lockFileMaintenance"], "groupName": "Yarn lock file maintenance", - "commitMessageAction": "Yarn lock file maintenance" + "commitMessageAction": "Yarn lock file update" } ], "ignorePaths": [ From 1a3d919258ff51abc7dfb5ecf6fdc9b15ff8a8c2 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 15 Jul 2026 12:14:08 +0200 Subject: [PATCH 35/37] Deny multiple EII impls on a single item This allows implementing EIIs that don't have a default impl without the involvement of symbol aliases by simply changing the mangled symbol name of the implementation, which would make them trivially compatible with all backends and targets. This change will be left to a followup PR. --- compiler/rustc_ast/src/ast.rs | 6 +- compiler/rustc_ast/src/visit.rs | 4 +- compiler/rustc_ast_lowering/src/item.rs | 24 +++-- .../rustc_ast_passes/src/ast_validation.rs | 30 +++---- .../rustc_ast_pretty/src/pprust/state/item.rs | 20 ++--- .../src/alloc_error_handler.rs | 2 +- compiler/rustc_builtin_macros/src/autodiff.rs | 2 +- .../src/deriving/generic/mod.rs | 2 +- .../rustc_builtin_macros/src/diagnostics.rs | 20 +++-- compiler/rustc_builtin_macros/src/eii.rs | 54 ++++++----- .../src/global_allocator.rs | 2 +- compiler/rustc_builtin_macros/src/offload.rs | 4 +- .../rustc_builtin_macros/src/test_harness.rs | 2 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 90 +++++++++---------- compiler/rustc_expand/src/build.rs | 2 +- compiler/rustc_expand/src/expand.rs | 4 +- .../rustc_hir/src/attrs/data_structures.rs | 2 +- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- .../src/check/compare_eii.rs | 3 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 16 ++-- compiler/rustc_hir_analysis/src/lib.rs | 1 - compiler/rustc_metadata/src/eii.rs | 14 +-- compiler/rustc_parse/src/parser/item.rs | 17 ++-- compiler/rustc_passes/src/check_attr.rs | 85 +++++++++--------- compiler/rustc_resolve/src/def_collector.rs | 2 +- compiler/rustc_resolve/src/late.rs | 11 +-- .../clippy/clippy_utils/src/ast_utils/mod.rs | 20 ++--- tests/ui/eii/duplicate/both_decl_and_impl.rs | 27 ++++++ .../eii/duplicate/both_decl_and_impl.stderr | 28 ++++++ tests/ui/eii/duplicate/multiple_impls.rs | 20 ++++- .../eii/duplicate/multiple_impls.run.stdout | 2 - tests/ui/eii/duplicate/multiple_impls.stderr | 30 +++++++ tests/ui/eii/static/multiple_impls.rs | 20 ----- tests/ui/eii/static/multiple_impls.run.stdout | 1 - tests/ui/eii/static/multiple_impls.stderr | 10 --- 35 files changed, 320 insertions(+), 259 deletions(-) create mode 100644 tests/ui/eii/duplicate/both_decl_and_impl.rs create mode 100644 tests/ui/eii/duplicate/both_decl_and_impl.stderr delete mode 100644 tests/ui/eii/duplicate/multiple_impls.run.stdout create mode 100644 tests/ui/eii/duplicate/multiple_impls.stderr delete mode 100644 tests/ui/eii/static/multiple_impls.rs delete mode 100644 tests/ui/eii/static/multiple_impls.run.stdout delete mode 100644 tests/ui/eii/static/multiple_impls.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 1f50fd8ac36e0..110c64c103acb 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3981,7 +3981,7 @@ pub struct Fn { /// This function is an implementation of an externally implementable item (EII). /// This means, there was an EII declared somewhere and this function is the /// implementation that should be run when the declaration is called. - pub eii_impls: ThinVec, + pub eii_impl: Option>, } impl Fn { @@ -4073,9 +4073,7 @@ pub struct StaticItem { /// This static is an implementation of an externally implementable item (EII). /// This means, there was an EII declared somewhere and this static is the /// implementation that should be used for the declaration. - /// - /// For statics, there may be at most one `EiiImpl`, but this is a `ThinVec` to make usages of this field nicer. - pub eii_impls: ThinVec, + pub eii_impl: Option>, } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a768935f38fc3..9d4c32825e1e4 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -933,12 +933,12 @@ macro_rules! common_visitor_and_walkers { _ctxt, // Visibility is visited as a part of the item. _vis, - Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impls }, + Fn { defaultness, ident, sig, generics, contract, body, define_opaque, eii_impl }, ) => { let FnSig { header, decl, span } = sig; visit_visitable!($($mut)? vis, defaultness, ident, header, generics, decl, - contract, body, span, define_opaque, eii_impls + contract, body, span, define_opaque, eii_impl ); } FnKind::Closure(binder, coroutine_kind, decl, body) => diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 34c7137b8676d..3cc27be600965 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -170,15 +170,13 @@ impl<'hir> LoweringContext<'_, 'hir> { i: &ItemKind, ) -> Vec { match i { - ItemKind::Fn(Fn { eii_impls, .. }) | ItemKind::Static(StaticItem { eii_impls, .. }) - if eii_impls.is_empty() => - { - Vec::new() - } - ItemKind::Fn(Fn { eii_impls, .. }) | ItemKind::Static(StaticItem { eii_impls, .. }) => { - vec![hir::Attribute::Parsed(AttributeKind::EiiImpls( - eii_impls.iter().map(|i| self.lower_eii_impl(i)).collect(), - ))] + ItemKind::Fn(Fn { eii_impl: None, .. }) + | ItemKind::Static(StaticItem { eii_impl: None, .. }) => Vec::new(), + ItemKind::Fn(Fn { eii_impl: Some(eii_impl), .. }) + | ItemKind::Static(StaticItem { eii_impl: Some(eii_impl), .. }) => { + vec![hir::Attribute::Parsed(AttributeKind::EiiImpl(Box::new( + self.lower_eii_impl(eii_impl), + )))] } ItemKind::MacroDef(name, MacroDef { eii_declaration: Some(target), .. }) => self .lower_eii_decl(id, *name, target) @@ -226,7 +224,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind, vis_span, span: self.lower_span(i.span), - eii: find_attr!(attrs, EiiImpls(..) | EiiDeclaration(..)), + eii: find_attr!(attrs, EiiImpl(..) | EiiDeclaration(..)), }; self.arena.alloc(item) } @@ -259,7 +257,7 @@ impl<'hir> LoweringContext<'_, 'hir> { mutability: m, expr: e, define_opaque, - eii_impls: _, + eii_impl: _, }) => { let ident = self.lower_ident(*ident); let ty = self @@ -696,7 +694,7 @@ impl<'hir> LoweringContext<'_, 'hir> { kind, vis_span, span: this.lower_span(use_tree.span()), - eii: find_attr!(attrs, EiiImpls(..) | EiiDeclaration(..)), + eii: find_attr!(attrs, EiiImpl(..) | EiiDeclaration(..)), }; hir::OwnerNode::Item(this.arena.alloc(item)) }); @@ -763,7 +761,7 @@ impl<'hir> LoweringContext<'_, 'hir> { expr: _, safety, define_opaque, - eii_impls: _, + eii_impl: _, }) => { let ty = self .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 12a345aaeaf1d..62134046746c0 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1259,10 +1259,10 @@ impl<'a> AstValidator<'a> { } // Check EII implementation attributes against an allowlist. - fn check_eii_impl_attrs(&self, attrs: &[Attribute], eii_impls: &[EiiImpl]) { - if eii_impls.is_empty() { + fn check_eii_impl_attrs(&self, attrs: &[Attribute], eii_impl: &Option>) { + let Some(eii_impl) = eii_impl else { return; - } + }; let allowed_attrs: &[Symbol] = &[ sym::allow, @@ -1289,14 +1289,12 @@ impl<'a> AstValidator<'a> { } let attr_name = pprust::path_to_string(&normal.item.path); - for eii_impl in eii_impls { - self.dcx().emit_err(diagnostics::EiiImplAttributeNotSupported { - attr_span: attr.span, - attr_name: &attr_name, - eii_span: eii_impl.span, - eii_name: pprust::path_to_string(&eii_impl.eii_macro_path), - }); - } + self.dcx().emit_err(diagnostics::EiiImplAttributeNotSupported { + attr_span: attr.span, + attr_name: &attr_name, + eii_span: eii_impl.span, + eii_name: pprust::path_to_string(&eii_impl.eii_macro_path), + }); } } } @@ -1479,16 +1477,16 @@ impl Visitor<'_> for AstValidator<'_> { contract: _, body, define_opaque: _, - eii_impls, + eii_impl, }, ) => { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No); - for EiiImpl { eii_macro_path, .. } in eii_impls { + if let Some(EiiImpl { eii_macro_path, .. }) = eii_impl { self.visit_path(eii_macro_path); } - self.check_eii_impl_attrs(&item.attrs, eii_impls); + self.check_eii_impl_attrs(&item.attrs, eii_impl); let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)); if body.is_none() && !is_intrinsic && !self.is_sdylib_interface { @@ -1664,9 +1662,9 @@ impl Visitor<'_> for AstValidator<'_> { visit::walk_item(self, item); } - ItemKind::Static(StaticItem { expr, safety, eii_impls, .. }) => { + ItemKind::Static(StaticItem { expr, safety, eii_impl, .. }) => { self.check_item_safety(item.span, *safety); - self.check_eii_impl_attrs(&item.attrs, eii_impls); + self.check_eii_impl_attrs(&item.attrs, eii_impl); if matches!(safety, Safety::Unsafe(_)) { self.dcx().emit_err(diagnostics::UnsafeStatic { span: item.span }); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 1fb71b7b06299..04f78ea7f467a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -42,7 +42,7 @@ impl<'a> State<'a> { expr, safety, define_opaque, - eii_impls, + eii_impl, }) => self.print_item_const( *ident, Some(*mutability), @@ -53,7 +53,7 @@ impl<'a> State<'a> { *safety, ast::Defaultness::Implicit, define_opaque.as_deref(), - eii_impls, + eii_impl.as_deref(), ), ast::ForeignItemKind::TyAlias(ast::TyAlias { defaultness, @@ -94,10 +94,10 @@ impl<'a> State<'a> { safety: ast::Safety, defaultness: ast::Defaultness, define_opaque: Option<&[(ast::NodeId, ast::Path)]>, - eii_impls: &[EiiImpl], + eii_impl: Option<&EiiImpl>, ) { self.print_define_opaques(define_opaque); - for eii_impl in eii_impls { + if let Some(eii_impl) = eii_impl { self.print_eii_impl(eii_impl); } let (cb, ib) = self.head(""); @@ -196,7 +196,7 @@ impl<'a> State<'a> { mutability: mutbl, expr: body, define_opaque, - eii_impls, + eii_impl, }) => { self.print_safety(*safety); self.print_item_const( @@ -209,7 +209,7 @@ impl<'a> State<'a> { ast::Safety::Default, ast::Defaultness::Implicit, define_opaque.as_deref(), - eii_impls, + eii_impl.as_deref(), ); } ast::ItemKind::ConstBlock(ast::ConstBlockItem { id: _, span: _, block }) => { @@ -242,7 +242,7 @@ impl<'a> State<'a> { ast::Safety::Default, *defaultness, define_opaque.as_deref(), - &[], + None, ); } ast::ItemKind::Fn(func) => { @@ -631,7 +631,7 @@ impl<'a> State<'a> { ast::Safety::Default, *defaultness, define_opaque.as_deref(), - &[], + None, ); } ast::AssocItemKind::Type(ast::TyAlias { @@ -731,12 +731,12 @@ impl<'a> State<'a> { } fn print_fn_full(&mut self, vis: &ast::Visibility, attrs: &[ast::Attribute], func: &ast::Fn) { - let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impls } = + let ast::Fn { defaultness, ident, generics, sig, contract, body, define_opaque, eii_impl } = func; self.print_define_opaques(define_opaque.as_deref()); - for eii_impl in eii_impls { + if let Some(eii_impl) = eii_impl { self.print_eii_impl(eii_impl); } diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index d50fc78b51e14..57e589ac5a1e8 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -96,7 +96,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span contract: None, body, define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })); let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)]; diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 51ab44d8a03ef..0618c28759a66 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -344,7 +344,7 @@ mod llvm_enzyme { contract: None, body: Some(d_body), define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, }); let mut rustc_ad_attr = Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff))); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index cc036fab83c9d..03ccdbb902a96 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1083,7 +1083,7 @@ impl<'a> MethodDef<'a> { contract: None, body: Some(body_block), define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })), tokens: None, }) diff --git a/compiler/rustc_builtin_macros/src/diagnostics.rs b/compiler/rustc_builtin_macros/src/diagnostics.rs index 71dc17b108a97..ce5fb4e86dab6 100644 --- a/compiler/rustc_builtin_macros/src/diagnostics.rs +++ b/compiler/rustc_builtin_macros/src/diagnostics.rs @@ -1094,6 +1094,13 @@ pub(crate) struct CfgSelectNoMatches { pub span: Span, } +#[derive(Diagnostic)] +#[diag("a single item cannot both declare and implement EIIs")] +pub(crate) struct EiiBothDeclAndImpl { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`#[eii_declaration(...)]` is only valid on macros")] pub(crate) struct EiiExternTargetExpectedMacro { @@ -1117,21 +1124,18 @@ pub(crate) struct EiiExternTargetExpectedUnsafe { } #[derive(Diagnostic)] -#[diag("`#[{$name}]` is only valid on functions and statics")] -pub(crate) struct EiiSharedMacroTarget { +#[diag("a single item cannot implement multiple EIIs")] +pub(crate) struct EiiMultipleImplementations { #[primary_span] pub span: Span, - pub name: String, } #[derive(Diagnostic)] -#[diag("static cannot implement multiple EIIs")] -#[note( - "this is not allowed because multiple externally implementable statics that alias may be unintuitive" -)] -pub(crate) struct EiiStaticMultipleImplementations { +#[diag("`#[{$name}]` is only valid on functions and statics")] +pub(crate) struct EiiSharedMacroTarget { #[primary_span] pub span: Span, + pub name: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 5a28416900372..cf50460422f52 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -10,10 +10,10 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::diagnostics::{ - EiiAttributeNotSupported, EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, - EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, - EiiSharedMacroInStatementPosition, EiiSharedMacroTarget, EiiStaticArgumentRequired, - EiiStaticDefaultApple, EiiStaticMultipleImplementations, EiiStaticMutable, + EiiAttributeNotSupported, EiiBothDeclAndImpl, EiiExternTargetExpectedList, + EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, + EiiMultipleImplementations, EiiOnlyOnce, EiiSharedMacroInStatementPosition, + EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefaultApple, EiiStaticMutable, }; /// ```rust @@ -125,6 +125,22 @@ fn eii_( } }; + match kind { + ItemKind::Fn(func) => { + if func.eii_impl.is_some() { + ecx.dcx().emit_err(EiiBothDeclAndImpl { span: eii_attr_span }); + return vec![Annotatable::Item(item)]; + } + } + ItemKind::Static(stat) => { + if stat.eii_impl.is_some() { + ecx.dcx().emit_err(EiiBothDeclAndImpl { span: eii_attr_span }); + return vec![Annotatable::Item(item)]; + } + } + _ => unreachable!("Target was checked earlier"), + }; + // only clone what we need let attrs = attrs.clone(); let vis = vis.clone(); @@ -298,7 +314,7 @@ fn generate_default_impl( _ => unreachable!("Target was checked earlier"), }; - let eii_impl = EiiImpl { + let eii_impl = Box::new(EiiImpl { node_id: DUMMY_NODE_ID, inner_span: macro_name.span, eii_macro_path: ast::Path::from_ident(macro_name), @@ -315,15 +331,17 @@ fn generate_default_impl( // NOTE: this is why EIIs can't be used on statements vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name], )), - }; + }); let mut item_kind = item_kind.clone(); match &mut item_kind { ItemKind::Fn(func) => { - func.eii_impls.push(eii_impl); + assert!(func.eii_impl.is_none()); + func.eii_impl = Some(eii_impl); } ItemKind::Static(stat) => { - stat.eii_impls.push(eii_impl); + assert!(stat.eii_impl.is_none()); + stat.eii_impl = Some(eii_impl); } _ => unreachable!("Target was checked earlier"), }; @@ -579,16 +597,9 @@ pub(crate) fn eii_shared_macro( return vec![item]; }; - let eii_impls = match &mut i.kind { - ItemKind::Fn(func) => &mut func.eii_impls, - ItemKind::Static(stat) => { - if !stat.eii_impls.is_empty() { - // Reject multiple implementations on one static item - // because it might be unintuitive for libraries defining statics the defined statics may alias - ecx.dcx().emit_err(EiiStaticMultipleImplementations { span }); - } - &mut stat.eii_impls - } + let eii_impl = match &mut i.kind { + ItemKind::Fn(func) => &mut func.eii_impl, + ItemKind::Static(stat) => &mut stat.eii_impl, _ => { ecx.dcx() .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) }); @@ -611,7 +622,10 @@ pub(crate) fn eii_shared_macro( return vec![item]; }; - eii_impls.push(EiiImpl { + if eii_impl.is_some() { + ecx.dcx().emit_err(EiiMultipleImplementations { span }); + } + *eii_impl = Some(Box::new(EiiImpl { node_id: DUMMY_NODE_ID, inner_span: meta_item.path.span, eii_macro_path: meta_item.path.clone(), @@ -619,7 +633,7 @@ pub(crate) fn eii_shared_macro( span, is_default, known_eii_macro_resolution: None, - }); + })); vec![item] } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 72b493e313326..00ed0f52d6a6f 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -97,7 +97,7 @@ impl AllocFnFactory<'_, '_> { contract: None, body, define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })); let item = self.cx.item(self.span, self.attrs(method), kind); self.cx.stmt_item(self.ty_span, item) diff --git a/compiler/rustc_builtin_macros/src/offload.rs b/compiler/rustc_builtin_macros/src/offload.rs index cdb3ba22ec6c8..e47ccc0d85f7d 100644 --- a/compiler/rustc_builtin_macros/src/offload.rs +++ b/compiler/rustc_builtin_macros/src/offload.rs @@ -85,7 +85,7 @@ pub(crate) fn expand_kernel( contract: None, body, define_opaque: None, - eii_impls: Default::default(), + eii_impl: None, }); let extern_gpu_kernel = ast::Extern::from_abi( @@ -157,7 +157,7 @@ pub(crate) fn expand_kernel( contract: None, body: Some(body), define_opaque: None, - eii_impls: Default::default(), + eii_impl: None, }); for param in host_fn.sig.decl.inputs.iter_mut() { diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index ff9d9f10dd4e1..5d20fed223468 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -347,7 +347,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box { contract: None, body: Some(main_body), define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })); let main = Box::new(ast::Item { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 0389aa56bafd6..3e24b62125fea 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -219,32 +219,31 @@ fn process_builtin_attrs( AttributeKind::RustcEiiForeignItem => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; } - AttributeKind::EiiImpls(impls) => { - for i in impls { - let foreign_item = match i.resolution { - EiiImplResolution::Macro(def_id) => { - let Some(extern_item) = find_attr!(tcx, def_id, EiiDeclaration(target) => target.foreign_item - ) else { - tcx.dcx().span_delayed_bug( - i.span, - "resolved to something that's not an EII", - ); - continue; - }; - extern_item - } - EiiImplResolution::Known(def_id) => def_id, - EiiImplResolution::Error(_eg) => continue, - }; + AttributeKind::EiiImpl(i) => { + let foreign_item = match i.resolution { + EiiImplResolution::Macro(def_id) => { + let Some(extern_item) = find_attr!(tcx, def_id, EiiDeclaration(target) => target.foreign_item + ) else { + tcx.dcx().span_delayed_bug( + i.span, + "resolved to something that's not an EII", + ); + continue; + }; + extern_item + } + EiiImplResolution::Known(def_id) => def_id, + EiiImplResolution::Error(_eg) => continue, + }; - // this is to prevent a bug where a single crate defines both the default and explicit implementation - // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure - // what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent. - // However, the fact that the default one of has weak linkage isn't considered and you sometimes get that - // the default implementation is used while an explicit implementation is given. - if - // if this is a default impl - i.is_default + // this is to prevent a bug where a single crate defines both the default and explicit implementation + // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure + // what happens, either rustc deduplicates the symbol or llvm, or it's random/order-dependent. + // However, the fact that the default one of has weak linkage isn't considered and you sometimes get that + // the default implementation is used while an explicit implementation is given. + if + // if this is a default impl + i.is_default // iterate over all implementations *in the current crate* // (this is ok since we generate codegen fn attrs in the local crate) // if any of them is *not default* then don't emit the alias. @@ -252,28 +251,27 @@ fn process_builtin_attrs( let (_, impls) = tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).unwrap_or_else(|| bug!("EII impl should have an entry")); impls.iter().any(|(_, imp)| !imp.is_default) } - { - continue; - } + { + continue; + } - codegen_fn_attrs.foreign_item_symbol_aliases.push(( - foreign_item, - if i.is_default { Linkage::WeakAny } else { Linkage::External }, - Visibility::Default, - )); - codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; - - // If the declaration is `#[track_caller]`, derive it onto the implementation - // too. The shim that forwards to this impl (see `add_function_aliases`) takes - // its ABI from the impl's `fn_abi`, so every impl must agree on whether the - // caller-location argument is present, otherwise it would be silently dropped. - if tcx - .codegen_fn_attrs(foreign_item) - .flags - .contains(CodegenFnAttrFlags::TRACK_CALLER) - { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; - } + codegen_fn_attrs.foreign_item_symbol_aliases.push(( + foreign_item, + if i.is_default { Linkage::WeakAny } else { Linkage::External }, + Visibility::Default, + )); + codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; + + // If the declaration is `#[track_caller]`, derive it onto the implementation + // too. The shim that forwards to this impl (see `add_function_aliases`) takes + // its ABI from the impl's `fn_abi`, so every impl must agree on whether the + // caller-location argument is present, otherwise it would be silently dropped. + if tcx + .codegen_fn_attrs(foreign_item) + .flags + .contains(CodegenFnAttrFlags::TRACK_CALLER) + { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } } AttributeKind::ThreadLocal => { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index b87dfd0198efc..4a8541ed4c6b7 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -707,7 +707,7 @@ impl<'a> ExtCtxt<'a> { mutability, expr: Some(expr), define_opaque: None, - eii_impls: Default::default(), + eii_impl: None, } .into(), ), diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 187dcea4f91a5..045233c0c4d21 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -804,7 +804,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { None, ) } - // When a function has EII implementations attached (via `eii_impls`), + // When a function has EII implementations attached (via `eii_impl`), // use fake tokens so the pretty-printer re-emits the EII attribute // (e.g. `#[hello]`) in the token stream. Without this, the EII // attribute is lost during the token roundtrip performed by @@ -812,7 +812,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // breaking the EII link on the resulting re-parsed item. Annotatable::Item(item_inner) if matches!(&item_inner.kind, - ItemKind::Fn(f) if !f.eii_impls.is_empty()) => + ItemKind::Fn(f) if f.eii_impl.is_some()) => { rustc_parse::fake_token_stream_for_item( &self.cx.sess.psess, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 165f06d2fde8b..78a15eeb923a5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1076,7 +1076,7 @@ pub enum AttributeKind { EiiDeclaration(EiiDecl), /// Implementation detail of `#[eii]` - EiiImpls(ThinVec), + EiiImpl(Box), /// Represents [`#[export_name]`](https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute). ExportName { diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index dded70ccd08ef..a5a1fc2482b4e 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -40,7 +40,7 @@ impl AttributeKind { Doc(_) => Yes, DocComment { .. } => Yes, EiiDeclaration(_) => Yes, - EiiImpls(..) => No, + EiiImpl(..) => No, ExportName { .. } => Yes, ExportStable => No, Feature(..) => No, diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index 57824a91a680f..d9fc3bbcf08c2 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -301,8 +301,7 @@ fn check_no_generics<'tcx>( // since in that case it looks like a duplicate error: the declaration of the EII already can't contain generics. // So, we check here if at least one of the eii impls has ImplResolution::Macro, which indicates it's // not generated as part of the declaration. - && find_attr!(tcx, external_impl, EiiImpls(impls) if impls.iter().any(|i| matches!(i.resolution, EiiImplResolution::Macro(_))) - ) + && find_attr!(tcx, external_impl, EiiImpl(i) if matches!(i.resolution, EiiImplResolution::Macro(_))) { tcx.dcx().emit_err(EiiWithGenerics { span: tcx.def_span(external_impl), diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 35628e54769b4..caf64fd6894f7 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1153,9 +1153,7 @@ fn check_item_fn( fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. - for EiiImpl { resolution, span, .. } in - find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_flat_iter() - { + if let Some(EiiImpl { resolution, span, .. }) = find_attr!(tcx, def_id, EiiImpl(i) => &**i) { let (foreign_item, name) = match resolution { EiiImplResolution::Macro(def_id) => { // we expect this macro to have the `EiiMacroFor` attribute, that points to a function @@ -1166,11 +1164,11 @@ fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { (foreign_item, tcx.item_name(*def_id)) } else { tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII"); - continue; + return; } } EiiImplResolution::Known(def_id) => (*def_id, tcx.item_name(*def_id)), - EiiImplResolution::Error(_eg) => continue, + EiiImplResolution::Error(_eg) => return, }; let _ = compare_eii_function_types(tcx, def_id, foreign_item, name, *span); @@ -1180,9 +1178,7 @@ fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { fn check_eiis_static<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, ty: Ty<'tcx>) { // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. - for EiiImpl { resolution, span, .. } in - find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_flat_iter() - { + if let Some(EiiImpl { resolution, span, .. }) = find_attr!(tcx, def_id, EiiImpl(i) => &**i) { let (foreign_item, name) = match resolution { EiiImplResolution::Macro(def_id) => { // we expect this macro to have the `EiiMacroFor` attribute, that points to a function @@ -1193,11 +1189,11 @@ fn check_eiis_static<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, ty: Ty<'tcx>) (foreign_item, tcx.item_name(*def_id)) } else { tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII"); - continue; + return; } } EiiImplResolution::Known(def_id) => (*def_id, tcx.item_name(*def_id)), - EiiImplResolution::Error(_eg) => continue, + EiiImplResolution::Error(_eg) => return, }; let _ = compare_eii_statics(tcx, def_id, ty, foreign_item, name, *span); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 1473b0d108fb3..ebbf63b947a93 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -60,7 +60,6 @@ This API is completely unstable and subject to change. #![feature(gen_blocks)] #![feature(iter_intersperse)] #![feature(never_type)] -#![feature(option_into_flat_iter)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] #![feature(unwrap_infallible)] diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index 4328e8de901d8..da6d9e85bc4fa 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -35,7 +35,12 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap // iterate over all items in the current crate for id in tcx.hir_crate_items(()).eiis() { - for i in find_attr!(tcx, id, EiiImpls(e) => e).into_flat_iter() { + // if we find a new declaration, add it to the list without a known implementation + if let Some(decl) = find_attr!(tcx, id, EiiDeclaration(d) => *d) { + eiis.entry(decl.foreign_item).or_insert((decl, Default::default())); + } + + if let Some(i) = find_attr!(tcx, id, EiiImpl(i) => i) { let (foreign_item, decl) = match i.resolution { EiiImplResolution::Macro(macro_defid) => { // find the decl for this one if it wasn't in yet (maybe it's from the local crate? not very useful but not illegal) @@ -63,12 +68,7 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap eiis.entry(foreign_item) .or_insert_with(|| (decl, Default::default())) .1 - .insert(id.into(), *i); - } - - // if we find a new declaration, add it to the list without a known implementation - if let Some(decl) = find_attr!(tcx, id, EiiDeclaration(d) => *d) { - eiis.entry(decl.foreign_item).or_insert((decl, Default::default())); + .insert(id.into(), **i); } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 0fa592459167a..1306f1fcfb1ce 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -283,7 +283,7 @@ impl<'a> Parser<'a> { contract, body, define_opaque: None, - eii_impls: ThinVec::new(), + eii_impl: None, })) } else if self.eat_keyword_case(exp!(Extern), case) { if self.eat_keyword_case(exp!(Crate), case) { @@ -1257,7 +1257,7 @@ impl<'a> Parser<'a> { mutability: _, expr, define_opaque, - eii_impls: _, + eii_impl: _, }) => { self.dcx() .emit_err(diagnostics::AssociatedStaticItemNotAllowed { span }); @@ -1523,7 +1523,7 @@ impl<'a> Parser<'a> { expr: body, safety: Safety::Default, define_opaque: None, - eii_impls: ThinVec::default(), + eii_impl: None, })) } _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), @@ -1661,15 +1661,8 @@ impl<'a> Parser<'a> { self.expect_semi()?; - let item = StaticItem { - ident, - ty, - safety, - mutability, - expr, - define_opaque: None, - eii_impls: ThinVec::default(), - }; + let item = + StaticItem { ident, ty, safety, mutability, expr, define_opaque: None, eii_impl: None }; Ok(ItemKind::Static(Box::new(item))) } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6489167afcdac..d1d82b2ed43e0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -211,7 +211,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_legacy_const_generics(item, *attr_span, fn_indexes) } AttributeKind::Doc(attr) => self.check_doc_attrs(attr, hir_id, target), - AttributeKind::EiiImpls(impls) => self.check_eii_impl(impls), + AttributeKind::EiiImpl(eii_impl) => self.check_eii_impl(eii_impl), AttributeKind::RustcMustImplementOneOf { attr_span, fn_names } => { self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id, target) } @@ -474,51 +474,50 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that each externally implementable item (EII) implementation uses `unsafe` /// exactly when its declaration requires it. - fn check_eii_impl(&self, impls: &[EiiImpl]) { - for EiiImpl { span, inner_span, resolution, impl_unsafe_span, is_default: _ } in impls { - let impl_unsafe = match resolution { - EiiImplResolution::Macro(eii_macro) => find_attr!( - self.tcx, - *eii_macro, - EiiDeclaration(EiiDecl { impl_unsafe, .. }) => *impl_unsafe - ), - EiiImplResolution::Known(foreign_item_did) => self - .tcx - .externally_implementable_items(foreign_item_did.krate) - .get(foreign_item_did) - .map(|(decl, _)| decl.impl_unsafe), - EiiImplResolution::Error(_) => None, - }; - let Some(needs_unsafe) = impl_unsafe else { - continue; - }; + fn check_eii_impl(&self, eii_impl: &EiiImpl) { + let EiiImpl { span, inner_span, resolution, impl_unsafe_span, is_default: _ } = eii_impl; + let impl_unsafe = match resolution { + EiiImplResolution::Macro(eii_macro) => find_attr!( + self.tcx, + *eii_macro, + EiiDeclaration(EiiDecl { impl_unsafe, .. }) => *impl_unsafe + ), + EiiImplResolution::Known(foreign_item_did) => self + .tcx + .externally_implementable_items(foreign_item_did.krate) + .get(foreign_item_did) + .map(|(decl, _)| decl.impl_unsafe), + EiiImplResolution::Error(_) => None, + }; + let Some(needs_unsafe) = impl_unsafe else { + return; + }; - let name = match resolution { - EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), - EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), - EiiImplResolution::Error(_) => unreachable!(), - }; + let name = match resolution { + EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), + EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), + EiiImplResolution::Error(_) => unreachable!(), + }; - match (needs_unsafe, *impl_unsafe_span) { - (true, None) => { - self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { - span: *span, - name, - suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { - left: inner_span.shrink_to_lo(), - right: inner_span.shrink_to_hi(), - }, - }); - } - (false, Some(unsafe_span)) => { - self.dcx().emit_err(diagnostics::EiiImplCannotBeUnsafe { - impl_span: *span, - unsafe_span, - name, - }); - } - _ => {} + match (needs_unsafe, *impl_unsafe_span) { + (true, None) => { + self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { + span: *span, + name, + suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { + left: inner_span.shrink_to_lo(), + right: inner_span.shrink_to_hi(), + }, + }); + } + (false, Some(unsafe_span)) => { + self.dcx().emit_err(diagnostics::EiiImplCannotBeUnsafe { + impl_span: *span, + unsafe_span, + name, + }); } + _ => {} } } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 63bff7a3f4498..97ca994c37540 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -303,7 +303,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { expr: _, safety, define_opaque: _, - eii_impls: _, + eii_impl: _, }) => { let safety = match safety { ast::Safety::Unsafe(_) | ast::Safety::Default => hir::Safety::Unsafe, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f30e6844c861c..fc723586c1acc 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1147,7 +1147,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc debug!("(resolving function) entering function"); if let FnKind::Fn(_, _, f) = fn_kind { - self.resolve_eii(&f.eii_impls); + self.resolve_eii(f.eii_impl.as_deref()); } // Create a value rib for the function. @@ -2940,7 +2940,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } ItemKind::Static(ast::StaticItem { - ident, ty, expr, define_opaque, eii_impls, .. + ident, ty, expr, define_opaque, eii_impl, .. }) => { self.with_static_rib(def_kind, |this| { this.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Static), |this| { @@ -2953,7 +2953,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } }); self.resolve_define_opaques(define_opaque); - self.resolve_eii(&eii_impls); + self.resolve_eii(eii_impl.as_deref()); } ItemKind::Const(ast::ConstItem { @@ -5568,8 +5568,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } - fn resolve_eii(&mut self, eii_impls: &[EiiImpl]) { - for EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. } in eii_impls { + fn resolve_eii(&mut self, eii_impl: Option<&EiiImpl>) { + if let Some(EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. }) = eii_impl + { // See docs on the `known_eii_macro_resolution` field: // if we already know the resolution statically, don't bother resolving it. if let Some(target) = known_eii_macro_resolution { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 3f0b99aa4d780..523e799ef2522 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -332,7 +332,7 @@ fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: le, safety: ls, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Static(box StaticItem { ident: ri, @@ -341,7 +341,7 @@ fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: re, safety: rs, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref()), ( @@ -381,7 +381,7 @@ fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { contract: lc, body: lb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Fn(box ast::Fn { defaultness: rd, @@ -391,7 +391,7 @@ fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { contract: rc, body: rb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => { eq_defaultness(*ld, *rd) @@ -539,7 +539,7 @@ fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { expr: le, safety: ls, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Static(box StaticItem { ident: ri, @@ -548,7 +548,7 @@ fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { expr: re, safety: rs, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_deref(), re.as_deref()) && ls == rs, ( @@ -560,7 +560,7 @@ fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { contract: lc, body: lb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Fn(box ast::Fn { defaultness: rd, @@ -570,7 +570,7 @@ fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { contract: rc, body: rb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => { eq_defaultness(*ld, *rd) @@ -649,7 +649,7 @@ fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { contract: lc, body: lb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), Fn(box ast::Fn { defaultness: rd, @@ -659,7 +659,7 @@ fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { contract: rc, body: rb, define_opaque: _, - eii_impls: _, + eii_impl: _, }), ) => { eq_defaultness(*ld, *rd) diff --git a/tests/ui/eii/duplicate/both_decl_and_impl.rs b/tests/ui/eii/duplicate/both_decl_and_impl.rs new file mode 100644 index 0000000000000..a2fc571d3f497 --- /dev/null +++ b/tests/ui/eii/duplicate/both_decl_and_impl.rs @@ -0,0 +1,27 @@ +//@ ignore-backends: gcc +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu +// Tests that one item can't both define and impl an EII at the same time +#![feature(extern_item_impls)] + +#[eii] +fn a(x: u64); + +#[a] +#[eii] +//~^ ERROR a single item cannot both declare and implement EIIs +fn b(x: u64) {} + +#[eii] +fn c(x: u64); +//~^ ERROR `#[c]` function required, but not found + +#[eii] +#[c] +fn d(x: u64) {} +//~^ ERROR only a small subset of attributes are supported on externally implementable items + +fn main() { + a(42); + b(42); +} diff --git a/tests/ui/eii/duplicate/both_decl_and_impl.stderr b/tests/ui/eii/duplicate/both_decl_and_impl.stderr new file mode 100644 index 0000000000000..1cec485a90cff --- /dev/null +++ b/tests/ui/eii/duplicate/both_decl_and_impl.stderr @@ -0,0 +1,28 @@ +error: a single item cannot both declare and implement EIIs + --> $DIR/both_decl_and_impl.rs:11:1 + | +LL | #[eii] + | ^^^^^^ + +error: only a small subset of attributes are supported on externally implementable items + --> $DIR/both_decl_and_impl.rs:21:1 + | +LL | fn d(x: u64) {} + | ^^^^^^^^^^^^ + | +note: this attribute is not supported + --> $DIR/both_decl_and_impl.rs:20:1 + | +LL | #[c] + | ^^^^ + +error: `#[c]` function required, but not found + --> $DIR/both_decl_and_impl.rs:16:4 + | +LL | fn c(x: u64); + | ^ expected because `#[c]` was declared here in crate `both_decl_and_impl` + | + = help: expected at least one implementation in crate `both_decl_and_impl` or any of its dependencies + +error: aborting due to 3 previous errors + diff --git a/tests/ui/eii/duplicate/multiple_impls.rs b/tests/ui/eii/duplicate/multiple_impls.rs index 80f6147789743..3e541cb16b131 100644 --- a/tests/ui/eii/duplicate/multiple_impls.rs +++ b/tests/ui/eii/duplicate/multiple_impls.rs @@ -1,25 +1,37 @@ -//@ run-pass -//@ check-run-results //@ ignore-backends: gcc // FIXME(#125418): linking on Windows GNU targets is not yet supported. //@ ignore-windows-gnu -// Tests whether one function could implement two EIIs. +// Tests that one item can't implement two EIIs #![feature(extern_item_impls)] #[eii] fn a(x: u64); +//~^ ERROR `#[a]` function required, but not found #[eii] fn b(x: u64); #[a] #[b] +//~^ ERROR a single item cannot implement multiple EIIs fn implementation(x: u64) { println!("{x:?}") } -// what you would write: +#[eii(c)] +//~^ ERROR `#[c]` static required, but not found +static C: u64; + +#[eii(d)] +static D: u64; + +#[c] +#[d] +//~^ ERROR a single item cannot implement multiple EIIs +static IMPL: u64 = 5; + fn main() { a(42); b(42); + println!("{C} {D} {IMPL}") } diff --git a/tests/ui/eii/duplicate/multiple_impls.run.stdout b/tests/ui/eii/duplicate/multiple_impls.run.stdout deleted file mode 100644 index daaac9e303029..0000000000000 --- a/tests/ui/eii/duplicate/multiple_impls.run.stdout +++ /dev/null @@ -1,2 +0,0 @@ -42 -42 diff --git a/tests/ui/eii/duplicate/multiple_impls.stderr b/tests/ui/eii/duplicate/multiple_impls.stderr new file mode 100644 index 0000000000000..efeec635c859a --- /dev/null +++ b/tests/ui/eii/duplicate/multiple_impls.stderr @@ -0,0 +1,30 @@ +error: a single item cannot implement multiple EIIs + --> $DIR/multiple_impls.rs:15:1 + | +LL | #[b] + | ^^^^ + +error: a single item cannot implement multiple EIIs + --> $DIR/multiple_impls.rs:29:1 + | +LL | #[d] + | ^^^^ + +error: `#[a]` function required, but not found + --> $DIR/multiple_impls.rs:8:4 + | +LL | fn a(x: u64); + | ^ expected because `#[a]` was declared here in crate `multiple_impls` + | + = help: expected at least one implementation in crate `multiple_impls` or any of its dependencies + +error: `#[c]` static required, but not found + --> $DIR/multiple_impls.rs:21:7 + | +LL | #[eii(c)] + | ^ expected because `#[c]` was declared here in crate `multiple_impls` + | + = help: expected at least one implementation in crate `multiple_impls` or any of its dependencies + +error: aborting due to 4 previous errors + diff --git a/tests/ui/eii/static/multiple_impls.rs b/tests/ui/eii/static/multiple_impls.rs deleted file mode 100644 index 1129417b958ca..0000000000000 --- a/tests/ui/eii/static/multiple_impls.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ ignore-backends: gcc -// FIXME(#125418): linking on Windows GNU targets is not yet supported. -//@ ignore-windows-gnu -// Tests whether one function could implement two EIIs. -#![feature(extern_item_impls)] - -#[eii(a)] -static A: u64; - -#[eii(b)] -static B: u64; - -#[a] -#[b] -//~^ ERROR static cannot implement multiple EIIs -static IMPL: u64 = 5; - -fn main() { - println!("{A} {B} {IMPL}") -} diff --git a/tests/ui/eii/static/multiple_impls.run.stdout b/tests/ui/eii/static/multiple_impls.run.stdout deleted file mode 100644 index 58945c2b48291..0000000000000 --- a/tests/ui/eii/static/multiple_impls.run.stdout +++ /dev/null @@ -1 +0,0 @@ -5 5 5 diff --git a/tests/ui/eii/static/multiple_impls.stderr b/tests/ui/eii/static/multiple_impls.stderr deleted file mode 100644 index b31331f2483f1..0000000000000 --- a/tests/ui/eii/static/multiple_impls.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: static cannot implement multiple EIIs - --> $DIR/multiple_impls.rs:14:1 - | -LL | #[b] - | ^^^^ - | - = note: this is not allowed because multiple externally implementable statics that alias may be unintuitive - -error: aborting due to 1 previous error - From 496056fb26fec158af8c045c0aefd813d82b9f9f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 3 Aug 2026 11:33:41 +0300 Subject: [PATCH 36/37] Use `thread::available_parallelism` as the default limit for backend parallelism --- compiler/rustc_interface/src/interface.rs | 6 ++--- compiler/rustc_session/src/config.rs | 29 ++++------------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 2ad6fb6450a16..18f869d24cbfb 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -15,7 +15,7 @@ use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::Recovery; use rustc_query_impl::print_query_stack; -use rustc_session::config::{self, BackendJobs, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; +use rustc_session::config::{self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName}; use rustc_session::parse::ParseSess; use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, lint}; use rustc_span::source_map::{FileLoader, RealFileLoader, SourceMapInputs}; @@ -375,9 +375,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se // Initialize jobserver as early as possible. let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); - if let Some(limit) = - config.opts.jobs.frontend.max(config.opts.jobs.backend.map(BackendJobs::value)) - { + if let Some(limit) = config.opts.jobs.frontend.max(config.opts.jobs.backend) { jobserver::initialize(limit.get(), |err| { let note = "the build environment is likely misconfigured"; early_dcx.early_struct_warn(err).with_note(note).emit() diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 022784b56d4ce..1cfd03288a417 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1649,26 +1649,6 @@ impl PointerAuthOption { } } -#[derive(Clone, Copy)] -pub enum BackendJobs { - /// The number of backend jobs has a static limit. - Limited(NonZero), - /// The number of backend jobs is either unlimited if there's an inherited jobserver, - /// or limited to 32 if there's no inherited jobserver. - /// This variant exists only to preserve the historical behavior. - /// FIXME: Just use `thread::available_parallelism` as the default static limit. - UnlimitedOr32, -} - -impl BackendJobs { - pub fn value(self) -> NonZero { - match self { - BackendJobs::Limited(n) => n, - BackendJobs::UnlimitedOr32 => NonZero::new(32).unwrap(), - } - } -} - #[derive(Clone, Copy)] pub enum LinkerJobs { /// Do not pass anything to the linker, use it's default behavior. @@ -1682,7 +1662,7 @@ pub enum LinkerJobs { #[derive(Clone, Copy)] pub struct Jobs { pub frontend: Option>, - pub backend: Option, + pub backend: Option>, pub linker: LinkerJobs, } @@ -1735,11 +1715,12 @@ fn parse_jobs_all( let backend = parse_jobs_one(early_dcx, opt_name, &jobs_backend, unstable, &mut available); check_upper_limit(backend, opt_name); - backend.map(BackendJobs::Limited) + backend } None => match jobs { - Some(n) => n.map(BackendJobs::Limited), - None => Some(BackendJobs::UnlimitedOr32), + Some(n) => n, + // Use all available parallelism as the default. + None => parse_jobs_one(early_dcx, "", "0", unstable, &mut available), }, }; let linker = match matches.opt_str("jobs-linker") { From 6daa152ce85185cd34ad4ad29880602e92a43aad Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Aug 2026 14:44:31 +0200 Subject: [PATCH 37/37] bump tracing-tree --- Cargo.lock | 4 ++-- compiler/rustc_log/Cargo.toml | 2 +- compiler/rustc_pattern_analysis/Cargo.toml | 2 +- src/librustdoc/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0addc566d6bdd..76b17e02c2359 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5927,9 +5927,9 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b56c62d2c80033cb36fae448730a2f2ef99410fe3ecbffc916681a32f6807dbe" +checksum = "ac87aa03b6a4d5a7e4810d1a80c19601dbe0f8a837e9177f23af721c7ba7beec" dependencies = [ "nu-ansi-term", "tracing-core", diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index d407351fd23dc..0e17378ecfc3d 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -8,7 +8,7 @@ edition = "2024" tracing = "0.1.41" tracing-core = "0.1.34" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi", "json"] } -tracing-tree = "0.3.1" +tracing-tree = "0.4.1" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index a644c6a7c01a2..57dc75961e24f 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -24,7 +24,7 @@ tracing = "0.1" [dev-dependencies] # tidy-alphabetical-start tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "ansi"] } -tracing-tree = "0.3.0" +tracing-tree = "0.4.1" # tidy-alphabetical-end [features] diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 1fcc29bf92d93..42142b3990ce0 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -26,7 +26,7 @@ stringdex = "=0.0.6" tempfile = "3" threadpool = "1.8.1" tracing = "0.1" -tracing-tree = "0.3.0" +tracing-tree = "0.4.1" unicode-segmentation = "1.9" # tidy-alphabetical-end