From 842c087427b1bd6d70da9ba3b1e7a7e013b786b0 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Mon, 29 Dec 2025 21:55:30 +0100 Subject: [PATCH 01/19] Fix requires_lto targets needing lto set in cargo Targets that set `requires_lto = true` were not actually using lto when compiling with cargo by default. They needed an extra `lto = true` in `Cargo.toml` to work. Fix this by letting lto take precedence over the `embed_bitcode` flag when lto is required by a target. If both these flags would be supplied by the user, an error is generated. However, this did not happen when lto was requested by the target instead of the user. --- compiler/rustc_codegen_ssa/src/back/write.rs | 2 +- .../src/platform-support/amdgcn-amd-amdhsa.md | 5 ---- tests/run-make-cargo/amdgpu-lto/Cargo.toml | 8 ++++++ tests/run-make-cargo/amdgpu-lto/lib.rs | 15 ++++++++++ tests/run-make-cargo/amdgpu-lto/rmake.rs | 28 +++++++++++++++++++ 5 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 tests/run-make-cargo/amdgpu-lto/Cargo.toml create mode 100644 tests/run-make-cargo/amdgpu-lto/lib.rs create mode 100644 tests/run-make-cargo/amdgpu-lto/rmake.rs diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 3e36bd8552b18..8409c927d1564 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -156,7 +156,7 @@ impl ModuleConfig { // `#![no_builtins]` is assumed to not participate in LTO and // instead goes on to generate object code. EmitObj::Bitcode - } else if need_bitcode_in_object(tcx) { + } else if need_bitcode_in_object(tcx) || sess.target.requires_lto { EmitObj::ObjectCode(BitcodeSection::Full) } else { EmitObj::ObjectCode(BitcodeSection::None) diff --git a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md index dbdb96283a5fa..8934e7085b8d7 100644 --- a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md +++ b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md @@ -59,11 +59,6 @@ Build the library as `cdylib`: # Cargo.toml [lib] crate-type = ["cdylib"] - -[profile.dev] -lto = true # LTO must be explicitly enabled for now -[profile.release] -lto = true ``` The target-cpu must be from the list [supported by LLVM] (or printed with `rustc --target amdgcn-amd-amdhsa --print target-cpus`). diff --git a/tests/run-make-cargo/amdgpu-lto/Cargo.toml b/tests/run-make-cargo/amdgpu-lto/Cargo.toml new file mode 100644 index 0000000000000..b2607b747fd7f --- /dev/null +++ b/tests/run-make-cargo/amdgpu-lto/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "amdgpu_lto" +version = "0.1.0" +edition = "2024" + +[lib] +path = "lib.rs" +crate-type = ["cdylib"] diff --git a/tests/run-make-cargo/amdgpu-lto/lib.rs b/tests/run-make-cargo/amdgpu-lto/lib.rs new file mode 100644 index 0000000000000..d17cf5a8316ca --- /dev/null +++ b/tests/run-make-cargo/amdgpu-lto/lib.rs @@ -0,0 +1,15 @@ +#![feature(abi_gpu_kernel)] +#![no_std] + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[unsafe(no_mangle)] +fn foo(a: i32, b: i32) -> i32 { + a + b +} + +#[unsafe(no_mangle)] +extern "gpu-kernel" fn kernel() {} diff --git a/tests/run-make-cargo/amdgpu-lto/rmake.rs b/tests/run-make-cargo/amdgpu-lto/rmake.rs new file mode 100644 index 0000000000000..cb3dc81f34d15 --- /dev/null +++ b/tests/run-make-cargo/amdgpu-lto/rmake.rs @@ -0,0 +1,28 @@ +// Check that compiling for the amdgpu target which needs LTO works with a default +// cargo configuration. + +//@ needs-llvm-components: amdgpu +//@ needs-rust-lld + +#![deny(warnings)] + +use run_make_support::{cargo, path}; + +fn main() { + let target_dir = path("target"); + + cargo() + .args(&[ + "build", + "--release", + "--lib", + "--manifest-path", + "Cargo.toml", + "-Zbuild-std=core", + "--target", + "amdgcn-amd-amdhsa", + ]) + .env("RUSTFLAGS", "-Ctarget-cpu=gfx900") + .env("CARGO_TARGET_DIR", &target_dir) + .run(); +} From 2c79213bc5c0c0e071fa73c144efaf563f469234 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:20:02 +0200 Subject: [PATCH 02/19] field representing types: remove explicit `Send` and `Sync` impls Commit cb37ee2c87be ("make field representing types invariant over the base type") made the auto-trait impl work even if `T: !Send` or `T: !Sync` thus we can remove these explicit unsafe impls while FRTs still implement `Send` and `Sync`. --- library/core/src/field.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 0e537e2f92fcf..4c479546a977f 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -14,18 +14,6 @@ pub struct FieldRepresentingType T>, } -// SAFETY: `FieldRepresentingType` doesn't contain any `T` -unsafe impl Send - for FieldRepresentingType -{ -} - -// SAFETY: `FieldRepresentingType` doesn't contain any `T` -unsafe impl Sync - for FieldRepresentingType -{ -} - impl Copy for FieldRepresentingType { From 59f18c21b08eccd2429a4496d26f0e83b959c09c Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:22:28 +0200 Subject: [PATCH 03/19] field representing types: implement Debug through reflection Using the reflection experiment, we can print the actual names of the field that an FRT is referring to. In case this breaks, there is an alternative implementation in the note comment. --- library/core/src/field.rs | 48 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 4c479546a977f..dfab5a836db3f 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -1,11 +1,11 @@ //! Field Reflection +use crate::fmt; use crate::marker::PhantomData; /// Field Representing Type #[unstable(feature = "field_representing_type_raw", issue = "none")] #[lang = "field_representing_type"] -#[expect(missing_debug_implementations)] #[fundamental] pub struct FieldRepresentingType { // We want this type to be invariant over `T`, because otherwise `field_of!(Struct<'short>, @@ -14,6 +14,52 @@ pub struct FieldRepresentingType T>, } +impl fmt::Debug + for FieldRepresentingType +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + enum Member { + Name(&'static str), + Index(u32), + } + impl fmt::Display for Member { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Name(name) => fmt::Display::fmt(name, f), + Self::Index(idx) => fmt::Display::fmt(idx, f), + } + } + } + let (variant, field) = const { + use crate::mem::type_info::{Type, TypeKind}; + match Type::of::().kind { + TypeKind::Struct(struct_) => { + (None, Member::Name(struct_.fields[FIELD as usize].name)) + } + TypeKind::Tuple(_) => (None, Member::Index(FIELD)), + TypeKind::Enum(enum_) => { + let variant = &enum_.variants[VARIANT as usize]; + (Some(variant.name), Member::Name(variant.fields[FIELD as usize].name)) + } + TypeKind::Union(union) => (None, Member::Name(union.fields[FIELD as usize].name)), + _ => unreachable!(), + } + }; + let type_name = const { crate::any::type_name::() }; + match variant { + Some(variant) => write!(f, "field_of!({type_name}, {variant}.{field})"), + None => write!(f, "field_of!({type_name}, {field})"), + } + // NOTE: if there are changes in the reflection work and the above no + // longer compiles, then the following debug impl could also work in + // the meantime: + // ```rust + // let type_name = const { type_name::() }; + // write!(f, "field_of!({type_name}, {VARIANT}.{FIELD})") + // ``` + } +} + impl Copy for FieldRepresentingType { From f3719b1686fdf28f7c2e198525cf7487152b4134 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:27:12 +0200 Subject: [PATCH 04/19] field representing types: implement Default --- library/core/src/field.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/field.rs b/library/core/src/field.rs index dfab5a836db3f..0af1d9902c425 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -73,6 +73,14 @@ impl Clone } } +impl Default + for FieldRepresentingType +{ + fn default() -> Self { + Self { _phantom: PhantomData::default() } + } +} + /// Expands to the field representing type of the given field. /// /// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the From 104914e614ee0f90fa6ac5f25dbb7512ffcc1651 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:28:14 +0200 Subject: [PATCH 05/19] field representing types: implement Hash, Eq & Ord --- library/core/src/field.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 0af1d9902c425..2289ce3889557 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -1,6 +1,7 @@ //! Field Reflection use crate::fmt; +use crate::hash::{Hash, Hasher}; use crate::marker::PhantomData; /// Field Representing Type @@ -81,6 +82,43 @@ impl Default } } +impl Hash + for FieldRepresentingType +{ + fn hash(&self, state: &mut H) { + self._phantom.hash(state); + } +} + +impl PartialEq + for FieldRepresentingType +{ + fn eq(&self, other: &Self) -> bool { + self._phantom == other._phantom + } +} + +impl Eq + for FieldRepresentingType +{ +} + +impl PartialOrd + for FieldRepresentingType +{ + fn partial_cmp(&self, other: &Self) -> Option { + self._phantom.partial_cmp(&other._phantom) + } +} + +impl Ord + for FieldRepresentingType +{ + fn cmp(&self, other: &Self) -> crate::cmp::Ordering { + self._phantom.cmp(&other._phantom) + } +} + /// Expands to the field representing type of the given field. /// /// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the From ca700edee3aaf5d9f8a63ed41585c32c8a0ac990 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:29:10 +0200 Subject: [PATCH 06/19] field representing types: test all implemented traits --- tests/ui/field_representing_types/traits.rs | 36 +++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/tests/ui/field_representing_types/traits.rs b/tests/ui/field_representing_types/traits.rs index 6b5bb15f9ee9a..bc31376e60ea2 100644 --- a/tests/ui/field_representing_types/traits.rs +++ b/tests/ui/field_representing_types/traits.rs @@ -1,13 +1,17 @@ //@ revisions: old next //@ [next] compile-flags: -Znext-solver //@ run-pass -#![feature(field_projections, freeze)] +#![feature(field_projections, freeze, unsafe_unpin)] #![expect(incomplete_features, dead_code)] use std::field::field_of; -use std::marker::{Freeze, Unpin}; +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::{Freeze, Unpin, UnsafeUnpin}; +use std::panic::{RefUnwindSafe, UnwindSafe}; struct Struct { field: u32, + tail: [u32], } union Union { @@ -19,11 +23,37 @@ enum Enum { Variant2(u32), } -fn assert_traits() {} +type Tuple = ((), usize, String, dyn Debug); + +fn assert_traits< + T: Sized + + Freeze + + RefUnwindSafe + + Send + + Sync + + Unpin + + UnsafeUnpin + + UnwindSafe + + Copy + + Debug + + Default + + Eq + + Hash + + Ord, +>() { +} fn main() { assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::(); assert_traits::(); + + assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::(); } From a041a7609740acc81c8437cb13d95b7320c44bc4 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Tue, 21 Apr 2026 09:10:24 +0200 Subject: [PATCH 07/19] field representing types: add test for unsized types --- .../not-field-if-unsized.next.stderr | 27 +++++++++++++++++++ .../not-field-if-unsized.old.stderr | 27 +++++++++++++++++++ .../not-field-if-unsized.rs | 23 ++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 tests/ui/field_representing_types/not-field-if-unsized.next.stderr create mode 100644 tests/ui/field_representing_types/not-field-if-unsized.old.stderr create mode 100644 tests/ui/field_representing_types/not-field-if-unsized.rs diff --git a/tests/ui/field_representing_types/not-field-if-unsized.next.stderr b/tests/ui/field_representing_types/not-field-if-unsized.next.stderr new file mode 100644 index 0000000000000..e3cee80907720 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.next.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:17:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:21:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 1)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-unsized.old.stderr b/tests/ui/field_representing_types/not-field-if-unsized.old.stderr new file mode 100644 index 0000000000000..e3cee80907720 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.old.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:17:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:21:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 1)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-unsized.rs b/tests/ui/field_representing_types/not-field-if-unsized.rs new file mode 100644 index 0000000000000..739fc91c29423 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.rs @@ -0,0 +1,23 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub trait Trait {} + +pub struct MyStruct(usize, dyn Trait); + +fn assert_field() {} + +fn main() { + // FIXME(FRTs): this requires relaxing the `Base: ?Sized` bound in the + // `Field` trait & compiler changes. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied [E0277] + + // FIXME(FRTs): improve this error message, point to the `dyn Trait` span. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied [E0277] +} From 4abc28f57013bd3a3d17c858fca660ca62daeb15 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 22 Apr 2026 10:51:13 -0700 Subject: [PATCH 08/19] `::read_buf`: Clarify local variable name. --- library/std/src/io/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 1166ba8baf430..54b1742c6f258 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -3084,7 +3084,6 @@ impl Read for Take { // SAFETY: no uninit data is written to ibuf let ibuf = unsafe { &mut buf.as_mut()[..limit] }; - let mut sliced_buf: BorrowedBuf<'_> = ibuf.into(); if is_init { @@ -3096,14 +3095,14 @@ impl Read for Take { let mut cursor = sliced_buf.unfilled(); let result = self.inner.read_buf(cursor.reborrow()); - let should_init = cursor.is_init(); + let did_init_up_to_limit = sliced_buf.is_init(); let filled = sliced_buf.len(); // cursor / sliced_buf / ibuf must drop here // Avoid accidentally quadratic behaviour by initializing the whole // cursor if only part of it was initialized. - if should_init { + if did_init_up_to_limit { // SAFETY: no uninit data is written let uninit = unsafe { &mut buf.as_mut()[limit..] }; uninit.write_filled(0); From 71076f2338a7912eed39d8584ab5e3d58145f78a Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 22 Apr 2026 11:14:18 -0700 Subject: [PATCH 09/19] `::read_buf`: Eliminate unneeded local variables. Eliminate `cursor` and `ibuf` as named variables, as their presence makes things more confusing. --- library/std/src/io/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 54b1742c6f258..83b9667f9a573 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -3083,8 +3083,7 @@ impl Read for Take { let is_init = buf.is_init(); // SAFETY: no uninit data is written to ibuf - let ibuf = unsafe { &mut buf.as_mut()[..limit] }; - let mut sliced_buf: BorrowedBuf<'_> = ibuf.into(); + let mut sliced_buf = BorrowedBuf::from(unsafe { &mut buf.as_mut()[..limit] }); if is_init { // SAFETY: `sliced_buf` is a subslice of `buf`, so if `buf` was initialized then @@ -3092,13 +3091,12 @@ impl Read for Take { unsafe { sliced_buf.set_init() }; } - let mut cursor = sliced_buf.unfilled(); - let result = self.inner.read_buf(cursor.reborrow()); + let result = self.inner.read_buf(sliced_buf.unfilled()); let did_init_up_to_limit = sliced_buf.is_init(); let filled = sliced_buf.len(); - // cursor / sliced_buf / ibuf must drop here + // sliced_buf must drop here // Avoid accidentally quadratic behaviour by initializing the whole // cursor if only part of it was initialized. From c716ce5c2eaa741f6a3b7383a09f473df5ed5ad3 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 22 Apr 2026 11:05:35 -0700 Subject: [PATCH 10/19] `::read_buf`: Clarify safety comments and naming. --- library/std/src/io/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 83b9667f9a573..1812bf3ac9276 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -3101,11 +3101,13 @@ impl Read for Take { // Avoid accidentally quadratic behaviour by initializing the whole // cursor if only part of it was initialized. if did_init_up_to_limit { - // SAFETY: no uninit data is written - let uninit = unsafe { &mut buf.as_mut()[limit..] }; - uninit.write_filled(0); - // SAFETY: all bytes that were not initialized by `T::read_buf` - // have just been written to. + // SAFETY: No uninit data will be written. + let unfilled_before_advance = unsafe { buf.as_mut() }; + + unfilled_before_advance[limit..].write_filled(0); + + // SAFETY: `unfilled_before_advance[..limit]` was initialized by `T::read_buf`, and + // `unfilled_before_advance[limit..]` was just initialized. unsafe { buf.set_init() }; } From 3a0a14fd7c187e27eeba7dfe0f7467d18625e162 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Wed, 22 Apr 2026 11:15:27 -0700 Subject: [PATCH 11/19] `::read_buf`: Don't initialize `buf` if it was already initialized. --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 1812bf3ac9276..0a644caa5016b 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -3100,7 +3100,7 @@ impl Read for Take { // Avoid accidentally quadratic behaviour by initializing the whole // cursor if only part of it was initialized. - if did_init_up_to_limit { + if did_init_up_to_limit && !is_init { // SAFETY: No uninit data will be written. let unfilled_before_advance = unsafe { buf.as_mut() }; From 1a8d8ae5ec237c0600d657af906e0dfe5d7047b3 Mon Sep 17 00:00:00 2001 From: Kjetil Kjeka Date: Sun, 1 Feb 2026 11:05:42 +0100 Subject: [PATCH 12/19] NVPTX: Drop support for old hw and old ISAs --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 3 ++ compiler/rustc_codegen_ssa/src/base.rs | 7 +++++ compiler/rustc_codegen_ssa/src/errors.rs | 6 ++++ .../rustc_codegen_ssa/src/target_features.rs | 14 +++++++++- compiler/rustc_target/src/spec/json.rs | 3 ++ compiler/rustc_target/src/spec/mod.rs | 5 ++++ .../src/spec/targets/nvptx64_nvidia_cuda.rs | 10 +++++-- compiler/rustc_target/src/target_features.rs | 28 ++----------------- .../platform-support/nvptx64-nvidia-cuda.md | 14 ++++++++-- tests/assembly-llvm/nvptx-arch-default.rs | 3 +- tests/assembly-llvm/nvptx-arch-emit-asm.rs | 3 +- tests/ui/check-cfg/target_feature.stderr | 24 ---------------- .../unsupported-target-cpu.nvptx-sm60.stderr | 4 +++ tests/ui/target-cpu/unsupported-target-cpu.rs | 14 ++++++++++ 14 files changed, 81 insertions(+), 57 deletions(-) create mode 100644 tests/ui/target-cpu/unsupported-target-cpu.nvptx-sm60.stderr create mode 100644 tests/ui/target-cpu/unsupported-target-cpu.rs diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 180559d28d848..32694135705e8 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -510,6 +510,9 @@ fn print_target_cpus(sess: &Session, tm: &llvm::TargetMachine, out: &mut String) }; let mut cpus = cpu_names .lines() + .filter(|cpu_name| { + !sess.target.unsupported_cpus.contains(&std::borrow::Cow::Borrowed(*cpu_name)) + }) .map(|cpu_name| Cpu { cpu_name, remark: make_remark(cpu_name) }) .collect::>(); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 50c439593c306..14f4dca532737 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -696,6 +696,13 @@ pub fn codegen_crate( tcx.dcx().emit_fatal(errors::CpuRequired); } + if let Some(target_cpu) = &tcx.sess.opts.cg.target_cpu + && tcx.sess.target.unsupported_cpus.contains(&target_cpu.into()) + { + // The target cpu is explicitly listed as an unsupported cpu + tcx.dcx().emit_fatal(errors::CpuUnsupported { target_cpu: target_cpu.clone() }); + } + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); // Run the monomorphization collector and partition the collected items into diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index f1112510af0f0..006b7f881ce23 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -540,6 +540,12 @@ pub(crate) struct InsufficientVSCodeProduct; #[diag("target requires explicitly specifying a cpu with `-C target-cpu`")] pub(crate) struct CpuRequired; +#[derive(Diagnostic)] +#[diag("target cpu `{$target_cpu}` is known but unsupported")] +pub(crate) struct CpuUnsupported { + pub target_cpu: String, +} + #[derive(Diagnostic)] #[diag("processing debug info with `dsymutil` failed: {$status}")] #[note("{$output}")] diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 24f731c01996d..4b1b0866f2eb9 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -387,8 +387,20 @@ pub fn target_spec_to_backend_features<'a>( sess: &'a Session, mut extend_backend_features: impl FnMut(&'a str, /* enable */ bool), ) { - // Compute implied features let mut rust_features = vec![]; + + // This check handles SM versions that defaults (by LLVM) to unsupported (by Rust) PTX ISA versions. + // sm_70, sm_72 and sm_75 defaults to PTX ISA versions with major version 6, while sm_80 default to 7.0 + if sess.target.arch == Arch::Nvptx64 + && matches!( + sess.opts.cg.target_cpu.as_deref(), + None | Some("sm_70") | Some("sm_72") | Some("sm_75") + ) + { + rust_features.push((true, "ptx70")); + } + + // Compute implied features parse_rust_feature_list( sess, &sess.target.features, diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 5507af0866758..8448c2ab51b3d 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -116,6 +116,7 @@ impl Target { forward!(asm_args); forward!(cpu); forward!(need_explicit_cpu); + forward!(unsupported_cpus); forward!(features); forward!(dynamic_linking); forward_opt!(direct_access_external_data); @@ -320,6 +321,7 @@ impl ToJson for Target { target_option_val!(asm_args); target_option_val!(cpu); target_option_val!(need_explicit_cpu); + target_option_val!(unsupported_cpus); target_option_val!(features); target_option_val!(dynamic_linking); target_option_val!(direct_access_external_data); @@ -543,6 +545,7 @@ struct TargetSpecJson { asm_args: Option]>>, cpu: Option>, need_explicit_cpu: Option, + unsupported_cpus: Option]>>, features: Option>, dynamic_linking: Option, direct_access_external_data: Option, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 768e43146a0c1..74cf01e77754e 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2361,6 +2361,10 @@ pub struct TargetOptions { /// Whether a cpu needs to be explicitly set. /// Set to true if there is no default cpu. Defaults to false. pub need_explicit_cpu: bool, + /// A list of CPUs that are provided by LLVM but are considered unsupported by Rust. + /// These CPUs are omitted from `--print target-cpus` output and will cause an error + /// if used with `-Ctarget-cpu`. + pub unsupported_cpus: StaticCow<[StaticCow]>, /// Default (Rust) target features to enable for this target. These features /// overwrite `-Ctarget-cpu` but can be overwritten with `-Ctarget-features`. /// Corresponds to `llc -mattr=$llvm_features` where `$llvm_features` is the @@ -2818,6 +2822,7 @@ impl Default for TargetOptions { asm_args: cvs![], cpu: "generic".into(), need_explicit_cpu: false, + unsupported_cpus: cvs![], features: "".into(), direct_access_external_data: None, dynamic_linking: false, diff --git a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs index 87c2693e9877f..d8a0bd50ee204 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -1,6 +1,6 @@ use crate::spec::{ Arch, LinkSelfContainedDefault, LinkerFlavor, MergeFunctions, Os, PanicStrategy, Target, - TargetMetadata, TargetOptions, + TargetMetadata, TargetOptions, cvs, }; pub(crate) fn target() -> Target { @@ -22,7 +22,13 @@ pub(crate) fn target() -> Target { linker_flavor: LinkerFlavor::Llbc, // With `ptx-linker` approach, it can be later overridden via link flags. - cpu: "sm_30".into(), + cpu: "sm_70".into(), + + // No longer supported architectures + unsupported_cpus: cvs!( + "sm_20", "sm_21", "sm_30", "sm_32", "sm_35", "sm_37", "sm_50", "sm_52", "sm_53", + "sm_60", "sm_61", "sm_62" + ), // FIXME: create tests for the atomics. max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 9040c4eb1e399..498806f8c1c51 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -536,19 +536,7 @@ const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ const NVPTX_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("sm_20", Unstable(sym::nvptx_target_feature), &[]), - ("sm_21", Unstable(sym::nvptx_target_feature), &["sm_20"]), - ("sm_30", Unstable(sym::nvptx_target_feature), &["sm_21"]), - ("sm_32", Unstable(sym::nvptx_target_feature), &["sm_30"]), - ("sm_35", Unstable(sym::nvptx_target_feature), &["sm_32"]), - ("sm_37", Unstable(sym::nvptx_target_feature), &["sm_35"]), - ("sm_50", Unstable(sym::nvptx_target_feature), &["sm_37"]), - ("sm_52", Unstable(sym::nvptx_target_feature), &["sm_50"]), - ("sm_53", Unstable(sym::nvptx_target_feature), &["sm_52"]), - ("sm_60", Unstable(sym::nvptx_target_feature), &["sm_53"]), - ("sm_61", Unstable(sym::nvptx_target_feature), &["sm_60"]), - ("sm_62", Unstable(sym::nvptx_target_feature), &["sm_61"]), - ("sm_70", Unstable(sym::nvptx_target_feature), &["sm_62"]), + ("sm_70", Unstable(sym::nvptx_target_feature), &[]), ("sm_72", Unstable(sym::nvptx_target_feature), &["sm_70"]), ("sm_75", Unstable(sym::nvptx_target_feature), &["sm_72"]), ("sm_80", Unstable(sym::nvptx_target_feature), &["sm_75"]), @@ -567,19 +555,7 @@ const NVPTX_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sm_120a", Unstable(sym::nvptx_target_feature), &["sm_120"]), // tidy-alphabetical-end // tidy-alphabetical-start - ("ptx32", Unstable(sym::nvptx_target_feature), &[]), - ("ptx40", Unstable(sym::nvptx_target_feature), &["ptx32"]), - ("ptx41", Unstable(sym::nvptx_target_feature), &["ptx40"]), - ("ptx42", Unstable(sym::nvptx_target_feature), &["ptx41"]), - ("ptx43", Unstable(sym::nvptx_target_feature), &["ptx42"]), - ("ptx50", Unstable(sym::nvptx_target_feature), &["ptx43"]), - ("ptx60", Unstable(sym::nvptx_target_feature), &["ptx50"]), - ("ptx61", Unstable(sym::nvptx_target_feature), &["ptx60"]), - ("ptx62", Unstable(sym::nvptx_target_feature), &["ptx61"]), - ("ptx63", Unstable(sym::nvptx_target_feature), &["ptx62"]), - ("ptx64", Unstable(sym::nvptx_target_feature), &["ptx63"]), - ("ptx65", Unstable(sym::nvptx_target_feature), &["ptx64"]), - ("ptx70", Unstable(sym::nvptx_target_feature), &["ptx65"]), + ("ptx70", Unstable(sym::nvptx_target_feature), &[]), ("ptx71", Unstable(sym::nvptx_target_feature), &["ptx70"]), ("ptx72", Unstable(sym::nvptx_target_feature), &["ptx71"]), ("ptx73", Unstable(sym::nvptx_target_feature), &["ptx72"]), diff --git a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md index 2d9fc85dad33d..02510d9f3dee2 100644 --- a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md +++ b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md @@ -25,13 +25,23 @@ There are two options for using the core library: ### Target and features -It is generally necessary to specify the target, such as `-C target-cpu=sm_89`, because the default is very old. This implies two target features: `sm_89` and `ptx78` (and all preceding features within `sm_*` and `ptx*`). Rust will default to using the oldest PTX version that supports the target processor (see [this table](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#release-notes-ptx-release-history)), which maximizes driver compatibility. -One can use `-C target-feature=+ptx80` to choose a later PTX version without changing the target (the default in this case, `ptx78`, requires CUDA driver version 11.8, while `ptx80` would require driver version 12.0). +It is often beneficial to specify the target SM architecture, such as `-C target-cpu=sm_89`, because the default prioritizes broad compatibility rather than performance. Doing so also selects the PTX version as the *maximum* of (a) the oldest PTX version that supports the chosen target processor (see [this table](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#release-notes-ptx-release-history)) and (b) the oldest PTX version supported by the Rust toolchain, which maximizes driver compatibility. +One can use `-C target-feature=+ptx80` to choose a later PTX version without changing the target SM architecture (the default in this case, `ptx78`, requires CUDA driver version 11.8, while `ptx80` would require driver version 12.0). Later PTX versions may allow more efficient code generation. Although Rust follows LLVM in representing `ptx*` and `sm_*` as target features, they should be thought of as having crate granularity, set via (either via `-Ctarget-cpu` and optionally `-Ctarget-feature`). While the compiler accepts `#[target_feature(enable = "ptx80", enable = "sm_89")]`, it is not supported, may not behave as intended, and may become erroneous in the future. +## Minimum SM and PTX support by Rust version +Support for old hardware architectures and PTX ISA versions is periodically dropped. This table shows the minimum supported versions per Rust version. + +| Rust | SM minimum | PTX ISA minimum | +| ------------ | -------------- | --------------- | +| - 1.96 | 2.0 | 3.2 | +| 1.97 - TBD | 7.0 (Volta+) | 7.0 (CUDA 11+) | + +For a full overview of which GPUs support code built for a specific SM version, see the [CUDA GPU Compute Capability documentation](https://developer.nvidia.com/cuda/gpus). + ## Building Rust kernels A `no_std` crate containing one or more functions with `extern "ptx-kernel"` can be compiled to PTX using a command like the following. diff --git a/tests/assembly-llvm/nvptx-arch-default.rs b/tests/assembly-llvm/nvptx-arch-default.rs index 22b4a680e322c..e71304e453303 100644 --- a/tests/assembly-llvm/nvptx-arch-default.rs +++ b/tests/assembly-llvm/nvptx-arch-default.rs @@ -8,5 +8,6 @@ extern crate breakpoint_panic_handler; // Verify default target arch with ptx-linker. -// CHECK: .target sm_30 +// CHECK: .version 7.0 +// CHECK: .target sm_70 // CHECK: .address_size 64 diff --git a/tests/assembly-llvm/nvptx-arch-emit-asm.rs b/tests/assembly-llvm/nvptx-arch-emit-asm.rs index e47f8e78e3679..9266309c6202e 100644 --- a/tests/assembly-llvm/nvptx-arch-emit-asm.rs +++ b/tests/assembly-llvm/nvptx-arch-emit-asm.rs @@ -5,5 +5,6 @@ #![no_std] // Verify default arch without ptx-linker involved. -// CHECK: .target sm_30 +// CHECK: .version 7.0 +// CHECK: .target sm_70 // CHECK: .address_size 64 diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index b53419c512b08..981c173242408 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -229,18 +229,6 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `power9-altivec` `power9-vector` `prfchw` -`ptx32` -`ptx40` -`ptx41` -`ptx42` -`ptx43` -`ptx50` -`ptx60` -`ptx61` -`ptx62` -`ptx63` -`ptx64` -`ptx65` `ptx70` `ptx71` `ptx72` @@ -290,18 +278,6 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `sm_101a` `sm_120` `sm_120a` -`sm_20` -`sm_21` -`sm_30` -`sm_32` -`sm_35` -`sm_37` -`sm_50` -`sm_52` -`sm_53` -`sm_60` -`sm_61` -`sm_62` `sm_70` `sm_72` `sm_75` diff --git a/tests/ui/target-cpu/unsupported-target-cpu.nvptx-sm60.stderr b/tests/ui/target-cpu/unsupported-target-cpu.nvptx-sm60.stderr new file mode 100644 index 0000000000000..76092b8391f33 --- /dev/null +++ b/tests/ui/target-cpu/unsupported-target-cpu.nvptx-sm60.stderr @@ -0,0 +1,4 @@ +error: target cpu `sm_60` is known but unsupported + +error: aborting due to 1 previous error + diff --git a/tests/ui/target-cpu/unsupported-target-cpu.rs b/tests/ui/target-cpu/unsupported-target-cpu.rs new file mode 100644 index 0000000000000..dafbfbc015ec1 --- /dev/null +++ b/tests/ui/target-cpu/unsupported-target-cpu.rs @@ -0,0 +1,14 @@ +//! Check that certain target *respect* the unsupported-cpus in `-C target-cpu`. + +//@ revisions: nvptx-sm60 + +//@[nvptx-sm60] compile-flags: --target=nvptx64-nvidia-cuda --crate-type=rlib -Ctarget-cpu=sm_60 +//@[nvptx-sm60] needs-llvm-components: nvptx +//@[nvptx-sm60] build-fail +//@ ignore-backends: gcc + +#![feature(no_core)] +#![no_core] +#![crate_type = "rlib"] + +//[nvptx-sm60]~? ERROR target cpu `sm_60` is known but unsupported From 5e00484c38c2ce8084f081678afe47474dbe85ad Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 25 Apr 2026 16:27:05 +0800 Subject: [PATCH 13/19] Avoid misleading closure return type note --- compiler/rustc_hir_typeck/src/coercion.rs | 3 +++ .../closure-return-block-note-issue-155670.rs | 14 ++++++++++++++ .../closure-return-block-note-issue-155670.stderr | 9 +++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/ui/closures/closure-return-block-note-issue-155670.rs create mode 100644 tests/ui/closures/closure-return-block-note-issue-155670.stderr diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index e34628bad66b4..4dac314b91812 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -2009,6 +2009,9 @@ impl<'tcx> CoerceMany<'tcx> { // note in this case, since it would be incorrect. && let Some(fn_sig) = fcx.body_fn_sig() && fn_sig.output().is_ty_var() + && fcx.ret_coercion.as_ref().is_some_and(|ret_coercion| { + fcx.resolve_vars_if_possible(ret_coercion.borrow().expected_ty()) == expected + }) { err.span_note(sp, format!("return type inferred to be `{expected}` here")); } diff --git a/tests/ui/closures/closure-return-block-note-issue-155670.rs b/tests/ui/closures/closure-return-block-note-issue-155670.rs new file mode 100644 index 0000000000000..fccce9ee54ca2 --- /dev/null +++ b/tests/ui/closures/closure-return-block-note-issue-155670.rs @@ -0,0 +1,14 @@ +struct SmolStr; + +const _: fn() = || { + match Some(()) { + Some(()) => (), + None => return, + }; + let _: String = { + SmolStr + //~^ ERROR mismatched types + }; +}; + +fn main() {} diff --git a/tests/ui/closures/closure-return-block-note-issue-155670.stderr b/tests/ui/closures/closure-return-block-note-issue-155670.stderr new file mode 100644 index 0000000000000..f38d2f14200d4 --- /dev/null +++ b/tests/ui/closures/closure-return-block-note-issue-155670.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/closure-return-block-note-issue-155670.rs:9:9 + | +LL | SmolStr + | ^^^^^^^ expected `String`, found `SmolStr` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 4fc64f4180b491e3e121abb707d6a01a3e0503c0 Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Thu, 23 Apr 2026 15:48:57 +0530 Subject: [PATCH 14/19] Add boxing suggestions for `impl Trait` return type mismatches Signed-off-by: Usman Akinyemi --- .../src/fn_ctxt/suggestions.rs | 12 ++++++- ...trait-in-return-position-impl-trait.stderr | 7 ++++ ...type-err-cause-on-impl-trait-return.stderr | 36 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 1f08dff18f3ed..68355903c38e9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -32,7 +32,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _ use tracing::{debug, instrument}; use super::FnCtxt; -use crate::errors; +use crate::errors::{self, SuggestBoxingForReturnImplTrait}; use crate::fn_ctxt::rustc_span::BytePos; use crate::method::probe; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; @@ -963,6 +963,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + let trait_def_id = trait_ref.trait_ref.path.res.def_id(); + if self.tcx.is_dyn_compatible(trait_def_id) { + err.subdiagnostic(SuggestBoxingForReturnImplTrait::ChangeReturnType { + start_sp: hir_ty.span.with_hi(hir_ty.span.lo() + BytePos(4)), + end_sp: hir_ty.span.shrink_to_hi(), + }); + + err.note("if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()`"); + } + self.try_suggest_return_impl_trait(err, expected, found, fn_id); self.try_note_caller_chooses_ty_for_ty_param(err, expected, found); return true; diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr index 2447a5d8d4b80..6e417b02f5688 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr @@ -21,6 +21,13 @@ LL | return A; LL | } LL | B | ^ expected `A`, found `B` + | + = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` +help: you could change the return type to be a boxed trait object + | +LL - fn cat() -> impl DynCompatible { +LL + fn cat() -> Box { + | error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr index 2a4c5ff4a5bed..a4d084c26e006 100644 --- a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr @@ -72,6 +72,12 @@ LL | } LL | 1u32 | ^^^^ expected `i32`, found `u32` | + = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` +help: you could change the return type to be a boxed trait object + | +LL - fn foo() -> impl std::fmt::Display { +LL + fn foo() -> Box { + | help: change the type of the numeric literal from `u32` to `i32` | LL - 1u32 @@ -90,6 +96,12 @@ LL | } else { LL | return 1u32; | ^^^^ expected `i32`, found `u32` | + = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` +help: you could change the return type to be a boxed trait object + | +LL - fn bar() -> impl std::fmt::Display { +LL + fn bar() -> Box { + | help: change the type of the numeric literal from `u32` to `i32` | LL - return 1u32; @@ -108,6 +120,12 @@ LL | } else { LL | 1u32 | ^^^^ expected `i32`, found `u32` | + = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` +help: you could change the return type to be a boxed trait object + | +LL - fn baz() -> impl std::fmt::Display { +LL + fn baz() -> Box { + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -153,6 +171,12 @@ LL | 0 => return 0i32, LL | _ => 1u32, | ^^^^ expected `i32`, found `u32` | + = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` +help: you could change the return type to be a boxed trait object + | +LL - fn bat() -> impl std::fmt::Display { +LL + fn bat() -> Box { + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -171,6 +195,12 @@ LL | | _ => 2u32, LL | | } | |_____^ expected `i32`, found `u32` | + = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` +help: you could change the return type to be a boxed trait object + | +LL - fn can() -> impl std::fmt::Display { +LL + fn can() -> Box { + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -188,6 +218,12 @@ LL | return 0i32; LL | 1u32 | ^^^^ expected `i32`, found `u32` | + = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` +help: you could change the return type to be a boxed trait object + | +LL - fn cat() -> impl std::fmt::Display { +LL + fn cat() -> Box { + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() From 163aedc8737747454e7b2ad16f62601c94260f34 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 26 Apr 2026 13:09:29 +0200 Subject: [PATCH 15/19] Convert attribute `FinalizeFn` to fn pointer --- compiler/rustc_attr_parsing/src/context.rs | 7 +++---- compiler/rustc_attr_parsing/src/interface.rs | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index cebbabfcbf1be..4aa7ebffbd3cf 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -84,8 +84,7 @@ pub(super) struct GroupTypeInnerAccept { pub(crate) type AcceptFn = Box Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>; -pub(crate) type FinalizeFn = - Box) -> Option>; +pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_, S>) -> Option; macro_rules! attribute_parsers { ( @@ -131,10 +130,10 @@ macro_rules! attribute_parsers { }), safety: <$names as crate::attributes::AttributeParser<$stage>>::SAFETY, allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, - finalizer: Box::new(|cx| { + finalizer: |cx| { let state = STATE_OBJECT.take(); state.finalize(cx) - }) + } }); } Entry::Occupied(_) => panic!("Attribute {path:?} has multiple accepters"), diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 0dfa67951e629..d350bfee7f348 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -297,7 +297,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { let mut attr_paths: Vec> = Vec::new(); let mut early_parsed_state = EarlyParsedState::default(); - let mut finalizers: Vec<&FinalizeFn> = Vec::with_capacity(attrs.len()); + let mut finalizers: Vec> = Vec::with_capacity(attrs.len()); for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. @@ -413,7 +413,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { }; (accept.accept_fn)(&mut cx, &args); - finalizers.push(&accept.finalizer); + finalizers.push(accept.finalizer); if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { Self::check_target(&accept.allowed_targets, target, &mut cx); From a677828c48af2a7017b63615c0753911e3683d29 Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Sun, 26 Apr 2026 17:15:57 +0530 Subject: [PATCH 16/19] Add boxing suggestions for return expressions in `impl Trait` functions Signed-off-by: Usman Akinyemi --- .../src/fn_ctxt/suggestions.rs | 28 ++++++++++++- ...trait-in-return-position-impl-trait.stderr | 7 +++- ...type-err-cause-on-impl-trait-return.stderr | 42 ++++++++++++++++--- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 68355903c38e9..afd5356d5a1e7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -7,6 +7,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan, listify, msg}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, @@ -26,6 +27,7 @@ use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::{ExpnKind, Ident, MacroKind, Span, Spanned, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::DefIdOrName; +use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -970,7 +972,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { end_sp: hir_ty.span.shrink_to_hi(), }); - err.note("if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()`"); + let body = self.tcx.hir_body_owned_by(fn_id); + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(&body); + + if !visitor.returns.is_empty() { + let starts: Vec = visitor + .returns + .iter() + .filter(|expr| expr.span.can_be_used_for_suggestions()) + .map(|expr| expr.span.shrink_to_lo()) + .collect(); + let ends: Vec = visitor + .returns + .iter() + .filter(|expr| expr.span.can_be_used_for_suggestions()) + .map(|expr| expr.span.shrink_to_hi()) + .collect(); + + if !starts.is_empty() { + err.subdiagnostic(SuggestBoxingForReturnImplTrait::BoxReturnExpr { + starts, + ends, + }); + } + } } self.try_suggest_return_impl_trait(err, expected, found, fn_id); diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr index 6e417b02f5688..bba17eb2494e2 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr @@ -22,12 +22,17 @@ LL | } LL | B | ^ expected `A`, found `B` | - = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` help: you could change the return type to be a boxed trait object | LL - fn cat() -> impl DynCompatible { LL + fn cat() -> Box { | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(A); +LL | } +LL ~ Box::new(B) + | error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr index a4d084c26e006..13a78cb0fcf35 100644 --- a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr @@ -72,12 +72,17 @@ LL | } LL | 1u32 | ^^^^ expected `i32`, found `u32` | - = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` help: you could change the return type to be a boxed trait object | LL - fn foo() -> impl std::fmt::Display { LL + fn foo() -> Box { | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(0i32); +LL | } +LL ~ Box::new(1u32) + | help: change the type of the numeric literal from `u32` to `i32` | LL - 1u32 @@ -96,12 +101,17 @@ LL | } else { LL | return 1u32; | ^^^^ expected `i32`, found `u32` | - = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` help: you could change the return type to be a boxed trait object | LL - fn bar() -> impl std::fmt::Display { LL + fn bar() -> Box { | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(0i32); +LL | } else { +LL ~ return Box::new(1u32); + | help: change the type of the numeric literal from `u32` to `i32` | LL - return 1u32; @@ -120,12 +130,17 @@ LL | } else { LL | 1u32 | ^^^^ expected `i32`, found `u32` | - = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` help: you could change the return type to be a boxed trait object | LL - fn baz() -> impl std::fmt::Display { LL + fn baz() -> Box { | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(0i32); +LL | } else { +LL ~ Box::new(1u32) + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -171,12 +186,16 @@ LL | 0 => return 0i32, LL | _ => 1u32, | ^^^^ expected `i32`, found `u32` | - = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` help: you could change the return type to be a boxed trait object | LL - fn bat() -> impl std::fmt::Display { LL + fn bat() -> Box { | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ 0 => return Box::new(0i32), +LL ~ _ => Box::new(1u32), + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -195,12 +214,17 @@ LL | | _ => 2u32, LL | | } | |_____^ expected `i32`, found `u32` | - = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` help: you could change the return type to be a boxed trait object | LL - fn can() -> impl std::fmt::Display { LL + fn can() -> Box { | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ 0 => return Box::new(0i32), +LL ~ 1 => Box::new(1u32), +LL ~ _ => Box::new(2u32), + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -218,12 +242,18 @@ LL | return 0i32; LL | 1u32 | ^^^^ expected `i32`, found `u32` | - = note: if you change the return type to expect trait objects, you'll need to wrap the returned values in `Box::new()` help: you could change the return type to be a boxed trait object | LL - fn cat() -> impl std::fmt::Display { LL + fn cat() -> Box { | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(0i32); +LL | } +LL | _ => { +LL ~ Box::new(1u32) + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() From a7330f48978cd46f9d90e767c2c0cfdae1f7e4a2 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sun, 26 Apr 2026 15:08:44 +0000 Subject: [PATCH 17/19] rustc_attr_parsing: use a `try {}` in `or_malformed` --- .../rustc_attr_parsing/src/attributes/diagnostic/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index e56ed166592aa..b215f77c39adb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -236,9 +236,11 @@ fn parse_directive_items<'p, S: Stage>( }} macro or_malformed($($code:tt)*) {{ - let Some(ret) = (||{ - Some($($code)*) - })() else { + let Some(ret) = ( + try { + $($code)* + } + ) else { malformed!() }; ret From d9e227e98ac123b9632b61c4c68afba9748ff584 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 26 Apr 2026 16:18:45 +0000 Subject: [PATCH 18/19] Fix broken logic in `incremental_verify_ich_failed` --- compiler/rustc_middle/src/error.rs | 2 +- compiler/rustc_middle/src/verify_ich.rs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 90af4d785945b..29ec97a6ca592 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -143,7 +143,7 @@ pub(crate) struct Reentrant; #[note( "an ideal reproduction consists of the code before and some patch that then triggers the bug when applied and compiled again" )] -#[note("as a workaround, you can run {$run_cmd} to allow your project to compile")] +#[note("as a workaround, you can {$run_cmd} to allow your project to compile")] pub(crate) struct IncrementCompilation { pub run_cmd: String, pub dep_node: String, diff --git a/compiler/rustc_middle/src/verify_ich.rs b/compiler/rustc_middle/src/verify_ich.rs index a1ab4d8cc4d00..5aa7a1d91f800 100644 --- a/compiler/rustc_middle/src/verify_ich.rs +++ b/compiler/rustc_middle/src/verify_ich.rs @@ -1,6 +1,8 @@ use std::cell::Cell; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_session::utils::was_invoked_from_cargo; use tracing::instrument; use crate::dep_graph::{DepGraphData, SerializedDepNodeIndex}; @@ -66,10 +68,10 @@ fn incremental_verify_ich_failed<'tcx>( if old_in_panic { tcx.dcx().emit_err(crate::error::Reentrant); } else { - let run_cmd = if let Some(crate_name) = &tcx.sess.opts.crate_name { - format!("`cargo clean -p {crate_name}` or `cargo clean`") + let run_cmd = if was_invoked_from_cargo() { + format!("run `cargo clean -p {}` or `cargo clean`", tcx.crate_name(LOCAL_CRATE)) } else { - "`cargo clean`".to_string() + "clean your build cache".to_owned() }; let dep_node = tcx.dep_graph.data().unwrap().prev_node_of(prev_index); From 7151184b46b05a5c96c70d104095c79e6d89aee4 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 26 Apr 2026 16:19:11 +0000 Subject: [PATCH 19/19] Add some debugging to `rustc_session` filename handling --- compiler/rustc_session/src/config.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d84bfeb8fff86..cd9d573957f45 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1205,6 +1205,7 @@ impl OutputFilenames { } pub fn interface_path(&self) -> PathBuf { + debug!("using crate_name={} for interface_path", self.crate_stem); self.out_directory.join(format!("lib{}.rs", self.crate_stem)) } @@ -1214,6 +1215,7 @@ impl OutputFilenames { let extension = flavor.extension(); match flavor { OutputType::Metadata => { + debug!("using crate_name={} for {extension}", self.crate_stem); self.out_directory.join(format!("lib{}.{}", self.crate_stem, extension)) } _ => self.with_directory_and_extension(&self.out_directory, extension), @@ -1288,6 +1290,7 @@ impl OutputFilenames { } pub fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf { + debug!("using filestem={} for {extension}", self.filestem); let mut path = directory.join(&self.filestem); path.set_extension(extension); path