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, 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
,
15 use rustc_span
::symbol
::Ident
;
16 use rustc_span
::{MultiSpan, Span}
;
18 use std
::ops
::ControlFlow
;
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
);
26 let (var_origin
, sub_origin
, sub_r
, sup_origin
, sup_r
) = match self.error
.as_ref()?
{
27 RegionResolutionError
::SubSupConflict(
34 ) if **sub_r
== RegionKind
::ReStatic
=> {
35 (var_origin
, sub_origin
, sub_r
, sup_origin
, sup_r
)
37 RegionResolutionError
::ConcreteFailure(
38 SubregionOrigin
::Subtype(box TypeTrace { cause, .. }
),
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() {
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
)
56 "an anonymous lifetime `'_`".to_string()
58 let mut err
= struct_span_err
!(
62 "{} has {} but calling `{}` introduces an implicit `'static` lifetime \
68 .map(|s
| format
!("`{}`", s
))
69 .unwrap_or_else(|| "`fn` parameter".to_string()),
71 ctxt
.assoc_item
.ident
,
73 err
.span_label(param
.param_ty_span
, &format
!("this data with {}...", lifetime
));
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(),
87 if self.find_impl_on_dyn_trait(&mut err
, param
.param_ty
, &ctxt
) {
89 return Some(ErrorReported
);
99 "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
100 var_origin
, sub_origin
, sub_r
, sup_origin
, sup_r
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
))
110 ("'_".to_owned(), "an anonymous lifetime `'_`".to_string())
112 let param_name
= param
116 .map(|s
| format
!("`{}`", s
))
117 .unwrap_or_else(|| "`fn` parameter".to_string());
118 let mut err
= struct_span_err
!(
122 "{} has {} but it needs to satisfy a `'static` lifetime requirement",
126 err
.span_label(param
.param_ty_span
, &format
!("this data with {}...", lifetime
));
127 debug
!("try_report_static_impl_trait: param_info={:?}", param
);
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
133 // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs`
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:
140 // error: cannot infer an appropriate lifetime
141 // --> $DIR/must_outlive_least_region_or_bound.rs:18:50
143 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
144 // | ---- ---------^-
148 // error: cannot infer an appropriate lifetime
149 // --> $DIR/must_outlive_least_region_or_bound.rs:18:50
151 // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) }
155 "...is captured here, requiring it to live as long as `'static`",
158 err
.span_label(sup_origin
.span(), "...is captured here...");
159 if return_sp
< sup_origin
.span() {
162 "...and is required to live as long as `'static` here",
167 "...and is required to live as long as `'static` here",
174 "...is captured and required to live as long as `'static` here",
178 let fn_returns
= tcx
.return_type_impl_or_dyn_traits(anon_reg_sup
.def_id
);
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
);
190 if let SubregionOrigin
::Subtype(box TypeTrace { cause, .. }
) = &sub_origin
{
191 let code
= match &cause
.code
{
192 ObligationCauseCode
::MatchImpl(parent
, ..) => &**parent
,
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:
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)
204 if self.suggest_constrain_dyn_trait_in_impl(&mut err
, &v
.0, ident
, self_ty
) {
205 override_error_code
= Some(ident
);
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 \
216 param_name
, lifetime
, ident
,
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(),
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`.
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
{
245 return Some(ErrorReported
);
248 if let Some(span
) = opaque
251 .filter_map(|arg
| match arg
{
252 GenericBound
::Outlives(Lifetime
{
253 name
: LifetimeName
::Static
,
261 err
.span_suggestion_verbose(
263 &format
!("{} `impl Trait`'s {}", consider
, explicit_static
),
264 lifetime_name
.clone(),
265 Applicability
::MaybeIncorrect
,
267 err
.span_suggestion_verbose(
270 param
.param_ty
.to_string(),
271 Applicability
::MaybeIncorrect
,
276 .filter_map(|arg
| match arg
{
277 GenericBound
::Outlives(Lifetime { name, span, .. }
)
278 if name
.ident().to_string() == lifetime_name
=>
288 err
.span_suggestion_verbose(
289 fn_return
.span
.shrink_to_hi(),
291 "{declare} `impl Trait` {captures}, {explicit}",
297 Applicability
::MaybeIncorrect
,
301 TyKind
::TraitObject(_
, lt
, _
) => match lt
.name
{
302 LifetimeName
::ImplicitObjectLifetimeDefault
=> {
303 err
.span_suggestion_verbose(
304 fn_return
.span
.shrink_to_hi(),
306 "{declare} trait object {captures}, {explicit}",
312 Applicability
::MaybeIncorrect
,
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(
322 &format
!("{} trait object's {}", consider
, explicit_static
),
323 lifetime_name
.clone(),
324 Applicability
::MaybeIncorrect
,
326 err
.span_suggestion_verbose(
329 param
.param_ty
.to_string(),
330 Applicability
::MaybeIncorrect
,
342 fn get_impl_ident_and_self_ty_from_trait(
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, .. }
),
354 })) => Some((impl_item
.ident
, self_ty
)),
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();
368 .trait_impls(trait_did
)
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, .. }
),
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
);
395 Some(self_ty
) => Some((trait_item
.ident
, self_ty
)),
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(
410 err
: &mut DiagnosticBuilder
<'_
>,
412 ctxt
: &UnifyReceiverContext
<'tcx
>,
414 let tcx
= self.tcx();
416 // Find the method being called.
417 let instance
= match ty
::Instance
::resolve(
420 ctxt
.assoc_item
.def_id
,
421 self.infcx
.resolve_vars_if_possible(ctxt
.substs
),
423 Ok(Some(instance
)) => instance
,
427 let mut v
= TraitObjectVisitor(FxHashSet
::default());
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,
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
)
442 fn suggest_constrain_dyn_trait_in_impl(
444 err
: &mut DiagnosticBuilder
<'_
>,
445 found_dids
: &FxHashSet
<DefId
>,
447 self_ty
: &hir
::Ty
<'_
>,
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(
458 "this has an implicit `'static` lifetime requirement".to_string(),
460 multi_span
.push_span_label(
462 "calling this method introduces the `impl`'s 'static` requirement".to_string(),
464 err
.span_note(multi_span
, "the used `impl` has a `'static` requirement");
465 err
.span_suggestion_verbose(
467 "consider relaxing the implicit `'static` requirement",
469 Applicability
::MaybeIncorrect
,
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
>);
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.
488 fn visit_ty(&mut self, t
: Ty
<'tcx
>) -> ControlFlow
<Self::BreakTy
> {
490 ty
::Dynamic(preds
, RegionKind
::ReStatic
) => {
491 if let Some(def_id
) = preds
.principal_def_id() {
492 self.0.insert
(def_id
);
494 ControlFlow
::CONTINUE
496 _
=> t
.super_visit_with(self),
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
);
504 impl<'a
, 'tcx
> Visitor
<'tcx
> for HirTraitObjectVisitor
<'a
> {
505 type Map
= ErasedMap
<'tcx
>;
507 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
508 NestedVisitorMap
::None
511 fn visit_ty(&mut self, t
: &'tcx hir
::Ty
<'tcx
>) {
512 if let TyKind
::TraitObject(
514 Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. }
,
518 for ptr
in poly_trait_refs
{
519 if Some(self.1) == ptr
.trait_ref
.trait_def_id() {
520 self.0.push(ptr
.span
);