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::stable_set::FxHashSet;
8 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
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, RegionKind, StaticLifetimeVisitor, Ty, TyCtxt, TypeFoldable,
16 use rustc_span::symbol::Ident;
17 use rustc_span::{MultiSpan, Span};
19 use std::ops::ControlFlow;
21 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
22 /// Print the error message for lifetime errors when the return type is a static `impl Trait`,
23 /// `dyn Trait` or if a method call on a trait object introduces a static requirement.
24 pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
25 debug!("try_report_static_impl_trait(error={:?})", self.error);
27 let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? {
28 RegionResolutionError::SubSupConflict(
36 ) if **sub_r == RegionKind::ReStatic => {
37 (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans)
39 RegionResolutionError::ConcreteFailure(
40 SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
43 ) if **sub_r == RegionKind::ReStatic => {
44 // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
45 if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
46 // This may have a closure and it would cause ICE
47 // through `find_param_with_region` (#78262).
48 let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
49 let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
50 if fn_returns.is_empty() {
54 let param = self.find_param_with_region(sup_r, sub_r)?;
55 let lifetime = if sup_r.has_name() {
56 format!("lifetime `{}`", sup_r)
58 "an anonymous lifetime `'_`".to_string()
60 let mut err = struct_span_err!(
64 "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
70 .map(|s| format!("`{}`", s))
71 .unwrap_or_else(|| "`fn` parameter".to_string()),
73 ctxt.assoc_item.ident,
75 err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
79 "...is used and required to live as long as `'static` here \
80 because of an implicit lifetime bound on the {}",
81 match ctxt.assoc_item.container {
82 AssocItemContainer::TraitContainer(id) =>
83 format!("`impl` of `{}`", tcx.def_path_str(id)),
84 AssocItemContainer::ImplContainer(_) =>
85 "inherent `impl`".to_string(),
89 if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
91 return Some(ErrorReported);
101 "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
102 var_origin, sub_origin, sub_r, sup_origin, sup_r
104 let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
105 debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
106 let sp = var_origin.span();
107 let return_sp = sub_origin.span();
108 let param = self.find_param_with_region(sup_r, sub_r)?;
109 let (lifetime_name, lifetime) = if sup_r.has_name() {
110 (sup_r.to_string(), format!("lifetime `{}`", sup_r))
112 ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
114 let param_name = param
118 .map(|s| format!("`{}`", s))
119 .unwrap_or_else(|| "`fn` parameter".to_string());
120 let mut err = struct_span_err!(
124 "{} has {} but it needs to satisfy a `'static` lifetime requirement",
129 let (mention_influencer, influencer_point) =
130 if sup_origin.span().overlaps(param.param_ty_span) {
131 // Account for `async fn` like in `async-await/issues/issue-62097.rs`.
132 // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same
133 // place (but with different `ctxt`, hence `overlaps` instead of `==` above).
135 // This avoids the following:
137 // LL | pub async fn run_dummy_fn(&self) {
140 // | this data with an anonymous lifetime `'_`...
141 // | ...is captured here...
142 (false, sup_origin.span())
144 (!sup_origin.span().overlaps(return_sp), param.param_ty_span)
146 err.span_label(influencer_point, &format!("this data with {}...", lifetime));
148 debug!("try_report_static_impl_trait: param_info={:?}", param);
150 let mut spans = spans.clone();
152 if mention_influencer {
153 spans.push(sup_origin.span());
155 // We dedup the spans *ignoring* expansion context.
157 spans.dedup_by_key(|span| (span.lo(), span.hi()));
159 // We try to make the output have fewer overlapping spans if possible.
160 let require_msg = if spans.is_empty() {
161 "...is used and required to live as long as `'static` here"
163 "...and is required to live as long as `'static` here"
166 if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
169 err.span_label(*span, "...is used here...");
172 if spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp) {
173 // If any of the "captured here" labels appears on the same line or after
174 // `require_span`, we put it on a note to ensure the text flows by appearing
175 // always at the end.
176 err.span_note(require_span, require_msg);
178 // We don't need a note, it's already at the end, it can be shown as a `span_label`.
179 err.span_label(require_span, require_msg);
182 if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
183 err.span_note(*bound, "`'static` lifetime requirement introduced by this bound");
185 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin {
186 if let ObligationCauseCode::ReturnValue(hir_id)
187 | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code()
189 let parent_id = tcx.hir().get_parent_item(*hir_id);
190 let parent_id = tcx.hir().local_def_id_to_hir_id(parent_id);
191 if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id) {
192 let mut span: MultiSpan = fn_decl.output.span().into();
193 let mut add_label = true;
194 if let hir::FnRetTy::Return(ty) = fn_decl.output {
195 let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
198 span = v.0.clone().into();
200 span.push_span_label(
202 "`'static` requirement introduced here".to_string(),
209 span.push_span_label(
210 fn_decl.output.span(),
211 "requirement introduced by this return type".to_string(),
214 span.push_span_label(
216 "because of this returned expression".to_string(),
220 "`'static` lifetime requirement introduced by the return type",
226 let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
228 let mut override_error_code = None;
229 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin {
230 if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
231 // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
232 // `'static` lifetime when called as a method on a binding: `bar.qux()`.
233 if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
234 override_error_code = Some(ctxt.assoc_item.ident);
238 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin {
239 let code = match cause.code() {
240 ObligationCauseCode::MatchImpl(parent, ..) => parent.code(),
243 if let (ObligationCauseCode::ItemObligation(item_def_id), None) =
244 (code, override_error_code)
246 // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
247 // lifetime as above, but called using a fully-qualified path to the method:
249 let mut v = TraitObjectVisitor(FxHashSet::default());
250 v.visit_ty(param.param_ty);
251 if let Some((ident, self_ty)) =
252 self.get_impl_ident_and_self_ty_from_trait(*item_def_id, &v.0)
254 if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) {
255 override_error_code = Some(ident);
260 if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
261 // Provide a more targeted error code and description.
262 err.code(rustc_errors::error_code!(E0772));
263 err.set_primary_message(&format!(
264 "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
266 param_name, lifetime, ident,
270 let arg = match param.param.pat.simple_ident() {
271 Some(simple_ident) => format!("argument `{}`", simple_ident),
272 None => "the argument".to_string(),
274 let captures = format!("captures data from {}", arg);
275 suggest_new_region_bound(
282 Some((param.param_ty_span, param.param_ty.to_string())),
290 pub fn suggest_new_region_bound(
292 err: &mut DiagnosticBuilder<'_>,
293 fn_returns: Vec<&rustc_hir::Ty<'_>>,
294 lifetime_name: String,
297 param: Option<(Span, String)>,
299 debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
300 // FIXME: account for the need of parens in `&(dyn Trait + '_)`
301 let consider = "consider changing the";
302 let declare = "to declare that the";
303 let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
304 let explicit_static =
305 arg.map(|arg| format!("explicit `'static` bound to the lifetime of {}", arg));
306 let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
307 let plus_lt = format!(" + {}", lifetime_name);
308 for fn_return in fn_returns {
309 if fn_return.span.desugaring_kind().is_some() {
310 // Skip `async` desugaring `impl Future`.
313 match fn_return.kind {
314 TyKind::OpaqueDef(item_id, _) => {
315 let item = tcx.hir().item(item_id);
316 let ItemKind::OpaqueTy(opaque) = &item.kind else {
320 if let Some(span) = opaque
323 .filter_map(|arg| match arg {
324 GenericBound::Outlives(Lifetime {
325 name: LifetimeName::Static,
333 if let Some(explicit_static) = &explicit_static {
334 err.span_suggestion_verbose(
336 &format!("{} `impl Trait`'s {}", consider, explicit_static),
337 lifetime_name.clone(),
338 Applicability::MaybeIncorrect,
341 if let Some((param_span, param_ty)) = param.clone() {
342 err.span_suggestion_verbose(
346 Applicability::MaybeIncorrect,
352 .filter_map(|arg| match arg {
353 GenericBound::Outlives(Lifetime { name, span, .. })
354 if name.ident().to_string() == lifetime_name =>
364 err.span_suggestion_verbose(
365 fn_return.span.shrink_to_hi(),
367 "{declare} `impl Trait` {captures}, {explicit}",
373 Applicability::MaybeIncorrect,
377 TyKind::TraitObject(_, lt, _) => match lt.name {
378 LifetimeName::ImplicitObjectLifetimeDefault => {
379 err.span_suggestion_verbose(
380 fn_return.span.shrink_to_hi(),
382 "{declare} trait object {captures}, {explicit}",
388 Applicability::MaybeIncorrect,
391 name if name.ident().to_string() != lifetime_name => {
392 // With this check we avoid suggesting redundant bounds. This
393 // would happen if there are nested impl/dyn traits and only
394 // one of them has the bound we'd suggest already there, like
395 // in `impl Foo<X = dyn Bar> + '_`.
396 if let Some(explicit_static) = &explicit_static {
397 err.span_suggestion_verbose(
399 &format!("{} trait object's {}", consider, explicit_static),
400 lifetime_name.clone(),
401 Applicability::MaybeIncorrect,
404 if let Some((param_span, param_ty)) = param.clone() {
405 err.span_suggestion_verbose(
409 Applicability::MaybeIncorrect,
420 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
421 fn get_impl_ident_and_self_ty_from_trait(
424 trait_objects: &FxHashSet<DefId>,
425 ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
426 let tcx = self.tcx();
427 match tcx.hir().get_if_local(def_id) {
428 Some(Node::ImplItem(impl_item)) => {
429 match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id())) {
430 Some(Node::Item(Item {
431 kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
433 })) => Some((impl_item.ident, self_ty)),
437 Some(Node::TraitItem(trait_item)) => {
438 let trait_did = tcx.hir().get_parent_item(trait_item.hir_id());
439 match tcx.hir().find_by_def_id(trait_did) {
440 Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => {
441 // The method being called is defined in the `trait`, but the `'static`
442 // obligation comes from the `impl`. Find that `impl` so that we can point
443 // at it in the suggestion.
444 let trait_did = trait_did.to_def_id();
447 .trait_impls(trait_did)
449 .filter_map(|&impl_did| {
450 match tcx.hir().get_if_local(impl_did.to_def_id()) {
451 Some(Node::Item(Item {
452 kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
454 })) if trait_objects.iter().all(|did| {
455 // FIXME: we should check `self_ty` against the receiver
456 // type in the `UnifyReceiver` context, but for now, use
457 // this imperfect proxy. This will fail if there are
458 // multiple `impl`s for the same trait like
459 // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
460 // In that case, only the first one will get suggestions.
461 let mut traits = vec![];
462 let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
463 hir_v.visit_ty(self_ty);
474 Some(self_ty) => Some((trait_item.ident, self_ty)),
485 /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
486 /// `'static` obligation. Suggest relaxing that implicit bound.
487 fn find_impl_on_dyn_trait(
489 err: &mut DiagnosticBuilder<'_>,
491 ctxt: &UnifyReceiverContext<'tcx>,
493 let tcx = self.tcx();
495 // Find the method being called.
496 let instance = match ty::Instance::resolve(
499 ctxt.assoc_item.def_id,
500 self.infcx.resolve_vars_if_possible(ctxt.substs),
502 Ok(Some(instance)) => instance,
506 let mut v = TraitObjectVisitor(FxHashSet::default());
509 // Get the `Ident` of the method being called and the corresponding `impl` (to point at
510 // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
511 let (ident, self_ty) =
512 match self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0) {
513 Some((ident, self_ty)) => (ident, self_ty),
514 None => return false,
517 // Find the trait object types in the argument, so we point at *only* the trait object.
518 self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty)
521 fn suggest_constrain_dyn_trait_in_impl(
523 err: &mut DiagnosticBuilder<'_>,
524 found_dids: &FxHashSet<DefId>,
526 self_ty: &hir::Ty<'_>,
528 let mut suggested = false;
529 for found_did in found_dids {
530 let mut traits = vec![];
531 let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
532 hir_v.visit_ty(&self_ty);
533 for span in &traits {
534 let mut multi_span: MultiSpan = vec![*span].into();
535 multi_span.push_span_label(
537 "this has an implicit `'static` lifetime requirement".to_string(),
539 multi_span.push_span_label(
541 "calling this method introduces the `impl`'s 'static` requirement".to_string(),
543 err.span_note(multi_span, "the used `impl` has a `'static` requirement");
544 err.span_suggestion_verbose(
546 "consider relaxing the implicit `'static` requirement",
548 Applicability::MaybeIncorrect,
557 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
558 pub(super) struct TraitObjectVisitor(pub(super) FxHashSet<DefId>);
560 impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor {
561 fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
563 ty::Dynamic(preds, RegionKind::ReStatic) => {
564 if let Some(def_id) = preds.principal_def_id() {
565 self.0.insert(def_id);
567 ControlFlow::CONTINUE
569 _ => t.super_visit_with(self),
574 /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
575 pub(super) struct HirTraitObjectVisitor<'a>(pub(super) &'a mut Vec<Span>, pub(super) DefId);
577 impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
578 fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
579 if let TyKind::TraitObject(
581 Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
585 for ptr in poly_trait_refs {
586 if Some(self.1) == ptr.trait_ref.trait_def_id() {
587 self.0.push(ptr.span);