@@ -26,16 +26,17 @@ use super::{
2626
2727use fmt_macros:: { Parser , Piece , Position } ;
2828use hir:: def_id:: DefId ;
29- use infer:: InferCtxt ;
30- use ty:: { self , ToPredicate , ToPolyTraitRef , Ty , TyCtxt } ;
29+ use infer:: { InferCtxt , TypeOrigin } ;
30+ use ty:: { self , ToPredicate , ToPolyTraitRef , TraitRef , Ty , TyCtxt , TypeFoldable , TypeVariants } ;
3131use ty:: fast_reject;
32- use ty:: fold:: { TypeFoldable , TypeFolder } ;
32+ use ty:: fold:: TypeFolder ;
33+ use ty:: subst:: { self , ParamSpace , Subst } ;
3334use util:: nodemap:: { FnvHashMap , FnvHashSet } ;
3435
3536use std:: cmp;
3637use std:: fmt;
37- use syntax:: attr:: { AttributeMethods , AttrMetaMethods } ;
3838use syntax:: ast;
39+ use syntax:: attr:: { AttributeMethods , AttrMetaMethods } ;
3940use syntax:: codemap:: Span ;
4041use syntax:: errors:: DiagnosticBuilder ;
4142
@@ -60,6 +61,128 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> {
6061 }
6162}
6263
64+ // Enum used to differentiate the "big" and "little" weights.
65+ enum Weight {
66+ Coarse ,
67+ Precise ,
68+ }
69+
70+ trait AssociatedWeight {
71+ fn get_weight ( & self ) -> ( u32 , u32 ) ;
72+ }
73+
74+ impl < ' a > AssociatedWeight for TypeVariants < ' a > {
75+ // Left number is for "global"/"big" weight and right number is for better precision.
76+ fn get_weight ( & self ) -> ( u32 , u32 ) {
77+ match * self {
78+ TypeVariants :: TyBool => ( 1 , 1 ) ,
79+ TypeVariants :: TyChar => ( 1 , 2 ) ,
80+ TypeVariants :: TyStr => ( 1 , 3 ) ,
81+
82+ TypeVariants :: TyInt ( _) => ( 2 , 1 ) ,
83+ TypeVariants :: TyUint ( _) => ( 2 , 2 ) ,
84+ TypeVariants :: TyFloat ( _) => ( 2 , 3 ) ,
85+ TypeVariants :: TyRawPtr ( _) => ( 2 , 4 ) ,
86+
87+ TypeVariants :: TyEnum ( _, _) => ( 3 , 1 ) ,
88+ TypeVariants :: TyStruct ( _, _) => ( 3 , 2 ) ,
89+ TypeVariants :: TyBox ( _) => ( 3 , 3 ) ,
90+ TypeVariants :: TyTuple ( _) => ( 3 , 4 ) ,
91+
92+ TypeVariants :: TyArray ( _, _) => ( 4 , 1 ) ,
93+ TypeVariants :: TySlice ( _) => ( 4 , 2 ) ,
94+
95+ TypeVariants :: TyRef ( _, _) => ( 5 , 1 ) ,
96+ TypeVariants :: TyFnDef ( _, _, _) => ( 5 , 2 ) ,
97+ TypeVariants :: TyFnPtr ( _) => ( 5 , 3 ) ,
98+
99+ TypeVariants :: TyTrait ( _) => ( 6 , 1 ) ,
100+
101+ TypeVariants :: TyClosure ( _, _) => ( 7 , 1 ) ,
102+
103+ TypeVariants :: TyProjection ( _) => ( 8 , 1 ) ,
104+ TypeVariants :: TyParam ( _) => ( 8 , 2 ) ,
105+ TypeVariants :: TyInfer ( _) => ( 8 , 3 ) ,
106+
107+ TypeVariants :: TyError => ( 9 , 1 ) ,
108+ }
109+ }
110+ }
111+
112+ // The "closer" the types are, the lesser the weight.
113+ fn get_weight_diff ( a : & ty:: TypeVariants , b : & TypeVariants , weight : Weight ) -> u32 {
114+ let ( w1, w2) = match weight {
115+ Weight :: Coarse => ( a. get_weight ( ) . 0 , b. get_weight ( ) . 0 ) ,
116+ Weight :: Precise => ( a. get_weight ( ) . 1 , b. get_weight ( ) . 1 ) ,
117+ } ;
118+ if w1 < w2 {
119+ w2 - w1
120+ } else {
121+ w1 - w2
122+ }
123+ }
124+
125+ // Once we have "globally matching" types, we need to run another filter on them.
126+ //
127+ // In the function `get_best_matching_type`, we got the types which might fit the
128+ // most to the type we're looking for. This second filter now intends to get (if
129+ // possible) the type which fits the most.
130+ //
131+ // For example, the trait expects an `usize` and here you have `u32` and `i32`.
132+ // Obviously, the "correct" one is `u32`.
133+ fn filter_matching_types < ' tcx > ( weights : & [ ( usize , u32 ) ] ,
134+ imps : & [ ( DefId , subst:: Substs < ' tcx > ) ] ,
135+ trait_types : & [ ty:: Ty < ' tcx > ] )
136+ -> usize {
137+ let matching_weight = weights[ 0 ] . 1 ;
138+ let iter = weights. iter ( ) . filter ( |& & ( _, weight) | weight == matching_weight) ;
139+ let mut filtered_weights = vec ! ( ) ;
140+
141+ for & ( pos, _) in iter {
142+ let mut weight = 0 ;
143+ for ( type_to_compare, original_type) in imps[ pos] . 1
144+ . types
145+ . get_slice ( ParamSpace :: TypeSpace )
146+ . iter ( )
147+ . zip ( trait_types. iter ( ) ) {
148+ weight += get_weight_diff ( & type_to_compare. sty , & original_type. sty , Weight :: Precise ) ;
149+ }
150+ filtered_weights. push ( ( pos, weight) ) ;
151+ }
152+ filtered_weights. sort_by ( |a, b| a. 1 . cmp ( & b. 1 ) ) ;
153+ filtered_weights[ 0 ] . 0
154+ }
155+
156+ // Here, we run the "big" filter. Little example:
157+ //
158+ // We receive a `String`, an `u32` and an `i32`.
159+ // The trait expected an `usize`.
160+ // From human point of view, it's easy to determine that `String` doesn't correspond to
161+ // the expected type at all whereas `u32` and `i32` could.
162+ //
163+ // This first filter intends to only keep the types which match the most.
164+ fn get_best_matching_type < ' tcx > ( imps : & [ ( DefId , subst:: Substs < ' tcx > ) ] ,
165+ trait_types : & [ ty:: Ty < ' tcx > ] ) -> usize {
166+ let mut weights = vec ! ( ) ;
167+ for ( pos, imp) in imps. iter ( ) . enumerate ( ) {
168+ let mut weight = 0 ;
169+ for ( type_to_compare, original_type) in imp. 1
170+ . types
171+ . get_slice ( ParamSpace :: TypeSpace )
172+ . iter ( )
173+ . zip ( trait_types. iter ( ) ) {
174+ weight += get_weight_diff ( & type_to_compare. sty , & original_type. sty , Weight :: Coarse ) ;
175+ }
176+ weights. push ( ( pos, weight) ) ;
177+ }
178+ weights. sort_by ( |a, b| a. 1 . cmp ( & b. 1 ) ) ;
179+ if weights[ 0 ] . 1 == weights[ 1 ] . 1 {
180+ filter_matching_types ( & weights, & imps, trait_types)
181+ } else {
182+ weights[ 0 ] . 0
183+ }
184+ }
185+
63186impl < ' a , ' gcx , ' tcx > InferCtxt < ' a , ' gcx , ' tcx > {
64187 pub fn report_fulfillment_errors ( & self , errors : & Vec < FulfillmentError < ' tcx > > ) {
65188 for error in errors {
@@ -126,16 +249,101 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
126249 }
127250 }
128251
252+ fn impl_substs ( & self ,
253+ did : DefId ,
254+ obligation : PredicateObligation < ' tcx > )
255+ -> subst:: Substs < ' tcx > {
256+ let tcx = self . tcx ;
257+
258+ let ity = tcx. lookup_item_type ( did) ;
259+ let ( tps, rps, _) =
260+ ( ity. generics . types . get_slice ( subst:: TypeSpace ) ,
261+ ity. generics . regions . get_slice ( subst:: TypeSpace ) ,
262+ ity. ty ) ;
263+
264+ let rps = self . region_vars_for_defs ( obligation. cause . span , rps) ;
265+ let mut substs = subst:: Substs :: new (
266+ subst:: VecPerParamSpace :: empty ( ) ,
267+ subst:: VecPerParamSpace :: new ( rps, Vec :: new ( ) , Vec :: new ( ) ) ) ;
268+ self . type_vars_for_defs ( obligation. cause . span ,
269+ subst:: ParamSpace :: TypeSpace ,
270+ & mut substs,
271+ tps) ;
272+ substs
273+ }
274+
275+ fn get_current_failing_impl ( & self ,
276+ trait_ref : & TraitRef < ' tcx > ,
277+ obligation : & PredicateObligation < ' tcx > )
278+ -> Option < ( DefId , subst:: Substs < ' tcx > ) > {
279+ let simp = fast_reject:: simplify_type ( self . tcx ,
280+ trait_ref. self_ty ( ) ,
281+ true ) ;
282+ let trait_def = self . tcx . lookup_trait_def ( trait_ref. def_id ) ;
283+
284+ match simp {
285+ Some ( _) => {
286+ let mut matching_impls = Vec :: new ( ) ;
287+ trait_def. for_each_impl ( self . tcx , |def_id| {
288+ let imp = self . tcx . impl_trait_ref ( def_id) . unwrap ( ) ;
289+ let substs = self . impl_substs ( def_id, obligation. clone ( ) ) ;
290+ let imp = imp. subst ( self . tcx , & substs) ;
291+
292+ if self . eq_types ( true ,
293+ TypeOrigin :: Misc ( obligation. cause . span ) ,
294+ trait_ref. self_ty ( ) ,
295+ imp. self_ty ( ) ) . is_ok ( ) {
296+ matching_impls. push ( ( def_id, imp. substs . clone ( ) ) ) ;
297+ }
298+ } ) ;
299+ if matching_impls. len ( ) == 0 {
300+ None
301+ } else if matching_impls. len ( ) == 1 {
302+ Some ( matching_impls[ 0 ] . clone ( ) )
303+ } else {
304+ let end = trait_ref. input_types ( ) . len ( ) - 1 ;
305+ // we need to determine which type is the good one!
306+ Some ( matching_impls[ get_best_matching_type ( & matching_impls,
307+ & trait_ref. input_types ( ) [ 0 ..end] ) ]
308+ . clone ( ) )
309+ }
310+ } ,
311+ None => None ,
312+ }
313+ }
314+
315+ fn find_attr ( & self ,
316+ def_id : DefId ,
317+ attr_name : & str )
318+ -> Option < ast:: Attribute > {
319+ for item in self . tcx . get_attrs ( def_id) . iter ( ) {
320+ if item. check_name ( attr_name) {
321+ return Some ( item. clone ( ) ) ;
322+ }
323+ }
324+ None
325+ }
326+
129327 fn on_unimplemented_note ( & self ,
130328 trait_ref : ty:: PolyTraitRef < ' tcx > ,
131- span : Span ) -> Option < String > {
329+ obligation : & PredicateObligation < ' tcx > ) -> Option < String > {
132330 let trait_ref = trait_ref. skip_binder ( ) ;
133- let def_id = trait_ref. def_id ;
331+ let def_id = match self . get_current_failing_impl ( trait_ref, obligation) {
332+ Some ( ( def_id, _) ) => {
333+ if let Some ( _) = self . find_attr ( def_id, "rustc_on_unimplemented" ) {
334+ def_id
335+ } else {
336+ trait_ref. def_id
337+ }
338+ } ,
339+ None => trait_ref. def_id ,
340+ } ;
341+ let span = obligation. cause . span ;
134342 let mut report = None ;
135343 for item in self . tcx . get_attrs ( def_id) . iter ( ) {
136344 if item. check_name ( "rustc_on_unimplemented" ) {
137345 let err_sp = item. meta ( ) . span . substitute_dummy ( span) ;
138- let def = self . tcx . lookup_trait_def ( def_id) ;
346+ let def = self . tcx . lookup_trait_def ( trait_ref . def_id ) ;
139347 let trait_str = def. trait_ref . to_string ( ) ;
140348 if let Some ( ref istring) = item. value_str ( ) {
141349 let mut generic_map = def. generics . types . iter_enumerated ( )
@@ -195,6 +403,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
195403 report
196404 }
197405
406+ fn find_similar_impl_candidates ( & self ,
407+ trait_ref : ty:: PolyTraitRef < ' tcx > )
408+ -> Vec < ty:: TraitRef < ' tcx > >
409+ {
410+ let simp = fast_reject:: simplify_type ( self . tcx ,
411+ trait_ref. skip_binder ( ) . self_ty ( ) ,
412+ true ) ;
413+ let mut impl_candidates = Vec :: new ( ) ;
414+ let trait_def = self . tcx . lookup_trait_def ( trait_ref. def_id ( ) ) ;
415+
416+ match simp {
417+ Some ( simp) => trait_def. for_each_impl ( self . tcx , |def_id| {
418+ let imp = self . tcx . impl_trait_ref ( def_id) . unwrap ( ) ;
419+ let imp_simp = fast_reject:: simplify_type ( self . tcx ,
420+ imp. self_ty ( ) ,
421+ true ) ;
422+ if let Some ( imp_simp) = imp_simp {
423+ if simp != imp_simp {
424+ return ;
425+ }
426+ }
427+ impl_candidates. push ( imp) ;
428+ } ) ,
429+ None => trait_def. for_each_impl ( self . tcx , |def_id| {
430+ impl_candidates. push (
431+ self . tcx . impl_trait_ref ( def_id) . unwrap ( ) ) ;
432+ } )
433+ } ;
434+ impl_candidates
435+ }
436+
198437 fn report_similar_impl_candidates ( & self ,
199438 trait_ref : ty:: PolyTraitRef < ' tcx > ,
200439 err : & mut DiagnosticBuilder )
@@ -425,8 +664,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
425664 // Try to report a help message
426665
427666 if !trait_ref. has_infer_types ( ) &&
428- self . predicate_can_apply ( trait_ref)
429- {
667+ self . predicate_can_apply ( trait_ref) {
430668 // If a where-clause may be useful, remind the
431669 // user that they can add it.
432670 //
@@ -435,22 +673,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
435673 // "the type `T` can't be frobnicated"
436674 // which is somewhat confusing.
437675 err. help ( & format ! ( "consider adding a `where {}` bound" ,
438- trait_ref. to_predicate( )
439- ) ) ;
440- } else if let Some ( s) =
441- self . on_unimplemented_note ( trait_ref, span) {
442- // Otherwise, if there is an on-unimplemented note,
443- // display it.
676+ trait_ref. to_predicate( ) ) ) ;
677+ } else if let Some ( s) = self . on_unimplemented_note ( trait_ref,
678+ obligation) {
679+ // If it has a custom "#[rustc_on_unimplemented]"
680+ // error message, let's display it!
444681 err. note ( & s) ;
445682 } else {
446683 // If we can't show anything useful, try to find
447684 // similar impls.
448-
449- self . report_similar_impl_candidates ( trait_ref, & mut err) ;
685+ let impl_candidates =
686+ self . find_similar_impl_candidates ( trait_ref) ;
687+ if impl_candidates. len ( ) > 0 {
688+ self . report_similar_impl_candidates ( trait_ref, & mut err) ;
689+ }
450690 }
451691 err
452692 }
453- } ,
693+ }
694+
454695 ty:: Predicate :: Equate ( ref predicate) => {
455696 let predicate = self . resolve_type_vars_if_possible ( predicate) ;
456697 let err = self . equality_predicate ( span,
0 commit comments