@@ -26,16 +26,17 @@ use super::{
26
26
27
27
use fmt_macros:: { Parser , Piece , Position } ;
28
28
use 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 } ;
31
31
use ty:: fast_reject;
32
- use ty:: fold:: { TypeFoldable , TypeFolder } ;
32
+ use ty:: fold:: TypeFolder ;
33
+ use ty:: subst:: { self , ParamSpace , Subst } ;
33
34
use util:: nodemap:: { FnvHashMap , FnvHashSet } ;
34
35
35
36
use std:: cmp;
36
37
use std:: fmt;
37
- use syntax:: attr:: { AttributeMethods , AttrMetaMethods } ;
38
38
use syntax:: ast;
39
+ use syntax:: attr:: { AttributeMethods , AttrMetaMethods } ;
39
40
use syntax:: codemap:: Span ;
40
41
use syntax:: errors:: DiagnosticBuilder ;
41
42
@@ -60,6 +61,128 @@ impl<'a, 'gcx, 'tcx> TraitErrorKey<'tcx> {
60
61
}
61
62
}
62
63
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
+
63
186
impl < ' a , ' gcx , ' tcx > InferCtxt < ' a , ' gcx , ' tcx > {
64
187
pub fn report_fulfillment_errors ( & self , errors : & Vec < FulfillmentError < ' tcx > > ) {
65
188
for error in errors {
@@ -126,16 +249,101 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
126
249
}
127
250
}
128
251
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
+
129
327
fn on_unimplemented_note ( & self ,
130
328
trait_ref : ty:: PolyTraitRef < ' tcx > ,
131
- span : Span ) -> Option < String > {
329
+ obligation : & PredicateObligation < ' tcx > ) -> Option < String > {
132
330
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 ;
134
342
let mut report = None ;
135
343
for item in self . tcx . get_attrs ( def_id) . iter ( ) {
136
344
if item. check_name ( "rustc_on_unimplemented" ) {
137
345
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 ) ;
139
347
let trait_str = def. trait_ref . to_string ( ) ;
140
348
if let Some ( ref istring) = item. value_str ( ) {
141
349
let mut generic_map = def. generics . types . iter_enumerated ( )
@@ -195,6 +403,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
195
403
report
196
404
}
197
405
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
+
198
437
fn report_similar_impl_candidates ( & self ,
199
438
trait_ref : ty:: PolyTraitRef < ' tcx > ,
200
439
err : & mut DiagnosticBuilder )
@@ -425,8 +664,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
425
664
// Try to report a help message
426
665
427
666
if !trait_ref. has_infer_types ( ) &&
428
- self . predicate_can_apply ( trait_ref)
429
- {
667
+ self . predicate_can_apply ( trait_ref) {
430
668
// If a where-clause may be useful, remind the
431
669
// user that they can add it.
432
670
//
@@ -435,22 +673,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
435
673
// "the type `T` can't be frobnicated"
436
674
// which is somewhat confusing.
437
675
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!
444
681
err. note ( & s) ;
445
682
} else {
446
683
// If we can't show anything useful, try to find
447
684
// 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
+ }
450
690
}
451
691
err
452
692
}
453
- } ,
693
+ }
694
+
454
695
ty:: Predicate :: Equate ( ref predicate) => {
455
696
let predicate = self . resolve_type_vars_if_possible ( predicate) ;
456
697
let err = self . equality_predicate ( span,
0 commit comments