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,229 @@ 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+ let ty:: Dynamic ( _, region, ty:: Dyn ) = ty. kind ( ) else {
628+ return ;
629+ } ;
630+ if ![ ty:: ReStatic , ty:: ReErased ] . contains ( & region. kind ( ) ) {
631+ return ;
632+ } ;
633+ if self . to_error_region ( outlived_fr) != Some ( tcx. lifetimes . re_static ) {
634+ return ;
635+ }
636+ // FIXME: there's a case that's yet to be handled: `impl dyn Trait + '_ where Self: '_`
637+ // causes *two* errors to be produded, one about `where Self: '_` not being allowed,
638+ // and the regular error with no additional information about "lifetime may not live
639+ // long enough for `'static`" without mentioning where it came from. This is because
640+ // our error recovery fallback is indeed `ReStatic`. We should at some point introduce
641+ // a `ReError` instead to avoid this and other similar issues.
642+
643+ // Look for `'static` bounds in the generics of the method and the `impl`.
644+ // ```
645+ // impl dyn Trait where Self: 'static {
646+ // fn foo(&self) where Self: 'static {}
647+ // }
648+ // ```
649+ let mut predicates: Vec < Span > = tcx
650+ . predicates_of ( * def_id)
651+ . predicates
652+ . iter ( )
653+ . chain ( tcx. predicates_of ( parent) . predicates . iter ( ) )
654+ . filter_map ( |( pred, pred_span) | {
655+ if let Some ( ty:: ClauseKind :: TypeOutlives ( ty:: OutlivesPredicate ( pred_ty, r) ) ) =
656+ pred. kind ( ) . no_bound_vars ( )
657+ // Look for `'static` bounds
658+ && r. kind ( ) == ty:: ReStatic
659+ // We only want bounds on `Self`
660+ && self . infcx . can_eq ( self . param_env , ty, pred_ty)
661+ {
662+ Some ( * pred_span)
663+ } else {
664+ None
665+ }
666+ } )
667+ . collect ( ) ;
668+
669+ // Look at the receiver for `&'static self`, which introduces a `'static` obligation.
670+ // ```
671+ // impl dyn Trait {
672+ // fn foo(&'static self) {}
673+ // }
674+ // ```
675+ if let ty:: Ref ( region, _, _) = self
676+ . infcx
677+ . instantiate_binder_with_fresh_vars (
678+ * call_span,
679+ BoundRegionConversionTime :: FnCall ,
680+ tcx. fn_sig ( * def_id) . instantiate_identity ( ) . inputs ( ) . map_bound ( |inputs| inputs[ 0 ] ) ,
681+ )
682+ . kind ( )
683+ && * region == tcx. lifetimes . re_static
684+ && let Some ( assoc) = tcx. opt_associated_item ( * def_id)
685+ && assoc. fn_has_self_parameter
686+ {
687+ // We have a `&'static self` receiver.
688+ if let Some ( def_id) = def_id. as_local ( )
689+ && let owner = tcx. expect_hir_owner_node ( def_id)
690+ && let Some ( decl) = owner. fn_decl ( )
691+ && let Some ( ty) = decl. inputs . get ( 0 )
692+ {
693+ // Point at the `&'static self` receiver.
694+ predicates. push ( ty. span ) ;
695+ } else {
696+ // The method is not defined on the local crate, point at the signature
697+ // instead of just the receiver as an approximation.
698+ predicates. push ( tcx. def_span ( * def_id) )
699+ }
700+ }
701+
702+ // When we have the HIR `Node` at hand, see if we can identify an
703+ // implicit `'static` bound in an `impl dyn Trait {}` and if that's
704+ // the only restriction, suggest relaxing it.
705+ if let Some ( hir:: Node :: Item ( hir:: Item {
706+ kind :
707+ hir:: ItemKind :: Impl ( hir:: Impl {
708+ self_ty : hir:: Ty { kind : hir:: TyKind :: TraitObject ( _, lt, _) , span, .. } ,
709+ ..
710+ } ) ,
711+ ..
712+ } ) ) = tcx. hir ( ) . get_if_local ( parent)
713+ && let Some ( hir:: Node :: ImplItem ( hir:: ImplItem { .. } ) ) = tcx. hir ( ) . get_if_local ( * def_id)
714+ {
715+ let suggestion = match lt. res {
716+ hir:: LifetimeName :: ImplicitObjectLifetimeDefault if predicates. is_empty ( ) => {
717+ // `impl dyn Trait {}`
718+ Some ( (
719+ span. shrink_to_hi ( ) ,
720+ "consider relaxing the implicit `'static` requirement on the impl" ,
721+ " + '_" ,
722+ ) )
723+ }
724+ hir:: LifetimeName :: Static if predicates. is_empty ( ) => {
725+ // `impl dyn Trait + 'static {}`
726+ Some ( ( lt. ident . span , "consider replacing this `'static` requirement" , "'_" ) )
727+ }
728+ _ => None ,
729+ } ;
730+ if let Some ( ( span, msg, sugg) ) = suggestion {
731+ // We only emit the suggestion to write `impl dyn Trait + '_ {}` if that's the only
732+ // thing needed.
733+ diag. span_suggestion_verbose ( span, msg, sugg, Applicability :: MachineApplicable ) ;
734+ // This is redundant but needed because we won't enter the section with the
735+ // additional note, so we point at the method call here too.
736+ diag. span_label (
737+ * call_span,
738+ "calling this method introduces a `'static` lifetime requirement" ,
739+ ) ;
740+ } else if let hir:: LifetimeName :: ImplicitObjectLifetimeDefault
741+ | hir:: LifetimeName :: Static = lt. res
742+ {
743+ // Otherwise, we add the right span for the note pointing at all the places where
744+ // a `'static` requirement is introduced when invoking the method on this `impl`.
745+ predicates. push ( lt. ident . span ) ;
746+ }
747+ } else if * region == tcx. lifetimes . re_static {
748+ // The `self_ty` has a `'static` bound, either implicit or explicit, but we don't
749+ // have access to the HIR to identify which one nor to provide a targetted enough
750+ // `Span`, so instead we fall back to pointing at the `impl` header instead.
751+ predicates. push ( tcx. def_span ( parent) ) ;
752+ }
753+ if !predicates. is_empty ( ) {
754+ diag. span_label (
755+ * call_span,
756+ "calling this method introduces a `'static` lifetime requirement" ,
757+ ) ;
758+ let a_static_lt = if predicates. len ( ) == 1 {
759+ "a `'static` lifetime requirement"
760+ } else {
761+ "`'static` lifetime requirements"
762+ } ;
763+ let span: MultiSpan = predicates. into ( ) ;
764+ diag. span_note ( span, format ! ( "the `impl` on `{ty}` has {a_static_lt}" ) ) ;
765+ }
766+ }
767+
557768 /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
558769 /// This function expects `fr` to be local and `outlived_fr` to not be local.
559770 ///
@@ -793,7 +1004,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
7931004 self . add_static_impl_trait_suggestion ( & mut diag, * fr, fr_name, * outlived_fr) ;
7941005 self . suggest_adding_lifetime_params ( & mut diag, * fr, * outlived_fr) ;
7951006 self . suggest_move_on_borrowing_closure ( & mut diag) ;
796-
7971007 diag
7981008 }
7991009
@@ -980,12 +1190,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
9801190 "calling this method introduces the `impl`'s `'static` requirement" ,
9811191 ) ;
9821192 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- ) ;
1193+ if let hir:: TyKind :: TraitObject ( traits, lt, Dyn ) = self_ty. kind
1194+ && lt. res == hir:: LifetimeName :: ImplicitObjectLifetimeDefault
1195+ && traits. iter ( ) . any ( |t| t. span == * span)
1196+ {
1197+ // We already handle the case where `self_ty` has an implicit 'static`
1198+ // requirement specifically in `explain_impl_static_obligation`.
1199+ } else {
1200+ err. span_suggestion_verbose (
1201+ span. shrink_to_hi ( ) ,
1202+ "consider relaxing the implicit `'static` requirement" ,
1203+ " + '_" ,
1204+ Applicability :: MaybeIncorrect ,
1205+ ) ;
1206+ }
9891207 suggested = true ;
9901208 }
9911209 }
0 commit comments