Skip to content

Suggest adding a where-clause when that can help #32583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/doc/book/closures.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,14 +371,13 @@ assert_eq!(6, answer);
This gives us these long, related errors:

```text
error: the trait `core::marker::Sized` is not implemented for the type
`core::ops::Fn(i32) -> i32` [E0277]
error: the trait bound `core::ops::Fn(i32) -> i32 : core::marker::Sized` is not satisfied [E0277]
fn factory() -> (Fn(i32) -> i32) {
^~~~~~~~~~~~~~~~
note: `core::ops::Fn(i32) -> i32` does not have a constant size known at compile-time
fn factory() -> (Fn(i32) -> i32) {
^~~~~~~~~~~~~~~~
error: the trait `core::marker::Sized` is not implemented for the type `core::ops::Fn(i32) -> i32` [E0277]
error: the trait bound `core::ops::Fn(i32) -> i32 : core::marker::Sized` is not satisfied [E0277]
let f = factory();
^
note: `core::ops::Fn(i32) -> i32` does not have a constant size known at compile-time
Expand Down
4 changes: 2 additions & 2 deletions src/doc/book/concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ fn main() {
This won't work, however, and will give us the error:

```text
13:9: 13:22 error: the trait `core::marker::Send` is not
implemented for the type `alloc::rc::Rc<collections::vec::Vec<i32>>`
13:9: 13:22 error: the trait bound `alloc::rc::Rc<collections::vec::Vec<i32>> : core::marker::Send`
is not satisfied
...
13:9: 13:22 note: `alloc::rc::Rc<collections::vec::Vec<i32>>`
cannot be sent between threads safely
Expand Down
4 changes: 2 additions & 2 deletions src/doc/book/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ print_area(5);
We get a compile-time error:

```text
error: the trait `HasArea` is not implemented for the type `_` [E0277]
error: the trait bound `_ : HasArea` is not satisfied [E0277]
```

## Trait bounds on generic structs
Expand Down Expand Up @@ -496,7 +496,7 @@ impl FooBar for Baz {
If we forget to implement `Foo`, Rust will tell us:

```text
error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]
error: the trait bound `main::Baz : main::Foo` is not satisfied [E0277]
```

# Deriving
Expand Down
4 changes: 2 additions & 2 deletions src/doc/book/vectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ v[j];
Indexing with a non-`usize` type gives an error that looks like this:

```text
error: the trait `core::ops::Index<i32>` is not implemented for the type
`collections::vec::Vec<_>` [E0277]
error: the trait bound `collections::vec::Vec<_> : core::ops::Index<i32>`
is not satisfied [E0277]
v[j];
^~~~
note: the type `collections::vec::Vec<_>` cannot be indexed by `i32`
Expand Down
2 changes: 1 addition & 1 deletion src/doc/nomicon/coercions.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ fn main() {
```

```text
<anon>:10:5: 10:8 error: the trait `Trait` is not implemented for the type `&mut i32` [E0277]
<anon>:10:5: 10:8 error: the trait bound `&mut i32 : Trait` is not satisfied [E0277]
<anon>:10 foo(t);
^~~
```
3 changes: 1 addition & 2 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1006,8 +1006,7 @@ fn some_func<T: Foo>(foo: T) {
fn main() {
// we now call the method with the i32 type, which doesn't implement
// the Foo trait
some_func(5i32); // error: the trait `Foo` is not implemented for the
// type `i32`
some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied
}
```

Expand Down
190 changes: 134 additions & 56 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ use super::{
FulfillmentErrorCode,
MismatchedProjectionTypes,
Obligation,
ObligationCause,
ObligationCauseCode,
OutputTypeParameterMismatch,
TraitNotObjectSafe,
PredicateObligation,
SelectionContext,
SelectionError,
ObjectSafetyViolation,
MethodViolationCode,
Expand All @@ -26,8 +28,9 @@ use super::{
use fmt_macros::{Parser, Piece, Position};
use middle::def_id::DefId;
use infer::InferCtxt;
use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable};
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
use ty::fast_reject;
use ty::fold::{TypeFoldable, TypeFolder};
use util::nodemap::{FnvHashMap, FnvHashSet};

use std::cmp;
Expand Down Expand Up @@ -90,12 +93,7 @@ pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
let predicate =
infcx.resolve_type_vars_if_possible(&obligation.predicate);

// The TyError created by normalize_to_error can end up being unified
// into all obligations: for example, if our obligation is something
// like `$X = <() as Foo<$X>>::Out` and () does not implement Foo<_>,
// then $X will be unified with TyError, but the error still needs to be
// reported.
if !infcx.tcx.sess.has_errors() || !predicate.references_error() {
if !predicate.references_error() {
let mut err = struct_span_err!(infcx.tcx.sess, obligation.cause.span, E0271,
"type mismatch resolving `{}`: {}",
predicate,
Expand All @@ -105,9 +103,10 @@ pub fn report_projection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
}
}

fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
trait_ref: &TraitRef<'tcx>,
span: Span) -> Option<String> {
fn on_unimplemented_note<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span) -> Option<String> {
let trait_ref = trait_ref.skip_binder();
let def_id = trait_ref.def_id;
let mut report = None;
for item in infcx.tcx.get_attrs(def_id).iter() {
Expand Down Expand Up @@ -175,6 +174,53 @@ fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
report
}

fn find_similar_impl_candidates<'a, 'tcx>(
infcx: &InferCtxt<'a, 'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>)
-> Vec<ty::TraitRef<'tcx>>
{
let simp = fast_reject::simplify_type(infcx.tcx,
trait_ref.skip_binder().self_ty(),
true);
let mut impl_candidates = Vec::new();
let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id());

match simp {
Some(simp) => trait_def.for_each_impl(infcx.tcx, |def_id| {
let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
let imp_simp = fast_reject::simplify_type(infcx.tcx,
imp.self_ty(),
true);
if let Some(imp_simp) = imp_simp {
if simp != imp_simp {
return;
}
}
impl_candidates.push(imp);
}),
None => trait_def.for_each_impl(infcx.tcx, |def_id| {
impl_candidates.push(
infcx.tcx.impl_trait_ref(def_id).unwrap());
})
};
impl_candidates
}

fn report_similar_impl_candidates(span: Span,
err: &mut DiagnosticBuilder,
impl_candidates: &[ty::TraitRef])
{
err.fileline_help(span, &format!("the following implementations were found:"));

let end = cmp::min(4, impl_candidates.len());
for candidate in &impl_candidates[0..end] {
err.fileline_help(span, &format!(" {:?}", candidate));
}
if impl_candidates.len() > 4 {
err.fileline_help(span, &format!("and {} others", impl_candidates.len()-4));
}
}

/// Reports that an overflow has occurred and halts compilation. We
/// halt compilation unconditionally because it is important that
/// overflows never be masked -- they basically represent computations
Expand Down Expand Up @@ -362,56 +408,39 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
let trait_ref = trait_predicate.to_poly_trait_ref();
let mut err = struct_span_err!(
infcx.tcx.sess, obligation.cause.span, E0277,
"the trait `{}` is not implemented for the type `{}`",
trait_ref, trait_ref.self_ty());

// Check if it has a custom "#[rustc_on_unimplemented]"
// error message, report with that message if it does
let custom_note = report_on_unimplemented(infcx, &trait_ref.0,
obligation.cause.span);
if let Some(s) = custom_note {
"the trait bound `{}` is not satisfied",
trait_ref.to_predicate());

// Try to report a help message

if !trait_ref.has_infer_types() &&
predicate_can_apply(infcx, trait_ref)
{
// If a where-clause may be useful, remind the
// user that they can add it.
//
// don't display an on-unimplemented note, as
// these notes will often be of the form
// "the type `T` can't be frobnicated"
// which is somewhat confusing.
err.fileline_help(obligation.cause.span, &format!(
"consider adding a `where {}` bound",
trait_ref.to_predicate()
));
} else if let Some(s) = on_unimplemented_note(infcx, trait_ref,
obligation.cause.span) {
// Otherwise, if there is an on-unimplemented note,
// display it.
err.fileline_note(obligation.cause.span, &s);
} else {
let simp = fast_reject::simplify_type(infcx.tcx,
trait_ref.self_ty(),
true);
let mut impl_candidates = Vec::new();
let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id());

match simp {
Some(simp) => trait_def.for_each_impl(infcx.tcx, |def_id| {
let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
let imp_simp = fast_reject::simplify_type(infcx.tcx,
imp.self_ty(),
true);
if let Some(imp_simp) = imp_simp {
if simp != imp_simp {
return;
}
}
impl_candidates.push(imp);
}),
None => trait_def.for_each_impl(infcx.tcx, |def_id| {
impl_candidates.push(
infcx.tcx.impl_trait_ref(def_id).unwrap());
})
};
// If we can't show anything useful, try to find
// similar impls.

let impl_candidates =
find_similar_impl_candidates(infcx, trait_ref);
if impl_candidates.len() > 0 {
err.fileline_help(
obligation.cause.span,
&format!("the following implementations were found:"));

let end = cmp::min(4, impl_candidates.len());
for candidate in &impl_candidates[0..end] {
err.fileline_help(obligation.cause.span,
&format!(" {:?}", candidate));
}
if impl_candidates.len() > 4 {
err.fileline_help(obligation.cause.span,
&format!("and {} others",
impl_candidates.len()-4));
}
report_similar_impl_candidates(obligation.cause.span,
&mut err, &impl_candidates);
}
}
note_obligation_cause(infcx, &mut err, obligation);
Expand Down Expand Up @@ -649,6 +678,55 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
}
}

/// Returns whether the trait predicate may apply for *some* assignment
/// to the type parameters.
fn predicate_can_apply<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
pred: ty::PolyTraitRef<'tcx>)
-> bool
{
struct ParamToVarFolder<'a, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'tcx>,
var_map: FnvHashMap<Ty<'tcx>, Ty<'tcx>>
}

impl<'a, 'tcx> TypeFolder<'tcx> for ParamToVarFolder<'a, 'tcx>
{
fn tcx(&self) -> &TyCtxt<'tcx> { self.infcx.tcx }

fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if let ty::TyParam(..) = ty.sty {
let infcx = self.infcx;
self.var_map.entry(ty).or_insert_with(|| infcx.next_ty_var())
} else {
ty.super_fold_with(self)
}
}
}

infcx.probe(|_| {
let mut selcx = SelectionContext::new(infcx);

let cleaned_pred = pred.fold_with(&mut ParamToVarFolder {
infcx: infcx,
var_map: FnvHashMap()
});

let cleaned_pred = super::project::normalize(
&mut selcx,
ObligationCause::dummy(),
&cleaned_pred
).value;

let obligation = Obligation::new(
ObligationCause::dummy(),
cleaned_pred.to_predicate()
);

selcx.evaluate_obligation(&obligation)
})
}


fn need_type_info<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
span: Span,
ty: Ty<'tcx>)
Expand Down
Loading