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