From 4fc64f4180b491e3e121abb707d6a01a3e0503c0 Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Thu, 23 Apr 2026 15:48:57 +0530 Subject: [PATCH 1/2] 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 1f08dff18f3e..68355903c38e 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 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 @@ pub(in super::super) fn suggest_missing_return_type( ); } + 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 2447a5d8d4b8..6e417b02f568 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 2a4c5ff4a5be..a4d084c26e00 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 a677828c48af2a7017b63615c0753911e3683d29 Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Sun, 26 Apr 2026 17:15:57 +0530 Subject: [PATCH 2/2] 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 68355903c38e..afd5356d5a1e 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_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_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 @@ pub(in super::super) fn suggest_missing_return_type( 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 6e417b02f568..bba17eb2494e 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 a4d084c26e00..13a78cb0fcf3 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()