1 //! Error Reporting for static impl Traits.
4 ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted,
5 ReqIntroducedLocations,
7 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
8 use crate::infer::lexical_region_resolve::RegionResolutionError;
9 use crate::infer::{SubregionOrigin, TypeTrace};
10 use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
11 use rustc_data_structures::fx::FxIndexSet;
12 use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
13 use rustc_hir::def_id::DefId;
14 use rustc_hir::intravisit::{walk_ty, Visitor};
16 self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node,
19 use rustc_middle::ty::{
20 self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
22 use rustc_span::symbol::Ident;
25 use rustc_span::def_id::LocalDefId;
26 use std::ops::ControlFlow;
28 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
29 /// Print the error message for lifetime errors when the return type is a static `impl Trait`,
30 /// `dyn Trait` or if a method call on a trait object introduces a static requirement.
31 pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
32 debug!("try_report_static_impl_trait(error={:?})", self.error);
34 let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? {
35 RegionResolutionError::SubSupConflict(
43 ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans),
44 RegionResolutionError::ConcreteFailure(
45 SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
48 ) if sub_r.is_static() => {
49 // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
50 if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
51 // This may have a closure and it would cause ICE
52 // through `find_param_with_region` (#78262).
53 let anon_reg_sup = tcx.is_suitable_region(*sup_r)?;
54 let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
55 if fn_returns.is_empty() {
59 let param = self.find_param_with_region(*sup_r, *sub_r)?;
60 let simple_ident = param.param.pat.simple_ident();
62 let (has_impl_path, impl_path) = match ctxt.assoc_item.container {
63 AssocItemContainer::TraitContainer => {
64 let id = ctxt.assoc_item.container_id(tcx);
65 (true, tcx.def_path_str(id))
67 AssocItemContainer::ImplContainer => (false, String::new()),
70 let mut err = self.tcx().sess.create_err(ButCallingIntroduces {
71 param_ty_span: param.param_ty_span,
72 cause_span: cause.span,
73 has_param_name: simple_ident.is_some(),
74 param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
75 has_lifetime: sup_r.has_name(),
76 lifetime: sup_r.to_string(),
77 assoc_item: ctxt.assoc_item.name,
81 if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
82 let reported = err.emit();
83 return Some(reported);
93 "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
94 var_origin, sub_origin, sub_r, sup_origin, sup_r
96 let anon_reg_sup = tcx.is_suitable_region(*sup_r)?;
97 debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
98 let sp = var_origin.span();
99 let return_sp = sub_origin.span();
100 let param = self.find_param_with_region(*sup_r, *sub_r)?;
101 let simple_ident = param.param.pat.simple_ident();
102 let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
104 let (mention_influencer, influencer_point) =
105 if sup_origin.span().overlaps(param.param_ty_span) {
106 // Account for `async fn` like in `async-await/issues/issue-62097.rs`.
107 // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same
108 // place (but with different `ctxt`, hence `overlaps` instead of `==` above).
110 // This avoids the following:
112 // LL | pub async fn run_dummy_fn(&self) {
115 // | this data with an anonymous lifetime `'_`...
116 // | ...is captured here...
117 (false, sup_origin.span())
119 (!sup_origin.span().overlaps(return_sp), param.param_ty_span)
122 debug!("try_report_static_impl_trait: param_info={:?}", param);
124 let mut spans = spans.clone();
126 if mention_influencer {
127 spans.push(sup_origin.span());
129 // We dedup the spans *ignoring* expansion context.
131 spans.dedup_by_key(|span| (span.lo(), span.hi()));
133 // We try to make the output have fewer overlapping spans if possible.
135 if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
137 let spans_empty = spans.is_empty();
138 let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp);
139 let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
145 let mut subdiag = None;
147 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin {
148 if let ObligationCauseCode::ReturnValue(hir_id)
149 | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code()
151 let parent_id = tcx.hir().get_parent_item(*hir_id);
152 if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) {
153 let mut span: MultiSpan = fn_decl.output.span().into();
154 let mut spans = Vec::new();
155 let mut add_label = true;
156 if let hir::FnRetTy::Return(ty) = fn_decl.output {
157 let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
160 span = v.0.clone().into();
165 let fn_decl_span = fn_decl.output.span();
167 subdiag = Some(ReqIntroducedLocations {
171 cause_span: cause.span,
178 let diag = ButNeedsToSatisfy {
181 spans: spans.clone(),
182 // If any of the "captured here" labels appears on the same line or after
183 // `require_span`, we put it on a note to ensure the text flows by appearing
184 // always at the end.
185 require_span_as_note: require_as_note.then_some(require_span),
186 // We don't need a note, it's already at the end, it can be shown as a `span_label`.
187 require_span_as_label: (!require_as_note).then_some(require_span),
188 req_introduces_loc: subdiag,
190 has_lifetime: sup_r.has_name(),
191 lifetime: lifetime_name.clone(),
192 has_param_name: simple_ident.is_some(),
193 param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
198 let mut err = self.tcx().sess.create_err(diag);
200 let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
202 let mut override_error_code = None;
203 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin
204 && let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code()
205 // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
206 // `'static` lifetime when called as a method on a binding: `bar.qux()`.
207 && self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt)
209 override_error_code = Some(ctxt.assoc_item.name);
212 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin
213 && let code = match cause.code() {
214 ObligationCauseCode::MatchImpl(parent, ..) => parent.code(),
217 && let (&ObligationCauseCode::ItemObligation(item_def_id) | &ObligationCauseCode::ExprItemObligation(item_def_id, ..), None) = (code, override_error_code)
219 // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
220 // lifetime as above, but called using a fully-qualified path to the method:
222 let mut v = TraitObjectVisitor(FxIndexSet::default());
223 v.visit_ty(param.param_ty);
224 if let Some((ident, self_ty)) =
225 NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0)
226 && self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty)
228 override_error_code = Some(ident.name);
231 if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
232 // Provide a more targeted error code and description.
233 let retarget_subdiag = MoreTargeted { ident };
234 retarget_subdiag.add_to_diagnostic(&mut err);
237 let arg = match param.param.pat.simple_ident() {
238 Some(simple_ident) => format!("argument `{}`", simple_ident),
239 None => "the argument".to_string(),
241 let captures = format!("captures data from {}", arg);
242 suggest_new_region_bound(
249 Some((param.param_ty_span, param.param_ty.to_string())),
250 Some(anon_reg_sup.def_id),
253 let reported = err.emit();
258 pub fn suggest_new_region_bound(
260 err: &mut Diagnostic,
261 fn_returns: Vec<&rustc_hir::Ty<'_>>,
262 lifetime_name: String,
265 param: Option<(Span, String)>,
266 scope_def_id: Option<LocalDefId>,
268 debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
269 // FIXME: account for the need of parens in `&(dyn Trait + '_)`
270 let consider = "consider changing";
271 let declare = "to declare that";
272 let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
273 let explicit_static =
274 arg.map(|arg| format!("explicit `'static` bound to the lifetime of {}", arg));
275 let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
276 let plus_lt = format!(" + {}", lifetime_name);
277 for fn_return in fn_returns {
278 if fn_return.span.desugaring_kind().is_some() {
279 // Skip `async` desugaring `impl Future`.
282 match fn_return.kind {
283 TyKind::OpaqueDef(item_id, _, _) => {
284 let item = tcx.hir().item(item_id);
285 let ItemKind::OpaqueTy(opaque) = &item.kind else {
289 // Get the identity type for this RPIT
290 let did = item_id.owner_id.to_def_id();
291 let ty = tcx.mk_opaque(did, ty::InternalSubsts::identity_for_item(tcx, did));
293 if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
294 GenericBound::Outlives(Lifetime {
295 res: LifetimeName::Static, ident, ..
296 }) => Some(ident.span),
299 if let Some(explicit_static) = &explicit_static {
300 err.span_suggestion_verbose(
302 &format!("{consider} `{ty}`'s {explicit_static}"),
304 Applicability::MaybeIncorrect,
307 if let Some((param_span, ref param_ty)) = param {
308 err.span_suggestion_verbose(
312 Applicability::MaybeIncorrect,
315 } else if opaque.bounds.iter().any(|arg| match arg {
316 GenericBound::Outlives(Lifetime { ident, .. })
317 if ident.name.to_string() == lifetime_name =>
324 // get a lifetime name of existing named lifetimes if any
325 let existing_lt_name = if let Some(id) = scope_def_id
326 && let Some(generics) = tcx.hir().get_generics(id)
327 && let named_lifetimes = generics
330 .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }))
331 .map(|p| { if let hir::ParamName::Plain(name) = p.name {Some(name.to_string())} else {None}})
332 .filter(|n| ! matches!(n, None))
334 && named_lifetimes.len() > 0 {
335 named_lifetimes[0].clone()
339 let name = if let Some(name) = &existing_lt_name {
344 // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
345 // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
346 if let Some(id) = scope_def_id
347 && let Some(generics) = tcx.hir().get_generics(id)
348 && let mut spans_suggs = generics
351 .filter(|p| p.is_elided_lifetime())
353 if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_)
354 (p.span.shrink_to_hi(),format!("{name} "))
355 } else { // Underscore (elided with '_)
356 (p.span, format!("{name}"))
360 && spans_suggs.len() > 1
363 if existing_lt_name == None {
364 spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
365 format!("you can introduce a named lifetime parameter `{name}`")
367 // make use the existing named lifetime
368 format!("you can use the named lifetime parameter `{name}`")
371 .push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
372 err.multipart_suggestion_verbose(
374 "{declare} `{ty}` {captures}, {use_lt}",
377 Applicability::MaybeIncorrect,
380 err.span_suggestion_verbose(
381 fn_return.span.shrink_to_hi(),
382 &format!("{declare} `{ty}` {captures}, {explicit}",),
384 Applicability::MaybeIncorrect,
389 TyKind::TraitObject(_, lt, _) => {
390 if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res {
391 err.span_suggestion_verbose(
392 fn_return.span.shrink_to_hi(),
394 "{declare} the trait object {captures}, {explicit}",
400 Applicability::MaybeIncorrect,
402 } else if lt.ident.name.to_string() != lifetime_name {
403 // With this check we avoid suggesting redundant bounds. This
404 // would happen if there are nested impl/dyn traits and only
405 // one of them has the bound we'd suggest already there, like
406 // in `impl Foo<X = dyn Bar> + '_`.
407 if let Some(explicit_static) = &explicit_static {
408 err.span_suggestion_verbose(
410 &format!("{} the trait object's {}", consider, explicit_static),
412 Applicability::MaybeIncorrect,
415 if let Some((param_span, param_ty)) = param.clone() {
416 err.span_suggestion_verbose(
420 Applicability::MaybeIncorrect,
430 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
431 pub fn get_impl_ident_and_self_ty_from_trait(
434 trait_objects: &FxIndexSet<DefId>,
435 ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
436 match tcx.hir().get_if_local(def_id)? {
437 Node::ImplItem(impl_item) => {
438 let impl_did = tcx.hir().get_parent_item(impl_item.hir_id());
439 if let hir::OwnerNode::Item(Item {
440 kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
442 }) = tcx.hir().owner(impl_did)
444 Some((impl_item.ident, self_ty))
449 Node::TraitItem(trait_item) => {
450 let trait_id = tcx.hir().get_parent_item(trait_item.hir_id());
451 debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait);
452 // The method being called is defined in the `trait`, but the `'static`
453 // obligation comes from the `impl`. Find that `impl` so that we can point
454 // at it in the suggestion.
455 let trait_did = trait_id.to_def_id();
456 tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| {
457 if let Node::Item(Item {
458 kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
460 }) = tcx.hir().find_by_def_id(impl_did)?
461 && trait_objects.iter().all(|did| {
462 // FIXME: we should check `self_ty` against the receiver
463 // type in the `UnifyReceiver` context, but for now, use
464 // this imperfect proxy. This will fail if there are
465 // multiple `impl`s for the same trait like
466 // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
467 // In that case, only the first one will get suggestions.
468 let mut traits = vec![];
469 let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
470 hir_v.visit_ty(self_ty);
474 Some((trait_item.ident, *self_ty))
484 /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
485 /// `'static` obligation. Suggest relaxing that implicit bound.
486 fn find_impl_on_dyn_trait(
488 err: &mut Diagnostic,
490 ctxt: &UnifyReceiverContext<'tcx>,
492 let tcx = self.tcx();
494 // Find the method being called.
495 let Ok(Some(instance)) = ty::Instance::resolve(
498 ctxt.assoc_item.def_id,
499 self.cx.resolve_vars_if_possible(ctxt.substs),
504 let mut v = TraitObjectVisitor(FxIndexSet::default());
507 // Get the `Ident` of the method being called and the corresponding `impl` (to point at
508 // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
509 let Some((ident, self_ty)) = NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0) else {
513 // Find the trait object types in the argument, so we point at *only* the trait object.
514 self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty)
517 fn suggest_constrain_dyn_trait_in_impl(
519 err: &mut Diagnostic,
520 found_dids: &FxIndexSet<DefId>,
522 self_ty: &hir::Ty<'_>,
524 let mut suggested = false;
525 for found_did in found_dids {
526 let mut traits = vec![];
527 let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
528 hir_v.visit_ty(&self_ty);
529 for &span in &traits {
530 let subdiag = DynTraitConstraintSuggestion { span, ident };
531 subdiag.add_to_diagnostic(err);
539 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
540 pub struct TraitObjectVisitor(pub FxIndexSet<DefId>);
542 impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor {
543 fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
545 ty::Dynamic(preds, re, _) if re.is_static() => {
546 if let Some(def_id) = preds.principal_def_id() {
547 self.0.insert(def_id);
549 ControlFlow::Continue(())
551 _ => t.super_visit_with(self),
556 /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
557 pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId);
559 impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
560 fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
561 if let TyKind::TraitObject(
563 Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. },
567 for ptr in poly_trait_refs {
568 if Some(self.1) == ptr.trait_ref.trait_def_id() {
569 self.0.push(ptr.span);