Skip to content

Commit 27a728a

Browse files
committed
Report 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 8a49772 commit 27a728a

File tree

18 files changed

+745
-53
lines changed

18 files changed

+745
-53
lines changed

compiler/rustc_borrowck/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = "2021"
88
either = "1.5.0"
99
itertools = "0.11"
1010
polonius-engine = "0.13.0"
11+
rustc_ast = { path = "../rustc_ast" }
1112
rustc_data_structures = { path = "../rustc_data_structures" }
1213
rustc_errors = { path = "../rustc_errors" }
1314
rustc_fluent_macro = { path = "../rustc_fluent_macro" }

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+236-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Error reporting machinery for lifetime errors.
22
3+
use rustc_ast::TraitObjectSyntax::Dyn;
34
use rustc_data_structures::fx::FxIndexSet;
45
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
56
use 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
};
2122
use rustc_middle::hir::place::PlaceBase;
2223
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};
2726
use rustc_span::symbol::{kw, Ident};
2827
use 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
}

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)