1
1
//! Error reporting machinery for lifetime errors.
2
2
3
+ use rustc_ast:: TraitObjectSyntax :: Dyn ;
3
4
use rustc_data_structures:: fx:: FxIndexSet ;
4
5
use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , MultiSpan } ;
5
6
use rustc_hir as hir;
@@ -16,14 +17,12 @@ use rustc_infer::infer::{
16
17
HirTraitObjectVisitor , NiceRegionError , TraitObjectVisitor ,
17
18
} ,
18
19
error_reporting:: unexpected_hidden_region_diagnostic,
19
- NllRegionVariableOrigin , RelateParamBound ,
20
+ BoundRegionConversionTime , NllRegionVariableOrigin , RelateParamBound ,
20
21
} ;
21
22
use rustc_middle:: hir:: place:: PlaceBase ;
22
23
use 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 } ;
27
26
use rustc_span:: symbol:: { kw, Ident } ;
28
27
use rustc_span:: Span ;
29
28
@@ -490,19 +489,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
490
489
}
491
490
} ;
492
491
492
+ self . explain_impl_static_obligation ( & mut diag, cause. code ( ) , outlived_fr) ;
493
+
493
494
match variance_info {
494
495
ty:: VarianceDiagInfo :: None => { }
495
496
ty:: VarianceDiagInfo :: Invariant { ty, param_index } => {
496
497
let ( desc, note) = match ty. kind ( ) {
497
498
ty:: RawPtr ( ty_mut) => {
498
- assert_eq ! ( ty_mut. mutbl, rustc_hir :: Mutability :: Mut ) ;
499
+ assert_eq ! ( ty_mut. mutbl, hir :: Mutability :: Mut ) ;
499
500
(
500
501
format ! ( "a mutable pointer to `{}`" , ty_mut. ty) ,
501
502
"mutable pointers are invariant over their type parameter" . to_string ( ) ,
502
503
)
503
504
}
504
505
ty:: Ref ( _, inner_ty, mutbl) => {
505
- assert_eq ! ( * mutbl, rustc_hir :: Mutability :: Mut ) ;
506
+ assert_eq ! ( * mutbl, hir :: Mutability :: Mut ) ;
506
507
(
507
508
format ! ( "a mutable reference to `{inner_ty}`" ) ,
508
509
"mutable references are invariant over their type parameter"
@@ -518,10 +519,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
518
519
let adt_desc = adt. descr ( ) ;
519
520
520
521
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"
522
524
) ;
523
525
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}`"
525
528
) ;
526
529
( desc, note)
527
530
}
@@ -539,21 +542,229 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
539
542
} ;
540
543
diag. note ( format ! ( "requirement occurs because of {desc}" , ) ) ;
541
544
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
+ ) ;
543
549
}
544
550
}
545
551
546
552
for extra in extra_info {
547
553
match extra {
548
554
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
+ ) ;
550
560
}
551
561
}
552
562
}
553
563
554
564
self . buffer_error ( diag) ;
555
565
}
556
566
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
+
557
768
/// Report a specialized error when `FnMut` closures return a reference to a captured variable.
558
769
/// This function expects `fr` to be local and `outlived_fr` to not be local.
559
770
///
@@ -793,7 +1004,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
793
1004
self . add_static_impl_trait_suggestion ( & mut diag, * fr, fr_name, * outlived_fr) ;
794
1005
self . suggest_adding_lifetime_params ( & mut diag, * fr, * outlived_fr) ;
795
1006
self . suggest_move_on_borrowing_closure ( & mut diag) ;
796
-
797
1007
diag
798
1008
}
799
1009
@@ -980,12 +1190,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
980
1190
"calling this method introduces the `impl`'s `'static` requirement" ,
981
1191
) ;
982
1192
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
+ }
989
1207
suggested = true ;
990
1208
}
991
1209
}
0 commit comments