11//! Error reporting machinery for lifetime errors.
22
3+ use rustc_ast:: TraitObjectSyntax :: Dyn ;
34use rustc_data_structures:: fx:: FxIndexSet ;
45use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , MultiSpan } ;
56use rustc_hir as hir;
@@ -16,14 +17,12 @@ use rustc_infer::infer::{
1617 HirTraitObjectVisitor , NiceRegionError , TraitObjectVisitor ,
1718 } ,
1819 error_reporting:: unexpected_hidden_region_diagnostic,
19- NllRegionVariableOrigin , RelateParamBound ,
20+ BoundRegionConversionTime , NllRegionVariableOrigin , RelateParamBound ,
2021} ;
2122use rustc_middle:: hir:: place:: PlaceBase ;
2223use rustc_middle:: mir:: { ConstraintCategory , ReturnConstraint } ;
23- use rustc_middle:: ty:: GenericArgs ;
24- use rustc_middle:: ty:: TypeVisitor ;
25- use rustc_middle:: ty:: { self , RegionVid , Ty } ;
26- use rustc_middle:: ty:: { Region , TyCtxt } ;
24+ use rustc_middle:: traits:: ObligationCauseCode ;
25+ use rustc_middle:: ty:: { self , GenericArgs , Region , RegionVid , Ty , TyCtxt , TypeVisitor } ;
2726use rustc_span:: symbol:: { kw, Ident } ;
2827use rustc_span:: Span ;
2928
@@ -490,19 +489,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
490489 }
491490 } ;
492491
492+ self . explain_impl_static_obligation ( & mut diag, cause. code ( ) , outlived_fr) ;
493+
493494 match variance_info {
494495 ty:: VarianceDiagInfo :: None => { }
495496 ty:: VarianceDiagInfo :: Invariant { ty, param_index } => {
496497 let ( desc, note) = match ty. kind ( ) {
497498 ty:: RawPtr ( ty_mut) => {
498- assert_eq ! ( ty_mut. mutbl, rustc_hir :: Mutability :: Mut ) ;
499+ assert_eq ! ( ty_mut. mutbl, hir :: Mutability :: Mut ) ;
499500 (
500501 format ! ( "a mutable pointer to `{}`" , ty_mut. ty) ,
501502 "mutable pointers are invariant over their type parameter" . to_string ( ) ,
502503 )
503504 }
504505 ty:: Ref ( _, inner_ty, mutbl) => {
505- assert_eq ! ( * mutbl, rustc_hir :: Mutability :: Mut ) ;
506+ assert_eq ! ( * mutbl, hir :: Mutability :: Mut ) ;
506507 (
507508 format ! ( "a mutable reference to `{inner_ty}`" ) ,
508509 "mutable references are invariant over their type parameter"
@@ -518,10 +519,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
518519 let adt_desc = adt. descr ( ) ;
519520
520521 let desc = format ! (
521- "the type `{ty}`, which makes the generic argument `{generic_arg}` invariant"
522+ "the type `{ty}`, which makes the generic argument `{generic_arg}` \
523+ invariant"
522524 ) ;
523525 let note = format ! (
524- "the {adt_desc} `{base_ty}` is invariant over the parameter `{base_generic_arg}`"
526+ "the {adt_desc} `{base_ty}` is invariant over the parameter \
527+ `{base_generic_arg}`"
525528 ) ;
526529 ( desc, note)
527530 }
@@ -539,21 +542,225 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
539542 } ;
540543 diag. note ( format ! ( "requirement occurs because of {desc}" , ) ) ;
541544 diag. note ( note) ;
542- diag. help ( "see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance" ) ;
545+ diag. help (
546+ "see <https://doc.rust-lang.org/nomicon/subtyping.html> for more \
547+ information about variance",
548+ ) ;
543549 }
544550 }
545551
546552 for extra in extra_info {
547553 match extra {
548554 ExtraConstraintInfo :: PlaceholderFromPredicate ( span) => {
549- diag. span_note ( span, "due to current limitations in the borrow checker, this implies a `'static` lifetime" ) ;
555+ diag. span_note (
556+ span,
557+ "due to current limitations in the borrow checker, this implies a \
558+ `'static` lifetime",
559+ ) ;
550560 }
551561 }
552562 }
553563
554564 self . buffer_error ( diag) ;
555565 }
556566
567+ /// Report a specialized error when a `'static` obligation comes from an `impl dyn Trait`
568+ ///
569+ /// ```text
570+ /// error: lifetime may not live long enough
571+ /// --> $DIR/static-impl-obligation.rs:8:27
572+ /// |
573+ /// LL | fn bar<'a>(x: &'a &'a u32) {
574+ /// | -- lifetime `'a` defined here
575+ /// LL | let y: &dyn Foo = x;
576+ /// | ^ cast requires that `'a` must outlive `'static`
577+ /// LL | y.hello();
578+ /// | --------- calling this method introduces a `'static` lifetime requirement
579+ /// |
580+ /// note: the `impl` on `(dyn a::Foo + 'static)` has a `'static` lifetime requirement
581+ /// --> $DIR/static-impl-obligation.rs:4:10
582+ /// |
583+ /// LL | impl dyn Foo {
584+ /// | ^^^^^^^
585+ /// help: relax the implicit `'static` bound on the impl
586+ /// |
587+ /// LL | impl dyn Foo + '_ {
588+ /// | ++++
589+ /// ```
590+ /// ```text
591+ /// error: lifetime may not live long enough
592+ /// --> $DIR/static-impl-obligation.rs:173:27
593+ /// |
594+ /// LL | fn bar<'a>(x: &'a &'a u32) {
595+ /// | -- lifetime `'a` defined here
596+ /// LL | let y: &dyn Foo = x;
597+ /// | ^ cast requires that `'a` must outlive `'static`
598+ /// LL | y.hello();
599+ /// | --------- calling this method introduces a `'static` lifetime requirement
600+ /// |
601+ /// note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements
602+ /// --> $DIR/static-impl-obligation.rs:169:20
603+ /// |
604+ /// LL | impl dyn Foo + 'static where Self: 'static {
605+ /// | ^^^^^^^ ^^^^^^^
606+ /// LL | fn hello(&self) where Self: 'static {}
607+ /// | ^^^^^^^
608+ /// ```
609+ fn explain_impl_static_obligation (
610+ & self ,
611+ diag : & mut DiagnosticBuilder < ' _ > ,
612+ code : & ObligationCauseCode < ' tcx > ,
613+ outlived_fr : RegionVid ,
614+ ) {
615+ let tcx = self . infcx . tcx ;
616+ let ObligationCauseCode :: MethodCallConstraint ( ty, call_span) = code else {
617+ return ;
618+ } ;
619+ let ty:: FnDef ( def_id, args) = ty. kind ( ) else {
620+ return ;
621+ } ;
622+ let parent = tcx. parent ( * def_id) ;
623+ let hir:: def:: DefKind :: Impl { .. } = tcx. def_kind ( parent) else {
624+ return ;
625+ } ;
626+ let ty = tcx. type_of ( parent) . instantiate ( tcx, args) ;
627+ if self . to_error_region ( outlived_fr) != Some ( tcx. lifetimes . re_static ) {
628+ return ;
629+ }
630+ // FIXME: there's a case that's yet to be handled: `impl dyn Trait + '_ where Self: '_`
631+ // causes *two* errors to be produded, one about `where Self: '_` not being allowed,
632+ // and the regular error with no additional information about "lifetime may not live
633+ // long enough for `'static`" without mentioning where it came from. This is because
634+ // our error recovery fallback is indeed `ReStatic`. We should at some point introduce
635+ // a `ReError` instead to avoid this and other similar issues.
636+
637+ // Look for `'static` bounds in the generics of the method and the `impl`.
638+ // ```
639+ // impl dyn Trait where Self: 'static {
640+ // fn foo(&self) where Self: 'static {}
641+ // }
642+ // ```
643+ let mut predicates: Vec < Span > = tcx
644+ . predicates_of ( * def_id)
645+ . predicates
646+ . iter ( )
647+ . chain ( tcx. predicates_of ( parent) . predicates . iter ( ) )
648+ . filter_map ( |( pred, pred_span) | {
649+ if let Some ( ty:: ClauseKind :: TypeOutlives ( ty:: OutlivesPredicate ( pred_ty, r) ) ) =
650+ pred. kind ( ) . no_bound_vars ( )
651+ // Look for `'static` bounds
652+ && r. kind ( ) == ty:: ReStatic
653+ // We only want bounds on `Self`
654+ && self . infcx . can_eq ( self . param_env , ty, pred_ty)
655+ {
656+ Some ( * pred_span)
657+ } else {
658+ None
659+ }
660+ } )
661+ . collect ( ) ;
662+
663+ // Look at the receiver for `&'static self`, which introduces a `'static` obligation.
664+ // ```
665+ // impl dyn Trait {
666+ // fn foo(&'static self) {}
667+ // }
668+ // ```
669+ if let ty:: Ref ( region, _, _) = self
670+ . infcx
671+ . instantiate_binder_with_fresh_vars (
672+ * call_span,
673+ BoundRegionConversionTime :: FnCall ,
674+ tcx. fn_sig ( * def_id) . instantiate_identity ( ) . inputs ( ) . map_bound ( |inputs| inputs[ 0 ] ) ,
675+ )
676+ . kind ( )
677+ && * region == tcx. lifetimes . re_static
678+ && let Some ( assoc) = tcx. opt_associated_item ( * def_id)
679+ && assoc. fn_has_self_parameter
680+ {
681+ // We have a `&'static self` receiver.
682+ if let Some ( def_id) = def_id. as_local ( )
683+ && let owner = tcx. expect_hir_owner_node ( def_id)
684+ && let Some ( decl) = owner. fn_decl ( )
685+ && let Some ( ty) = decl. inputs . get ( 0 )
686+ {
687+ // Point at the `&'static self` receiver.
688+ predicates. push ( ty. span ) ;
689+ } else {
690+ // The method is not defined on the local crate, point at the signature
691+ // instead of just the receiver as an approximation.
692+ predicates. push ( tcx. def_span ( * def_id) )
693+ }
694+ }
695+
696+ // When we have the HIR `Node` at hand, see if we can identify an
697+ // implicit `'static` bound in an `impl dyn Trait {}` and if that's
698+ // the only restriction, suggest relaxing it.
699+ if let Some ( hir:: Node :: Item ( hir:: Item {
700+ kind :
701+ hir:: ItemKind :: Impl ( hir:: Impl {
702+ self_ty : hir:: Ty { kind : hir:: TyKind :: TraitObject ( _, lt, _) , span, .. } ,
703+ ..
704+ } ) ,
705+ ..
706+ } ) ) = tcx. hir ( ) . get_if_local ( parent)
707+ && let Some ( hir:: Node :: ImplItem ( hir:: ImplItem { .. } ) ) = tcx. hir ( ) . get_if_local ( * def_id)
708+ {
709+ let suggestion = match lt. res {
710+ hir:: LifetimeName :: ImplicitObjectLifetimeDefault if predicates. is_empty ( ) => {
711+ // `impl dyn Trait {}`
712+ Some ( (
713+ span. shrink_to_hi ( ) ,
714+ "consider relaxing the implicit `'static` requirement on the impl" ,
715+ " + '_" ,
716+ ) )
717+ }
718+ hir:: LifetimeName :: Static if predicates. is_empty ( ) => {
719+ // `impl dyn Trait + 'static {}`
720+ Some ( ( lt. ident . span , "consider replacing this `'static` requirement" , "'_" ) )
721+ }
722+ _ => None ,
723+ } ;
724+ if let Some ( ( span, msg, sugg) ) = suggestion {
725+ // We only emit the suggestion to write `impl dyn Trait + '_ {}` if that's the only
726+ // thing needed.
727+ diag. span_suggestion_verbose ( span, msg, sugg, Applicability :: MachineApplicable ) ;
728+ // This is redundant but needed because we won't enter the section with the
729+ // additional note, so we point at the method call here too.
730+ diag. span_label (
731+ * call_span,
732+ "calling this method introduces a `'static` lifetime requirement" ,
733+ ) ;
734+ } else if let hir:: LifetimeName :: ImplicitObjectLifetimeDefault
735+ | hir:: LifetimeName :: Static = lt. res
736+ {
737+ // Otherwise, we add the right span for the note pointing at all the places where
738+ // a `'static` requirement is introduced when invoking the method on this `impl`.
739+ predicates. push ( lt. ident . span ) ;
740+ }
741+ } else if let ty:: Dynamic ( _, region, ty:: Dyn ) = ty. kind ( )
742+ && * region == tcx. lifetimes . re_static
743+ {
744+ // The `self_ty` has a `'static` bound on a `dyn Trait`, either implicit or explicit,
745+ // but we don't have access to the HIR to identify which one nor to provide a targetted
746+ // enough `Span`, so instead we fall back to pointing at the `impl` header instead.
747+ predicates. push ( tcx. def_span ( parent) ) ;
748+ }
749+ if !predicates. is_empty ( ) {
750+ diag. span_label (
751+ * call_span,
752+ "calling this method introduces a `'static` lifetime requirement" ,
753+ ) ;
754+ let a_static_lt = if predicates. len ( ) == 1 {
755+ "a `'static` lifetime requirement"
756+ } else {
757+ "`'static` lifetime requirements"
758+ } ;
759+ let span: MultiSpan = predicates. into ( ) ;
760+ diag. span_note ( span, format ! ( "the `impl` on `{ty}` has {a_static_lt}" ) ) ;
761+ }
762+ }
763+
557764 /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
558765 /// This function expects `fr` to be local and `outlived_fr` to not be local.
559766 ///
@@ -793,7 +1000,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
7931000 self . add_static_impl_trait_suggestion ( & mut diag, * fr, fr_name, * outlived_fr) ;
7941001 self . suggest_adding_lifetime_params ( & mut diag, * fr, * outlived_fr) ;
7951002 self . suggest_move_on_borrowing_closure ( & mut diag) ;
796-
7971003 diag
7981004 }
7991005
@@ -980,12 +1186,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
9801186 "calling this method introduces the `impl`'s `'static` requirement" ,
9811187 ) ;
9821188 err. subdiagnostic ( self . dcx ( ) , RequireStaticErr :: UsedImpl { multi_span } ) ;
983- err. span_suggestion_verbose (
984- span. shrink_to_hi ( ) ,
985- "consider relaxing the implicit `'static` requirement" ,
986- " + '_" ,
987- Applicability :: MaybeIncorrect ,
988- ) ;
1189+ if let hir:: TyKind :: TraitObject ( traits, lt, Dyn ) = self_ty. kind
1190+ && lt. res == hir:: LifetimeName :: ImplicitObjectLifetimeDefault
1191+ && traits. iter ( ) . any ( |t| t. span == * span)
1192+ {
1193+ // We already handle the case where `self_ty` has an implicit 'static`
1194+ // requirement specifically in `explain_impl_static_obligation`.
1195+ } else {
1196+ err. span_suggestion_verbose (
1197+ span. shrink_to_hi ( ) ,
1198+ "consider relaxing the implicit `'static` requirement" ,
1199+ " + '_" ,
1200+ Applicability :: MaybeIncorrect ,
1201+ ) ;
1202+ }
9891203 suggested = true ;
9901204 }
9911205 }
0 commit comments