1 //! Error Reporting for static impl Traits.
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};
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);
19 let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? {
20 RegionResolutionError::SubSupConflict(
27 ) if **sub_r == RegionKind::ReStatic => {
28 (var_origin, sub_origin, sub_r, sup_origin, sup_r)
30 RegionResolutionError::ConcreteFailure(
31 SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
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)
41 "an anonymous lifetime `'_`".to_string()
43 let mut err = struct_span_err!(
47 "cannot infer an appropriate lifetime"
49 err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime));
52 "...is captured and required to live as long as `'static` here",
54 if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &assoc.container) {
56 return Some(ErrorReported);
66 "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
67 var_origin, sub_origin, sub_r, sup_origin, sup_r
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))
77 ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
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);
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
87 // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs`
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:
94 // error: cannot infer an appropriate lifetime
95 // --> $DIR/must_outlive_least_region_or_bound.rs:18:50
97 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
102 // error: cannot infer an appropriate lifetime
103 // --> $DIR/must_outlive_least_region_or_bound.rs:18:50
105 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
109 "...is captured here, requiring it to live as long as `'static`",
112 err.span_label(sup_origin.span(), "...is captured here...");
113 if return_sp < sup_origin.span() {
116 "...and is required to live as long as `'static` here",
121 "...and is required to live as long as `'static` here",
128 "...is captured and required to live as long as `'static` here",
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);
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(),
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`.
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 {
164 return Some(ErrorReported);
167 if let Some(span) = opaque
170 .filter_map(|arg| match arg {
171 GenericBound::Outlives(Lifetime {
172 name: LifetimeName::Static,
180 err.span_suggestion_verbose(
182 &format!("{} `impl Trait`'s {}", consider, explicit_static),
183 lifetime_name.clone(),
184 Applicability::MaybeIncorrect,
186 err.span_suggestion_verbose(
189 param.param_ty.to_string(),
190 Applicability::MaybeIncorrect,
192 } else if let Some(_) = opaque
195 .filter_map(|arg| match arg {
196 GenericBound::Outlives(Lifetime { name, span, .. })
197 if name.ident().to_string() == lifetime_name =>
206 err.span_suggestion_verbose(
207 fn_return.span.shrink_to_hi(),
209 "{declare} `impl Trait` {captures}, {explicit}",
215 Applicability::MaybeIncorrect,
219 TyKind::TraitObject(_, lt) => match lt.name {
220 LifetimeName::ImplicitObjectLifetimeDefault => {
221 err.span_suggestion_verbose(
222 fn_return.span.shrink_to_hi(),
224 "{declare} trait object {captures}, {explicit}",
230 Applicability::MaybeIncorrect,
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(
240 &format!("{} trait object's {}", consider, explicit_static),
241 lifetime_name.clone(),
242 Applicability::MaybeIncorrect,
244 err.span_suggestion_verbose(
247 param.param_ty.to_string(),
248 Applicability::MaybeIncorrect,
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(
264 err: &mut DiagnosticBuilder<'_>,
266 container: &AssocItemContainer,
268 let tcx = self.tcx();
269 let mut suggested = false;
271 // Find the trait object types in the argument.
272 let mut v = TraitObjectVisitor(vec![]);
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,
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)
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(
295 "this `impl` introduces an implicit `'static` requirement, \
296 consider changing it",
298 Applicability::MaybeIncorrect,
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)
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), .. },
321 })) if of_trait.trait_def_id() == Some(*container_id) => Some(self_ty),
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(
335 "this `impl` introduces an implicit `'static` requirement, \
336 consider changing it",
338 Applicability::MaybeIncorrect,
348 /// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
349 struct TraitObjectVisitor(Vec<DefId>);
351 impl TypeVisitor<'_> for TraitObjectVisitor {
352 fn visit_ty(&mut self, t: Ty<'_>) -> bool {
354 ty::Dynamic(preds, RegionKind::ReStatic) => {
355 if let Some(def_id) = preds.principal_def_id() {
360 _ => t.super_visit_with(self),
365 /// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
366 struct HirTraitObjectVisitor(Vec<Span>, DefId);
368 impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor {
369 type Map = ErasedMap<'tcx>;
371 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
372 NestedVisitorMap::None
375 fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
379 Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
381 for ptr in poly_trait_refs {
382 if Some(self.1) == ptr.trait_ref.trait_def_id() {
383 self.0.push(ptr.span);