From 76a82134d2e4e9a30da299498a7db25c83212ccd Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Sun, 25 Oct 2026 18:24:51 -0300 Subject: [PATCH 1/4] Explain implicit object lifetimes in static calls An omitted object bound can share the reference's lifetime, so E0521 points at the borrow even when the call requires a static trait object. Explain the lifetime source when suggesting an explicit object bound. Check callee bounds and trait lifetime arguments before offering an edit, and avoid making output lifetime elision ambiguous. --- .../src/diagnostics/region_errors.rs | 218 +++++++++- .../implicit-object-lifetime-no-suggestion.rs | 121 ++++++ ...licit-object-lifetime-no-suggestion.stderr | 362 ++++++++++++++++ ...it-object-lifetime-static-suggestion.fixed | 122 ++++++ ...licit-object-lifetime-static-suggestion.rs | 122 ++++++ ...t-object-lifetime-static-suggestion.stderr | 392 ++++++++++++++++++ ...yn-trait-with-implicit-static-bound.stderr | 5 + 7 files changed, 1341 insertions(+), 1 deletion(-) create mode 100644 tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.rs create mode 100644 tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.stderr create mode 100644 tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.fixed create mode 100644 tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.rs create mode 100644 tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 9bb60d47ac849..7ddbe769878d1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::{AnnotationSource, ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::{ self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitor, fold_regions, }; -use rustc_span::{Ident, Span, Symbol, bug, kw}; +use rustc_span::{Ident, Pos, Span, Symbol, bug, kw}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::infer::nice_region_error::{ self, HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, find_anon_type, @@ -672,6 +672,7 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { if let ConstraintCategory::CallArgument(Some(ty)) = category { self.explain_impl_static_obligation(&mut diag, ty, outlived_fr); } + self.suggest_explicit_static_object_lifetime(&mut diag, &errci); match variance_info { ty::VarianceDiagInfo::None => {} @@ -1254,6 +1255,221 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { } } + fn suggest_explicit_static_object_lifetime( + &self, + diag: &mut Diag<'_>, + errci: &ErrorConstraintInfo<'tcx>, + ) { + let tcx = self.infcx.tcx; + let (Some(region), Some(outlived_region)) = ( + self.regioncx.to_error_region(errci.fr), + self.regioncx.to_error_region(errci.outlived_fr), + ) else { + return; + }; + if !outlived_region.is_static() { + return; + } + let ConstraintCategory::CallArgument(Some(fn_ty)) = errci.category else { return }; + let ty::FnDef(def_id, args) = *fn_ty.kind() else { return }; + let Some(args) = args.no_bound_vars() else { return }; + if tcx.is_closure_like(def_id) { + return; + } + // CallArgument erases lifetime arguments. Bounds can still require the reference to + // outlive another region, even if the instantiated signature contains no `&'static`. + let clauses = tcx.clauses_of(def_id); + if clauses.instantiate_identity(tcx).into_iter().chain(clauses.instantiate(tcx, args)).any( + |(clause, _)| match clause.skip_norm_wip().kind().skip_binder() { + ty::ClauseKind::RegionOutlives(ty::OutlivesClause(longer, shorter)) => { + longer != shorter && !longer.is_static() + } + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty, lifetime)) + if lifetime.is_static() => + { + ty.walk().any(|arg| { + matches!(arg.as_type().map(Ty::kind), + Some(ty::Ref(region, ..)) if !region.is_static()) + }) + } + _ => false, + }, + ) { + return; + } + let Some(call) = self.find_expr(errci.span) else { return }; + let call_args: Vec<_> = match call.kind { + hir::ExprKind::Call(_, args) => args.iter().collect(), + hir::ExprKind::MethodCall(_, receiver, args, _) => { + std::iter::once(receiver).chain(args).collect() + } + _ => return, + }; + let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return }; + let Some(decl) = tcx.hir_fn_decl_by_hir_id(self.mir_hir_id()) else { return }; + let callee_sig = tcx.fn_sig(def_id).instantiate(tcx, args).skip_norm_wip().skip_binder(); + // Typeck results have erased regions. The liberated signature retains the object lifetime. + let caller_sig = tcx.liberate_late_bound_regions( + self.mir_def_id().to_def_id(), + tcx.fn_sig(self.mir_def_id()).instantiate_identity().skip_norm_wip(), + ); + let needs_static_reference = |mut ty: Ty<'tcx>| { + while let ty::Ref(lifetime, inner, _) = *ty.kind() { + if lifetime.is_static() { + return true; + } + ty = inner; + } + false + }; + let mut seen_params = FxIndexSet::default(); + for (arg, expected) in std::iter::zip(call_args.iter().copied(), callee_sig.inputs()) { + if needs_static_reference(*expected) { + continue; + } + let ty::Dynamic(expected_predicates, expected_region) = *expected.peel_refs().kind() + else { + continue; + }; + if !expected_region.is_static() { + continue; + } + + // Follow the actual argument back to its parameter, rather than editing another + // parameter that happens to use the same lifetime or trait. + let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = arg.kind else { + continue; + }; + let hir::def::Res::Local(binding) = path.res else { continue }; + let Some(index) = body.params.iter().position(|param| param.pat.hir_id == binding) + else { + continue; + }; + let ty::Dynamic(predicates, object_region) = + *caller_sig.inputs()[index].peel_refs().kind() + else { + continue; + }; + if object_region != region { + continue; + } + // Callee lifetimes may be bound or erased here. Preserve explicit `'static` and + // repeated lifetime arguments: changing the object bound cannot fix those mismatches. + let comparable_predicates = |predicates| { + let mut regions = FxIndexSet::default(); + fold_regions(tcx, predicates, |region, _| { + if region.is_static() { + region + } else { + let (index, _) = regions.insert_full(region); + ty::Region::new_late_param( + tcx, + self.mir_def_id().to_def_id(), + ty::LateParamRegionKind::Anon(index as u32), + ) + } + }) + }; + let source_predicates = comparable_predicates(predicates); + let target_predicates = comparable_predicates(expected_predicates); + if source_predicates.principal().map(|p| tcx.anonymize_bound_vars(p)) + != target_predicates.principal().map(|p| tcx.anonymize_bound_vars(p)) + || !source_predicates + .projection_bounds() + .map(|p| tcx.anonymize_bound_vars(p)) + .eq(target_predicates.projection_bounds().map(|p| tcx.anonymize_bound_vars(p))) + { + continue; + } + // The same parameter may also be passed where the reference itself must be static. + if !seen_params.insert(index) + || std::iter::zip(&call_args, callee_sig.inputs()).any(|(other, expected)| { + needs_static_reference(*expected) + && matches!(other.kind, + hir::ExprKind::Path(hir::QPath::Resolved(None, path)) + if path.res == hir::def::Res::Local(binding)) + }) + { + continue; + } + let param_ty = decl.inputs[index]; + let object_ty = param_ty.peel_refs(); + let hir::TyKind::TraitObject(_, lifetime) = object_ty.kind else { continue }; + if lifetime.kind != hir::LifetimeKind::ImplicitObjectLifetimeDefault + || !object_ty.span.can_be_used_for_suggestions() + { + continue; + } + let lifetime_explanation = + if rustc_trait_selection::traits::wf::object_region_bounds(tcx, predicates) + .is_empty() + { + "this trait object's lifetime defaults to the reference's lifetime" + } else { + "this trait object's lifetime is inferred from its trait's lifetime bounds" + }; + + struct ElidedOutputLifetime<'tcx> { + tcx: TyCtxt<'tcx>, + object_lifetime: &'tcx hir::Lifetime, + found: bool, + } + impl<'tcx> Visitor<'tcx> for ElidedOutputLifetime<'tcx> { + fn visit_lifetime(&mut self, lifetime: &'tcx hir::Lifetime) { + let resolved = self.tcx.named_bound_var(lifetime.hir_id); + self.found |= lifetime.syntax != hir::LifetimeSyntax::ExplicitBound + && resolved.is_some() + && resolved == self.tcx.named_bound_var(self.object_lifetime.hir_id); + } + } + let mut output_lifetime = + ElidedOutputLifetime { tcx, object_lifetime: lifetime.pointer(), found: false }; + if let hir::FnRetTy::Return(output) = decl.output { + output_lifetime.visit_ty_unambig(output); + } + if output_lifetime.found { + // Adding another input lifetime would make output lifetime elision ambiguous. + diag.span_note(object_ty.span, lifetime_explanation); + continue; + } + + let source_map = tcx.sess.source_map(); + let Ok(mut snippet) = source_map.span_to_snippet(object_ty.span) else { continue }; + if let hir::TyKind::TraitObject(bounds, _) = object_ty.kind + && let Some(bound) = bounds.last() + && let Some(segment) = bound.trait_ref.path.segments.last() + && let Some(output) = segment.args().paren_sugar_output() + && let output = output.peel_refs() + && let hir::TyKind::TraitObject(..) = output.kind + { + if !object_ty.span.contains(output.span) { + continue; + } + // Otherwise `+ 'static` would bind to the callable's output trait object. + let start = (output.span.lo() - object_ty.span.lo()).to_usize(); + let end = (output.span.hi() - object_ty.span.lo()).to_usize(); + snippet.insert_str(end, ")"); + snippet.insert_str(start, "("); + } + let prefix = source_map + .span_to_snippet(param_ty.span.with_hi(object_ty.span.lo())) + .unwrap_or_default(); + let replacement = if prefix.trim_end().ends_with('(') { + format!("{snippet} + 'static") + } else { + format!("({snippet} + 'static)") + }; + diag.span_suggestion( + object_ty.span, + format!( + "{lifetime_explanation}; consider adding an explicit `'static` bound to the trait object" + ), + replacement, + Applicability::MaybeIncorrect, + ); + } + } + fn maybe_suggest_constrain_dyn_trait_impl( &self, diag: &mut Diag<'_>, diff --git a/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.rs b/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.rs new file mode 100644 index 0000000000000..055438a925724 --- /dev/null +++ b/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.rs @@ -0,0 +1,121 @@ +#![allow(dead_code, mismatched_lifetime_syntaxes)] + +use std::error::Error; + +fn explicit_bound<'a, T: Error + 'static>(err: &(dyn Error + 'a)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn explicit_placeholder(err: &(dyn Error + '_)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn return_static(err: &dyn Error) -> &'static dyn Error { + err //~ ERROR lifetime may not live long enough +} + +fn return_other<'a, 'b>(err: &'a dyn Error) -> &'b dyn Error { + err //~ ERROR lifetime may not live long enough +} + +fn takes_static_reference(_: &'static dyn Error) {} + +fn static_reference_argument(err: &dyn Error) { + takes_static_reference(err); //~ ERROR borrowed data escapes outside of function +} + +fn takes_static_object_and_reference(_: &'static (dyn Error + 'static)) {} + +fn static_reference_and_object(err: &dyn Error) { + takes_static_object_and_reference(err); //~ ERROR borrowed data escapes outside of function +} + +fn takes_object_and_static_reference(_: &(dyn Error + 'static), _: &'static dyn Error) {} + +fn repeated_static_reference(err: &dyn Error) { + takes_object_and_static_reference(err, err); //~ ERROR borrowed data escapes outside of function +} + +fn takes_object_and_reference<'a>(_: &(dyn Error + 'a), _: &'static u32) {} + +fn unrelated_parameter<'a>(err: &'a dyn Error, other: &'a u32) { + takes_object_and_reference(err, other); //~ ERROR borrowed data escapes outside of function +} + +fn takes_generic_static(_: &T) {} + +fn generic_bound(err: &dyn Error) { + takes_generic_static(err); //~ ERROR borrowed data escapes outside of function +} + +trait Trait { + fn needs_static(&self) where Self: 'static {} +} + +fn where_self(err: &dyn Trait) { + err.needs_static(); //~ ERROR borrowed data escapes outside of function +} + +fn elided_output(err: &dyn Error) -> Option<&T> { + err.downcast_ref::() //~ ERROR borrowed data escapes outside of function +} + +fn named_input_elided_output<'a, T: Error + 'static>(err: &'a dyn Error) -> Option<&T> { + err.downcast_ref::() //~ ERROR borrowed data escapes outside of function +} + +fn placeholder_output(err: &dyn Error) -> Option<&'_ T> { + err.downcast_ref::() //~ ERROR borrowed data escapes outside of function +} + +fn takes_predicate_reference<'a: 'static>(_: &'a (dyn Error + 'static)) {} + +fn predicate_reference(err: &dyn Error) { + takes_predicate_reference(err); //~ ERROR borrowed data escapes outside of function +} + +fn takes_transitive_reference<'a: 'b, 'b: 'static>(_: &'a (dyn Error + 'static)) {} + +fn transitive_reference(err: &dyn Error) { + takes_transitive_reference(err); //~ ERROR borrowed data escapes outside of function +} + +fn takes_reference_type<'a>(_: &'a (dyn Error + 'static)) +where + &'a (dyn Error + 'static): 'static, +{ +} + +fn reference_type_bound(err: &dyn Error) { + takes_reference_type(err); //~ ERROR borrowed data escapes outside of function +} + +trait LifetimeArgument<'a> {} + +fn takes_static_argument(_: &(dyn LifetimeArgument<'static> + 'static)) {} + +fn mismatched_argument<'a>(arg: &'a dyn LifetimeArgument<'a>) { + takes_static_argument(arg); //~ ERROR borrowed data escapes outside of function +} + +trait AssociatedType { + type Item; +} + +fn takes_static_associated_type(_: &(dyn AssociatedType + 'static)) {} + +fn mismatched_associated_type<'a>(arg: &'a dyn AssociatedType) { + takes_static_associated_type(arg); //~ ERROR borrowed data escapes outside of function +} + +trait RepeatedLifetime<'a, 'b> {} + +fn takes_repeated_lifetime<'c>(_: &(dyn RepeatedLifetime<'c, 'c> + 'static)) {} + +fn mismatched_repeated_lifetime<'a, 'b>(arg: &'a dyn RepeatedLifetime<'a, 'b>) { + //~^ ERROR one or more lifetime errors were found in this item + takes_repeated_lifetime(arg); //~ ERROR borrowed data escapes outside of function + //~^ ERROR borrowed data escapes outside of function +} + +fn main() {} diff --git a/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.stderr b/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.stderr new file mode 100644 index 0000000000000..db3ff9c85dc99 --- /dev/null +++ b/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.stderr @@ -0,0 +1,362 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:6:5 + | +LL | fn explicit_bound<'a, T: Error + 'static>(err: &(dyn Error + 'a)) -> bool { + | -- --- `err` is only valid in the function body + | | + | lifetime `'a` defined here +LL | err.is::() + | ^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:10:5 + | +LL | fn explicit_placeholder(err: &(dyn Error + '_)) -> bool { + | --- + | | + | `err` is only valid in the function body + | has type `&(dyn std::error::Error + '1)` +LL | err.is::() + | ^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + +error: lifetime may not live long enough + --> $DIR/implicit-object-lifetime-no-suggestion.rs:14:5 + | +LL | fn return_static(err: &dyn Error) -> &'static dyn Error { + | - let's call the lifetime of this reference `'1` +LL | err + | ^^^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `err`, you can add an explicit `'_` lifetime bound + | +LL | fn return_static(err: &dyn Error) -> &'static dyn Error + '_ { + | ++++ + +error: lifetime may not live long enough + --> $DIR/implicit-object-lifetime-no-suggestion.rs:18:5 + | +LL | fn return_other<'a, 'b>(err: &'a dyn Error) -> &'b dyn Error { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | err + | ^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:24:5 + | +LL | fn static_reference_argument(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_static_reference(err); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:30:5 + | +LL | fn static_reference_and_object(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_static_object_and_reference(err); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:36:5 + | +LL | fn repeated_static_reference(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_object_and_static_reference(err, err); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:42:5 + | +LL | fn unrelated_parameter<'a>(err: &'a dyn Error, other: &'a u32) { + | -- --- `err` is only valid in the function body + | | + | lifetime `'a` defined here +LL | takes_object_and_reference(err, other); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'a` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:48:5 + | +LL | fn generic_bound(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_generic_static(err); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `takes_generic_static` introduced here + --> $DIR/implicit-object-lifetime-no-suggestion.rs:45:37 + | +LL | fn takes_generic_static(_: &T) {} + | ^^^^^^^ lifetime requirement introduced here + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:56:5 + | +LL | fn where_self(err: &dyn Trait) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | err.needs_static(); + | ^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `Trait::needs_static` introduced here + --> $DIR/implicit-object-lifetime-no-suggestion.rs:52:40 + | +LL | trait Trait { +LL | fn needs_static(&self) where Self: 'static {} + | ^^^^^^^ lifetime requirement introduced here + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:60:5 + | +LL | fn elided_output(err: &dyn Error) -> Option<&T> { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | err.downcast_ref::() + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::downcast_ref` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +note: this trait object's lifetime defaults to the reference's lifetime + --> $DIR/implicit-object-lifetime-no-suggestion.rs:59:44 + | +LL | fn elided_output(err: &dyn Error) -> Option<&T> { + | ^^^^^^^^^ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:64:5 + | +LL | fn named_input_elided_output<'a, T: Error + 'static>(err: &'a dyn Error) -> Option<&T> { + | -- --- `err` is only valid in the function body + | | + | lifetime `'a` defined here +LL | err.downcast_ref::() + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::downcast_ref` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +note: this trait object's lifetime defaults to the reference's lifetime + --> $DIR/implicit-object-lifetime-no-suggestion.rs:63:63 + | +LL | fn named_input_elided_output<'a, T: Error + 'static>(err: &'a dyn Error) -> Option<&T> { + | ^^^^^^^^^ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:68:5 + | +LL | fn placeholder_output(err: &dyn Error) -> Option<&'_ T> { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | err.downcast_ref::() + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::downcast_ref` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +note: this trait object's lifetime defaults to the reference's lifetime + --> $DIR/implicit-object-lifetime-no-suggestion.rs:67:49 + | +LL | fn placeholder_output(err: &dyn Error) -> Option<&'_ T> { + | ^^^^^^^^^ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:74:5 + | +LL | fn predicate_reference(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_predicate_reference(err); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `takes_predicate_reference` introduced here + --> $DIR/implicit-object-lifetime-no-suggestion.rs:71:34 + | +LL | fn takes_predicate_reference<'a: 'static>(_: &'a (dyn Error + 'static)) {} + | ^^^^^^^ lifetime requirement introduced here + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:80:5 + | +LL | fn transitive_reference(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_transitive_reference(err); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `takes_transitive_reference` introduced here + --> $DIR/implicit-object-lifetime-no-suggestion.rs:77:43 + | +LL | fn takes_transitive_reference<'a: 'b, 'b: 'static>(_: &'a (dyn Error + 'static)) {} + | ^^^^^^^ lifetime requirement introduced here + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:90:5 + | +LL | fn reference_type_bound(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_reference_type(err); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `takes_reference_type` introduced here + --> $DIR/implicit-object-lifetime-no-suggestion.rs:85:32 + | +LL | fn takes_reference_type<'a>(_: &'a (dyn Error + 'static)) +LL | where +LL | &'a (dyn Error + 'static): 'static, + | ^^^^^^^ lifetime requirement introduced here + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:98:5 + | +LL | fn mismatched_argument<'a>(arg: &'a dyn LifetimeArgument<'a>) { + | -- --- `arg` is only valid in the function body + | | + | lifetime `'a` defined here +LL | takes_static_argument(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'a` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:108:5 + | +LL | fn mismatched_associated_type<'a>(arg: &'a dyn AssociatedType) { + | -- --- `arg` is only valid in the function body + | | + | lifetime `'a` defined here +LL | takes_static_associated_type(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'a` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:117:5 + | +LL | fn mismatched_repeated_lifetime<'a, 'b>(arg: &'a dyn RepeatedLifetime<'a, 'b>) { + | -- --- `arg` is only valid in the function body + | | + | lifetime `'a` defined here +LL | +LL | takes_repeated_lifetime(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'a` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-no-suggestion.rs:117:5 + | +LL | fn mismatched_repeated_lifetime<'a, 'b>(arg: &'a dyn RepeatedLifetime<'a, 'b>) { + | -- --- `arg` is only valid in the function body + | | + | lifetime `'b` defined here +LL | +LL | takes_repeated_lifetime(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'b` must outlive `'static` + +error: one or more lifetime errors were found in this item + --> $DIR/implicit-object-lifetime-no-suggestion.rs:115:4 + | +LL | fn mismatched_repeated_lifetime<'a, 'b>(arg: &'a dyn RepeatedLifetime<'a, 'b>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: replace `'a` with `'static` + = help: replace `'b` with `'static` + +error: aborting due to 21 previous errors + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.fixed b/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.fixed new file mode 100644 index 0000000000000..ffd8e2ff1a8d2 --- /dev/null +++ b/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.fixed @@ -0,0 +1,122 @@ +//@ run-rustfix + +#![allow(dead_code)] + +use std::error::Error; + +fn is_caused_by(err: &(dyn Error + 'static)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function + || err.source().is_some_and(|cause| is_caused_by::(cause)) +} + +fn named<'a, T: Error + 'static>(err: &'a (dyn Error + 'static)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn early_bound<'a: 'a, T: Error + 'static>(err: &'a (dyn Error + 'static)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn mutable<'a, T: Error + 'static>(err: &'a mut (dyn Error + 'static)) -> Option<&'a mut T> { + err.downcast_mut::() //~ ERROR borrowed data escapes outside of function +} + +fn parenthesized(err: &(dyn Error + 'static)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn other_bounds(err: &(dyn Error + Send + Sync + 'static)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn takes_static_object(_: &(dyn Error + 'static)) {} + +fn function_call(err: &(dyn Error + 'static)) { + takes_static_object(err); //~ ERROR borrowed data escapes outside of function +} + +fn takes_two_static_objects(_: &(dyn Error + 'static), _: &(dyn Error + 'static)) {} + +fn repeated_argument(err: &(dyn Error + 'static)) { + takes_two_static_objects(err, err); //~ ERROR borrowed data escapes outside of function +} + +fn shared_region<'a, T: Error + 'static>(_: &'a u32, err: &'a (dyn Error + 'static)) -> bool { + err.is::() //~ ERROR lifetime may not live long enough +} + +fn same_trait<'a, T: Error + 'static>(_: &'a dyn Error, err: &'a (dyn Error + 'static)) -> bool { + err.is::() //~ ERROR lifetime may not live long enough +} + +fn takes_static_callable(_: &(dyn Fn() -> &'static (dyn std::fmt::Debug) + 'static)) {} + +fn callable(value: &(dyn Fn() -> &'static (dyn std::fmt::Debug) + 'static)) { + takes_static_callable(value); //~ ERROR borrowed data escapes outside of function +} + +trait DerivedLifetime<'a>: 'a {} + +fn takes_derived_lifetime<'a>(_: &(dyn DerivedLifetime<'a> + 'static)) {} + +fn derived_lifetime<'a, 'b>(arg: &'b (dyn DerivedLifetime<'a> + 'static)) { + takes_derived_lifetime(arg); //~ ERROR borrowed data escapes outside of function +} + +trait LifetimeArgument<'a> {} + +fn takes_lifetime_argument<'a>(_: &(dyn LifetimeArgument<'a> + 'static)) {} + +fn matching_lifetime_argument<'a>(arg: &'a (dyn LifetimeArgument<'a> + 'static)) { + takes_lifetime_argument(arg); //~ ERROR borrowed data escapes outside of function +} + +trait IndependentLifetimes<'a, 'b> {} + +fn takes_independent_lifetimes<'c, 'd>(_: &(dyn IndependentLifetimes<'c, 'd> + 'static)) {} + +fn matching_independent_lifetimes<'a, 'b>(arg: &'a (dyn IndependentLifetimes<'a, 'b> + 'static)) { + takes_independent_lifetimes(arg); //~ ERROR borrowed data escapes outside of function +} + +trait AssociatedType { + type Item; +} + +fn takes_associated_type<'a>(_: &(dyn AssociatedType + 'static)) {} + +fn matching_associated_type<'a>(arg: &'a (dyn AssociatedType + 'static)) { + takes_associated_type(arg); //~ ERROR borrowed data escapes outside of function +} + +fn drop_auto_trait(arg: &(dyn Error + Send + 'static)) { + takes_static_object(arg); //~ ERROR borrowed data escapes outside of function +} + +fn takes_higher_ranked(_: &(dyn Fn(&str) + 'static)) {} + +fn higher_ranked(arg: &(dyn Fn(&str) + 'static)) { + takes_higher_ranked(arg); //~ ERROR borrowed data escapes outside of function +} + +trait Typed {} + +fn takes_typed(_: &(dyn Typed + 'static)) {} + +fn typed(arg: &(dyn Typed + 'static)) { + takes_typed(arg); //~ ERROR borrowed data escapes outside of function +} + +fn main() { + let mut err = std::fmt::Error; + is_caused_by::(&err); + named::(&err); + early_bound::(&err); + mutable::(&mut err); + parenthesized::(&err); + other_bounds::(&err); + function_call(&err); + repeated_argument(&err); + shared_region::(&0, &err); + same_trait::(&err, &err); +} diff --git a/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.rs b/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.rs new file mode 100644 index 0000000000000..75dc3762111c2 --- /dev/null +++ b/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.rs @@ -0,0 +1,122 @@ +//@ run-rustfix + +#![allow(dead_code)] + +use std::error::Error; + +fn is_caused_by(err: &dyn Error) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function + || err.source().is_some_and(|cause| is_caused_by::(cause)) +} + +fn named<'a, T: Error + 'static>(err: &'a dyn Error) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn early_bound<'a: 'a, T: Error + 'static>(err: &'a dyn Error) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn mutable<'a, T: Error + 'static>(err: &'a mut dyn Error) -> Option<&'a mut T> { + err.downcast_mut::() //~ ERROR borrowed data escapes outside of function +} + +fn parenthesized(err: &(dyn Error)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn other_bounds(err: &(dyn Error + Send + Sync)) -> bool { + err.is::() //~ ERROR borrowed data escapes outside of function +} + +fn takes_static_object(_: &(dyn Error + 'static)) {} + +fn function_call(err: &dyn Error) { + takes_static_object(err); //~ ERROR borrowed data escapes outside of function +} + +fn takes_two_static_objects(_: &(dyn Error + 'static), _: &(dyn Error + 'static)) {} + +fn repeated_argument(err: &dyn Error) { + takes_two_static_objects(err, err); //~ ERROR borrowed data escapes outside of function +} + +fn shared_region<'a, T: Error + 'static>(_: &'a u32, err: &'a dyn Error) -> bool { + err.is::() //~ ERROR lifetime may not live long enough +} + +fn same_trait<'a, T: Error + 'static>(_: &'a dyn Error, err: &'a dyn Error) -> bool { + err.is::() //~ ERROR lifetime may not live long enough +} + +fn takes_static_callable(_: &(dyn Fn() -> &'static (dyn std::fmt::Debug) + 'static)) {} + +fn callable(value: &dyn Fn() -> &'static dyn std::fmt::Debug) { + takes_static_callable(value); //~ ERROR borrowed data escapes outside of function +} + +trait DerivedLifetime<'a>: 'a {} + +fn takes_derived_lifetime<'a>(_: &(dyn DerivedLifetime<'a> + 'static)) {} + +fn derived_lifetime<'a, 'b>(arg: &'b dyn DerivedLifetime<'a>) { + takes_derived_lifetime(arg); //~ ERROR borrowed data escapes outside of function +} + +trait LifetimeArgument<'a> {} + +fn takes_lifetime_argument<'a>(_: &(dyn LifetimeArgument<'a> + 'static)) {} + +fn matching_lifetime_argument<'a>(arg: &'a dyn LifetimeArgument<'a>) { + takes_lifetime_argument(arg); //~ ERROR borrowed data escapes outside of function +} + +trait IndependentLifetimes<'a, 'b> {} + +fn takes_independent_lifetimes<'c, 'd>(_: &(dyn IndependentLifetimes<'c, 'd> + 'static)) {} + +fn matching_independent_lifetimes<'a, 'b>(arg: &'a dyn IndependentLifetimes<'a, 'b>) { + takes_independent_lifetimes(arg); //~ ERROR borrowed data escapes outside of function +} + +trait AssociatedType { + type Item; +} + +fn takes_associated_type<'a>(_: &(dyn AssociatedType + 'static)) {} + +fn matching_associated_type<'a>(arg: &'a dyn AssociatedType) { + takes_associated_type(arg); //~ ERROR borrowed data escapes outside of function +} + +fn drop_auto_trait(arg: &(dyn Error + Send)) { + takes_static_object(arg); //~ ERROR borrowed data escapes outside of function +} + +fn takes_higher_ranked(_: &(dyn Fn(&str) + 'static)) {} + +fn higher_ranked(arg: &dyn Fn(&str)) { + takes_higher_ranked(arg); //~ ERROR borrowed data escapes outside of function +} + +trait Typed {} + +fn takes_typed(_: &(dyn Typed + 'static)) {} + +fn typed(arg: &dyn Typed) { + takes_typed(arg); //~ ERROR borrowed data escapes outside of function +} + +fn main() { + let mut err = std::fmt::Error; + is_caused_by::(&err); + named::(&err); + early_bound::(&err); + mutable::(&mut err); + parenthesized::(&err); + other_bounds::(&err); + function_call(&err); + repeated_argument(&err); + shared_region::(&0, &err); + same_trait::(&err, &err); +} diff --git a/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.stderr b/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.stderr new file mode 100644 index 0000000000000..a44f292732859 --- /dev/null +++ b/tests/ui/object-lifetime/implicit-object-lifetime-static-suggestion.stderr @@ -0,0 +1,392 @@ +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:8:5 + | +LL | fn is_caused_by(err: &dyn Error) -> bool { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | err.is::() + | ^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn is_caused_by(err: &dyn Error) -> bool { +LL + fn is_caused_by(err: &(dyn Error + 'static)) -> bool { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:13:5 + | +LL | fn named<'a, T: Error + 'static>(err: &'a dyn Error) -> bool { + | -- --- `err` is only valid in the function body + | | + | lifetime `'a` defined here +LL | err.is::() + | ^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn named<'a, T: Error + 'static>(err: &'a dyn Error) -> bool { +LL + fn named<'a, T: Error + 'static>(err: &'a (dyn Error + 'static)) -> bool { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:17:5 + | +LL | fn early_bound<'a: 'a, T: Error + 'static>(err: &'a dyn Error) -> bool { + | -- --- `err` is only valid in the function body + | | + | lifetime `'a` defined here +LL | err.is::() + | ^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn early_bound<'a: 'a, T: Error + 'static>(err: &'a dyn Error) -> bool { +LL + fn early_bound<'a: 'a, T: Error + 'static>(err: &'a (dyn Error + 'static)) -> bool { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:21:5 + | +LL | fn mutable<'a, T: Error + 'static>(err: &'a mut dyn Error) -> Option<&'a mut T> { + | -- --- `err` is only valid in the function body + | | + | lifetime `'a` defined here +LL | err.downcast_mut::() + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::downcast_mut` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + = note: requirement occurs because of a mutable reference to `(dyn std::error::Error + 'static)` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn mutable<'a, T: Error + 'static>(err: &'a mut dyn Error) -> Option<&'a mut T> { +LL + fn mutable<'a, T: Error + 'static>(err: &'a mut (dyn Error + 'static)) -> Option<&'a mut T> { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:25:5 + | +LL | fn parenthesized(err: &(dyn Error)) -> bool { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | err.is::() + | ^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL | fn parenthesized(err: &(dyn Error + 'static)) -> bool { + | +++++++++ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:29:5 + | +LL | fn other_bounds(err: &(dyn Error + Send + Sync)) -> bool { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | err.is::() + | ^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + Send + Sync + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL | fn other_bounds(err: &(dyn Error + Send + Sync + 'static)) -> bool { + | +++++++++ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:35:5 + | +LL | fn function_call(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_static_object(err); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn function_call(err: &dyn Error) { +LL + fn function_call(err: &(dyn Error + 'static)) { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:41:5 + | +LL | fn repeated_argument(err: &dyn Error) { + | --- - let's call the lifetime of this reference `'1` + | | + | `err` is only valid in the function body +LL | takes_two_static_objects(err, err); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `err` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn repeated_argument(err: &dyn Error) { +LL + fn repeated_argument(err: &(dyn Error + 'static)) { + | + +error: lifetime may not live long enough + --> $DIR/implicit-object-lifetime-static-suggestion.rs:45:5 + | +LL | fn shared_region<'a, T: Error + 'static>(_: &'a u32, err: &'a dyn Error) -> bool { + | -- lifetime `'a` defined here +LL | err.is::() + | ^^^^^^^^^^^^^ argument requires that `'a` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn shared_region<'a, T: Error + 'static>(_: &'a u32, err: &'a dyn Error) -> bool { +LL + fn shared_region<'a, T: Error + 'static>(_: &'a u32, err: &'a (dyn Error + 'static)) -> bool { + | + +error: lifetime may not live long enough + --> $DIR/implicit-object-lifetime-static-suggestion.rs:49:5 + | +LL | fn same_trait<'a, T: Error + 'static>(_: &'a dyn Error, err: &'a dyn Error) -> bool { + | -- lifetime `'a` defined here +LL | err.is::() + | ^^^^^^^^^^^^^ argument requires that `'a` must outlive `'static` + | +note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>::is` introduced here + --> $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here + ::: $SRC_DIR/core/src/error.rs:LL:COL + | + = note: lifetime requirement introduced here +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn same_trait<'a, T: Error + 'static>(_: &'a dyn Error, err: &'a dyn Error) -> bool { +LL + fn same_trait<'a, T: Error + 'static>(_: &'a dyn Error, err: &'a (dyn Error + 'static)) -> bool { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:55:5 + | +LL | fn callable(value: &dyn Fn() -> &'static dyn std::fmt::Debug) { + | ----- - let's call the lifetime of this reference `'1` + | | + | `value` declared here, outside of the function body + | `value` is only valid in the function body +LL | takes_static_callable(value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `value` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn callable(value: &dyn Fn() -> &'static dyn std::fmt::Debug) { +LL + fn callable(value: &(dyn Fn() -> &'static (dyn std::fmt::Debug) + 'static)) { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:63:5 + | +LL | fn derived_lifetime<'a, 'b>(arg: &'b dyn DerivedLifetime<'a>) { + | -- --- `arg` is only valid in the function body + | | + | lifetime `'b` defined here +LL | takes_derived_lifetime(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'b` must outlive `'static` + | +help: this trait object's lifetime is inferred from its trait's lifetime bounds; consider adding an explicit `'static` bound to the trait object + | +LL - fn derived_lifetime<'a, 'b>(arg: &'b dyn DerivedLifetime<'a>) { +LL + fn derived_lifetime<'a, 'b>(arg: &'b (dyn DerivedLifetime<'a> + 'static)) { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:71:5 + | +LL | fn matching_lifetime_argument<'a>(arg: &'a dyn LifetimeArgument<'a>) { + | -- --- `arg` is only valid in the function body + | | + | lifetime `'a` defined here +LL | takes_lifetime_argument(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn matching_lifetime_argument<'a>(arg: &'a dyn LifetimeArgument<'a>) { +LL + fn matching_lifetime_argument<'a>(arg: &'a (dyn LifetimeArgument<'a> + 'static)) { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:79:5 + | +LL | fn matching_independent_lifetimes<'a, 'b>(arg: &'a dyn IndependentLifetimes<'a, 'b>) { + | -- --- `arg` is only valid in the function body + | | + | lifetime `'a` defined here +LL | takes_independent_lifetimes(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn matching_independent_lifetimes<'a, 'b>(arg: &'a dyn IndependentLifetimes<'a, 'b>) { +LL + fn matching_independent_lifetimes<'a, 'b>(arg: &'a (dyn IndependentLifetimes<'a, 'b> + 'static)) { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:89:5 + | +LL | fn matching_associated_type<'a>(arg: &'a dyn AssociatedType) { + | -- --- `arg` is only valid in the function body + | | + | lifetime `'a` defined here +LL | takes_associated_type(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'a` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn matching_associated_type<'a>(arg: &'a dyn AssociatedType) { +LL + fn matching_associated_type<'a>(arg: &'a (dyn AssociatedType + 'static)) { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:93:5 + | +LL | fn drop_auto_trait(arg: &(dyn Error + Send)) { + | --- - let's call the lifetime of this reference `'1` + | | + | `arg` is only valid in the function body +LL | takes_static_object(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL | fn drop_auto_trait(arg: &(dyn Error + Send + 'static)) { + | +++++++++ + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:99:5 + | +LL | fn higher_ranked(arg: &dyn Fn(&str)) { + | --- - let's call the lifetime of this reference `'1` + | | + | `arg` is only valid in the function body +LL | takes_higher_ranked(arg); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn higher_ranked(arg: &dyn Fn(&str)) { +LL + fn higher_ranked(arg: &(dyn Fn(&str) + 'static)) { + | + +error[E0521]: borrowed data escapes outside of function + --> $DIR/implicit-object-lifetime-static-suggestion.rs:107:5 + | +LL | fn typed(arg: &dyn Typed) { + | --- - let's call the lifetime of this reference `'1` + | | + | `arg` is only valid in the function body +LL | takes_typed(arg); + | ^^^^^^^^^^^^^^^^ + | | + | `arg` escapes the function body here + | argument requires that `'1` must outlive `'static` + | +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn typed(arg: &dyn Typed) { +LL + fn typed(arg: &(dyn Typed + 'static)) { + | + +error: aborting due to 18 previous errors + +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr index 9793b7fb8dc7a..c17cb32aaf11b 100644 --- a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr +++ b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr @@ -61,6 +61,11 @@ help: consider relaxing the implicit `'static` requirement | LL | impl dyn ObjectTrait + '_ { | ++++ +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn use_it<'a>(val: &'a dyn ObjectTrait) -> impl OtherTrait<'a> + 'a { +LL + fn use_it<'a>(val: &'a (dyn ObjectTrait + 'static)) -> impl OtherTrait<'a> + 'a { + | error[E0521]: borrowed data escapes outside of function --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:92:9 From b34b23d7e911c52dcf602dad66cecd720bd8f312 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Mon, 26 Oct 2026 09:09:41 -0300 Subject: [PATCH 2/4] Use character inserts in lifetime suggestions --- compiler/rustc_borrowck/src/diagnostics/region_errors.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 7ddbe769878d1..16330b42201e9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1448,8 +1448,8 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { // Otherwise `+ 'static` would bind to the callable's output trait object. let start = (output.span.lo() - object_ty.span.lo()).to_usize(); let end = (output.span.hi() - object_ty.span.lo()).to_usize(); - snippet.insert_str(end, ")"); - snippet.insert_str(start, "("); + snippet.insert(end, ')'); + snippet.insert(start, '('); } let prefix = source_map .span_to_snippet(param_ty.span.with_hi(object_ty.span.lo())) From f67767e262bf880f3185a566012ab3b052bd4c5b Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Mon, 26 Oct 2026 10:46:09 -0300 Subject: [PATCH 3/4] Skip closure bodies in object lifetime suggestions --- compiler/rustc_borrowck/src/diagnostics/region_errors.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 16330b42201e9..01341c4c0f7f5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1306,7 +1306,8 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { _ => return, }; let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return }; - let Some(decl) = tcx.hir_fn_decl_by_hir_id(self.mir_hir_id()) else { return }; + let Some(hir_sig) = tcx.hir_fn_sig_by_hir_id(self.mir_hir_id()) else { return }; + let decl = hir_sig.decl; let callee_sig = tcx.fn_sig(def_id).instantiate(tcx, args).skip_norm_wip().skip_binder(); // Typeck results have erased regions. The liberated signature retains the object lifetime. let caller_sig = tcx.liberate_late_bound_regions( From 9b983ccdef9bd31880bb2233e97355f821b92542 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Mon, 26 Oct 2026 10:46:10 -0300 Subject: [PATCH 4/4] Refresh the static object lifetime diagnostic output --- .../implicit-object-lifetime-no-suggestion.stderr | 2 +- tests/ui/regions/regions-static-bound-on-type-param.stderr | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.stderr b/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.stderr index db3ff9c85dc99..fdd9c09959f58 100644 --- a/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.stderr +++ b/tests/ui/object-lifetime/implicit-object-lifetime-no-suggestion.stderr @@ -23,7 +23,7 @@ error[E0521]: borrowed data escapes outside of function --> $DIR/implicit-object-lifetime-no-suggestion.rs:10:5 | LL | fn explicit_placeholder(err: &(dyn Error + '_)) -> bool { - | --- + | --- -- lifetime `'1` introduced here | | | `err` is only valid in the function body | has type `&(dyn std::error::Error + '1)` diff --git a/tests/ui/regions/regions-static-bound-on-type-param.stderr b/tests/ui/regions/regions-static-bound-on-type-param.stderr index 64496cb703bee..f39b4f0c50126 100644 --- a/tests/ui/regions/regions-static-bound-on-type-param.stderr +++ b/tests/ui/regions/regions-static-bound-on-type-param.stderr @@ -18,6 +18,11 @@ note: `'static` lifetime requirement from `<(dyn std::error::Error + 'static)>:: ::: $SRC_DIR/core/src/error.rs:LL:COL | = note: lifetime requirement introduced here +help: this trait object's lifetime defaults to the reference's lifetime; consider adding an explicit `'static` bound to the trait object + | +LL - fn err_is(err: &dyn Error) -> bool { +LL + fn err_is(err: &(dyn Error + 'static)) -> bool { + | error: aborting due to 1 previous error