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