1
Fork 0

Point at impl blocks when they introduce unmet obligations

Group obligations by `impl` block that introduced them.
This commit is contained in:
Esteban Kuber 2021-10-13 14:28:28 +00:00
parent 8f433adf75
commit 9fa165d11b
9 changed files with 184 additions and 59 deletions

View file

@ -12,6 +12,7 @@ pub use self::CandidateSource::*;
pub use self::MethodError::*;
use crate::check::FnCtxt;
use crate::ObligationCause;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
@ -71,7 +72,8 @@ pub enum MethodError<'tcx> {
#[derive(Debug)]
pub struct NoMatchData<'tcx> {
pub static_candidates: Vec<CandidateSource>,
pub unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
pub unsatisfied_predicates:
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
pub out_of_scope_traits: Vec<DefId>,
pub lev_candidate: Option<ty::AssocItem>,
pub mode: probe::Mode,
@ -80,7 +82,11 @@ pub struct NoMatchData<'tcx> {
impl<'tcx> NoMatchData<'tcx> {
pub fn new(
static_candidates: Vec<CandidateSource>,
unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
unsatisfied_predicates: Vec<(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)>,
out_of_scope_traits: Vec<DefId>,
lev_candidate: Option<ty::AssocItem>,
mode: probe::Mode,

View file

@ -78,7 +78,8 @@ struct ProbeContext<'a, 'tcx> {
/// Collects near misses when trait bounds for type parameters are unsatisfied and is only used
/// for error reporting
unsatisfied_predicates: Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
unsatisfied_predicates:
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
is_suggestion: IsSuggestion,
@ -1351,6 +1352,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
possibly_unsatisfied_predicates: &mut Vec<(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)>,
unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>>
@ -1497,6 +1499,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
possibly_unsatisfied_predicates: &mut Vec<(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)>,
) -> ProbeResult {
debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe);
@ -1508,8 +1511,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
.sup(probe.xform_self_ty, self_ty)
{
Ok(InferOk { obligations, value: () }) => obligations,
Err(_) => {
debug!("--> cannot relate self-types");
Err(err) => {
debug!("--> cannot relate self-types {:?}", err);
return ProbeResult::NoMatch;
}
};
@ -1558,7 +1561,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let o = self.resolve_vars_if_possible(o);
if !self.predicate_may_hold(&o) {
result = ProbeResult::NoMatch;
possibly_unsatisfied_predicates.push((o.predicate, None));
possibly_unsatisfied_predicates.push((
o.predicate,
None,
Some(o.cause),
));
}
}
}
@ -1604,8 +1611,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
} else {
Some(predicate)
};
possibly_unsatisfied_predicates
.push((nested_predicate, p));
possibly_unsatisfied_predicates.push((
nested_predicate,
p,
Some(obligation.cause.clone()),
));
}
}
}
@ -1613,7 +1623,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
// Some nested subobligation of this predicate
// failed.
let predicate = self.resolve_vars_if_possible(predicate);
possibly_unsatisfied_predicates.push((predicate, None));
possibly_unsatisfied_predicates.push((predicate, None, None));
}
}
false
@ -1632,7 +1642,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let o = self.resolve_vars_if_possible(o);
if !self.predicate_may_hold(&o) {
result = ProbeResult::NoMatch;
possibly_unsatisfied_predicates.push((o.predicate, None));
possibly_unsatisfied_predicates.push((o.predicate, None, Some(o.cause)));
}
}

View file

@ -17,7 +17,9 @@ use rustc_span::lev_distance;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{source_map, FileName, MultiSpan, Span, Symbol};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{FulfillmentError, Obligation};
use rustc_trait_selection::traits::{
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
};
use std::cmp::Ordering;
use std::iter;
@ -791,9 +793,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => None,
}
};
// Find all the requirements that come from a local `impl` block.
let mut skip_list: FxHashSet<_> = Default::default();
let mut spanned_predicates: FxHashMap<MultiSpan, _> = Default::default();
for (data, p, parent_p) in unsatisfied_predicates
.iter()
.filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c)))
.filter_map(|(p, parent, c)| match c.code {
ObligationCauseCode::ImplDerivedObligation(ref data) => {
Some((data, p, parent))
}
_ => None,
})
{
let parent_trait_ref = data.parent_trait_ref;
let parent_def_id = parent_trait_ref.def_id();
let path = parent_trait_ref.print_only_trait_path();
let tr_self_ty = parent_trait_ref.skip_binder().self_ty();
let mut candidates = vec![];
self.tcx.for_each_relevant_impl(
parent_def_id,
parent_trait_ref.self_ty().skip_binder(),
|impl_def_id| match self.tcx.hir().get_if_local(impl_def_id) {
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { .. }),
..
})) => {
candidates.push(impl_def_id);
}
_ => {}
},
);
if let [def_id] = &candidates[..] {
match self.tcx.hir().get_if_local(*def_id) {
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
..
})) => {
if let Some(pred) = parent_p {
// Done to add the "doesn't satisfy" `span_label`.
let _ = format_pred(*pred);
}
skip_list.insert(p);
let mut spans = Vec::with_capacity(2);
if let Some(trait_ref) = of_trait {
spans.push(trait_ref.path.span);
}
spans.push(self_ty.span);
let entry = spanned_predicates.entry(spans.into());
entry
.or_insert_with(|| (path, tr_self_ty, Vec::new()))
.2
.push(p);
}
_ => {}
}
}
}
for (span, (path, self_ty, preds)) in spanned_predicates {
err.span_note(
span,
&format!(
"the following trait bounds were not satisfied because of the \
requirements of the implementation of `{}` for `{}`:\n{}",
path,
self_ty,
preds
.into_iter()
// .map(|pred| format!("{:?}", pred))
.filter_map(|pred| format_pred(*pred))
.map(|(p, _)| format!("`{}`", p))
.collect::<Vec<_>>()
.join("\n"),
),
);
}
// The requirements that didn't have an `impl` span to show.
let mut bound_list = unsatisfied_predicates
.iter()
.filter_map(|(pred, parent_pred)| {
.filter(|(pred, _, _parent_pred)| !skip_list.contains(&pred))
.filter_map(|(pred, parent_pred, _cause)| {
format_pred(*pred).map(|(p, self_ty)| match parent_pred {
None => format!("`{}`", &p),
Some(parent_pred) => match format_pred(*parent_pred) {
@ -836,7 +917,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
for (span, msg) in bound_spans.into_iter() {
err.span_label(span, &msg);
}
if !bound_list.is_empty() {
if !bound_list.is_empty() || !skip_list.is_empty() {
let bound_list = bound_list
.into_iter()
.map(|(_, path)| path)
@ -846,9 +927,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.set_primary_message(&format!(
"the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied"
));
err.note(&format!(
"the following trait bounds were not satisfied:\n{bound_list}"
));
if !bound_list.is_empty() {
err.note(&format!(
"the following trait bounds were not satisfied:\n{bound_list}"
));
}
self.suggest_derive(&mut err, &unsatisfied_predicates);
unsatisfied_bounds = true;
@ -1062,18 +1145,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_note(spans, &msg);
}
let preds: Vec<_> = errors.iter().map(|e| (e.obligation.predicate, None)).collect();
let preds: Vec<_> = errors
.iter()
.map(|e| (e.obligation.predicate, None, Some(e.obligation.cause.clone())))
.collect();
self.suggest_derive(err, &preds);
}
fn suggest_derive(
&self,
err: &mut DiagnosticBuilder<'_>,
unsatisfied_predicates: &Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)>,
unsatisfied_predicates: &Vec<(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)>,
) {
let mut derives = Vec::<(String, Span, String)>::new();
let mut traits = Vec::<Span>::new();
for (pred, _) in unsatisfied_predicates {
for (pred, _, _) in unsatisfied_predicates {
let trait_pred = match pred.kind().skip_binder() {
ty::PredicateKind::Trait(trait_pred) => trait_pred,
_ => continue,
@ -1264,7 +1354,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_name: Ident,
source: SelfSource<'tcx>,
valid_out_of_scope_traits: Vec<DefId>,
unsatisfied_predicates: &[(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>)],
unsatisfied_predicates: &[(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)],
unsatisfied_bounds: bool,
) {
let mut alt_rcvr_sugg = false;
@ -1380,7 +1474,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// this isn't perfect (that is, there are cases when
// implementing a trait would be legal but is rejected
// here).
unsatisfied_predicates.iter().all(|(p, _)| {
unsatisfied_predicates.iter().all(|(p, _, _)| {
match p.kind().skip_binder() {
// Hide traits if they are present in predicates as they can be fixed without
// having to implement them.