Track all predicates in errors, not just trait obligations

Surface associated type projection bounds that could not be fulfilled in
E0599 errors. Always present the list of unfulfilled trait bounds,
regardless of whether we're pointing at the ADT or trait that didn't
satisfy it.
This commit is contained in:
Esteban Küber 2020-02-18 16:35:47 -08:00
parent ad4777dbca
commit 8119d0853d
19 changed files with 134 additions and 103 deletions

View file

@ -581,6 +581,20 @@ impl<'tcx, N> Vtable<'tcx, N> {
}
}
pub fn borrow_nested_obligations(&self) -> &[N] {
match &self {
VtableImpl(i) => &i.nested[..],
VtableParam(n) => &n[..],
VtableBuiltin(i) => &i.nested[..],
VtableAutoImpl(d) => &d.nested[..],
VtableClosure(c) => &c.nested[..],
VtableGenerator(c) => &c.nested[..],
VtableObject(d) => &d.nested[..],
VtableFnPointer(d) => &d.nested[..],
VtableTraitAlias(d) => &d.nested[..],
}
}
pub fn map<M, F>(self, f: F) -> Vtable<'tcx, M>
where
F: FnMut(N) -> M,

View file

@ -14,7 +14,7 @@ use crate::check::FnCtxt;
use rustc::ty::subst::Subst;
use rustc::ty::subst::{InternalSubsts, SubstsRef};
use rustc::ty::GenericParamDefKind;
use rustc::ty::{self, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TypeFoldable, WithConstness};
use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
@ -67,7 +67,7 @@ pub enum MethodError<'tcx> {
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
pub struct NoMatchData<'tcx> {
pub static_candidates: Vec<CandidateSource>,
pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
pub unsatisfied_predicates: Vec<ty::Predicate<'tcx>>,
pub out_of_scope_traits: Vec<DefId>,
pub lev_candidate: Option<ty::AssocItem>,
pub mode: probe::Mode,
@ -76,7 +76,7 @@ pub struct NoMatchData<'tcx> {
impl<'tcx> NoMatchData<'tcx> {
pub fn new(
static_candidates: Vec<CandidateSource>,
unsatisfied_predicates: Vec<TraitRef<'tcx>>,
unsatisfied_predicates: Vec<ty::Predicate<'tcx>>,
out_of_scope_traits: Vec<DefId>,
lev_candidate: Option<ty::AssocItem>,
mode: probe::Mode,

View file

@ -14,8 +14,7 @@ use rustc::session::config::nightly_options;
use rustc::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc::ty::GenericParamDefKind;
use rustc::ty::{
self, ParamEnvAnd, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
WithConstness,
self, ParamEnvAnd, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
@ -78,7 +77,7 @@ 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<TraitRef<'tcx>>,
unsatisfied_predicates: Vec<ty::Predicate<'tcx>>,
is_suggestion: IsSuggestion,
}
@ -1224,7 +1223,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
&self,
self_ty: Ty<'tcx>,
probes: ProbesIter,
possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>,
possibly_unsatisfied_predicates: &mut Vec<ty::Predicate<'tcx>>,
unstable_candidates: Option<&mut Vec<(&'b Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>>
where
@ -1343,7 +1342,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
&self,
self_ty: Ty<'tcx>,
probe: &Candidate<'tcx>,
possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>,
possibly_unsatisfied_predicates: &mut Vec<ty::Predicate<'tcx>>,
) -> ProbeResult {
debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe);
@ -1398,47 +1397,40 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let predicate = trait_ref.without_const().to_predicate();
let obligation = traits::Obligation::new(cause, self.param_env, predicate);
if !self.predicate_may_hold(&obligation) {
if self.probe(|_| self.select_trait_candidate(trait_ref).is_err()) {
if self.probe(|_| {
match self.select_trait_candidate(trait_ref) {
Err(_) => return true,
Ok(Some(vtable))
if !vtable.borrow_nested_obligations().is_empty() =>
{
for obligation in vtable.borrow_nested_obligations() {
// Determine exactly which obligation wasn't met, so
// that we can give more context in the error.
if !self.predicate_may_hold(&obligation) {
result = ProbeResult::NoMatch;
let o = self.resolve_vars_if_possible(obligation);
possibly_unsatisfied_predicates.push(o.predicate);
}
}
}
_ => {
// Some nested subobligation of this predicate
// failed.
//
// FIXME: try to find the exact nested subobligation
// and point at it rather than reporting the entire
// trait-ref?
result = ProbeResult::NoMatch;
let predicate = self.resolve_vars_if_possible(&predicate);
possibly_unsatisfied_predicates.push(predicate);
}
}
false
}) {
// This candidate's primary obligation doesn't even
// select - don't bother registering anything in
// `potentially_unsatisfied_predicates`.
return ProbeResult::NoMatch;
} else {
self.probe(|_| {
match self.select_trait_candidate(trait_ref) {
Ok(Some(traits::VtableImpl(traits::VtableImplData {
nested,
..
}))) if !nested.is_empty() => {
for obligation in nested {
// Determine exactly which obligation wasn't met, so
// that we can give more context in the error.
if !self.predicate_may_hold(&obligation) {
result = ProbeResult::NoMatch;
if let Some(poly_trait_ref) =
obligation.predicate.to_opt_poly_trait_ref()
{
let trait_ref = poly_trait_ref.clone();
let trait_ref = trait_ref.skip_binder();
possibly_unsatisfied_predicates
.push(*trait_ref);
}
}
}
}
_ => {
// Some nested subobligation of this predicate
// failed.
//
// FIXME: try to find the exact nested subobligation
// and point at it rather than reporting the entire
// trait-ref?
result = ProbeResult::NoMatch;
let trait_ref = self.resolve_vars_if_possible(&trait_ref);
possibly_unsatisfied_predicates.push(trait_ref);
}
}
});
}
}
vec![]
@ -1455,9 +1447,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let o = self.resolve_vars_if_possible(&o);
if !self.predicate_may_hold(&o) {
result = ProbeResult::NoMatch;
if let &ty::Predicate::Trait(ref pred, _) = &o.predicate {
possibly_unsatisfied_predicates.push(pred.skip_binder().trait_ref);
}
possibly_unsatisfied_predicates.push(o.predicate);
}
}

View file

@ -535,57 +535,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if !unsatisfied_predicates.is_empty() {
let def_span =
|def_id| self.tcx.sess.source_map().def_span(self.tcx.def_span(def_id));
let mut bound_spans = vec![];
let mut bound_list = unsatisfied_predicates
.iter()
.filter_map(|p| {
let self_ty = p.self_ty();
match &self_ty.kind {
ty::Adt(def, _) => {
bound_spans.push((
self.tcx
.sess
.source_map()
.def_span(self.tcx.def_span(def.did)),
format!(
"the method `{}` exists but this type doesn't satisfy \
the bound `{}: {}`",
item_name,
p.self_ty(),
p.print_only_trait_path()
),
));
None
}
ty::Dynamic(preds, _) => {
for pred in *preds.skip_binder() {
match pred {
ty::ExistentialPredicate::Trait(tr) => bound_spans
.push((
self.tcx
.sess
.source_map()
.def_span(self.tcx.def_span(tr.def_id)),
format!(
"the method `{}` exists but this trait \
doesn't satisfy the bound `{}: {}`",
item_name,
p.self_ty(),
p.print_only_trait_path()
),
)),
ty::ExistentialPredicate::Projection(_)
| ty::ExistentialPredicate::AutoTrait(_) => {}
.filter_map(|pred| match pred {
ty::Predicate::Projection(pred) => {
// `<Foo as Iterator>::Item = String`.
let trait_ref =
pred.skip_binder().projection_ty.trait_ref(self.tcx);
let assoc = self
.tcx
.associated_item(pred.skip_binder().projection_ty.item_def_id);
let ty = pred.skip_binder().ty;
Some(format!("`{}::{} = {}`", trait_ref, assoc.ident, ty))
}
ty::Predicate::Trait(poly_trait_ref, _) => {
let p = poly_trait_ref.skip_binder().trait_ref;
let self_ty = p.self_ty();
let path = p.print_only_trait_path();
match &self_ty.kind {
ty::Adt(def, _) => {
// Point at the type that couldn't satisfy the bound.
bound_spans.push((
def_span(def.did),
format!("doesn't satisfy `{}: {}`", self_ty, path),
));
}
ty::Dynamic(preds, _) => {
// Point at the trait object that couldn't satisfy the bound.
for pred in *preds.skip_binder() {
match pred {
ty::ExistentialPredicate::Trait(tr) => bound_spans
.push((
def_span(tr.def_id),
format!(
"doesn't satisfy `{}: {}`",
self_ty, path
),
)),
ty::ExistentialPredicate::Projection(_)
| ty::ExistentialPredicate::AutoTrait(_) => {}
}
}
}
None
_ => {}
}
_ => Some(format!(
"`{}: {}`",
p.self_ty(),
p.print_only_trait_path()
)),
Some(format!("`{}: {}`", self_ty, path))
}
_ => None,
})
.collect::<Vec<_>>();
bound_list.sort();

View file

@ -5,11 +5,13 @@ LL | struct Bar<T: Foo> {
| ------------------ method `clone` not found for this
...
LL | struct NotClone;
| ---------------- the method `clone` exists but this type doesn't satisfy the bound `NotClone: std::clone::Clone`
| ---------------- doesn't satisfy `NotClone: std::clone::Clone`
...
LL | Bar::<NotClone> { x: 1 }.clone();
| ^^^^^ method not found in `Bar<NotClone>`
|
= note: the method `clone` exists but the following trait bounds were not satisfied:
`NotClone: std::clone::Clone`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `std::clone::Clone`

View file

@ -16,7 +16,11 @@ LL | .collect();
::: $SRC_DIR/libcore/iter/adapters/mod.rs:LL:COL
|
LL | pub struct Cloned<I> {
| -------------------- the method `collect` exists but this type doesn't satisfy the bound `std::iter::Cloned<std::iter::TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]>>: std::iter::Iterator`
| -------------------- doesn't satisfy `std::iter::Cloned<std::iter::TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]>>: std::iter::Iterator`
|
= note: the method `collect` exists but the following trait bounds were not satisfied:
`<std::iter::TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]> as std::iter::Iterator>::Item = &_`
`std::iter::Cloned<std::iter::TakeWhile<&mut std::vec::IntoIter<u8>, [closure@$DIR/issue-31173.rs:10:39: 13:6 found_e:_]>>: std::iter::Iterator`
error: aborting due to 2 previous errors

View file

@ -5,6 +5,7 @@ LL | let _result = &Some(42).as_deref();
| ^^^^^^^^ help: there is a method with a similar name: `as_ref`
|
= note: the method `as_deref` exists but the following trait bounds were not satisfied:
`<{integer} as std::ops::Deref>::Target = _`
`{integer}: std::ops::Deref`
error: aborting due to previous error

View file

@ -5,6 +5,7 @@ LL | let _result = &mut Some(42).as_deref_mut();
| ^^^^^^^^^^^^ method not found in `std::option::Option<{integer}>`
|
= note: the method `as_deref_mut` exists but the following trait bounds were not satisfied:
`<{integer} as std::ops::Deref>::Target = _`
`{integer}: std::ops::DerefMut`
error: aborting due to previous error

View file

@ -5,6 +5,7 @@ LL | let _result = &Ok(42).as_deref();
| ^^^^^^^^ help: there is a method with a similar name: `as_ref`
|
= note: the method `as_deref` exists but the following trait bounds were not satisfied:
`<{integer} as std::ops::Deref>::Target = _`
`{integer}: std::ops::Deref`
error: aborting due to previous error

View file

@ -5,6 +5,7 @@ LL | let _result = &Err(41).as_deref_err();
| ^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_mut`
|
= note: the method `as_deref_err` exists but the following trait bounds were not satisfied:
`<{integer} as std::ops::Deref>::Target = _`
`{integer}: std::ops::Deref`
error: aborting due to previous error

View file

@ -5,6 +5,7 @@ LL | let _result = &mut Ok(42).as_deref_mut();
| ^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_err`
|
= note: the method `as_deref_mut` exists but the following trait bounds were not satisfied:
`<{integer} as std::ops::Deref>::Target = _`
`{integer}: std::ops::DerefMut`
error: aborting due to previous error

View file

@ -5,6 +5,7 @@ LL | let _result = &mut Err(41).as_deref_mut_err();
| ^^^^^^^^^^^^^^^^ help: there is a method with a similar name: `as_deref_mut`
|
= note: the method `as_deref_mut_err` exists but the following trait bounds were not satisfied:
`<{integer} as std::ops::Deref>::Target = _`
`{integer}: std::ops::DerefMut`
error: aborting due to previous error

View file

@ -38,11 +38,13 @@ LL | pub struct Foo;
| ---------------
| |
| method `take` not found for this
| the method `take` exists but this type doesn't satisfy the bound `Foo: std::iter::Iterator`
| doesn't satisfy `Foo: std::iter::Iterator`
...
LL | .take()
| ^^^^ method not found in `Foo`
|
= note: the method `take` exists but the following trait bounds were not satisfied:
`Foo: std::iter::Iterator`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following traits define an item `take`, perhaps you need to implement one of them:
candidate #1: `std::io::Read`

View file

@ -7,10 +7,12 @@ LL | once::<&str>("str").fuse().filter(|a: &str| true).count();
::: $SRC_DIR/libcore/iter/adapters/mod.rs:LL:COL
|
LL | pub struct Filter<I, P> {
| ----------------------- the method `count` exists but this type doesn't satisfy the bound `std::iter::Filter<std::iter::Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>: std::iter::Iterator`
| ----------------------- doesn't satisfy `std::iter::Filter<std::iter::Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>: std::iter::Iterator`
|
= note: the method `count` exists but the following trait bounds were not satisfied:
`[closure@$DIR/issue-36053-2.rs:11:39: 11:53]: std::ops::FnMut<(&_,)>`
`<[closure@$DIR/issue-36053-2.rs:11:39: 11:53] as std::ops::FnOnce<(&&str,)>>::Output = bool`
`[closure@$DIR/issue-36053-2.rs:11:39: 11:53]: std::ops::FnMut<(&&str,)>`
`std::iter::Filter<std::iter::Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:11:39: 11:53]>: std::iter::Iterator`
error[E0631]: type mismatch in closure arguments
--> $DIR/issue-36053-2.rs:11:32

View file

@ -2,10 +2,13 @@ error[E0599]: no method named `unwrap` found for enum `std::result::Result<(), F
--> $DIR/method-help-unsatisfied-bound.rs:5:7
|
LL | struct Foo;
| ----------- the method `unwrap` exists but this type doesn't satisfy the bound `Foo: std::fmt::Debug`
| ----------- doesn't satisfy `Foo: std::fmt::Debug`
...
LL | a.unwrap();
| ^^^^^^ method not found in `std::result::Result<(), Foo>`
|
= note: the method `unwrap` exists but the following trait bounds were not satisfied:
`Foo: std::fmt::Debug`
error: aborting due to previous error

View file

@ -8,11 +8,13 @@ LL | struct MyStruct;
| ----------------
| |
| method `foo_one` not found for this
| the method `foo_one` exists but this type doesn't satisfy the bound `MyStruct: Foo`
| doesn't satisfy `MyStruct: Foo`
...
LL | println!("{}", MyStruct.foo_one());
| ^^^^^^^ method not found in `MyStruct`
|
= note: the method `foo_one` exists but the following trait bounds were not satisfied:
`MyStruct: Foo`
= help: items from traits can only be used if the trait is implemented and in scope
error: aborting due to previous error

View file

@ -14,11 +14,13 @@ LL | union U5<T> {
| ----------- method `clone` not found for this
...
LL | struct CloneNoCopy;
| ------------------- the method `clone` exists but this type doesn't satisfy the bound `CloneNoCopy: std::marker::Copy`
| ------------------- doesn't satisfy `CloneNoCopy: std::marker::Copy`
...
LL | let w = u.clone();
| ^^^^^ method not found in `U5<CloneNoCopy>`
|
= note: the method `clone` exists but the following trait bounds were not satisfied:
`CloneNoCopy: std::marker::Copy`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `std::clone::Clone`

View file

@ -4,12 +4,15 @@ error[E0599]: no method named `clone` found for struct `std::boxed::Box<dyn Foo>
LL | trait Foo {
| ---------
| |
| the method `clone` exists but this trait doesn't satisfy the bound `dyn Foo: std::clone::Clone`
| the method `clone` exists but this trait doesn't satisfy the bound `dyn Foo: std::marker::Sized`
| doesn't satisfy `dyn Foo: std::clone::Clone`
| doesn't satisfy `dyn Foo: std::marker::Sized`
...
LL | let _z = y.clone();
| ^^^^^ method not found in `std::boxed::Box<dyn Foo>`
|
= note: the method `clone` exists but the following trait bounds were not satisfied:
`dyn Foo: std::clone::Clone`
`dyn Foo: std::marker::Sized`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `std::clone::Clone`

View file

@ -2,11 +2,13 @@ error[E0599]: no method named `clone` found for struct `std::boxed::Box<R>` in t
--> $DIR/unique-pinned-nocopy.rs:12:16
|
LL | struct R {
| -------- the method `clone` exists but this type doesn't satisfy the bound `R: std::clone::Clone`
| -------- doesn't satisfy `R: std::clone::Clone`
...
LL | let _j = i.clone();
| ^^^^^ method not found in `std::boxed::Box<R>`
|
= note: the method `clone` exists but the following trait bounds were not satisfied:
`R: std::clone::Clone`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `std::clone::Clone`