Skip to content

Commit 444a118

Browse files
committed
Auto merge of #32583 - arielb1:need-a-bound, r=nikomatsakis
Suggest adding a where-clause when that can help Suggest adding a where-clause when there is an unmet trait-bound that can be satisfied if some type can implement it. r? @nikomatsakis
2 parents 455fa01 + 0ac5e48 commit 444a118

File tree

168 files changed

+656
-523
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+656
-523
lines changed

src/doc/book/closures.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -371,14 +371,13 @@ assert_eq!(6, answer);
371371
This gives us these long, related errors:
372372

373373
```text
374-
error: the trait `core::marker::Sized` is not implemented for the type
375-
`core::ops::Fn(i32) -> i32` [E0277]
374+
error: the trait bound `core::ops::Fn(i32) -> i32 : core::marker::Sized` is not satisfied [E0277]
376375
fn factory() -> (Fn(i32) -> i32) {
377376
^~~~~~~~~~~~~~~~
378377
note: `core::ops::Fn(i32) -> i32` does not have a constant size known at compile-time
379378
fn factory() -> (Fn(i32) -> i32) {
380379
^~~~~~~~~~~~~~~~
381-
error: the trait `core::marker::Sized` is not implemented for the type `core::ops::Fn(i32) -> i32` [E0277]
380+
error: the trait bound `core::ops::Fn(i32) -> i32 : core::marker::Sized` is not satisfied [E0277]
382381
let f = factory();
383382
^
384383
note: `core::ops::Fn(i32) -> i32` does not have a constant size known at compile-time

src/doc/book/concurrency.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ fn main() {
234234
This won't work, however, and will give us the error:
235235

236236
```text
237-
13:9: 13:22 error: the trait `core::marker::Send` is not
238-
implemented for the type `alloc::rc::Rc<collections::vec::Vec<i32>>`
237+
13:9: 13:22 error: the trait bound `alloc::rc::Rc<collections::vec::Vec<i32>> : core::marker::Send`
238+
is not satisfied
239239
...
240240
13:9: 13:22 note: `alloc::rc::Rc<collections::vec::Vec<i32>>`
241241
cannot be sent between threads safely

src/doc/book/traits.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ print_area(5);
154154
We get a compile-time error:
155155

156156
```text
157-
error: the trait `HasArea` is not implemented for the type `_` [E0277]
157+
error: the trait bound `_ : HasArea` is not satisfied [E0277]
158158
```
159159

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

498498
```text
499-
error: the trait `main::Foo` is not implemented for the type `main::Baz` [E0277]
499+
error: the trait bound `main::Baz : main::Foo` is not satisfied [E0277]
500500
```
501501

502502
# Deriving

src/doc/book/vectors.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ v[j];
5656
Indexing with a non-`usize` type gives an error that looks like this:
5757

5858
```text
59-
error: the trait `core::ops::Index<i32>` is not implemented for the type
60-
`collections::vec::Vec<_>` [E0277]
59+
error: the trait bound `collections::vec::Vec<_> : core::ops::Index<i32>`
60+
is not satisfied [E0277]
6161
v[j];
6262
^~~~
6363
note: the type `collections::vec::Vec<_>` cannot be indexed by `i32`

src/doc/nomicon/coercions.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ fn main() {
6464
```
6565

6666
```text
67-
<anon>:10:5: 10:8 error: the trait `Trait` is not implemented for the type `&mut i32` [E0277]
67+
<anon>:10:5: 10:8 error: the trait bound `&mut i32 : Trait` is not satisfied [E0277]
6868
<anon>:10 foo(t);
6969
^~~
7070
```

src/librustc/diagnostics.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1006,8 +1006,7 @@ fn some_func<T: Foo>(foo: T) {
10061006
fn main() {
10071007
// we now call the method with the i32 type, which doesn't implement
10081008
// the Foo trait
1009-
some_func(5i32); // error: the trait `Foo` is not implemented for the
1010-
// type `i32`
1009+
some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied
10111010
}
10121011
```
10131012

src/librustc/traits/error_reporting.rs

+134-56
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ use super::{
1313
FulfillmentErrorCode,
1414
MismatchedProjectionTypes,
1515
Obligation,
16+
ObligationCause,
1617
ObligationCauseCode,
1718
OutputTypeParameterMismatch,
1819
TraitNotObjectSafe,
1920
PredicateObligation,
21+
SelectionContext,
2022
SelectionError,
2123
ObjectSafetyViolation,
2224
MethodViolationCode,
@@ -26,8 +28,9 @@ use super::{
2628
use fmt_macros::{Parser, Piece, Position};
2729
use hir::def_id::DefId;
2830
use infer::InferCtxt;
29-
use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable};
31+
use ty::{self, ToPredicate, ToPolyTraitRef, Ty, TyCtxt};
3032
use ty::fast_reject;
33+
use ty::fold::{TypeFoldable, TypeFolder};
3134
use util::nodemap::{FnvHashMap, FnvHashSet};
3235

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

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

108-
fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
109-
trait_ref: &TraitRef<'tcx>,
110-
span: Span) -> Option<String> {
106+
fn on_unimplemented_note<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
107+
trait_ref: ty::PolyTraitRef<'tcx>,
108+
span: Span) -> Option<String> {
109+
let trait_ref = trait_ref.skip_binder();
111110
let def_id = trait_ref.def_id;
112111
let mut report = None;
113112
for item in infcx.tcx.get_attrs(def_id).iter() {
@@ -175,6 +174,53 @@ fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
175174
report
176175
}
177176

177+
fn find_similar_impl_candidates<'a, 'tcx>(
178+
infcx: &InferCtxt<'a, 'tcx>,
179+
trait_ref: ty::PolyTraitRef<'tcx>)
180+
-> Vec<ty::TraitRef<'tcx>>
181+
{
182+
let simp = fast_reject::simplify_type(infcx.tcx,
183+
trait_ref.skip_binder().self_ty(),
184+
true);
185+
let mut impl_candidates = Vec::new();
186+
let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id());
187+
188+
match simp {
189+
Some(simp) => trait_def.for_each_impl(infcx.tcx, |def_id| {
190+
let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
191+
let imp_simp = fast_reject::simplify_type(infcx.tcx,
192+
imp.self_ty(),
193+
true);
194+
if let Some(imp_simp) = imp_simp {
195+
if simp != imp_simp {
196+
return;
197+
}
198+
}
199+
impl_candidates.push(imp);
200+
}),
201+
None => trait_def.for_each_impl(infcx.tcx, |def_id| {
202+
impl_candidates.push(
203+
infcx.tcx.impl_trait_ref(def_id).unwrap());
204+
})
205+
};
206+
impl_candidates
207+
}
208+
209+
fn report_similar_impl_candidates(span: Span,
210+
err: &mut DiagnosticBuilder,
211+
impl_candidates: &[ty::TraitRef])
212+
{
213+
err.fileline_help(span, &format!("the following implementations were found:"));
214+
215+
let end = cmp::min(4, impl_candidates.len());
216+
for candidate in &impl_candidates[0..end] {
217+
err.fileline_help(span, &format!(" {:?}", candidate));
218+
}
219+
if impl_candidates.len() > 4 {
220+
err.fileline_help(span, &format!("and {} others", impl_candidates.len()-4));
221+
}
222+
}
223+
178224
/// Reports that an overflow has occurred and halts compilation. We
179225
/// halt compilation unconditionally because it is important that
180226
/// overflows never be masked -- they basically represent computations
@@ -362,56 +408,39 @@ pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
362408
let trait_ref = trait_predicate.to_poly_trait_ref();
363409
let mut err = struct_span_err!(
364410
infcx.tcx.sess, obligation.cause.span, E0277,
365-
"the trait `{}` is not implemented for the type `{}`",
366-
trait_ref, trait_ref.self_ty());
367-
368-
// Check if it has a custom "#[rustc_on_unimplemented]"
369-
// error message, report with that message if it does
370-
let custom_note = report_on_unimplemented(infcx, &trait_ref.0,
371-
obligation.cause.span);
372-
if let Some(s) = custom_note {
411+
"the trait bound `{}` is not satisfied",
412+
trait_ref.to_predicate());
413+
414+
// Try to report a help message
415+
416+
if !trait_ref.has_infer_types() &&
417+
predicate_can_apply(infcx, trait_ref)
418+
{
419+
// If a where-clause may be useful, remind the
420+
// user that they can add it.
421+
//
422+
// don't display an on-unimplemented note, as
423+
// these notes will often be of the form
424+
// "the type `T` can't be frobnicated"
425+
// which is somewhat confusing.
426+
err.fileline_help(obligation.cause.span, &format!(
427+
"consider adding a `where {}` bound",
428+
trait_ref.to_predicate()
429+
));
430+
} else if let Some(s) = on_unimplemented_note(infcx, trait_ref,
431+
obligation.cause.span) {
432+
// Otherwise, if there is an on-unimplemented note,
433+
// display it.
373434
err.fileline_note(obligation.cause.span, &s);
374435
} else {
375-
let simp = fast_reject::simplify_type(infcx.tcx,
376-
trait_ref.self_ty(),
377-
true);
378-
let mut impl_candidates = Vec::new();
379-
let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id());
380-
381-
match simp {
382-
Some(simp) => trait_def.for_each_impl(infcx.tcx, |def_id| {
383-
let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
384-
let imp_simp = fast_reject::simplify_type(infcx.tcx,
385-
imp.self_ty(),
386-
true);
387-
if let Some(imp_simp) = imp_simp {
388-
if simp != imp_simp {
389-
return;
390-
}
391-
}
392-
impl_candidates.push(imp);
393-
}),
394-
None => trait_def.for_each_impl(infcx.tcx, |def_id| {
395-
impl_candidates.push(
396-
infcx.tcx.impl_trait_ref(def_id).unwrap());
397-
})
398-
};
436+
// If we can't show anything useful, try to find
437+
// similar impls.
399438

439+
let impl_candidates =
440+
find_similar_impl_candidates(infcx, trait_ref);
400441
if impl_candidates.len() > 0 {
401-
err.fileline_help(
402-
obligation.cause.span,
403-
&format!("the following implementations were found:"));
404-
405-
let end = cmp::min(4, impl_candidates.len());
406-
for candidate in &impl_candidates[0..end] {
407-
err.fileline_help(obligation.cause.span,
408-
&format!(" {:?}", candidate));
409-
}
410-
if impl_candidates.len() > 4 {
411-
err.fileline_help(obligation.cause.span,
412-
&format!("and {} others",
413-
impl_candidates.len()-4));
414-
}
442+
report_similar_impl_candidates(obligation.cause.span,
443+
&mut err, &impl_candidates);
415444
}
416445
}
417446
note_obligation_cause(infcx, &mut err, obligation);
@@ -649,6 +678,55 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
649678
}
650679
}
651680

681+
/// Returns whether the trait predicate may apply for *some* assignment
682+
/// to the type parameters.
683+
fn predicate_can_apply<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
684+
pred: ty::PolyTraitRef<'tcx>)
685+
-> bool
686+
{
687+
struct ParamToVarFolder<'a, 'tcx: 'a> {
688+
infcx: &'a InferCtxt<'a, 'tcx>,
689+
var_map: FnvHashMap<Ty<'tcx>, Ty<'tcx>>
690+
}
691+
692+
impl<'a, 'tcx> TypeFolder<'tcx> for ParamToVarFolder<'a, 'tcx>
693+
{
694+
fn tcx(&self) -> &TyCtxt<'tcx> { self.infcx.tcx }
695+
696+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
697+
if let ty::TyParam(..) = ty.sty {
698+
let infcx = self.infcx;
699+
self.var_map.entry(ty).or_insert_with(|| infcx.next_ty_var())
700+
} else {
701+
ty.super_fold_with(self)
702+
}
703+
}
704+
}
705+
706+
infcx.probe(|_| {
707+
let mut selcx = SelectionContext::new(infcx);
708+
709+
let cleaned_pred = pred.fold_with(&mut ParamToVarFolder {
710+
infcx: infcx,
711+
var_map: FnvHashMap()
712+
});
713+
714+
let cleaned_pred = super::project::normalize(
715+
&mut selcx,
716+
ObligationCause::dummy(),
717+
&cleaned_pred
718+
).value;
719+
720+
let obligation = Obligation::new(
721+
ObligationCause::dummy(),
722+
cleaned_pred.to_predicate()
723+
);
724+
725+
selcx.evaluate_obligation(&obligation)
726+
})
727+
}
728+
729+
652730
fn need_type_info<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
653731
span: Span,
654732
ty: Ty<'tcx>)

0 commit comments

Comments
 (0)