]> git.proxmox.com Git - rustc.git/blame - src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_infer / infer / error_reporting / nice_region_error / static_impl_trait.rs
CommitLineData
8faf50e0
XL
1//! Error Reporting for static impl Traits.
2
9fa01778
XL
3use crate::infer::error_reporting::nice_region_error::NiceRegionError;
4use crate::infer::lexical_region_resolve::RegionResolutionError;
3dfed10e
XL
5use crate::infer::{SubregionOrigin, TypeTrace};
6use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
7use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
8use rustc_hir::def_id::DefId;
9use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor};
10use rustc_hir::{
11 self as hir, GenericBound, ImplItem, Item, ItemKind, Lifetime, LifetimeName, Node, TraitItem,
12 TyKind,
13};
14use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, TypeVisitor};
15use rustc_span::symbol::Ident;
16use rustc_span::{MultiSpan, Span};
8faf50e0 17
dc9dc135 18impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
3dfed10e
XL
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.
8faf50e0 21 pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorReported> {
f035d41b 22 debug!("try_report_static_impl_trait(error={:?})", self.error);
3dfed10e
XL
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 }
f035d41b
XL
84 return None;
85 }
3dfed10e
XL
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 // | ---- ^
f035d41b 143 err.span_label(
3dfed10e
XL
144 sup_origin.span(),
145 "...is captured here, requiring it to live as long as `'static`",
f035d41b 146 );
3dfed10e
XL
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);
0bf4aa26 169
3dfed10e
XL
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[..])
f035d41b 189 {
3dfed10e
XL
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,
f9f354fc 259 );
3dfed10e
XL
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 {
f035d41b 274 } else {
3dfed10e
XL
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 );
f9f354fc 286 }
f035d41b 287 }
3dfed10e
XL
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 }
f9f354fc 328
3dfed10e
XL
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))
f035d41b 340 }
3dfed10e
XL
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, .. },
f035d41b 361 ..
3dfed10e
XL
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 }) =>
f035d41b 373 {
3dfed10e 374 Some(self_ty)
f035d41b
XL
375 }
376 _ => None,
3dfed10e
XL
377 }
378 })
379 .next()
380 {
381 Some(self_ty) => Some((*ident, self_ty)),
382 _ => None,
f035d41b 383 }
3dfed10e
XL
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.
464struct TraitObjectVisitor(Vec<DefId>);
465
466impl 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.
481struct HirTraitObjectVisitor(Vec<Span>, DefId);
482
483impl<'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);
8faf50e0
XL
499 }
500 }
8faf50e0 501 }
3dfed10e 502 _ => {}
8faf50e0 503 }
3dfed10e 504 walk_ty(self, t);
8faf50e0
XL
505 }
506}