Skip to content

Commit 61e6169

Browse files
Improve weight algorithm and tests
1 parent b8fad79 commit 61e6169

File tree

3 files changed

+134
-60
lines changed

3 files changed

+134
-60
lines changed

src/librustc/traits/error_reporting.rs

+107-52
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ use super::{
2727
use fmt_macros::{Parser, Piece, Position};
2828
use hir::def_id::DefId;
2929
use infer::{self, InferCtxt, TypeOrigin};
30-
use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable};
30+
use ty::{self, ToPredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVariants};
3131
use ty::fast_reject;
3232
use ty::fold::TypeFolder;
33-
use ty::subst::{self, Subst};
33+
use ty::subst::{self, ParamSpace, Subst};
3434
use util::nodemap::{FnvHashMap, FnvHashSet};
3535

3636
use std::cmp;
@@ -63,9 +63,9 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> {
6363
}
6464

6565
fn impl_substs<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>,
66-
did: DefId,
67-
obligation: PredicateObligation<'tcx>)
68-
-> subst::Substs<'tcx> {
66+
did: DefId,
67+
obligation: PredicateObligation<'tcx>)
68+
-> subst::Substs<'tcx> {
6969
let tcx = fcx.tcx;
7070

7171
let ity = tcx.lookup_item_type(did);
@@ -82,44 +82,109 @@ fn impl_substs<'a, 'tcx>(fcx: &InferCtxt<'a, 'tcx>,
8282
substs
8383
}
8484

85-
/*fn check_type_parameters<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
86-
trait_substs: &subst::Substs<'tcx>,
87-
impl_substs: &subst::Substs<'tcx>,
88-
obligation: &PredicateObligation<'tcx>) -> bool {
89-
let trait_types = trait_substs.types.as_slice();
90-
let impl_types = impl_substs.types.as_slice();
91-
92-
let mut failed = 0;
93-
for index_to_ignore in 0..trait_types.len() {
94-
for (index, (trait_type, impl_type)) in trait_types.iter()
95-
.zip(impl_types.iter())
96-
.enumerate() {
97-
if index_to_ignore != index &&
98-
infer::mk_eqty(infcx, true,
99-
TypeOrigin::Misc(obligation.cause.span),
100-
trait_type,
101-
impl_type).is_err() {
102-
failed += 1;
103-
break;
104-
}
85+
trait AssociatedWeight {
86+
fn get_weight(&self) -> usize;
87+
}
88+
89+
impl<'a> AssociatedWeight for TypeVariants<'a> {
90+
// First number is for "global" weight and second number is for bigger precision
91+
fn get_weight(&self) -> usize {
92+
match *self {
93+
TypeVariants::TyBool => 11,
94+
TypeVariants::TyChar => 12,
95+
TypeVariants::TyStr => 13,
96+
TypeVariants::TyInt(_) => 21,
97+
TypeVariants::TyUint(_) => 22,
98+
TypeVariants::TyFloat(_) => 23,
99+
TypeVariants::TyRawPtr(_) => 24,
100+
TypeVariants::TyEnum(_, _) => 31,
101+
TypeVariants::TyStruct(_, _) => 32,
102+
TypeVariants::TyBox(_) => 33,
103+
TypeVariants::TyTuple(_) => 34,
104+
TypeVariants::TyArray(_, _) => 41,
105+
TypeVariants::TySlice(_) => 42,
106+
TypeVariants::TyRef(_, _) => 51,
107+
TypeVariants::TyFnDef(_, _, _) => 52,
108+
TypeVariants::TyFnPtr(_) => 53,
109+
TypeVariants::TyTrait(_) => 61,
110+
TypeVariants::TyClosure(_, _) => 71,
111+
TypeVariants::TyProjection(_) => 81,
112+
TypeVariants::TyParam(_) => 82,
113+
TypeVariants::TyInfer(_) => 83,
114+
TypeVariants::TyError => 91,
105115
}
106116
}
107-
failed == trait_types.len() - 1
108-
}*/
117+
}
118+
119+
// The "closer" the types are, the lesser the weight
120+
fn get_weight_diff(a: &ty::TypeVariants, b: &TypeVariants, big_weight: bool) -> usize {
121+
let w1 = if big_weight { a.get_weight() / 10 } else { a.get_weight() % 10 };
122+
let w2 = if big_weight { b.get_weight() / 10 } else { b.get_weight() % 10 };
123+
124+
if w1 < w2 {
125+
w2 - w1
126+
} else {
127+
w1 - w2
128+
}
129+
}
130+
131+
// Once we have "globally matching" types, we need to run another filter on them
132+
fn filter_matching_types<'tcx>(weights: &[(usize, usize)],
133+
imps: &[(DefId, subst::Substs<'tcx>)],
134+
trait_types: &[ty::Ty<'tcx>])
135+
-> usize {
136+
let matching_weight = weights[0].1;
137+
let iter = weights.iter().filter(|&&(_, weight)| weight == matching_weight);
138+
let mut filtered_weights = vec!();
139+
140+
for &(pos, _) in iter {
141+
let mut weight = 0;
142+
for (type_to_compare, original_type) in imps[pos].1
143+
.types
144+
.get_slice(ParamSpace::TypeSpace)
145+
.iter()
146+
.zip(trait_types.iter()) {
147+
weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, false);
148+
}
149+
filtered_weights.push((pos, weight));
150+
}
151+
filtered_weights.sort_by(|a, b| a.1.cmp(&b.1));
152+
filtered_weights[0].0
153+
}
154+
155+
fn get_best_matching_type<'tcx>(imps: &[(DefId, subst::Substs<'tcx>)],
156+
trait_types: &[ty::Ty<'tcx>]) -> usize {
157+
let mut weights = vec!();
158+
for (pos, imp) in imps.iter().enumerate() {
159+
let mut weight = 0;
160+
for (type_to_compare, original_type) in imp.1
161+
.types
162+
.get_slice(ParamSpace::TypeSpace)
163+
.iter()
164+
.zip(trait_types.iter()) {
165+
weight += get_weight_diff(&type_to_compare.sty, &original_type.sty, true);
166+
}
167+
weights.push((pos, weight));
168+
}
169+
weights.sort_by(|a, b| a.1.cmp(&b.1));
170+
if weights[0].1 == weights[1].1 {
171+
filter_matching_types(&weights, &imps, trait_types)
172+
} else {
173+
weights[0].0
174+
}
175+
}
109176

110177
fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
111178
trait_ref: &TraitRef<'tcx>,
112179
obligation: &PredicateObligation<'tcx>)
113180
-> Option<(DefId, subst::Substs<'tcx>)> {
114-
println!("1");
115181
let simp = fast_reject::simplify_type(infcx.tcx,
116182
trait_ref.self_ty(),
117183
true);
118184
let trait_def = infcx.tcx.lookup_trait_def(trait_ref.def_id);
119185

120186
match simp {
121187
Some(_) => {
122-
println!("2");
123188
let mut matching_impls = Vec::new();
124189
trait_def.for_each_impl(infcx.tcx, |def_id| {
125190
let imp = infcx.tcx.impl_trait_ref(def_id).unwrap();
@@ -130,25 +195,19 @@ fn get_current_failing_impl<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
130195
TypeOrigin::Misc(obligation.cause.span),
131196
trait_ref.self_ty(),
132197
imp.self_ty()).is_ok() {
133-
//if check_type_parameters(infcx, &trait_ref.substs, &imp.substs, obligation) {
134-
matching_impls.push((def_id, imp.substs.clone()));
135-
//}
198+
matching_impls.push((def_id, imp.substs.clone()));
136199
}
137-
println!("=> {:?} /// {:?}", def_id, imp.substs);
138200
});
139201
if matching_impls.len() == 0 {
140-
println!("3");
141202
None
142203
} else if matching_impls.len() == 1 {
143-
println!("4");
144204
Some(matching_impls[0].clone())
145205
} else {
146-
println!("5");
206+
let end = trait_ref.input_types().len() - 1;
147207
// we need to determine which type is the good one!
148-
for &(ref m, ref n) in matching_impls.iter() {
149-
println!("=> {:?} /// {:?}", m, n);
150-
}
151-
Some(matching_impls[0].clone())
208+
Some(matching_impls[get_best_matching_type(&matching_impls,
209+
&trait_ref.input_types()[0..end])]
210+
.clone())
152211
}
153212
},
154213
None => None,
@@ -171,7 +230,6 @@ fn on_unimplemented_note<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
171230
trait_ref: ty::PolyTraitRef<'tcx>,
172231
obligation: &PredicateObligation<'tcx>) -> Option<String> {
173232
let trait_ref = trait_ref.skip_binder();
174-
//let def_id = trait_ref.def_id;
175233
let mut report = None;
176234
let def_id = match get_current_failing_impl(infcx, trait_ref, obligation) {
177235
Some((def_id, _)) => {
@@ -603,8 +661,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
603661
// Try to report a help message
604662

605663
if !trait_ref.has_infer_types() &&
606-
predicate_can_apply(infcx, trait_ref)
607-
{
664+
predicate_can_apply(infcx, trait_ref) {
608665
// If a where-clause may be useful, remind the
609666
// user that they can add it.
610667
//
@@ -662,18 +719,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
662719
// similar impls.
663720

664721
self.report_similar_impl_candidates(trait_ref, &mut err);
722+
// If we can't show anything useful, try to find
723+
// similar impls.
724+
let impl_candidates =
725+
find_similar_impl_candidates(infcx, trait_ref);
726+
if impl_candidates.len() > 0 {
727+
self.report_similar_impl_candidates(trait_ref, &mut err);
665728
}
666729
err
667730
}
668-
// Check if it has a custom "#[rustc_on_unimplemented]"
669-
// error message, report with that message if it does
670-
/*let custom_note = report_on_unimplemented(infcx, &trait_ref.0,
671-
obligation);
672-
if let Some(s) = custom_note {
673-
err.fileline_note(obligation.cause.span, &s);
674-
} else {
675-
note_obligation_cause(infcx, &mut err, obligation);
676-
}*/
731+
note_obligation_cause(infcx, &mut err, obligation);
677732
err.emit();
678733
}
679734

src/test/compile-fail/check_on_unimplemented.rs

+1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ impl Index<usize> for [i32] {
3131
fn main() {
3232
Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277
3333
//~| NOTE a usize is required
34+
//~| NOTE required by
3435
}

src/test/compile-fail/on_unimplemented.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,11 @@ trait Index<Idx: ?Sized> {
1919
fn index(&self, index: Idx) -> &Self::Output;
2020
}
2121

22-
struct Foo {
23-
i: usize,
24-
}
25-
26-
#[rustc_on_unimplemented = "a Foo is required to index into a slice"]
27-
impl Index<Foo> for [i32] {
22+
#[rustc_on_unimplemented = "a isize is required to index into a slice"]
23+
impl Index<isize> for [i32] {
2824
type Output = i32;
29-
fn index(&self, index: Foo) -> &i32 {
30-
&self[index.i]
25+
fn index(&self, index: isize) -> &i32 {
26+
&self[index as usize]
3127
}
3228
}
3329

@@ -39,8 +35,30 @@ impl Index<usize> for [i32] {
3935
}
4036
}
4137

38+
trait Foo<A, B> {
39+
fn f(&self, a: &A, b: &B);
40+
}
41+
42+
#[rustc_on_unimplemented = "two i32 Foo trait takes"]
43+
impl Foo<i32, i32> for [i32] {
44+
fn f(&self, a: &i32, b: &i32) {}
45+
}
46+
47+
#[rustc_on_unimplemented = "two u32 Foo trait takes"]
48+
impl Foo<u32, u32> for [i32] {
49+
fn f(&self, a: &u32, b: &u32) {}
50+
}
51+
4252
#[rustc_error]
4353
fn main() {
4454
Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32); //~ ERROR E0277
4555
//~| NOTE a usize is required
56+
//~| NOTE required by
57+
Index::<i32>::index(&[1, 2, 3] as &[i32], 2i32); //~ ERROR E0277
58+
//~| NOTE a isize is required
59+
//~| NOTE required by
60+
61+
Foo::<usize, usize>::f(&[1, 2, 3] as &[i32], &2usize, &2usize); //~ ERROR E0277
62+
//~| NOTE two u32 Foo trait
63+
//~| NOTE required by
4664
}

0 commit comments

Comments
 (0)