]> git.lizzy.rs Git - rust.git/blob - src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs
Merge branch 'master' into feature/incorporate-tracing
[rust.git] / src / librustc_infer / infer / error_reporting / nice_region_error / static_impl_trait.rs
1 //! Error Reporting for static impl Traits.
2
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};
10 use rustc_hir::{
11     self as hir, GenericBound, ImplItem, Item, ItemKind, Lifetime, LifetimeName, Node, TraitItem,
12     TyKind,
13 };
14 use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, TypeVisitor};
15 use rustc_span::symbol::Ident;
16 use rustc_span::{MultiSpan, Span};
17
18 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
19     /// Print the error message for lifetime errors when the return type is a static `impl Trait`,
20     /// `dyn Trait` or if a method call on a trait object introduces a static requirement.
21     pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
22         debug!("try_report_static_impl_trait(error={:?})", self.error);
23         let tcx = self.tcx();
24         let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? {
25             RegionResolutionError::SubSupConflict(
26                 _,
27                 var_origin,
28                 sub_origin,
29                 sub_r,
30                 sup_origin,
31                 sup_r,
32             ) if **sub_r == RegionKind::ReStatic => {
33                 (var_origin, sub_origin, sub_r, sup_origin, sup_r)
34             }
35             RegionResolutionError::ConcreteFailure(
36                 SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
37                 sub_r,
38                 sup_r,
39             ) if **sub_r == RegionKind::ReStatic => {
40                 // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
41                 if let ObligationCauseCode::UnifyReceiver(ctxt) = &cause.code {
42                     let param = self.find_param_with_region(sup_r, sub_r)?;
43                     let lifetime = if sup_r.has_name() {
44                         format!("lifetime `{}`", sup_r)
45                     } else {
46                         "an anonymous lifetime `'_`".to_string()
47                     };
48                     let mut err = struct_span_err!(
49                         tcx.sess,
50                         cause.span,
51                         E0772,
52                         "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
53                          requirement",
54                         param
55                             .param
56                             .pat
57                             .simple_ident()
58                             .map(|s| format!("`{}`", s))
59                             .unwrap_or_else(|| "`fn` parameter".to_string()),
60                         lifetime,
61                         ctxt.assoc_item.ident,
62                     );
63                     err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
64                     err.span_label(
65                         cause.span,
66                         &format!(
67                             "...is captured and required to live as long as `'static` here \
68                              because of an implicit lifetime bound on the {}",
69                             match ctxt.assoc_item.container {
70                                 AssocItemContainer::TraitContainer(id) =>
71                                     format!("`impl` of `{}`", tcx.def_path_str(id)),
72                                 AssocItemContainer::ImplContainer(_) =>
73                                     "inherent `impl`".to_string(),
74                             },
75                         ),
76                     );
77                     if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
78                         err.emit();
79                         return Some(ErrorReported);
80                     } else {
81                         err.cancel();
82                     }
83                 }
84                 return None;
85             }
86             _ => return None,
87         };
88         debug!(
89             "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
90             var_origin, sub_origin, sub_r, sup_origin, sup_r
91         );
92         let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
93         debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
94         let sp = var_origin.span();
95         let return_sp = sub_origin.span();
96         let param = self.find_param_with_region(sup_r, sub_r)?;
97         let (lifetime_name, lifetime) = if sup_r.has_name() {
98             (sup_r.to_string(), format!("lifetime `{}`", sup_r))
99         } else {
100             ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
101         };
102         let param_name = param
103             .param
104             .pat
105             .simple_ident()
106             .map(|s| format!("`{}`", s))
107             .unwrap_or_else(|| "`fn` parameter".to_string());
108         let mut err = struct_span_err!(
109             tcx.sess,
110             sp,
111             E0759,
112             "{} has {} but it needs to satisfy a `'static` lifetime requirement",
113             param_name,
114             lifetime,
115         );
116         err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
117         debug!("try_report_static_impl_trait: param_info={:?}", param);
118
119         // We try to make the output have fewer overlapping spans if possible.
120         if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span()))
121             && sup_origin.span() != return_sp
122         {
123             // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs`
124
125             // Customize the spans and labels depending on their relative order so
126             // that split sentences flow correctly.
127             if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() {
128                 // Avoid the following:
129                 //
130                 // error: cannot infer an appropriate lifetime
131                 //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
132                 //    |
133                 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
134                 //    |           ----                      ---------^-
135                 //
136                 // and instead show:
137                 //
138                 // error: cannot infer an appropriate lifetime
139                 //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
140                 //    |
141                 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
142                 //    |           ----                               ^
143                 err.span_label(
144                     sup_origin.span(),
145                     "...is captured here, requiring it to live as long as `'static`",
146                 );
147             } else {
148                 err.span_label(sup_origin.span(), "...is captured here...");
149                 if return_sp < sup_origin.span() {
150                     err.span_note(
151                         return_sp,
152                         "...and is required to live as long as `'static` here",
153                     );
154                 } else {
155                     err.span_label(
156                         return_sp,
157                         "...and is required to live as long as `'static` here",
158                     );
159                 }
160             }
161         } else {
162             err.span_label(
163                 return_sp,
164                 "...is captured and required to live as long as `'static` here",
165             );
166         }
167
168         let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
169
170         let mut override_error_code = None;
171         if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin {
172             if let ObligationCauseCode::UnifyReceiver(ctxt) = &cause.code {
173                 // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
174                 // `'static` lifetime when called as a method on a binding: `bar.qux()`.
175                 if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
176                     override_error_code = Some(ctxt.assoc_item.ident);
177                 }
178             }
179         }
180         if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin {
181             if let ObligationCauseCode::ItemObligation(item_def_id) = cause.code {
182                 // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
183                 // lifetime as above, but called using a fully-qualified path to the method:
184                 // `Foo::qux(bar)`.
185                 let mut v = TraitObjectVisitor(vec![]);
186                 v.visit_ty(param.param_ty);
187                 if let Some((ident, self_ty)) =
188                     self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0[..])
189                 {
190                     if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0[..], ident, self_ty)
191                     {
192                         override_error_code = Some(ident);
193                     }
194                 }
195             }
196         }
197         if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
198             // Provide a more targeted error code and description.
199             err.code(rustc_errors::error_code!(E0772));
200             err.set_primary_message(&format!(
201                 "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
202                 requirement",
203                 param_name, lifetime, ident,
204             ));
205         }
206
207         debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
208         // FIXME: account for the need of parens in `&(dyn Trait + '_)`
209         let consider = "consider changing the";
210         let declare = "to declare that the";
211         let arg = match param.param.pat.simple_ident() {
212             Some(simple_ident) => format!("argument `{}`", simple_ident),
213             None => "the argument".to_string(),
214         };
215         let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
216         let explicit_static = format!("explicit `'static` bound to the lifetime of {}", arg);
217         let captures = format!("captures data from {}", arg);
218         let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
219         let plus_lt = format!(" + {}", lifetime_name);
220         for fn_return in fn_returns {
221             if fn_return.span.desugaring_kind().is_some() {
222                 // Skip `async` desugaring `impl Future`.
223                 continue;
224             }
225             match fn_return.kind {
226                 TyKind::OpaqueDef(item_id, _) => {
227                     let item = tcx.hir().item(item_id.id);
228                     let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind {
229                         opaque
230                     } else {
231                         err.emit();
232                         return Some(ErrorReported);
233                     };
234
235                     if let Some(span) = opaque
236                         .bounds
237                         .iter()
238                         .filter_map(|arg| match arg {
239                             GenericBound::Outlives(Lifetime {
240                                 name: LifetimeName::Static,
241                                 span,
242                                 ..
243                             }) => Some(*span),
244                             _ => None,
245                         })
246                         .next()
247                     {
248                         err.span_suggestion_verbose(
249                             span,
250                             &format!("{} `impl Trait`'s {}", consider, explicit_static),
251                             lifetime_name.clone(),
252                             Applicability::MaybeIncorrect,
253                         );
254                         err.span_suggestion_verbose(
255                             param.param_ty_span,
256                             add_static_bound,
257                             param.param_ty.to_string(),
258                             Applicability::MaybeIncorrect,
259                         );
260                     } else if opaque
261                         .bounds
262                         .iter()
263                         .filter_map(|arg| match arg {
264                             GenericBound::Outlives(Lifetime { name, span, .. })
265                                 if name.ident().to_string() == lifetime_name =>
266                             {
267                                 Some(*span)
268                             }
269                             _ => None,
270                         })
271                         .next()
272                         .is_some()
273                     {
274                     } else {
275                         err.span_suggestion_verbose(
276                             fn_return.span.shrink_to_hi(),
277                             &format!(
278                                 "{declare} `impl Trait` {captures}, {explicit}",
279                                 declare = declare,
280                                 captures = captures,
281                                 explicit = explicit,
282                             ),
283                             plus_lt.clone(),
284                             Applicability::MaybeIncorrect,
285                         );
286                     }
287                 }
288                 TyKind::TraitObject(_, lt) => match lt.name {
289                     LifetimeName::ImplicitObjectLifetimeDefault => {
290                         err.span_suggestion_verbose(
291                             fn_return.span.shrink_to_hi(),
292                             &format!(
293                                 "{declare} trait object {captures}, {explicit}",
294                                 declare = declare,
295                                 captures = captures,
296                                 explicit = explicit,
297                             ),
298                             plus_lt.clone(),
299                             Applicability::MaybeIncorrect,
300                         );
301                     }
302                     name if name.ident().to_string() != lifetime_name => {
303                         // With this check we avoid suggesting redundant bounds. This
304                         // would happen if there are nested impl/dyn traits and only
305                         // one of them has the bound we'd suggest already there, like
306                         // in `impl Foo<X = dyn Bar> + '_`.
307                         err.span_suggestion_verbose(
308                             lt.span,
309                             &format!("{} trait object's {}", consider, explicit_static),
310                             lifetime_name.clone(),
311                             Applicability::MaybeIncorrect,
312                         );
313                         err.span_suggestion_verbose(
314                             param.param_ty_span,
315                             add_static_bound,
316                             param.param_ty.to_string(),
317                             Applicability::MaybeIncorrect,
318                         );
319                     }
320                     _ => {}
321                 },
322                 _ => {}
323             }
324         }
325         err.emit();
326         Some(ErrorReported)
327     }
328
329     fn get_impl_ident_and_self_ty_from_trait(
330         &self,
331         def_id: DefId,
332         trait_objects: &[DefId],
333     ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
334         let tcx = self.tcx();
335         match tcx.hir().get_if_local(def_id) {
336             Some(Node::ImplItem(ImplItem { ident, hir_id, .. })) => {
337                 match tcx.hir().find(tcx.hir().get_parent_item(*hir_id)) {
338                     Some(Node::Item(Item { kind: ItemKind::Impl { self_ty, .. }, .. })) => {
339                         Some((*ident, self_ty))
340                     }
341                     _ => None,
342                 }
343             }
344             Some(Node::TraitItem(TraitItem { ident, hir_id, .. })) => {
345                 let parent_id = tcx.hir().get_parent_item(*hir_id);
346                 match tcx.hir().find(parent_id) {
347                     Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => {
348                         // The method being called is defined in the `trait`, but the `'static`
349                         // obligation comes from the `impl`. Find that `impl` so that we can point
350                         // at it in the suggestion.
351                         let trait_did = tcx.hir().local_def_id(parent_id).to_def_id();
352                         match tcx
353                             .hir()
354                             .trait_impls(trait_did)
355                             .iter()
356                             .filter_map(|impl_node| {
357                                 let impl_did = tcx.hir().local_def_id(*impl_node);
358                                 match tcx.hir().get_if_local(impl_did.to_def_id()) {
359                                     Some(Node::Item(Item {
360                                         kind: ItemKind::Impl { self_ty, .. },
361                                         ..
362                                     })) if trait_objects.iter().all(|did| {
363                                         // FIXME: we should check `self_ty` against the receiver
364                                         // type in the `UnifyReceiver` context, but for now, use
365                                         // this imperfect proxy. This will fail if there are
366                                         // multiple `impl`s for the same trait like
367                                         // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
368                                         // In that case, only the first one will get suggestions.
369                                         let mut hir_v = HirTraitObjectVisitor(vec![], *did);
370                                         hir_v.visit_ty(self_ty);
371                                         !hir_v.0.is_empty()
372                                     }) =>
373                                     {
374                                         Some(self_ty)
375                                     }
376                                     _ => None,
377                                 }
378                             })
379                             .next()
380                         {
381                             Some(self_ty) => Some((*ident, self_ty)),
382                             _ => None,
383                         }
384                     }
385                     _ => None,
386                 }
387             }
388             _ => None,
389         }
390     }
391
392     /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
393     /// `'static` obligation. Suggest relaxing that implicit bound.
394     fn find_impl_on_dyn_trait(
395         &self,
396         err: &mut DiagnosticBuilder<'_>,
397         ty: Ty<'_>,
398         ctxt: &UnifyReceiverContext<'tcx>,
399     ) -> bool {
400         let tcx = self.tcx();
401
402         // Find the method being called.
403         let instance = match ty::Instance::resolve(
404             tcx,
405             ctxt.param_env,
406             ctxt.assoc_item.def_id,
407             self.infcx.resolve_vars_if_possible(&ctxt.substs),
408         ) {
409             Ok(Some(instance)) => instance,
410             _ => return false,
411         };
412
413         let mut v = TraitObjectVisitor(vec![]);
414         v.visit_ty(ty);
415
416         // Get the `Ident` of the method being called and the corresponding `impl` (to point at
417         // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
418         let (ident, self_ty) =
419             match self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0[..]) {
420                 Some((ident, self_ty)) => (ident, self_ty),
421                 None => return false,
422             };
423
424         // Find the trait object types in the argument, so we point at *only* the trait object.
425         self.suggest_constrain_dyn_trait_in_impl(err, &v.0[..], ident, self_ty)
426     }
427
428     fn suggest_constrain_dyn_trait_in_impl(
429         &self,
430         err: &mut DiagnosticBuilder<'_>,
431         found_dids: &[DefId],
432         ident: Ident,
433         self_ty: &hir::Ty<'_>,
434     ) -> bool {
435         let mut suggested = false;
436         for found_did in found_dids {
437             let mut hir_v = HirTraitObjectVisitor(vec![], *found_did);
438             hir_v.visit_ty(&self_ty);
439             for span in &hir_v.0 {
440                 let mut multi_span: MultiSpan = vec![*span].into();
441                 multi_span.push_span_label(
442                     *span,
443                     "this has an implicit `'static` lifetime requirement".to_string(),
444                 );
445                 multi_span.push_span_label(
446                     ident.span,
447                     "calling this method introduces the `impl`'s 'static` requirement".to_string(),
448                 );
449                 err.span_note(multi_span, "the used `impl` has a `'static` requirement");
450                 err.span_suggestion_verbose(
451                     span.shrink_to_hi(),
452                     "consider relaxing the implicit `'static` requirement",
453                     " + '_".to_string(),
454                     Applicability::MaybeIncorrect,
455                 );
456                 suggested = true;
457             }
458         }
459         suggested
460     }
461 }
462
463 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
464 struct TraitObjectVisitor(Vec<DefId>);
465
466 impl TypeVisitor<'_> for TraitObjectVisitor {
467     fn visit_ty(&mut self, t: Ty<'_>) -> bool {
468         match t.kind {
469             ty::Dynamic(preds, RegionKind::ReStatic) => {
470                 if let Some(def_id) = preds.principal_def_id() {
471                     self.0.push(def_id);
472                 }
473                 false
474             }
475             _ => t.super_visit_with(self),
476         }
477     }
478 }
479
480 /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
481 struct HirTraitObjectVisitor(Vec<Span>, DefId);
482
483 impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
484     type Map = ErasedMap<'tcx>;
485
486     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
487         NestedVisitorMap::None
488     }
489
490     fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
491         match t.kind {
492             TyKind::TraitObject(
493                 poly_trait_refs,
494                 Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
495             ) => {
496                 for ptr in poly_trait_refs {
497                     if Some(self.1) == ptr.trait_ref.trait_def_id() {
498                         self.0.push(ptr.span);
499                     }
500                 }
501             }
502             _ => {}
503         }
504         walk_ty(self, t);
505     }
506 }