@@ -12,6 +12,7 @@ use rustc_hir::lang_items::LangItem;
1212use rustc_hir:: { ExprKind , Node , QPath } ;
1313use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
1414use rustc_middle:: hir:: map as hir_map;
15+ use rustc_middle:: ty:: fast_reject:: simplify_type;
1516use rustc_middle:: ty:: print:: with_crate_prefix;
1617use rustc_middle:: ty:: {
1718 self , ToPolyTraitRef , ToPredicate , Ty , TyCtxt , TypeFoldable , WithConstness ,
@@ -1074,19 +1075,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10741075 } else {
10751076 "items from traits can only be used if the trait is implemented and in scope"
10761077 } ) ;
1078+ let candidates_len = candidates. len ( ) ;
10771079 let message = |action| {
10781080 format ! (
10791081 "the following {traits_define} an item `{name}`, perhaps you need to {action} \
10801082 {one_of_them}:",
10811083 traits_define =
1082- if candidates . len ( ) == 1 { "trait defines" } else { "traits define" } ,
1084+ if candidates_len == 1 { "trait defines" } else { "traits define" } ,
10831085 action = action,
1084- one_of_them = if candidates . len ( ) == 1 { "it" } else { "one of them" } ,
1086+ one_of_them = if candidates_len == 1 { "it" } else { "one of them" } ,
10851087 name = item_name,
10861088 )
10871089 } ;
10881090 // Obtain the span for `param` and use it for a structured suggestion.
1089- let mut suggested = false ;
10901091 if let ( Some ( ref param) , Some ( ref table) ) =
10911092 ( param_type, self . in_progress_typeck_results )
10921093 {
@@ -1147,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11471148 Applicability :: MaybeIncorrect ,
11481149 ) ;
11491150 }
1150- suggested = true ;
1151+ return ;
11511152 }
11521153 Node :: Item ( hir:: Item {
11531154 kind : hir:: ItemKind :: Trait ( .., bounds, _) ,
@@ -1167,45 +1168,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11671168 } ) ,
11681169 Applicability :: MaybeIncorrect ,
11691170 ) ;
1170- suggested = true ;
1171+ return ;
11711172 }
11721173 _ => { }
11731174 }
11741175 }
11751176 }
11761177
1177- if !suggested {
1178- let action = if let Some ( param) = param_type {
1179- format ! ( "restrict type parameter `{}` with" , param)
1180- } else {
1181- // FIXME: it might only need to be imported into scope, not implemented.
1182- "implement" . to_string ( )
1183- } ;
1184- let mut use_note = true ;
1185- if let [ trait_info] = & candidates[ ..] {
1186- if let Some ( span) = self . tcx . hir ( ) . span_if_local ( trait_info. def_id ) {
1187- err. span_note (
1188- self . tcx . sess . source_map ( ) . guess_head_span ( span) ,
1189- & format ! (
1190- "`{}` defines an item `{}`, perhaps you need to {} it" ,
1191- self . tcx. def_path_str( trait_info. def_id) ,
1192- item_name,
1193- action
1194- ) ,
1195- ) ;
1196- use_note = false
1178+ let ( potential_candidates, explicitly_negative) = if param_type. is_some ( ) {
1179+ // FIXME: Even though negative bounds are not implemented, we could maybe handle
1180+ // cases where a positive bound implies a negative impl.
1181+ ( candidates, Vec :: new ( ) )
1182+ } else if let Some ( simp_rcvr_ty) = simplify_type ( self . tcx , rcvr_ty, true ) {
1183+ let mut potential_candidates = Vec :: new ( ) ;
1184+ let mut explicitly_negative = Vec :: new ( ) ;
1185+ for candidate in candidates {
1186+ // Check if there's a negative impl of `candidate` for `rcvr_ty`
1187+ if self
1188+ . tcx
1189+ . all_impls ( candidate. def_id )
1190+ . filter ( |imp_did| {
1191+ self . tcx . impl_polarity ( * imp_did) == ty:: ImplPolarity :: Negative
1192+ } )
1193+ . any ( |imp_did| {
1194+ let imp = self . tcx . impl_trait_ref ( imp_did) . unwrap ( ) ;
1195+ let imp_simp = simplify_type ( self . tcx , imp. self_ty ( ) , true ) ;
1196+ imp_simp. map ( |s| s == simp_rcvr_ty) . unwrap_or ( false )
1197+ } )
1198+ {
1199+ explicitly_negative. push ( candidate) ;
1200+ } else {
1201+ potential_candidates. push ( candidate) ;
11971202 }
11981203 }
1199- if use_note {
1204+ ( potential_candidates, explicitly_negative)
1205+ } else {
1206+ // We don't know enough about `recv_ty` to make proper suggestions.
1207+ ( candidates, Vec :: new ( ) )
1208+ } ;
1209+
1210+ let action = if let Some ( param) = param_type {
1211+ format ! ( "restrict type parameter `{}` with" , param)
1212+ } else {
1213+ // FIXME: it might only need to be imported into scope, not implemented.
1214+ "implement" . to_string ( )
1215+ } ;
1216+ match & potential_candidates[ ..] {
1217+ [ ] => { }
1218+ [ trait_info] if trait_info. def_id . is_local ( ) => {
1219+ let span = self . tcx . hir ( ) . span_if_local ( trait_info. def_id ) . unwrap ( ) ;
1220+ err. span_note (
1221+ self . tcx . sess . source_map ( ) . guess_head_span ( span) ,
1222+ & format ! (
1223+ "`{}` defines an item `{}`, perhaps you need to {} it" ,
1224+ self . tcx. def_path_str( trait_info. def_id) ,
1225+ item_name,
1226+ action
1227+ ) ,
1228+ ) ;
1229+ }
1230+ trait_infos => {
12001231 let mut msg = message ( action) ;
1201- for ( i, trait_info) in candidates . iter ( ) . enumerate ( ) {
1232+ for ( i, trait_info) in trait_infos . iter ( ) . enumerate ( ) {
12021233 msg. push_str ( & format ! (
12031234 "\n candidate #{}: `{}`" ,
12041235 i + 1 ,
12051236 self . tcx. def_path_str( trait_info. def_id) ,
12061237 ) ) ;
12071238 }
1208- err. note ( & msg[ ..] ) ;
1239+ err. note ( & msg) ;
1240+ }
1241+ }
1242+ match & explicitly_negative[ ..] {
1243+ [ ] => { }
1244+ [ trait_info] => {
1245+ let msg = format ! (
1246+ "the trait `{}` defines an item `{}`, but is explicitely unimplemented" ,
1247+ self . tcx. def_path_str( trait_info. def_id) ,
1248+ item_name
1249+ ) ;
1250+ err. note ( & msg) ;
1251+ }
1252+ trait_infos => {
1253+ let mut msg = format ! (
1254+ "the following traits define an item `{}`, but are explicitely unimplemented:" ,
1255+ item_name
1256+ ) ;
1257+ for trait_info in trait_infos {
1258+ msg. push_str ( & format ! ( "\n {}" , self . tcx. def_path_str( trait_info. def_id) ) ) ;
1259+ }
1260+ err. note ( & msg) ;
12091261 }
12101262 }
12111263 }
0 commit comments