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