1 //! Error Reporting for static impl Traits.
3 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
4 use crate::infer::lexical_region_resolve::RegionResolutionError;
5 use crate::infer::{SubregionOrigin, TypeTrace};
6 use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
7 use rustc_data_structures::fx::FxIndexSet;
8 use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
9 use rustc_hir::def_id::DefId;
10 use rustc_hir::intravisit::{walk_ty, Visitor};
11 use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
12 use rustc_middle::ty::{
13 self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
15 use rustc_span::symbol::Ident;
18 use std::ops::ControlFlow;
20 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
21 /// Print the error message for lifetime errors when the return type is a static `impl Trait`,
22 /// `dyn Trait` or if a method call on a trait object introduces a static requirement.
23 pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
24 debug!("try_report_static_impl_trait(error={:?})", self.error);
26 let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? {
27 RegionResolutionError::SubSupConflict(
35 ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans),
36 RegionResolutionError::ConcreteFailure(
37 SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
40 ) if sub_r.is_static() => {
41 // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
42 if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
43 // This may have a closure and it would cause ICE
44 // through `find_param_with_region` (#78262).
45 let anon_reg_sup = tcx.is_suitable_region(*sup_r)?;
46 let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
47 if fn_returns.is_empty() {
51 let param = self.find_param_with_region(*sup_r, *sub_r)?;
52 let lifetime = if sup_r.has_name() {
53 format!("lifetime `{}`", sup_r)
55 "an anonymous lifetime `'_`".to_string()
57 let mut err = struct_span_err!(
61 "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
67 .map(|s| format!("`{}`", s))
68 .unwrap_or_else(|| "`fn` parameter".to_string()),
72 err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
76 "...is used and required to live as long as `'static` here \
77 because of an implicit lifetime bound on the {}",
78 match ctxt.assoc_item.container {
79 AssocItemContainer::TraitContainer => {
80 let id = ctxt.assoc_item.container_id(tcx);
81 format!("`impl` of `{}`", tcx.def_path_str(id))
83 AssocItemContainer::ImplContainer => "inherent `impl`".to_string(),
87 if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
88 let reported = err.emit();
89 return Some(reported);
99 "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
100 var_origin, sub_origin, sub_r, sup_origin, sup_r
102 let anon_reg_sup = tcx.is_suitable_region(*sup_r)?;
103 debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
104 let sp = var_origin.span();
105 let return_sp = sub_origin.span();
106 let param = self.find_param_with_region(*sup_r, *sub_r)?;
107 let (lifetime_name, lifetime) = if sup_r.has_name() {
108 (sup_r.to_string(), format!("lifetime `{}`", sup_r))
110 ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
112 let param_name = param
116 .map(|s| format!("`{}`", s))
117 .unwrap_or_else(|| "`fn` parameter".to_string());
118 let mut err = struct_span_err!(
122 "{} has {} but it needs to satisfy a `'static` lifetime requirement",
127 let (mention_influencer, influencer_point) =
128 if sup_origin.span().overlaps(param.param_ty_span) {
129 // Account for `async fn` like in `async-await/issues/issue-62097.rs`.
130 // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same
131 // place (but with different `ctxt`, hence `overlaps` instead of `==` above).
133 // This avoids the following:
135 // LL | pub async fn run_dummy_fn(&self) {
138 // | this data with an anonymous lifetime `'_`...
139 // | ...is captured here...
140 (false, sup_origin.span())
142 (!sup_origin.span().overlaps(return_sp), param.param_ty_span)
144 err.span_label(influencer_point, &format!("this data with {}...", lifetime));
146 debug!("try_report_static_impl_trait: param_info={:?}", param);
148 let mut spans = spans.clone();
150 if mention_influencer {
151 spans.push(sup_origin.span());
153 // We dedup the spans *ignoring* expansion context.
155 spans.dedup_by_key(|span| (span.lo(), span.hi()));
157 // We try to make the output have fewer overlapping spans if possible.
158 let require_msg = if spans.is_empty() {
159 "...is used and required to live as long as `'static` here"
161 "...and is required to live as long as `'static` here"
164 if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
167 err.span_label(*span, "...is used here...");
170 if spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp) {
171 // If any of the "captured here" labels appears on the same line or after
172 // `require_span`, we put it on a note to ensure the text flows by appearing
173 // always at the end.
174 err.span_note(require_span, require_msg);
176 // We don't need a note, it's already at the end, it can be shown as a `span_label`.
177 err.span_label(require_span, require_msg);
180 if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
181 err.span_note(*bound, "`'static` lifetime requirement introduced by this bound");
183 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin {
184 if let ObligationCauseCode::ReturnValue(hir_id)
185 | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code()
187 let parent_id = tcx.hir().get_parent_item(*hir_id);
188 if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) {
189 let mut span: MultiSpan = fn_decl.output.span().into();
190 let mut add_label = true;
191 if let hir::FnRetTy::Return(ty) = fn_decl.output {
192 let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
195 span = v.0.clone().into();
197 span.push_span_label(sp, "`'static` requirement introduced here");
203 span.push_span_label(
204 fn_decl.output.span(),
205 "requirement introduced by this return type",
208 span.push_span_label(cause.span, "because of this returned expression");
211 "`'static` lifetime requirement introduced by the return type",
217 let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
219 let mut override_error_code = None;
220 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin
221 && let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code()
222 // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
223 // `'static` lifetime when called as a method on a binding: `bar.qux()`.
224 && self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt)
226 override_error_code = Some(ctxt.assoc_item.name);
229 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin
230 && let code = match cause.code() {
231 ObligationCauseCode::MatchImpl(parent, ..) => parent.code(),
234 && let (&ObligationCauseCode::ItemObligation(item_def_id) | &ObligationCauseCode::ExprItemObligation(item_def_id, ..), None) = (code, override_error_code)
236 // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
237 // lifetime as above, but called using a fully-qualified path to the method:
239 let mut v = TraitObjectVisitor(FxIndexSet::default());
240 v.visit_ty(param.param_ty);
241 if let Some((ident, self_ty)) =
242 NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0)
243 && self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty)
245 override_error_code = Some(ident.name);
248 if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
249 // Provide a more targeted error code and description.
250 err.code(rustc_errors::error_code!(E0772));
251 err.set_primary_message(&format!(
252 "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
254 param_name, lifetime, ident,
258 let arg = match param.param.pat.simple_ident() {
259 Some(simple_ident) => format!("argument `{}`", simple_ident),
260 None => "the argument".to_string(),
262 let captures = format!("captures data from {}", arg);
263 suggest_new_region_bound(
270 Some((param.param_ty_span, param.param_ty.to_string())),
273 let reported = err.emit();
278 pub fn suggest_new_region_bound(
280 err: &mut Diagnostic,
281 fn_returns: Vec<&rustc_hir::Ty<'_>>,
282 lifetime_name: String,
285 param: Option<(Span, String)>,
287 debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
288 // FIXME: account for the need of parens in `&(dyn Trait + '_)`
289 let consider = "consider changing";
290 let declare = "to declare that";
291 let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
292 let explicit_static =
293 arg.map(|arg| format!("explicit `'static` bound to the lifetime of {}", arg));
294 let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
295 let plus_lt = format!(" + {}", lifetime_name);
296 for fn_return in fn_returns {
297 if fn_return.span.desugaring_kind().is_some() {
298 // Skip `async` desugaring `impl Future`.
301 match fn_return.kind {
302 TyKind::OpaqueDef(item_id, _, _) => {
303 let item = tcx.hir().item(item_id);
304 let ItemKind::OpaqueTy(opaque) = &item.kind else {
308 // Get the identity type for this RPIT
309 let did = item_id.owner_id.to_def_id();
310 let ty = tcx.mk_opaque(did, ty::InternalSubsts::identity_for_item(tcx, did));
312 if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
313 GenericBound::Outlives(Lifetime {
314 res: LifetimeName::Static, ident, ..
315 }) => Some(ident.span),
318 if let Some(explicit_static) = &explicit_static {
319 err.span_suggestion_verbose(
321 &format!("{consider} `{ty}`'s {explicit_static}"),
323 Applicability::MaybeIncorrect,
326 if let Some((param_span, ref param_ty)) = param {
327 err.span_suggestion_verbose(
331 Applicability::MaybeIncorrect,
334 } else if opaque.bounds.iter().any(|arg| match arg {
335 GenericBound::Outlives(Lifetime { ident, .. })
336 if ident.name.to_string() == lifetime_name =>
343 err.span_suggestion_verbose(
344 fn_return.span.shrink_to_hi(),
345 &format!("{declare} `{ty}` {captures}, {explicit}",),
347 Applicability::MaybeIncorrect,
351 TyKind::TraitObject(_, lt, _) => {
352 if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res {
353 err.span_suggestion_verbose(
354 fn_return.span.shrink_to_hi(),
356 "{declare} the trait object {captures}, {explicit}",
362 Applicability::MaybeIncorrect,
364 } else if lt.ident.name.to_string() != lifetime_name {
365 // With this check we avoid suggesting redundant bounds. This
366 // would happen if there are nested impl/dyn traits and only
367 // one of them has the bound we'd suggest already there, like
368 // in `impl Foo<X = dyn Bar> + '_`.
369 if let Some(explicit_static) = &explicit_static {
370 err.span_suggestion_verbose(
372 &format!("{} the trait object's {}", consider, explicit_static),
374 Applicability::MaybeIncorrect,
377 if let Some((param_span, param_ty)) = param.clone() {
378 err.span_suggestion_verbose(
382 Applicability::MaybeIncorrect,
392 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
393 pub fn get_impl_ident_and_self_ty_from_trait(
396 trait_objects: &FxIndexSet<DefId>,
397 ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
398 match tcx.hir().get_if_local(def_id)? {
399 Node::ImplItem(impl_item) => {
400 let impl_did = tcx.hir().get_parent_item(impl_item.hir_id());
401 if let hir::OwnerNode::Item(Item {
402 kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
404 }) = tcx.hir().owner(impl_did)
406 Some((impl_item.ident, self_ty))
411 Node::TraitItem(trait_item) => {
412 let trait_id = tcx.hir().get_parent_item(trait_item.hir_id());
413 debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait);
414 // The method being called is defined in the `trait`, but the `'static`
415 // obligation comes from the `impl`. Find that `impl` so that we can point
416 // at it in the suggestion.
417 let trait_did = trait_id.to_def_id();
418 tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| {
419 if let Node::Item(Item {
420 kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
422 }) = tcx.hir().find_by_def_id(impl_did)?
423 && trait_objects.iter().all(|did| {
424 // FIXME: we should check `self_ty` against the receiver
425 // type in the `UnifyReceiver` context, but for now, use
426 // this imperfect proxy. This will fail if there are
427 // multiple `impl`s for the same trait like
428 // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
429 // In that case, only the first one will get suggestions.
430 let mut traits = vec![];
431 let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
432 hir_v.visit_ty(self_ty);
436 Some((trait_item.ident, *self_ty))
446 /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
447 /// `'static` obligation. Suggest relaxing that implicit bound.
448 fn find_impl_on_dyn_trait(
450 err: &mut Diagnostic,
452 ctxt: &UnifyReceiverContext<'tcx>,
454 let tcx = self.tcx();
456 // Find the method being called.
457 let Ok(Some(instance)) = ty::Instance::resolve(
460 ctxt.assoc_item.def_id,
461 self.cx.resolve_vars_if_possible(ctxt.substs),
466 let mut v = TraitObjectVisitor(FxIndexSet::default());
469 // Get the `Ident` of the method being called and the corresponding `impl` (to point at
470 // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
471 let Some((ident, self_ty)) = NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0) else {
475 // Find the trait object types in the argument, so we point at *only* the trait object.
476 self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty)
479 fn suggest_constrain_dyn_trait_in_impl(
481 err: &mut Diagnostic,
482 found_dids: &FxIndexSet<DefId>,
484 self_ty: &hir::Ty<'_>,
486 let mut suggested = false;
487 for found_did in found_dids {
488 let mut traits = vec![];
489 let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
490 hir_v.visit_ty(&self_ty);
491 for span in &traits {
492 let mut multi_span: MultiSpan = vec![*span].into();
494 .push_span_label(*span, "this has an implicit `'static` lifetime requirement");
495 multi_span.push_span_label(
497 "calling this method introduces the `impl`'s 'static` requirement",
499 err.span_note(multi_span, "the used `impl` has a `'static` requirement");
500 err.span_suggestion_verbose(
502 "consider relaxing the implicit `'static` requirement",
504 Applicability::MaybeIncorrect,
513 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
514 pub struct TraitObjectVisitor(pub FxIndexSet<DefId>);
516 impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor {
517 fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
519 ty::Dynamic(preds, re, _) if re.is_static() => {
520 if let Some(def_id) = preds.principal_def_id() {
521 self.0.insert(def_id);
523 ControlFlow::CONTINUE
525 _ => t.super_visit_with(self),
530 /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
531 pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId);
533 impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
534 fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
535 if let TyKind::TraitObject(
537 Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. },
541 for ptr in poly_trait_refs {
542 if Some(self.1) == ptr.trait_ref.trait_def_id() {
543 self.0.push(ptr.span);