]> git.lizzy.rs Git - rust.git/blob - src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs
9c2e02968f607bc3472e82d008f259b3ead21013
[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::{MultiSpan, Span};
16
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);
22         let tcx = self.tcx();
23         let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? {
24             RegionResolutionError::SubSupConflict(
25                 _,
26                 var_origin,
27                 sub_origin,
28                 sub_r,
29                 sup_origin,
30                 sup_r,
31             ) if **sub_r == RegionKind::ReStatic => {
32                 (var_origin, sub_origin, sub_r, sup_origin, sup_r)
33             }
34             RegionResolutionError::ConcreteFailure(
35                 SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
36                 sub_r,
37                 sup_r,
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)
44                     } else {
45                         "an anonymous lifetime `'_`".to_string()
46                     };
47                     let mut err = struct_span_err!(
48                         tcx.sess,
49                         cause.span,
50                         E0767,
51                         "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
52                          requirement",
53                         param
54                             .param
55                             .pat
56                             .simple_ident()
57                             .map(|s| format!("`{}`", s))
58                             .unwrap_or_else(|| "`fn` parameter".to_string()),
59                         lifetime,
60                         ctxt.assoc_item.ident,
61                     );
62                     err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
63                     err.span_label(
64                         cause.span,
65                         &format!(
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(),
73                             },
74                         ),
75                     );
76                     if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) {
77                         err.emit();
78                         return Some(ErrorReported);
79                     } else {
80                         err.cancel();
81                     }
82                 }
83                 return None;
84             }
85             _ => return None,
86         };
87         debug!(
88             "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
89             var_origin, sub_origin, sub_r, sup_origin, sup_r
90         );
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))
98         } else {
99             ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
100         };
101         let param_name = param
102             .param
103             .pat
104             .simple_ident()
105             .map(|s| format!("`{}`", s))
106             .unwrap_or_else(|| "`fn` parameter".to_string());
107         let mut err = struct_span_err!(
108             tcx.sess,
109             sp,
110             E0759,
111             "{} has {} but it needs to satisfy a `'static` lifetime requirement",
112             param_name,
113             lifetime,
114         );
115         err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
116         debug!("try_report_static_impl_trait: param_info={:?}", param);
117
118         let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
119
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()
125                 {
126                     err.code(rustc_errors::error_code!(E0767));
127                     err.set_primary_message(&format!(
128                         "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
129                          requirement",
130                         param_name, lifetime, ctxt.assoc_item.ident,
131                     ));
132                     postfix = format!(
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(),
138                         },
139                     );
140                 }
141                 // }
142             }
143         }
144
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
148         {
149             // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs`
150
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:
155                 //
156                 // error: cannot infer an appropriate lifetime
157                 //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
158                 //    |
159                 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
160                 //    |           ----                      ---------^-
161                 //
162                 // and instead show:
163                 //
164                 // error: cannot infer an appropriate lifetime
165                 //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
166                 //    |
167                 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
168                 //    |           ----                               ^
169                 err.span_label(
170                     sup_origin.span(),
171                     &format!(
172                         "...is captured here, requiring it to live as long as `'static`{}",
173                         postfix
174                     ),
175                 );
176             } else {
177                 err.span_label(sup_origin.span(), "...is captured here...");
178                 if return_sp < sup_origin.span() {
179                     err.span_note(
180                         return_sp,
181                         &format!("...and is required to live as long as `'static` here{}", postfix),
182                     );
183                 } else {
184                     err.span_label(
185                         return_sp,
186                         &format!("...and is required to live as long as `'static` here{}", postfix),
187                     );
188                 }
189             }
190         } else {
191             err.span_label(
192                 return_sp,
193                 &format!(
194                     "...is captured and required to live as long as `'static` here{}",
195                     postfix
196                 ),
197             );
198         }
199
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(),
207         };
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`.
216                 continue;
217             }
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 {
222                         opaque
223                     } else {
224                         err.emit();
225                         return Some(ErrorReported);
226                     };
227
228                     if let Some(span) = opaque
229                         .bounds
230                         .iter()
231                         .filter_map(|arg| match arg {
232                             GenericBound::Outlives(Lifetime {
233                                 name: LifetimeName::Static,
234                                 span,
235                                 ..
236                             }) => Some(*span),
237                             _ => None,
238                         })
239                         .next()
240                     {
241                         err.span_suggestion_verbose(
242                             span,
243                             &format!("{} `impl Trait`'s {}", consider, explicit_static),
244                             lifetime_name.clone(),
245                             Applicability::MaybeIncorrect,
246                         );
247                         err.span_suggestion_verbose(
248                             param.param_ty_span,
249                             add_static_bound,
250                             param.param_ty.to_string(),
251                             Applicability::MaybeIncorrect,
252                         );
253                     } else if let Some(_) = opaque
254                         .bounds
255                         .iter()
256                         .filter_map(|arg| match arg {
257                             GenericBound::Outlives(Lifetime { name, span, .. })
258                                 if name.ident().to_string() == lifetime_name =>
259                             {
260                                 Some(*span)
261                             }
262                             _ => None,
263                         })
264                         .next()
265                     {
266                     } else {
267                         err.span_suggestion_verbose(
268                             fn_return.span.shrink_to_hi(),
269                             &format!(
270                                 "{declare} `impl Trait` {captures}, {explicit}",
271                                 declare = declare,
272                                 captures = captures,
273                                 explicit = explicit,
274                             ),
275                             plus_lt.clone(),
276                             Applicability::MaybeIncorrect,
277                         );
278                     }
279                 }
280                 TyKind::TraitObject(_, lt) => match lt.name {
281                     LifetimeName::ImplicitObjectLifetimeDefault => {
282                         err.span_suggestion_verbose(
283                             fn_return.span.shrink_to_hi(),
284                             &format!(
285                                 "{declare} trait object {captures}, {explicit}",
286                                 declare = declare,
287                                 captures = captures,
288                                 explicit = explicit,
289                             ),
290                             plus_lt.clone(),
291                             Applicability::MaybeIncorrect,
292                         );
293                     }
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(
300                             lt.span,
301                             &format!("{} trait object's {}", consider, explicit_static),
302                             lifetime_name.clone(),
303                             Applicability::MaybeIncorrect,
304                         );
305                         err.span_suggestion_verbose(
306                             param.param_ty_span,
307                             add_static_bound,
308                             param.param_ty.to_string(),
309                             Applicability::MaybeIncorrect,
310                         );
311                     }
312                     _ => {}
313                 },
314                 _ => {}
315             }
316         }
317         err.emit();
318         Some(ErrorReported)
319     }
320
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(
324         &self,
325         err: &mut DiagnosticBuilder<'_>,
326         ty: Ty<'_>,
327         ctxt: &UnifyReceiverContext<'tcx>,
328     ) -> bool {
329         let tcx = self.tcx();
330         let mut suggested = false;
331
332         // Find the method being called.
333         let instance = match ty::Instance::resolve(
334             tcx,
335             ctxt.param_env,
336             ctxt.assoc_item.def_id,
337             self.infcx.resolve_vars_if_possible(&ctxt.substs),
338         ) {
339             Ok(Some(instance)) => instance,
340             _ => return false,
341         };
342
343         // Get the `Ident` of the method being called and the corresponding `impl` (to point at
344         // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
345         let (ident, self_ty) = match tcx.hir().get_if_local(instance.def_id()) {
346             Some(Node::ImplItem(ImplItem { ident, hir_id, .. })) => {
347                 match tcx.hir().find(tcx.hir().get_parent_item(*hir_id)) {
348                     Some(Node::Item(Item { kind: ItemKind::Impl { self_ty, .. }, .. })) => {
349                         (ident, self_ty)
350                     }
351                     _ => return false,
352                 }
353             }
354             Some(Node::TraitItem(TraitItem { ident, hir_id, .. })) => {
355                 let parent_id = tcx.hir().get_parent_item(*hir_id);
356                 match tcx.hir().find(parent_id) {
357                     Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => {
358                         // The method being called is defined in the `trait`, but the `'static`
359                         // obligation comes from the `impl`. Find that `impl` so that we can point
360                         // at it in the suggestion.
361                         let trait_did = tcx.hir().local_def_id(parent_id).to_def_id();
362                         match tcx.hir().trait_impls(trait_did)
363                             .iter()
364                             .filter_map(|impl_node| {
365                                 let impl_did = tcx.hir().local_def_id(*impl_node);
366                                 match tcx.hir().get_if_local(impl_did.to_def_id()) {
367                                     Some(Node::Item(Item {
368                                         kind: ItemKind::Impl { self_ty, of_trait: Some(of_trait), .. },
369                                         ..
370                                     })) if of_trait.trait_def_id() == Some(trait_did) => Some(self_ty),
371                                     _ => None,
372                                 }
373                             })
374                             .next()
375                         {
376                             Some(self_ty) => (ident, self_ty),
377                             _ => return false,
378                         }
379                     }
380                     _ => return false,
381                 }
382             }
383             _ => return false,
384         };
385
386         // Find the trait object types in the argument, so we point at *only* the trait object.
387         let mut v = TraitObjectVisitor(vec![]);
388         v.visit_ty(ty);
389         for found_did in &v.0 {
390             let mut hir_v = HirTraitObjectVisitor(vec![], *found_did);
391             hir_v.visit_ty(self_ty);
392             for span in &hir_v.0 {
393                 let mut multi_span: MultiSpan = vec![*span].into();
394                 multi_span.push_span_label(
395                     *span,
396                     "this has an implicit `'static` lifetime requirement".to_string(),
397                 );
398                 multi_span.push_span_label(
399                     ident.span,
400                     "calling this method introduces the `impl`'s 'static` requirement".to_string(),
401                 );
402                 err.span_note(
403                     multi_span,
404                     &format!(
405                         "{} has a `'static` requirement",
406                         match ctxt.assoc_item.container {
407                             AssocItemContainer::TraitContainer(id) =>
408                                 format!("`impl` of `{}`", tcx.def_path_str(id)),
409                             AssocItemContainer::ImplContainer(_) => "inherent `impl`".to_string(),
410                         },
411                     ),
412                 );
413                 err.span_suggestion_verbose(
414                     span.shrink_to_hi(),
415                     "consider relaxing the implicit `'static` requirement",
416                     " + '_".to_string(),
417                     Applicability::MaybeIncorrect,
418                 );
419                 suggested = true;
420             }
421         }
422         suggested
423     }
424 }
425
426 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
427 struct TraitObjectVisitor(Vec<DefId>);
428
429 impl TypeVisitor<'_> for TraitObjectVisitor {
430     fn visit_ty(&mut self, t: Ty<'_>) -> bool {
431         match t.kind {
432             ty::Dynamic(preds, RegionKind::ReStatic) => {
433                 if let Some(def_id) = preds.principal_def_id() {
434                     self.0.push(def_id);
435                 }
436                 false
437             }
438             _ => t.super_visit_with(self),
439         }
440     }
441 }
442
443 /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
444 struct HirTraitObjectVisitor(Vec<Span>, DefId);
445
446 impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
447     type Map = ErasedMap<'tcx>;
448
449     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
450         NestedVisitorMap::None
451     }
452
453     fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
454         match t.kind {
455             TyKind::TraitObject(
456                 poly_trait_refs,
457                 Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
458             ) => {
459                 for ptr in poly_trait_refs {
460                     if Some(self.1) == ptr.trait_ref.trait_def_id() {
461                         self.0.push(ptr.span);
462                     }
463                 }
464             }
465             _ => {}
466         }
467         walk_ty(self, t);
468     }
469 }