From dc24aae43e6e1751c498df619ca572eaa7adf4a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 29 Jan 2026 21:13:09 +0100 Subject: [PATCH] Modernize diagnostic for indeterminate trait object lifetime bounds --- compiler/rustc_hir_analysis/src/collect.rs | 32 +++++++++++-------- .../src/hir_ty_lowering/dyn_trait.rs | 2 +- .../src/hir_ty_lowering/mod.rs | 2 +- .../object-lifetime-default-ambiguous.rs | 6 ++-- .../object-lifetime-default-ambiguous.stderr | 21 ++++++++++-- ...lifetime-default-dyn-binding-nonstatic1.rs | 2 +- ...time-default-dyn-binding-nonstatic1.stderr | 7 +++- ...lifetime-default-dyn-binding-nonstatic2.rs | 2 +- ...time-default-dyn-binding-nonstatic2.stderr | 7 +++- ...lifetime-default-dyn-binding-nonstatic3.rs | 2 +- ...time-default-dyn-binding-nonstatic3.stderr | 7 +++- 11 files changed, 63 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index dd399f9d90def..919199f0b573c 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -22,9 +22,7 @@ use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey, struct_span_code_err, -}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -316,16 +314,24 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { } fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> { - if let RegionInferReason::ObjectLifetimeDefault = reason { - let e = struct_span_code_err!( - self.dcx(), - span, - E0228, - "the lifetime bound for this object type cannot be deduced \ - from context; please supply an explicit bound" - ) - .emit(); - ty::Region::new_error(self.tcx(), e) + if let RegionInferReason::ObjectLifetimeDefault(sugg_sp) = reason { + // FIXME: Account for trailing plus `dyn Trait+`, the need of parens in + // `*const dyn Trait` and `Fn() -> *const dyn Trait`. + let guar = self + .dcx() + .struct_span_err( + span, + "cannot deduce the lifetime bound for this trait object type from context", + ) + .with_code(E0228) + .with_span_suggestion_verbose( + sugg_sp, + "please supply an explicit bound", + " + /* 'a */", + Applicability::HasPlaceholders, + ) + .emit(); + ty::Region::new_error(self.tcx(), guar) } else { // This indicates an illegal lifetime in a non-assoc-trait position ty::Region::new_error_with_message(self.tcx(), span, "unelided lifetime in signature") diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index 29f29761b6055..37a9f41e8942d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -460,7 +460,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Parent lifetime must have failed to resolve. Don't emit a redundant error. RegionInferReason::ExplicitObjectLifetime } else { - RegionInferReason::ObjectLifetimeDefault + RegionInferReason::ObjectLifetimeDefault(span.shrink_to_hi()) } } else { RegionInferReason::ExplicitObjectLifetime diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 867c588e302d3..ccccb6145c46d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -105,7 +105,7 @@ pub enum RegionInferReason<'a> { /// Lifetime on a trait object that is spelled explicitly, e.g. `+ 'a` or `+ '_`. ExplicitObjectLifetime, /// A trait object's lifetime when it is elided, e.g. `dyn Any`. - ObjectLifetimeDefault, + ObjectLifetimeDefault(Span), /// Generic lifetime parameter Param(&'a ty::GenericParamDef), RegionPredicate, diff --git a/tests/ui/object-lifetime/object-lifetime-default-ambiguous.rs b/tests/ui/object-lifetime/object-lifetime-default-ambiguous.rs index 5dae92fee5f90..0a0f44073740e 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-ambiguous.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-ambiguous.rs @@ -21,11 +21,11 @@ struct Ref2<'a,'b:'a,T:'a+'b+?Sized> { } fn a<'a,'b>(t: Ref2<'a,'b, dyn Test>) { - //~^ ERROR lifetime bound for this object type cannot be deduced from context + //~^ ERROR cannot deduce the lifetime bound for this trait object type } fn b(t: Ref2) { - //~^ ERROR lifetime bound for this object type cannot be deduced from context + //~^ ERROR cannot deduce the lifetime bound for this trait object type } fn c(t: Ref2<&dyn Test>) { @@ -41,7 +41,7 @@ fn e(t: Ref2>) { } fn f(t: &Ref2) { - //~^ ERROR lifetime bound for this object type cannot be deduced from context + //~^ ERROR cannot deduce the lifetime bound for this trait object type } fn main() { diff --git a/tests/ui/object-lifetime/object-lifetime-default-ambiguous.stderr b/tests/ui/object-lifetime/object-lifetime-default-ambiguous.stderr index bd50a27fd5e58..6aa3da66fda0f 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-ambiguous.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default-ambiguous.stderr @@ -1,20 +1,35 @@ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound +error[E0228]: cannot deduce the lifetime bound for this trait object type from context --> $DIR/object-lifetime-default-ambiguous.rs:23:28 | LL | fn a<'a,'b>(t: Ref2<'a,'b, dyn Test>) { | ^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn a<'a,'b>(t: Ref2<'a,'b, dyn Test + /* 'a */>) { + | ++++++++++ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound +error[E0228]: cannot deduce the lifetime bound for this trait object type from context --> $DIR/object-lifetime-default-ambiguous.rs:27:14 | LL | fn b(t: Ref2) { | ^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn b(t: Ref2) { + | ++++++++++ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound +error[E0228]: cannot deduce the lifetime bound for this trait object type from context --> $DIR/object-lifetime-default-ambiguous.rs:43:15 | LL | fn f(t: &Ref2) { | ^^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn f(t: &Ref2) { + | ++++++++++ error: aborting due to 3 previous errors diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.rs b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.rs index 7337383e29784..7a7bba02879ec 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.rs @@ -18,7 +18,7 @@ fn is_static(_: T) where T: 'static { } // Here, we should default to `dyn Bar + 'static`, but the current // code forces us into a conservative, hacky path. fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } -//~^ ERROR please supply an explicit bound +//~^ ERROR cannot deduce the lifetime bound for this trait object type fn main() { let s = format!("foo"); diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr index 8d44b4de55a47..5744d0dd162cd 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic1.stderr @@ -1,8 +1,13 @@ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound +error[E0228]: cannot deduce the lifetime bound for this trait object type from context --> $DIR/object-lifetime-default-dyn-binding-nonstatic1.rs:20:50 | LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar + /* 'a */> { &() } + | ++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.rs b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.rs index 2a7415174f8a0..9ddf792eaada4 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.rs @@ -18,7 +18,7 @@ fn is_static(_: T) where T: 'static { } // Here, we default to `dyn Bar + 'a`. Or, we *should*, but the // current code forces us into a conservative, hacky path. fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } -//~^ ERROR please supply an explicit bound +//~^ ERROR cannot deduce the lifetime bound for this trait object type fn main() { let s = format!("foo"); diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr index 0846dd60723a4..ca8612f57934e 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic2.stderr @@ -1,8 +1,13 @@ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound +error[E0228]: cannot deduce the lifetime bound for this trait object type from context --> $DIR/object-lifetime-default-dyn-binding-nonstatic2.rs:20:50 | LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar> { &() } | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn bar<'a>(x: &'a str) -> &'a dyn Foo<'a, Item = dyn Bar + /* 'a */> { &() } + | ++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs index 51be999a6329d..48ac5623f898b 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs @@ -14,7 +14,7 @@ fn is_static(_: T) where T: 'static { } // Here, we should default to `dyn Bar + 'static`, but the current // code forces us into a conservative, hacky path. fn bar(x: &str) -> &dyn Foo { &() } -//~^ ERROR please supply an explicit bound +//~^ ERROR cannot deduce the lifetime bound for this trait object type fn main() { let s = format!("foo"); diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr index 688f8af0822b4..05620d3878f75 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr @@ -1,8 +1,13 @@ -error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound +error[E0228]: cannot deduce the lifetime bound for this trait object type from context --> $DIR/object-lifetime-default-dyn-binding-nonstatic3.rs:16:36 | LL | fn bar(x: &str) -> &dyn Foo { &() } | ^^^^^^^ + | +help: please supply an explicit bound + | +LL | fn bar(x: &str) -> &dyn Foo { &() } + | ++++++++++ error: aborting due to 1 previous error