11//! Error reporting machinery for lifetime errors.
22
3- use rustc_errors:: { Diagnostic , DiagnosticBuilder , ErrorGuaranteed } ;
3+ use rustc_data_structures:: stable_set:: FxHashSet ;
4+ use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed , MultiSpan } ;
5+ use rustc_hir:: def_id:: DefId ;
6+ use rustc_hir:: intravisit:: Visitor ;
7+ use rustc_hir:: { self as hir, Item , ItemKind , Node } ;
48use rustc_infer:: infer:: {
59 error_reporting:: nice_region_error:: {
610 self , find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
7- NiceRegionError ,
11+ HirTraitObjectVisitor , NiceRegionError , TraitObjectVisitor ,
812 } ,
913 error_reporting:: unexpected_hidden_region_diagnostic,
1014 NllRegionVariableOrigin , RelateParamBound ,
1115} ;
1216use rustc_middle:: hir:: place:: PlaceBase ;
1317use rustc_middle:: mir:: { ConstraintCategory , ReturnConstraint } ;
1418use rustc_middle:: ty:: subst:: InternalSubsts ;
19+ use rustc_middle:: ty:: Region ;
20+ use rustc_middle:: ty:: TypeVisitor ;
1521use rustc_middle:: ty:: { self , RegionVid , Ty } ;
1622use rustc_span:: symbol:: sym;
23+ use rustc_span:: symbol:: Ident ;
1724use rustc_span:: Span ;
1825
1926use crate :: borrowck_errors;
@@ -27,7 +34,7 @@ use crate::{
2734 MirBorrowckCtxt ,
2835} ;
2936
30- impl ConstraintDescription for ConstraintCategory {
37+ impl < ' tcx > ConstraintDescription for ConstraintCategory < ' tcx > {
3138 fn description ( & self ) -> & ' static str {
3239 // Must end with a space. Allows for empty names to be provided.
3340 match self {
@@ -37,7 +44,7 @@ impl ConstraintDescription for ConstraintCategory {
3744 ConstraintCategory :: UseAsConst => "using this value as a constant " ,
3845 ConstraintCategory :: UseAsStatic => "using this value as a static " ,
3946 ConstraintCategory :: Cast => "cast " ,
40- ConstraintCategory :: CallArgument => "argument " ,
47+ ConstraintCategory :: CallArgument ( _ ) => "argument " ,
4148 ConstraintCategory :: TypeAnnotation => "type annotation " ,
4249 ConstraintCategory :: ClosureBounds => "closure body " ,
4350 ConstraintCategory :: SizedBound => "proving this value is `Sized` " ,
@@ -101,15 +108,15 @@ pub(crate) enum RegionErrorKind<'tcx> {
101108
102109/// Information about the various region constraints involved in a borrow checker error.
103110#[ derive( Clone , Debug ) ]
104- pub struct ErrorConstraintInfo {
111+ pub struct ErrorConstraintInfo < ' tcx > {
105112 // fr: outlived_fr
106113 pub ( super ) fr : RegionVid ,
107114 pub ( super ) fr_is_local : bool ,
108115 pub ( super ) outlived_fr : RegionVid ,
109116 pub ( super ) outlived_fr_is_local : bool ,
110117
111118 // Category and span for best blame constraint
112- pub ( super ) category : ConstraintCategory ,
119+ pub ( super ) category : ConstraintCategory < ' tcx > ,
113120 pub ( super ) span : Span ,
114121}
115122
@@ -256,6 +263,70 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
256263 outlives_suggestion. add_suggestion ( self ) ;
257264 }
258265
266+ fn get_impl_ident_and_self_ty_from_trait (
267+ & self ,
268+ def_id : DefId ,
269+ trait_objects : & FxHashSet < DefId > ,
270+ ) -> Option < ( Ident , & ' tcx hir:: Ty < ' tcx > ) > {
271+ let tcx = self . infcx . tcx ;
272+ match tcx. hir ( ) . get_if_local ( def_id) {
273+ Some ( Node :: ImplItem ( impl_item) ) => {
274+ match tcx. hir ( ) . find_by_def_id ( tcx. hir ( ) . get_parent_item ( impl_item. hir_id ( ) ) ) {
275+ Some ( Node :: Item ( Item {
276+ kind : ItemKind :: Impl ( hir:: Impl { self_ty, .. } ) ,
277+ ..
278+ } ) ) => Some ( ( impl_item. ident , self_ty) ) ,
279+ _ => None ,
280+ }
281+ }
282+ Some ( Node :: TraitItem ( trait_item) ) => {
283+ let trait_did = tcx. hir ( ) . get_parent_item ( trait_item. hir_id ( ) ) ;
284+ match tcx. hir ( ) . find_by_def_id ( trait_did) {
285+ Some ( Node :: Item ( Item { kind : ItemKind :: Trait ( ..) , .. } ) ) => {
286+ // The method being called is defined in the `trait`, but the `'static`
287+ // obligation comes from the `impl`. Find that `impl` so that we can point
288+ // at it in the suggestion.
289+ let trait_did = trait_did. to_def_id ( ) ;
290+ match tcx
291+ . hir ( )
292+ . trait_impls ( trait_did)
293+ . iter ( )
294+ . filter_map ( |& impl_did| {
295+ match tcx. hir ( ) . get_if_local ( impl_did. to_def_id ( ) ) {
296+ Some ( Node :: Item ( Item {
297+ kind : ItemKind :: Impl ( hir:: Impl { self_ty, .. } ) ,
298+ ..
299+ } ) ) if trait_objects. iter ( ) . all ( |did| {
300+ // FIXME: we should check `self_ty` against the receiver
301+ // type in the `UnifyReceiver` context, but for now, use
302+ // this imperfect proxy. This will fail if there are
303+ // multiple `impl`s for the same trait like
304+ // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
305+ // In that case, only the first one will get suggestions.
306+ let mut traits = vec ! [ ] ;
307+ let mut hir_v = HirTraitObjectVisitor ( & mut traits, * did) ;
308+ hir_v. visit_ty ( self_ty) ;
309+ !traits. is_empty ( )
310+ } ) =>
311+ {
312+ Some ( self_ty)
313+ }
314+ _ => None ,
315+ }
316+ } )
317+ . next ( )
318+ {
319+ Some ( self_ty) => Some ( ( trait_item. ident , self_ty) ) ,
320+ _ => None ,
321+ }
322+ }
323+ _ => None ,
324+ }
325+ }
326+ _ => None ,
327+ }
328+ }
329+
259330 /// Report an error because the universal region `fr` was required to outlive
260331 /// `outlived_fr` but it is not known to do so. For example:
261332 ///
@@ -279,6 +350,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
279350 } ) ;
280351
281352 debug ! ( "report_region_error: category={:?} {:?} {:?}" , category, cause, variance_info) ;
353+
282354 // Check if we can use one of the "nice region errors".
283355 if let ( Some ( f) , Some ( o) ) = ( self . to_error_region ( fr) , self . to_error_region ( outlived_fr) ) {
284356 let nice = NiceRegionError :: new_from_span ( self . infcx , cause. span , o, f) ;
@@ -312,7 +384,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
312384 self . report_fnmut_error ( & errci, kind)
313385 }
314386 ( ConstraintCategory :: Assignment , true , false )
315- | ( ConstraintCategory :: CallArgument , true , false ) => {
387+ | ( ConstraintCategory :: CallArgument ( _ ) , true , false ) => {
316388 let mut db = self . report_escaping_data_error ( & errci) ;
317389
318390 outlives_suggestion. intermediate_suggestion ( self , & errci, & mut db) ;
@@ -405,7 +477,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
405477 /// ```
406478 fn report_fnmut_error (
407479 & self ,
408- errci : & ErrorConstraintInfo ,
480+ errci : & ErrorConstraintInfo < ' tcx > ,
409481 kind : ReturnConstraint ,
410482 ) -> DiagnosticBuilder < ' tcx , ErrorGuaranteed > {
411483 let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
@@ -486,7 +558,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
486558 /// ```
487559 fn report_escaping_data_error (
488560 & self ,
489- errci : & ErrorConstraintInfo ,
561+ errci : & ErrorConstraintInfo < ' tcx > ,
490562 ) -> DiagnosticBuilder < ' tcx , ErrorGuaranteed > {
491563 let ErrorConstraintInfo { span, category, .. } = errci;
492564
@@ -548,24 +620,28 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
548620 // Only show an extra note if we can find an 'error region' for both of the region
549621 // variables. This avoids showing a noisy note that just mentions 'synthetic' regions
550622 // that don't help the user understand the error.
551- if self . to_error_region ( errci. fr ) . is_some ( )
552- && self . to_error_region ( errci. outlived_fr ) . is_some ( )
553- {
554- let fr_region_name = self . give_region_a_name ( errci. fr ) . unwrap ( ) ;
555- fr_region_name. highlight_region_name ( & mut diag) ;
556- let outlived_fr_region_name = self . give_region_a_name ( errci. outlived_fr ) . unwrap ( ) ;
557- outlived_fr_region_name. highlight_region_name ( & mut diag) ;
623+ match ( self . to_error_region ( errci. fr ) , self . to_error_region ( errci. outlived_fr ) ) {
624+ ( Some ( f) , Some ( o) ) => {
625+ self . maybe_suggest_constrain_dyn_trait_impl ( & mut diag, f, o, category) ;
558626
559- diag. span_label (
560- * span,
561- format ! (
562- "{}requires that `{}` must outlive `{}`" ,
563- category. description( ) ,
564- fr_region_name,
565- outlived_fr_region_name,
566- ) ,
567- ) ;
627+ let fr_region_name = self . give_region_a_name ( errci. fr ) . unwrap ( ) ;
628+ fr_region_name. highlight_region_name ( & mut diag) ;
629+ let outlived_fr_region_name = self . give_region_a_name ( errci. outlived_fr ) . unwrap ( ) ;
630+ outlived_fr_region_name. highlight_region_name ( & mut diag) ;
631+
632+ diag. span_label (
633+ * span,
634+ format ! (
635+ "{}requires that `{}` must outlive `{}`" ,
636+ category. description( ) ,
637+ fr_region_name,
638+ outlived_fr_region_name,
639+ ) ,
640+ ) ;
641+ }
642+ _ => { }
568643 }
644+
569645 diag
570646 }
571647
@@ -586,7 +662,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
586662 /// ```
587663 fn report_general_error (
588664 & self ,
589- errci : & ErrorConstraintInfo ,
665+ errci : & ErrorConstraintInfo < ' tcx > ,
590666 ) -> DiagnosticBuilder < ' tcx , ErrorGuaranteed > {
591667 let ErrorConstraintInfo {
592668 fr,
@@ -699,6 +775,99 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
699775 }
700776 }
701777
778+ fn maybe_suggest_constrain_dyn_trait_impl (
779+ & self ,
780+ diag : & mut DiagnosticBuilder < ' tcx , ErrorGuaranteed > ,
781+ f : Region < ' tcx > ,
782+ o : Region < ' tcx > ,
783+ category : & ConstraintCategory < ' tcx > ,
784+ ) {
785+ if !o. is_static ( ) {
786+ return ;
787+ }
788+
789+ let tcx = self . infcx . tcx ;
790+
791+ let instance = if let ConstraintCategory :: CallArgument ( Some ( ( fn_did, substs) ) ) = category {
792+ debug ! ( ?fn_did, ?substs) ;
793+
794+ // Only suggest this on function calls, not closures
795+ let ty = tcx. type_of ( fn_did) ;
796+ debug ! ( "ty: {:?}, ty.kind: {:?}" , ty, ty. kind( ) ) ;
797+ if let ty:: Closure ( _, _) = ty. kind ( ) {
798+ return ;
799+ }
800+
801+ if let Ok ( Some ( instance) ) = ty:: Instance :: resolve (
802+ tcx,
803+ self . param_env ,
804+ * fn_did,
805+ self . infcx . resolve_vars_if_possible ( substs) ,
806+ ) {
807+ instance
808+ } else {
809+ return ;
810+ }
811+ } else {
812+ return ;
813+ } ;
814+
815+ let param = match find_param_with_region ( tcx, f, o) {
816+ Some ( param) => param,
817+ None => return ,
818+ } ;
819+ debug ! ( ?param) ;
820+
821+ let mut visitor = TraitObjectVisitor ( FxHashSet :: default ( ) ) ;
822+ visitor. visit_ty ( param. param_ty ) ;
823+
824+ if let Some ( ( ident, self_ty) ) =
825+ self . get_impl_ident_and_self_ty_from_trait ( instance. def_id ( ) , & visitor. 0 )
826+ {
827+ self . suggest_constrain_dyn_trait_in_impl ( diag, & visitor. 0 , ident, self_ty)
828+ } else {
829+ return ;
830+ } ;
831+ }
832+
833+ #[ instrument( skip( self , err) , level = "debug" ) ]
834+ fn suggest_constrain_dyn_trait_in_impl (
835+ & self ,
836+ err : & mut Diagnostic ,
837+ found_dids : & FxHashSet < DefId > ,
838+ ident : Ident ,
839+ self_ty : & hir:: Ty < ' _ > ,
840+ ) -> bool {
841+ debug ! ( "err: {:#?}" , err) ;
842+ let mut suggested = false ;
843+ for found_did in found_dids {
844+ let mut traits = vec ! [ ] ;
845+ let mut hir_v = HirTraitObjectVisitor ( & mut traits, * found_did) ;
846+ hir_v. visit_ty ( & self_ty) ;
847+ debug ! ( "trait spans found: {:?}" , traits) ;
848+ for span in & traits {
849+ let mut multi_span: MultiSpan = vec ! [ * span] . into ( ) ;
850+ multi_span. push_span_label (
851+ * span,
852+ "this has an implicit `'static` lifetime requirement" . to_string ( ) ,
853+ ) ;
854+ multi_span. push_span_label (
855+ ident. span ,
856+ "calling this method introduces the `impl`'s 'static` requirement" . to_string ( ) ,
857+ ) ;
858+ err. span_note ( multi_span, "the used `impl` has a `'static` requirement" ) ;
859+ err. span_suggestion_verbose (
860+ span. shrink_to_hi ( ) ,
861+ "consider relaxing the implicit `'static` requirement" ,
862+ " + '_" . to_string ( ) ,
863+ Applicability :: MaybeIncorrect ,
864+ ) ;
865+ suggested = true ;
866+ }
867+ }
868+ suggested
869+ }
870+
702871 fn suggest_adding_lifetime_params (
703872 & self ,
704873 diag : & mut Diagnostic ,
0 commit comments