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_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
8 use rustc_hir::def_id::DefId;
9 use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor};
11 self as hir, GenericBound, ImplItem, Item, ItemKind, Lifetime, LifetimeName, Node, TraitItem,
14 use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, TypeVisitor};
15 use rustc_span::{MultiSpan, Span};
17 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
18 /// Print the error message for lifetime errors when the return type is a static `impl Trait`,
19 /// `dyn Trait` or if a method call on a trait object introduces a static requirement.
20 pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
21 debug!("try_report_static_impl_trait(error={:?})", self.error);
23 let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? {
24 RegionResolutionError::SubSupConflict(
31 ) if **sub_r == RegionKind::ReStatic => {
32 (var_origin, sub_origin, sub_r, sup_origin, sup_r)
34 RegionResolutionError::ConcreteFailure(
35 SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
38 ) if **sub_r == RegionKind::ReStatic => {
39 // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
40 if let ObligationCauseCode::UnifyReceiver(ctxt) = &cause.code {
41 let param = self.find_param_with_region(sup_r, sub_r)?;
42 let lifetime = if sup_r.has_name() {
43 format!("lifetime `{}`", sup_r)
45 "an anonymous lifetime `'_`".to_string()
47 let mut err = struct_span_err!(
51 "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
57 .map(|s| format!("`{}`", s))
58 .unwrap_or_else(|| "`fn` parameter".to_string()),
60 ctxt.assoc_item.ident,
62 err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
66 "...is captured and required to live as long as `'static` here \
67 because of an implicit lifetime bound on the {}",
68 match ctxt.assoc_item.container {
69 AssocItemContainer::TraitContainer(id) =>
70 format!("`impl` of `{}`", tcx.def_path_str(id)),
71 AssocItemContainer::ImplContainer(_) =>
72 "inherent `impl`".to_string(),
76 if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
78 return Some(ErrorReported);
88 "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
89 var_origin, sub_origin, sub_r, sup_origin, sup_r
91 let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
92 debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
93 let sp = var_origin.span();
94 let return_sp = sub_origin.span();
95 let param = self.find_param_with_region(sup_r, sub_r)?;
96 let (lifetime_name, lifetime) = if sup_r.has_name() {
97 (sup_r.to_string(), format!("lifetime `{}`", sup_r))
99 ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
101 let param_name = param
105 .map(|s| format!("`{}`", s))
106 .unwrap_or_else(|| "`fn` parameter".to_string());
107 let mut err = struct_span_err!(
111 "{} has {} but it needs to satisfy a `'static` lifetime requirement",
115 err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
116 debug!("try_report_static_impl_trait: param_info={:?}", param);
118 let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
120 let mut postfix = String::new();
121 if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin {
122 if let ObligationCauseCode::UnifyReceiver(ctxt) = &cause.code {
123 if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt)
124 && fn_returns.is_empty()
126 err.code(rustc_errors::error_code!(E0767));
127 err.set_primary_message(&format!(
128 "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
130 param_name, lifetime, ctxt.assoc_item.ident,
133 " because of an implicit lifetime on the {}",
134 match ctxt.assoc_item.container {
135 AssocItemContainer::TraitContainer(id) =>
136 format!("`impl` of `{}`", tcx.def_path_str(id)),
137 AssocItemContainer::ImplContainer(_) => "inherent `impl`".to_string(),
145 // We try to make the output have fewer overlapping spans if possible.
146 if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span()))
147 && sup_origin.span() != return_sp
149 // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs`
151 // Customize the spans and labels depending on their relative order so
152 // that split sentences flow correctly.
153 if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() {
154 // Avoid the following:
156 // error: cannot infer an appropriate lifetime
157 // --> $DIR/must_outlive_least_region_or_bound.rs:18:50
159 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
160 // | ---- ---------^-
164 // error: cannot infer an appropriate lifetime
165 // --> $DIR/must_outlive_least_region_or_bound.rs:18:50
167 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
172 "...is captured here, requiring it to live as long as `'static`{}",
177 err.span_label(sup_origin.span(), "...is captured here...");
178 if return_sp < sup_origin.span() {
181 &format!("...and is required to live as long as `'static` here{}", postfix),
186 &format!("...and is required to live as long as `'static` here{}", postfix),
194 "...is captured and required to live as long as `'static` here{}",
200 debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
201 // FIXME: account for the need of parens in `&(dyn Trait + '_)`
202 let consider = "consider changing the";
203 let declare = "to declare that the";
204 let arg = match param.param.pat.simple_ident() {
205 Some(simple_ident) => format!("argument `{}`", simple_ident),
206 None => "the argument".to_string(),
208 let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
209 let explicit_static = format!("explicit `'static` bound to the lifetime of {}", arg);
210 let captures = format!("captures data from {}", arg);
211 let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
212 let plus_lt = format!(" + {}", lifetime_name);
213 for fn_return in fn_returns {
214 if fn_return.span.desugaring_kind().is_some() {
215 // Skip `async` desugaring `impl Future`.
218 match fn_return.kind {
219 TyKind::OpaqueDef(item_id, _) => {
220 let item = tcx.hir().item(item_id.id);
221 let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind {
225 return Some(ErrorReported);
228 if let Some(span) = opaque
231 .filter_map(|arg| match arg {
232 GenericBound::Outlives(Lifetime {
233 name: LifetimeName::Static,
241 err.span_suggestion_verbose(
243 &format!("{} `impl Trait`'s {}", consider, explicit_static),
244 lifetime_name.clone(),
245 Applicability::MaybeIncorrect,
247 err.span_suggestion_verbose(
250 param.param_ty.to_string(),
251 Applicability::MaybeIncorrect,
253 } else if let Some(_) = opaque
256 .filter_map(|arg| match arg {
257 GenericBound::Outlives(Lifetime { name, span, .. })
258 if name.ident().to_string() == lifetime_name =>
267 err.span_suggestion_verbose(
268 fn_return.span.shrink_to_hi(),
270 "{declare} `impl Trait` {captures}, {explicit}",
276 Applicability::MaybeIncorrect,
280 TyKind::TraitObject(_, lt) => match lt.name {
281 LifetimeName::ImplicitObjectLifetimeDefault => {
282 err.span_suggestion_verbose(
283 fn_return.span.shrink_to_hi(),
285 "{declare} trait object {captures}, {explicit}",
291 Applicability::MaybeIncorrect,
294 name if name.ident().to_string() != lifetime_name => {
295 // With this check we avoid suggesting redundant bounds. This
296 // would happen if there are nested impl/dyn traits and only
297 // one of them has the bound we'd suggest already there, like
298 // in `impl Foo<X = dyn Bar> + '_`.
299 err.span_suggestion_verbose(
301 &format!("{} trait object's {}", consider, explicit_static),
302 lifetime_name.clone(),
303 Applicability::MaybeIncorrect,
305 err.span_suggestion_verbose(
308 param.param_ty.to_string(),
309 Applicability::MaybeIncorrect,
321 /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
322 /// `'static` obligation. Suggest relaxing that implicit bound.
323 fn find_impl_on_dyn_trait(
325 err: &mut DiagnosticBuilder<'_>,
327 ctxt: &UnifyReceiverContext<'tcx>,
329 let tcx = self.tcx();
330 let mut suggested = false;
332 // Find the method being called.
333 let instance = match ty::Instance::resolve(
336 ctxt.assoc_item.def_id,
337 self.infcx.resolve_vars_if_possible(&ctxt.substs),
339 Ok(Some(instance)) => instance,
343 let mut v = TraitObjectVisitor(vec![]);
346 // Get the `Ident` of the method being called and the corresponding `impl` (to point at
347 // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
348 let (ident, self_ty) = match tcx.hir().get_if_local(instance.def_id()) {
349 Some(Node::ImplItem(ImplItem { ident, hir_id, .. })) => {
350 match tcx.hir().find(tcx.hir().get_parent_item(*hir_id)) {
351 Some(Node::Item(Item { kind: ItemKind::Impl { self_ty, .. }, .. })) => {
357 Some(Node::TraitItem(TraitItem { ident, hir_id, .. })) => {
358 let parent_id = tcx.hir().get_parent_item(*hir_id);
359 match tcx.hir().find(parent_id) {
360 Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => {
361 // The method being called is defined in the `trait`, but the `'static`
362 // obligation comes from the `impl`. Find that `impl` so that we can point
363 // at it in the suggestion.
364 let trait_did = tcx.hir().local_def_id(parent_id).to_def_id();
367 .trait_impls(trait_did)
369 .filter_map(|impl_node| {
370 let impl_did = tcx.hir().local_def_id(*impl_node);
371 match tcx.hir().get_if_local(impl_did.to_def_id()) {
372 Some(Node::Item(Item {
373 kind: ItemKind::Impl { self_ty, .. },
375 })) if v.0.iter().all(|did| {
376 // FIXME: we should check `self_ty` against the receiver
377 // type in the `UnifyReceiver` context, but for now, use
378 // this imperfect proxy. This will fail if there are
379 // multiple `impl`s for the same trait like
380 // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
381 // In that case, only the first one will get suggestions.
382 let mut hir_v = HirTraitObjectVisitor(vec![], *did);
383 hir_v.visit_ty(self_ty);
394 Some(self_ty) => (ident, self_ty),
404 // Find the trait object types in the argument, so we point at *only* the trait object.
405 for found_did in &v.0 {
406 let mut hir_v = HirTraitObjectVisitor(vec![], *found_did);
407 hir_v.visit_ty(self_ty);
408 for span in &hir_v.0 {
409 let mut multi_span: MultiSpan = vec![*span].into();
410 multi_span.push_span_label(
412 "this has an implicit `'static` lifetime requirement".to_string(),
414 multi_span.push_span_label(
416 "calling this method introduces the `impl`'s 'static` requirement".to_string(),
421 "{} has a `'static` requirement",
422 match ctxt.assoc_item.container {
423 AssocItemContainer::TraitContainer(id) =>
424 format!("`impl` of `{}`", tcx.def_path_str(id)),
425 AssocItemContainer::ImplContainer(_) => "inherent `impl`".to_string(),
429 err.span_suggestion_verbose(
431 "consider relaxing the implicit `'static` requirement",
433 Applicability::MaybeIncorrect,
442 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
443 struct TraitObjectVisitor(Vec<DefId>);
445 impl TypeVisitor<'_> for TraitObjectVisitor {
446 fn visit_ty(&mut self, t: Ty<'_>) -> bool {
448 ty::Dynamic(preds, RegionKind::ReStatic) => {
449 if let Some(def_id) = preds.principal_def_id() {
454 _ => t.super_visit_with(self),
459 /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
460 struct HirTraitObjectVisitor(Vec<Span>, DefId);
462 impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
463 type Map = ErasedMap<'tcx>;
465 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
466 NestedVisitorMap::None
469 fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
473 Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
475 for ptr in poly_trait_refs {
476 if Some(self.1) == ptr.trait_ref.trait_def_id() {
477 self.0.push(ptr.span);