Skip to content

Commit 9c53987

Browse files
committed
eport a specialized error when a 'static obligation comes from an impl dyn Trait
```text error: lifetime may not live long enough --> $DIR/static-impl-obligation.rs:8:27 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; | ^ cast requires that `'a` must outlive `'static` LL | y.hello(); | --------- calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn a::Foo + 'static)` has a `'static` lifetime requirement --> $DIR/static-impl-obligation.rs:4:10 | LL | impl dyn Foo { | ^^^^^^^ help: relax the implicit `'static` bound on the impl | LL | impl dyn Foo + '_ { | ++++ ``` ```text error: lifetime may not live long enough --> $DIR/static-impl-obligation.rs:173:27 | LL | fn bar<'a>(x: &'a &'a u32) { | -- lifetime `'a` defined here LL | let y: &dyn Foo = x; | ^ cast requires that `'a` must outlive `'static` LL | y.hello(); | --------- calling this method introduces a `'static` lifetime requirement | note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements --> $DIR/static-impl-obligation.rs:169:20 | LL | impl dyn Foo + 'static where Self: 'static { | ^^^^^^^ ^^^^^^^ LL | fn hello(&self) where Self: 'static {} | ^^^^^^^ ```
1 parent 0f806a9 commit 9c53987

File tree

17 files changed

+761
-36
lines changed

17 files changed

+761
-36
lines changed

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+225-18
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@ use rustc_infer::infer::{
1616
HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor,
1717
},
1818
error_reporting::unexpected_hidden_region_diagnostic,
19-
NllRegionVariableOrigin, RelateParamBound,
19+
BoundRegionConversionTime, NllRegionVariableOrigin, RelateParamBound,
2020
};
2121
use rustc_middle::hir::place::PlaceBase;
2222
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};
23+
use rustc_middle::traits::ObligationCauseCode;
24+
use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor};
2725
use rustc_span::symbol::{kw, Ident};
2826
use rustc_span::Span;
2927

@@ -490,19 +488,21 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
490488
}
491489
};
492490

491+
self.explain_impl_static_obligation(&mut diag, cause.code(), outlived_fr);
492+
493493
match variance_info {
494494
ty::VarianceDiagInfo::None => {}
495495
ty::VarianceDiagInfo::Invariant { ty, param_index } => {
496496
let (desc, note) = match ty.kind() {
497497
ty::RawPtr(ty_mut) => {
498-
assert_eq!(ty_mut.mutbl, rustc_hir::Mutability::Mut);
498+
assert_eq!(ty_mut.mutbl, hir::Mutability::Mut);
499499
(
500500
format!("a mutable pointer to `{}`", ty_mut.ty),
501501
"mutable pointers are invariant over their type parameter".to_string(),
502502
)
503503
}
504504
ty::Ref(_, inner_ty, mutbl) => {
505-
assert_eq!(*mutbl, rustc_hir::Mutability::Mut);
505+
assert_eq!(*mutbl, hir::Mutability::Mut);
506506
(
507507
format!("a mutable reference to `{inner_ty}`"),
508508
"mutable references are invariant over their type parameter"
@@ -518,10 +518,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
518518
let adt_desc = adt.descr();
519519

520520
let desc = format!(
521-
"the type `{ty}`, which makes the generic argument `{generic_arg}` invariant"
521+
"the type `{ty}`, which makes the generic argument `{generic_arg}` \
522+
invariant"
522523
);
523524
let note = format!(
524-
"the {adt_desc} `{base_ty}` is invariant over the parameter `{base_generic_arg}`"
525+
"the {adt_desc} `{base_ty}` is invariant over the parameter \
526+
`{base_generic_arg}`"
525527
);
526528
(desc, note)
527529
}
@@ -539,21 +541,224 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
539541
};
540542
diag.note(format!("requirement occurs because of {desc}",));
541543
diag.note(note);
542-
diag.help("see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance");
544+
diag.help(
545+
"see <https://doc.rust-lang.org/nomicon/subtyping.html> for more \
546+
information about variance",
547+
);
543548
}
544549
}
545550

546551
for extra in extra_info {
547552
match extra {
548553
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
549-
diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
554+
diag.span_note(
555+
span,
556+
"due to current limitations in the borrow checker, this implies a \
557+
`'static` lifetime",
558+
);
550559
}
551560
}
552561
}
553562

554563
self.buffer_error(diag);
555564
}
556565

566+
/// Report a specialized error when a `'static` obligation comes from an `impl dyn Trait`
567+
///
568+
/// ```text
569+
/// error: lifetime may not live long enough
570+
/// --> $DIR/static-impl-obligation.rs:8:27
571+
/// |
572+
/// LL | fn bar<'a>(x: &'a &'a u32) {
573+
/// | -- lifetime `'a` defined here
574+
/// LL | let y: &dyn Foo = x;
575+
/// | ^ cast requires that `'a` must outlive `'static`
576+
/// LL | y.hello();
577+
/// | --------- calling this method introduces a `'static` lifetime requirement
578+
/// |
579+
/// note: the `impl` on `(dyn a::Foo + 'static)` has a `'static` lifetime requirement
580+
/// --> $DIR/static-impl-obligation.rs:4:10
581+
/// |
582+
/// LL | impl dyn Foo {
583+
/// | ^^^^^^^
584+
/// help: relax the implicit `'static` bound on the impl
585+
/// |
586+
/// LL | impl dyn Foo + '_ {
587+
/// | ++++
588+
/// ```
589+
/// ```text
590+
/// error: lifetime may not live long enough
591+
/// --> $DIR/static-impl-obligation.rs:173:27
592+
/// |
593+
/// LL | fn bar<'a>(x: &'a &'a u32) {
594+
/// | -- lifetime `'a` defined here
595+
/// LL | let y: &dyn Foo = x;
596+
/// | ^ cast requires that `'a` must outlive `'static`
597+
/// LL | y.hello();
598+
/// | --------- calling this method introduces a `'static` lifetime requirement
599+
/// |
600+
/// note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements
601+
/// --> $DIR/static-impl-obligation.rs:169:20
602+
/// |
603+
/// LL | impl dyn Foo + 'static where Self: 'static {
604+
/// | ^^^^^^^ ^^^^^^^
605+
/// LL | fn hello(&self) where Self: 'static {}
606+
/// | ^^^^^^^
607+
/// ```
608+
fn explain_impl_static_obligation(
609+
&self,
610+
diag: &mut DiagnosticBuilder<'_>,
611+
code: &ObligationCauseCode<'tcx>,
612+
outlived_fr: RegionVid,
613+
) {
614+
let tcx = self.infcx.tcx;
615+
let ObligationCauseCode::MethodCallConstraint(ty, call_span) = code else {
616+
return;
617+
};
618+
let ty::FnDef(def_id, args) = ty.kind() else {
619+
return;
620+
};
621+
let parent = tcx.parent(*def_id);
622+
let hir::def::DefKind::Impl { .. } = tcx.def_kind(parent) else {
623+
return;
624+
};
625+
let ty = tcx.type_of(parent).instantiate(tcx, args);
626+
let ty::Dynamic(_, region, ty::Dyn) = ty.kind() else {
627+
return;
628+
};
629+
if ![ty::ReStatic, ty::ReErased].contains(&region.kind()) {
630+
return;
631+
};
632+
if self.to_error_region(outlived_fr) != Some(tcx.lifetimes.re_static) {
633+
return;
634+
}
635+
// FIXME: there's a case that's yet to be handled: `impl dyn Trait + '_ where Self: '_`
636+
// causes *two* errors to be produded, one about `where Self: '_` not being allowed,
637+
// and the regular error with no additional information about "lifetime may not live
638+
// long enough for `'static`" without mentioning where it came from. This is because
639+
// our error recovery fallback is indeed `ReStatic`. We should at some point introduce
640+
// a `ReError` instead to avoid this and other similar issues.
641+
642+
// Look for `'static` bounds in the generics of the method and the `impl`.
643+
// ```
644+
// impl dyn Trait where Self: 'static {
645+
// fn foo(&self) where Self: 'static {}
646+
// }
647+
// ```
648+
let mut predicates: Vec<Span> = tcx
649+
.predicates_of(*def_id)
650+
.predicates
651+
.iter()
652+
.chain(tcx.predicates_of(parent).predicates.iter())
653+
.filter_map(|(pred, pred_span)| {
654+
if let Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(pred_ty, r))) =
655+
pred.kind().no_bound_vars()
656+
// Look for `'static` bounds
657+
&& r.kind() == ty::ReStatic
658+
// We only want bounds on `Self`
659+
&& self.infcx.can_eq(self.param_env, ty, pred_ty)
660+
{
661+
Some(*pred_span)
662+
} else {
663+
None
664+
}
665+
})
666+
.collect();
667+
668+
// Look at the receiver for `&'static self`, which introduces a `'static` obligation.
669+
// ```
670+
// impl dyn Trait {
671+
// fn foo(&'static self) {}
672+
// }
673+
// ```
674+
if let ty::Ref(region, _, _) = self
675+
.infcx
676+
.instantiate_binder_with_fresh_vars(
677+
*call_span,
678+
BoundRegionConversionTime::FnCall,
679+
tcx.fn_sig(*def_id).instantiate_identity().inputs().map_bound(|inputs| inputs[0]),
680+
)
681+
.kind()
682+
&& *region == tcx.lifetimes.re_static
683+
&& let Some(assoc) = tcx.opt_associated_item(*def_id)
684+
&& assoc.fn_has_self_parameter
685+
{
686+
// We have a `&'static self` receiver.
687+
if let Some(def_id) = def_id.as_local()
688+
&& let owner = tcx.expect_hir_owner_node(def_id)
689+
&& let Some(decl) = owner.fn_decl()
690+
&& let Some(ty) = decl.inputs.get(0)
691+
{
692+
// Point at the `&'static self` receiver.
693+
predicates.push(ty.span);
694+
} else {
695+
// The method is not defined on the local crate, point at the signature
696+
// instead of just the receiver as an approximation.
697+
predicates.push(tcx.def_span(*def_id))
698+
}
699+
}
700+
701+
// When we have the HIR `Node` at hand, see if we can identify an
702+
// implicit `'static` bound in an `impl dyn Trait {}` and if that's
703+
// the only restriction, suggest relaxing it.
704+
if let Some(hir::Node::Item(hir::Item {
705+
kind:
706+
hir::ItemKind::Impl(hir::Impl {
707+
self_ty: hir::Ty { kind: hir::TyKind::TraitObject(_, lt, _), span, .. },
708+
..
709+
}),
710+
..
711+
})) = tcx.hir().get_if_local(parent)
712+
&& let Some(hir::Node::ImplItem(hir::ImplItem { .. })) = tcx.hir().get_if_local(*def_id)
713+
{
714+
let suggestion = match lt.res {
715+
hir::LifetimeName::ImplicitObjectLifetimeDefault if predicates.is_empty() => {
716+
// ```
717+
// impl dyn Trait {}
718+
// ```
719+
Some((
720+
span.shrink_to_hi(),
721+
"consider relaxing the implicit `'static` requirement on the impl",
722+
" + '_",
723+
))
724+
}
725+
hir::LifetimeName::Static if predicates.is_empty() => {
726+
// ```
727+
// impl dyn Trait + 'static {}
728+
// ```
729+
Some((lt.ident.span, "consider replacing this `'static` requirement", "'_"))
730+
}
731+
_ => None,
732+
};
733+
if let Some((span, msg, sugg)) = suggestion {
734+
diag.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable);
735+
}
736+
if let hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static =
737+
lt.res
738+
{
739+
predicates.push(lt.ident.span);
740+
}
741+
} else if *region == tcx.lifetimes.re_static {
742+
// The `self_ty` has a `'static` bound, either implicit or explicit, but we don't
743+
// have access to the HIR to identify which one nor to provide a targetted enough
744+
// `Span`, so instead we fall back to pointing at the `impl` header instead.
745+
predicates.push(tcx.def_span(parent));
746+
}
747+
if !predicates.is_empty() {
748+
diag.span_label(
749+
*call_span,
750+
"calling this method introduces a `'static` lifetime requirement",
751+
);
752+
let a_static_lt = if predicates.len() == 1 {
753+
"a `'static` lifetime requirement"
754+
} else {
755+
"`'static` lifetime requirements"
756+
};
757+
let span: MultiSpan = predicates.into();
758+
diag.span_note(span, format!("the `impl` on `{ty}` has {a_static_lt}"));
759+
}
760+
}
761+
557762
/// Report a specialized error when `FnMut` closures return a reference to a captured variable.
558763
/// This function expects `fr` to be local and `outlived_fr` to not be local.
559764
///
@@ -793,7 +998,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
793998
self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
794999
self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
7951000
self.suggest_move_on_borrowing_closure(&mut diag);
796-
7971001
diag
7981002
}
7991003

@@ -980,12 +1184,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
9801184
"calling this method introduces the `impl`'s `'static` requirement",
9811185
);
9821186
err.subdiagnostic(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-
);
1187+
// We already handle the `self_ty` specifically in `explain_impl_static_obligation`.
1188+
if *span != self_ty.span {
1189+
err.span_suggestion_verbose(
1190+
span.shrink_to_hi(),
1191+
"consider relaxing the implicit `'static` requirement",
1192+
" + '_",
1193+
Applicability::MaybeIncorrect,
1194+
);
1195+
}
9891196
suggested = true;
9901197
}
9911198
}

compiler/rustc_borrowck/src/region_infer/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2051,6 +2051,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
20512051
CRATE_DEF_ID.to_def_id(),
20522052
predicate_span,
20532053
))
2054+
} else if let ConstraintCategory::CallArgument(Some(fn_def)) = constraint.category {
2055+
Some(ObligationCauseCode::MethodCallConstraint(fn_def, constraint.span))
20542056
} else {
20552057
None
20562058
}

compiler/rustc_borrowck/src/type_check/canonical.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
100100
locations: Locations,
101101
) {
102102
for (predicate, span) in instantiated_predicates {
103-
debug!(?predicate);
103+
debug!(?span, ?predicate);
104104
let category = ConstraintCategory::Predicate(span);
105105
let predicate = self.normalize_with_category(predicate, locations, category);
106106
self.prove_predicate(predicate, locations, category);

compiler/rustc_borrowck/src/type_check/constraint_conversion.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Const
88
use rustc_middle::traits::query::NoSolution;
99
use rustc_middle::traits::ObligationCause;
1010
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
11-
use rustc_span::{Span, DUMMY_SP};
11+
use rustc_span::Span;
1212
use rustc_trait_selection::solve::deeply_normalize;
1313
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
1414
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -183,7 +183,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
183183

184184
// we don't actually use this for anything, but
185185
// the `TypeOutlives` code needs an origin.
186-
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
186+
let origin = infer::RelateParamBound(self.span, t1, None);
187187

188188
TypeOutlives::new(
189189
&mut *self,

0 commit comments

Comments
 (0)