]> git.lizzy.rs Git - rust.git/blob - src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs
0aa1d65612ee6bb813e05467fd0ee824b7444353
[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;
7 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
8 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
9 use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor};
10 use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
11 use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, TypeVisitor};
12 use rustc_span::Span;
13
14 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
15     /// Print the error message for lifetime errors when the return type is a static impl Trait.
16     pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
17         debug!("try_report_static_impl_trait(error={:?})", self.error);
18         let tcx = self.tcx();
19         let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? {
20             RegionResolutionError::SubSupConflict(
21                 _,
22                 var_origin,
23                 sub_origin,
24                 sub_r,
25                 sup_origin,
26                 sup_r,
27             ) if **sub_r == RegionKind::ReStatic => {
28                 (var_origin, sub_origin, sub_r, sup_origin, sup_r)
29             }
30             RegionResolutionError::ConcreteFailure(
31                 SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
32                 sub_r,
33                 sup_r,
34             ) if **sub_r == RegionKind::ReStatic => {
35                 // This is for the implicit `'static` requirement coming from `impl dyn Trait {}`.
36                 if let ObligationCauseCode::UnifyReceiver(assoc) = &cause.code {
37                     let param = self.find_param_with_region(sup_r, sub_r)?;
38                     let lifetime = if sup_r.has_name() {
39                         format!("lifetime `{}`", sup_r)
40                     } else {
41                         "an anonymous lifetime `'_`".to_string()
42                     };
43                     let mut err = struct_span_err!(
44                         tcx.sess,
45                         cause.span,
46                         E0759,
47                         "cannot infer an appropriate lifetime"
48                     );
49                     err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
50                     err.span_label(
51                         cause.span,
52                         "...is captured and required to live as long as `'static` here",
53                     );
54                     if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &assoc.container) {
55                         err.emit();
56                         return Some(ErrorReported);
57                     } else {
58                         err.cancel();
59                     }
60                 }
61                 return None;
62             }
63             _ => return None,
64         };
65         debug!(
66             "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
67             var_origin, sub_origin, sub_r, sup_origin, sup_r
68         );
69         let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
70         debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
71         let sp = var_origin.span();
72         let return_sp = sub_origin.span();
73         let param = self.find_param_with_region(sup_r, sub_r)?;
74         let (lifetime_name, lifetime) = if sup_r.has_name() {
75             (sup_r.to_string(), format!("lifetime `{}`", sup_r))
76         } else {
77             ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
78         };
79         let mut err = struct_span_err!(tcx.sess, sp, E0759, "cannot infer an appropriate lifetime");
80         err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
81         debug!("try_report_static_impl_trait: param_info={:?}", param);
82
83         // We try to make the output have fewer overlapping spans if possible.
84         if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span()))
85             && sup_origin.span() != return_sp
86         {
87             // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs`
88
89             // Customize the spans and labels depending on their relative order so
90             // that split sentences flow correctly.
91             if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() {
92                 // Avoid the following:
93                 //
94                 // error: cannot infer an appropriate lifetime
95                 //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
96                 //    |
97                 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
98                 //    |           ----                      ---------^-
99                 //
100                 // and instead show:
101                 //
102                 // error: cannot infer an appropriate lifetime
103                 //   --> $DIR/must_outlive_least_region_or_bound.rs:18:50
104                 //    |
105                 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
106                 //    |           ----                               ^
107                 err.span_label(
108                     sup_origin.span(),
109                     "...is captured here, requiring it to live as long as `'static`",
110                 );
111             } else {
112                 err.span_label(sup_origin.span(), "...is captured here...");
113                 if return_sp < sup_origin.span() {
114                     err.span_note(
115                         return_sp,
116                         "...and is required to live as long as `'static` here",
117                     );
118                 } else {
119                     err.span_label(
120                         return_sp,
121                         "...and is required to live as long as `'static` here",
122                     );
123                 }
124             }
125         } else {
126             err.span_label(
127                 return_sp,
128                 "...is captured and required to live as long as `'static` here",
129             );
130         }
131
132         if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin {
133             if let ObligationCauseCode::UnifyReceiver(assoc) = &cause.code {
134                 self.find_impl_on_dyn_trait(&mut err, param.param_ty, &assoc.container);
135             }
136         }
137
138         let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
139         debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
140         // FIXME: account for the need of parens in `&(dyn Trait + '_)`
141         let consider = "consider changing the";
142         let declare = "to declare that the";
143         let arg = match param.param.pat.simple_ident() {
144             Some(simple_ident) => format!("argument `{}`", simple_ident),
145             None => "the argument".to_string(),
146         };
147         let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name);
148         let explicit_static = format!("explicit `'static` bound to the lifetime of {}", arg);
149         let captures = format!("captures data from {}", arg);
150         let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
151         let plus_lt = format!(" + {}", lifetime_name);
152         for fn_return in fn_returns {
153             if fn_return.span.desugaring_kind().is_some() {
154                 // Skip `async` desugaring `impl Future`.
155                 continue;
156             }
157             match fn_return.kind {
158                 TyKind::OpaqueDef(item_id, _) => {
159                     let item = tcx.hir().item(item_id.id);
160                     let opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind {
161                         opaque
162                     } else {
163                         err.emit();
164                         return Some(ErrorReported);
165                     };
166
167                     if let Some(span) = opaque
168                         .bounds
169                         .iter()
170                         .filter_map(|arg| match arg {
171                             GenericBound::Outlives(Lifetime {
172                                 name: LifetimeName::Static,
173                                 span,
174                                 ..
175                             }) => Some(*span),
176                             _ => None,
177                         })
178                         .next()
179                     {
180                         err.span_suggestion_verbose(
181                             span,
182                             &format!("{} `impl Trait`'s {}", consider, explicit_static),
183                             lifetime_name.clone(),
184                             Applicability::MaybeIncorrect,
185                         );
186                         err.span_suggestion_verbose(
187                             param.param_ty_span,
188                             add_static_bound,
189                             param.param_ty.to_string(),
190                             Applicability::MaybeIncorrect,
191                         );
192                     } else if let Some(_) = opaque
193                         .bounds
194                         .iter()
195                         .filter_map(|arg| match arg {
196                             GenericBound::Outlives(Lifetime { name, span, .. })
197                                 if name.ident().to_string() == lifetime_name =>
198                             {
199                                 Some(*span)
200                             }
201                             _ => None,
202                         })
203                         .next()
204                     {
205                     } else {
206                         err.span_suggestion_verbose(
207                             fn_return.span.shrink_to_hi(),
208                             &format!(
209                                 "{declare} `impl Trait` {captures}, {explicit}",
210                                 declare = declare,
211                                 captures = captures,
212                                 explicit = explicit,
213                             ),
214                             plus_lt.clone(),
215                             Applicability::MaybeIncorrect,
216                         );
217                     }
218                 }
219                 TyKind::TraitObject(_, lt) => match lt.name {
220                     LifetimeName::ImplicitObjectLifetimeDefault => {
221                         err.span_suggestion_verbose(
222                             fn_return.span.shrink_to_hi(),
223                             &format!(
224                                 "{declare} trait object {captures}, {explicit}",
225                                 declare = declare,
226                                 captures = captures,
227                                 explicit = explicit,
228                             ),
229                             plus_lt.clone(),
230                             Applicability::MaybeIncorrect,
231                         );
232                     }
233                     name if name.ident().to_string() != lifetime_name => {
234                         // With this check we avoid suggesting redundant bounds. This
235                         // would happen if there are nested impl/dyn traits and only
236                         // one of them has the bound we'd suggest already there, like
237                         // in `impl Foo<X = dyn Bar> + '_`.
238                         err.span_suggestion_verbose(
239                             lt.span,
240                             &format!("{} trait object's {}", consider, explicit_static),
241                             lifetime_name.clone(),
242                             Applicability::MaybeIncorrect,
243                         );
244                         err.span_suggestion_verbose(
245                             param.param_ty_span,
246                             add_static_bound,
247                             param.param_ty.to_string(),
248                             Applicability::MaybeIncorrect,
249                         );
250                     }
251                     _ => {}
252                 },
253                 _ => {}
254             }
255         }
256         err.emit();
257         Some(ErrorReported)
258     }
259
260     /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
261     /// `'static` obligation. Find `impl` blocks that are implemented
262     fn find_impl_on_dyn_trait(
263         &self,
264         err: &mut DiagnosticBuilder<'_>,
265         ty: Ty<'_>,
266         container: &AssocItemContainer,
267     ) -> bool {
268         let tcx = self.tcx();
269         let mut suggested = false;
270
271         // Find the trait object types in the argument.
272         let mut v = TraitObjectVisitor(vec![]);
273         v.visit_ty(ty);
274
275         let container_id = match container {
276             // When the obligation comes from an `impl Foo for dyn Bar {}`, we
277             // have the `DefId` of the `trait` itself, not the relevant `impl`
278             // block. Because of this, we have to look at all the `trait`s
279             // available, and filter out all that are not of `Foo` (this `def_id`)
280             // and not of `Bar` (the `filter_map` later in this method).
281             AssocItemContainer::TraitContainer(def_id) => def_id,
282
283             // When the obligation comes from an `impl dyn Trait {}`, we already
284             // have the `DefId` of the relevant `Item`, so we use it directly.
285             AssocItemContainer::ImplContainer(def_id) => {
286                 if let Some(Node::Item(Item { kind: ItemKind::Impl { self_ty, .. }, .. })) =
287                     tcx.hir().get_if_local(*def_id)
288                 {
289                     for found_did in &v.0 {
290                         let mut hir_v = HirTraitObjectVisitor(vec![], *found_did);
291                         hir_v.visit_ty(self_ty);
292                         if let [span] = &hir_v.0[..] {
293                             err.span_suggestion_verbose(
294                                 span.shrink_to_hi(),
295                                 "this `impl` introduces an implicit `'static` requirement, \
296                                  consider changing it",
297                                 " + '_".to_string(),
298                                 Applicability::MaybeIncorrect,
299                             );
300                             suggested = true;
301                         }
302                     }
303                 }
304                 return suggested;
305             }
306         };
307
308         // Find all the `impl`s in the local scope that can be called on the type parameter. And
309         // retain all that are `impl`s of the trait that originated the `'static` obligation.
310         // This doesn't find `impl dyn Trait { /**/ }`, but that case is handled above.
311         let impl_self_tys = tcx
312             .all_traits(LOCAL_CRATE)
313             .iter()
314             .flat_map(|trait_did| tcx.hir().trait_impls(*trait_did))
315             .filter_map(|impl_node| {
316                 let impl_did = tcx.hir().local_def_id(*impl_node);
317                 match tcx.hir().get_if_local(impl_did.to_def_id()) {
318                     Some(Node::Item(Item {
319                         kind: ItemKind::Impl { self_ty, of_trait: Some(of_trait), .. },
320                         ..
321                     })) if of_trait.trait_def_id() == Some(*container_id) => Some(self_ty),
322                     _ => None,
323                 }
324             });
325
326         // Given all the `impl`s of the relevant `trait`, look for those that are implemented for
327         // the trait object in the `fn` parameter type.
328         for self_ty in impl_self_tys {
329             for found_did in &v.0 {
330                 let mut hir_v = HirTraitObjectVisitor(vec![], *found_did);
331                 hir_v.visit_ty(self_ty);
332                 if let [span] = &hir_v.0[..] {
333                     err.span_suggestion_verbose(
334                         span.shrink_to_hi(),
335                         "this `impl` introduces an implicit `'static` requirement, \
336                             consider changing it",
337                         " + '_".to_string(),
338                         Applicability::MaybeIncorrect,
339                     );
340                     suggested = true;
341                 }
342             }
343         }
344         suggested
345     }
346 }
347
348 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
349 struct TraitObjectVisitor(Vec<DefId>);
350
351 impl TypeVisitor<'_> for TraitObjectVisitor {
352     fn visit_ty(&mut self, t: Ty<'_>) -> bool {
353         match t.kind {
354             ty::Dynamic(preds, RegionKind::ReStatic) => {
355                 if let Some(def_id) = preds.principal_def_id() {
356                     self.0.push(def_id);
357                 }
358                 false
359             }
360             _ => t.super_visit_with(self),
361         }
362     }
363 }
364
365 /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
366 struct HirTraitObjectVisitor(Vec<Span>, DefId);
367
368 impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
369     type Map = ErasedMap<'tcx>;
370
371     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
372         NestedVisitorMap::None
373     }
374
375     fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
376         match t.kind {
377             TyKind::TraitObject(
378                 poly_trait_refs,
379                 Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
380             ) => {
381                 for ptr in poly_trait_refs {
382                     if Some(self.1) == ptr.trait_ref.trait_def_id() {
383                         self.0.push(ptr.span);
384                     }
385                 }
386             }
387             _ => {}
388         }
389         walk_ty(self, t);
390     }
391 }