Skip to content

Commit 5e52df5

Browse files
committed
Provide more detail on lifetime failure of trait object in field type
``` error[E0478]: lifetime bound not satisfied --> tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:21 | 4 | broken: Box<dyn Any + 'a> | ^^^ ^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here --> tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18 | 3 | struct Something<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime note: unmet `'static` obligations introduced here --> rust/library/core/src/any.rs:115:16 | 115 | pub trait Any: 'static { | ^^^^^^^ ``` Fix rust-lang#83325.
1 parent 4e51aeb commit 5e52df5

6 files changed

+179
-29
lines changed

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+149-23
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@ use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags};
5353
use crate::infer;
5454
use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
5555
use crate::infer::ExpectedFound;
56+
use crate::traits::util::elaborate;
5657
use crate::traits::{
5758
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
5859
PredicateObligation,
5960
};
6061

61-
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
62+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
6263
use rustc_errors::{
6364
codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString,
6465
ErrorGuaranteed, IntoDiagnosticArg, MultiSpan,
@@ -458,11 +459,32 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
458459
// the error. If all of these fails, we fall back to a rather
459460
// general bit of code that displays the error information
460461
RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
462+
let extra_info = self.find_trait_object_relate_failure_reason(
463+
generic_param_scope,
464+
&origin,
465+
sub,
466+
sup,
467+
);
461468
let mut err = if sub.is_placeholder() || sup.is_placeholder() {
462469
self.report_placeholder_failure(origin, sub, sup)
463470
} else {
464471
self.report_concrete_failure(origin, sub, sup)
465472
};
473+
if let Some((primary_spans, relevant_bindings)) = extra_info {
474+
if primary_spans.has_primary_spans() {
475+
// We shorten the span from the whole field type to only the traits
476+
// and lifetime bound that failed.
477+
err.span(primary_spans);
478+
}
479+
if relevant_bindings.has_primary_spans() {
480+
// Point at all the trait obligations for the lifetime that
481+
// couldn't be met.
482+
err.span_note(
483+
relevant_bindings,
484+
format!("unmet `{sub}` obligations introduced here"),
485+
);
486+
}
487+
}
466488
if let hir::def::DefKind::Impl { .. } =
467489
self.tcx.def_kind(generic_param_scope)
468490
&& let Some(def_id) =
@@ -471,28 +493,36 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
471493
// Collect all the `Span`s corresponding to the predicates introducing
472494
// the `sub` lifetime that couldn't be met (sometimes `'static`) on
473495
// both the `trait` and the `impl`.
474-
let spans: Vec<Span> = self
475-
.tcx
476-
.predicates_of(def_id)
477-
.predicates
478-
.iter()
479-
.chain(
480-
self.tcx.predicates_of(generic_param_scope).predicates.iter(),
481-
)
482-
.filter_map(|(pred, span)| {
483-
if let Some(ty::ClauseKind::TypeOutlives(
484-
ty::OutlivesPredicate(pred_ty, r),
485-
)) = pred.kind().no_bound_vars()
486-
&& r == sub
487-
&& let ty::Param(param) = pred_ty.kind()
488-
&& param.name == kw::SelfUpper
489-
{
490-
Some(*span)
491-
} else {
492-
None
493-
}
494-
})
495-
.collect();
496+
let spans: Vec<Span> = elaborate(
497+
self.tcx,
498+
self.tcx
499+
.predicates_of(def_id)
500+
.predicates
501+
.iter()
502+
.map(|(p, sp)| (p.as_predicate(), *sp)),
503+
)
504+
.chain(elaborate(
505+
self.tcx,
506+
self.tcx
507+
.predicates_of(generic_param_scope)
508+
.predicates
509+
.iter()
510+
.map(|(p, sp)| (p.as_predicate(), *sp)),
511+
))
512+
.filter_map(|(pred, pred_span)| {
513+
if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder()
514+
&& let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
515+
_pred_ty,
516+
r,
517+
)) = clause
518+
&& r.kind() == ty::ReStatic
519+
{
520+
Some(pred_span)
521+
} else {
522+
None
523+
}
524+
})
525+
.collect();
496526

497527
if !spans.is_empty() {
498528
let spans_len = spans.len();
@@ -2666,6 +2696,102 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
26662696
if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() }
26672697
}
26682698

2699+
/// If a field on a struct has a trait object with lifetime requirement that can't be satisfied
2700+
/// by one of the traits in the trait object, shorten the span from the whole field type to only
2701+
/// the relevant traits and the lifetime. We also collect the spans for the places where the
2702+
/// traits' obligations were introduced.
2703+
fn find_trait_object_relate_failure_reason(
2704+
&self,
2705+
generic_param_scope: LocalDefId,
2706+
origin: &SubregionOrigin<'tcx>,
2707+
sub: ty::Region<'tcx>,
2708+
sup: ty::Region<'tcx>,
2709+
) -> Option<(MultiSpan, MultiSpan)> {
2710+
let infer::RelateRegionParamBound(span) = origin else {
2711+
return None;
2712+
};
2713+
let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Struct(item, _), .. })) =
2714+
self.tcx.hir().get_if_local(generic_param_scope.into())
2715+
else {
2716+
return None;
2717+
};
2718+
/// Collect all `hir::Ty<'_>` `Span`s for trait objects with the sup lifetime.
2719+
pub struct HirTraitObjectVisitor<'tcx>(
2720+
pub Vec<&'tcx hir::PolyTraitRef<'tcx>>,
2721+
pub ty::Region<'tcx>,
2722+
pub FxHashSet<Span>,
2723+
);
2724+
impl<'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'tcx> {
2725+
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
2726+
// Find all the trait objects that have the lifetime that was found.
2727+
if let hir::TyKind::TraitObject(poly_trait_refs, lt, _) = t.kind
2728+
&& match (lt.res, self.1.kind()) {
2729+
(
2730+
hir::LifetimeName::ImplicitObjectLifetimeDefault
2731+
| hir::LifetimeName::Static,
2732+
ty::RegionKind::ReStatic,
2733+
) => true,
2734+
(hir::LifetimeName::Param(a), ty::RegionKind::ReEarlyParam(b)) => {
2735+
a.to_def_id() == b.def_id
2736+
}
2737+
_ => false,
2738+
}
2739+
{
2740+
for ptr in poly_trait_refs {
2741+
// We'll filter the traits later, after collection.
2742+
self.0.push(ptr);
2743+
}
2744+
// We want to keep a span to the lifetime bound on the trait object.
2745+
self.2.insert(lt.ident.span);
2746+
}
2747+
hir::intravisit::walk_ty(self, t);
2748+
}
2749+
}
2750+
let mut visitor = HirTraitObjectVisitor(vec![], sup, Default::default());
2751+
for field in item.fields() {
2752+
if field.ty.span == *span {
2753+
// `span` points at the type of a field, we only want to look for trait objects in
2754+
// the field that failed.
2755+
visitor.visit_ty(field.ty);
2756+
}
2757+
}
2758+
2759+
// The display of these spans will not change regardless or sorting.
2760+
#[allow(rustc::potential_query_instability)]
2761+
let mut primary_spans: Vec<Span> = visitor.2.into_iter().collect();
2762+
let mut relevant_bindings: Vec<Span> = vec![];
2763+
for ptr in visitor.0 {
2764+
if let Some(def_id) = ptr.trait_ref.trait_def_id() {
2765+
// Find the bounds on the trait with the lifetime that couldn't be met.
2766+
let bindings: Vec<Span> = elaborate(
2767+
self.tcx,
2768+
self.tcx
2769+
.predicates_of(def_id)
2770+
.predicates
2771+
.iter()
2772+
.map(|(p, sp)| (p.as_predicate(), *sp)),
2773+
)
2774+
.filter_map(|(pred, pred_span)| {
2775+
if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder()
2776+
&& let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_pred_ty, r)) =
2777+
clause
2778+
&& r == sub
2779+
{
2780+
Some(pred_span)
2781+
} else {
2782+
None
2783+
}
2784+
})
2785+
.collect();
2786+
if !bindings.is_empty() {
2787+
primary_spans.push(ptr.span);
2788+
relevant_bindings.extend(bindings);
2789+
}
2790+
}
2791+
}
2792+
Some((primary_spans.into(), relevant_bindings.into()))
2793+
}
2794+
26692795
/// Determine whether an error associated with the given span and definition
26702796
/// should be treated as being caused by the implicit `From` conversion
26712797
/// within `?` desugaring.

tests/ui/error-codes/E0478.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0478]: lifetime bound not satisfied
2-
--> $DIR/E0478.rs:4:12
2+
--> $DIR/E0478.rs:4:37
33
|
44
LL | child: Box<dyn Wedding<'kiss> + 'SnowWhite>,
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^
66
|
77
note: lifetime parameter instantiated with the lifetime `'SnowWhite` as defined here
88
--> $DIR/E0478.rs:3:22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
use std::any::Any;
2+
3+
struct Something<'a> {
4+
broken: Box<dyn Any + 'a> //~ ERROR lifetime bound not satisfied
5+
}
6+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0478]: lifetime bound not satisfied
2+
--> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:21
3+
|
4+
LL | broken: Box<dyn Any + 'a>
5+
| ^^^ ^^
6+
|
7+
note: lifetime parameter instantiated with the lifetime `'a` as defined here
8+
--> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18
9+
|
10+
LL | struct Something<'a> {
11+
| ^^
12+
= note: but lifetime parameter must outlive the static lifetime
13+
note: unmet `'static` obligations introduced here
14+
--> $SRC_DIR/core/src/any.rs:LL:COL
15+
16+
error: aborting due to 1 previous error
17+
18+
For more information about this error, try `rustc --explain E0478`.

tests/ui/regions/region-bounds-on-objects-and-type-parameters.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ LL | z: Box<dyn Is<'a>+'b+'c>,
55
| ^^
66

77
error[E0478]: lifetime bound not satisfied
8-
--> $DIR/region-bounds-on-objects-and-type-parameters.rs:21:8
8+
--> $DIR/region-bounds-on-objects-and-type-parameters.rs:21:23
99
|
1010
LL | z: Box<dyn Is<'a>+'b+'c>,
11-
| ^^^^^^^^^^^^^^^^^^^^^
11+
| ^^
1212
|
1313
note: lifetime parameter instantiated with the lifetime `'b` as defined here
1414
--> $DIR/region-bounds-on-objects-and-type-parameters.rs:11:15

tests/ui/regions/regions-wf-trait-object.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0478]: lifetime bound not satisfied
2-
--> $DIR/regions-wf-trait-object.rs:7:8
2+
--> $DIR/regions-wf-trait-object.rs:7:29
33
|
44
LL | x: Box<dyn TheTrait<'a>+'b>
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^
66
|
77
note: lifetime parameter instantiated with the lifetime `'b` as defined here
88
--> $DIR/regions-wf-trait-object.rs:6:15

0 commit comments

Comments
 (0)