@@ -40,10 +40,13 @@ use suggestions::InferCtxtExt as _;
4040pub use rustc_infer:: traits:: error_reporting:: * ;
4141
4242// When outputting impl candidates, prefer showing those that are more similar.
43+ //
44+ // We also compare candidates after skipping lifetimes, which has a lower
45+ // priority than exact matches.
4346#[ derive( Debug , Copy , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
4447pub enum CandidateSimilarity {
45- Exact ,
46- Fuzzy ,
48+ Exact { ignoring_lifetimes : bool } ,
49+ Fuzzy { ignoring_lifetimes : bool } ,
4750}
4851
4952#[ derive( Debug , Clone , Copy ) ]
@@ -1155,7 +1158,12 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
11551158 error : & MismatchedProjectionTypes < ' tcx > ,
11561159 ) ;
11571160
1158- fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> bool ;
1161+ fn fuzzy_match_tys (
1162+ & self ,
1163+ a : Ty < ' tcx > ,
1164+ b : Ty < ' tcx > ,
1165+ ignoring_lifetimes : bool ,
1166+ ) -> Option < CandidateSimilarity > ;
11591167
11601168 fn describe_generator ( & self , body_id : hir:: BodyId ) -> Option < & ' static str > ;
11611169
@@ -1458,24 +1466,32 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
14581466 } ) ;
14591467 }
14601468
1461- fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> bool {
1469+ fn fuzzy_match_tys (
1470+ & self ,
1471+ mut a : Ty < ' tcx > ,
1472+ mut b : Ty < ' tcx > ,
1473+ ignoring_lifetimes : bool ,
1474+ ) -> Option < CandidateSimilarity > {
14621475 /// returns the fuzzy category of a given type, or None
14631476 /// if the type can be equated to any type.
1464- fn type_category ( t : Ty < ' _ > ) -> Option < u32 > {
1477+ fn type_category ( tcx : TyCtxt < ' _ > , t : Ty < ' _ > ) -> Option < u32 > {
14651478 match t. kind ( ) {
14661479 ty:: Bool => Some ( 0 ) ,
14671480 ty:: Char => Some ( 1 ) ,
14681481 ty:: Str => Some ( 2 ) ,
1469- ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Infer ( ty:: IntVar ( ..) ) => Some ( 3 ) ,
1470- ty:: Float ( ..) | ty:: Infer ( ty:: FloatVar ( ..) ) => Some ( 4 ) ,
1482+ ty:: Adt ( def, _) if tcx. is_diagnostic_item ( sym:: String , def. did ) => Some ( 2 ) ,
1483+ ty:: Int ( ..)
1484+ | ty:: Uint ( ..)
1485+ | ty:: Float ( ..)
1486+ | ty:: Infer ( ty:: IntVar ( ..) | ty:: FloatVar ( ..) ) => Some ( 4 ) ,
14711487 ty:: Ref ( ..) | ty:: RawPtr ( ..) => Some ( 5 ) ,
14721488 ty:: Array ( ..) | ty:: Slice ( ..) => Some ( 6 ) ,
14731489 ty:: FnDef ( ..) | ty:: FnPtr ( ..) => Some ( 7 ) ,
14741490 ty:: Dynamic ( ..) => Some ( 8 ) ,
14751491 ty:: Closure ( ..) => Some ( 9 ) ,
14761492 ty:: Tuple ( ..) => Some ( 10 ) ,
1477- ty:: Projection ( ..) => Some ( 11 ) ,
1478- ty:: Param ( ..) => Some ( 12 ) ,
1493+ ty:: Param ( ..) => Some ( 11 ) ,
1494+ ty:: Projection ( ..) => Some ( 12 ) ,
14791495 ty:: Opaque ( ..) => Some ( 13 ) ,
14801496 ty:: Never => Some ( 14 ) ,
14811497 ty:: Adt ( ..) => Some ( 15 ) ,
@@ -1497,17 +1513,33 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
14971513 }
14981514 } ;
14991515
1500- match ( type_category ( a) , type_category ( b) ) {
1501- ( Some ( cat_a) , Some ( cat_b) ) => match ( a. kind ( ) , b. kind ( ) ) {
1516+ if !ignoring_lifetimes {
1517+ a = strip_references ( a) ;
1518+ b = strip_references ( b) ;
1519+ }
1520+
1521+ let cat_a = type_category ( self . tcx , a) ?;
1522+ let cat_b = type_category ( self . tcx , b) ?;
1523+ if a == b {
1524+ Some ( CandidateSimilarity :: Exact { ignoring_lifetimes } )
1525+ } else if cat_a == cat_b {
1526+ match ( a. kind ( ) , b. kind ( ) ) {
15021527 ( ty:: Adt ( def_a, _) , ty:: Adt ( def_b, _) ) => def_a == def_b,
1503- _ if cat_a == cat_b => true ,
1504- ( ty:: Ref ( ..) , _) | ( _, ty:: Ref ( ..) ) => {
1505- self . fuzzy_match_tys ( strip_references ( a) , strip_references ( b) )
1528+ // Matching on references results in a lot of unhelpful
1529+ // suggestions, so let's just not do that for now.
1530+ //
1531+ // We still upgrade successful matches to `ignoring_lifetimes: true`
1532+ // to prioritize that impl.
1533+ ( ty:: Ref ( ..) | ty:: RawPtr ( ..) , ty:: Ref ( ..) | ty:: RawPtr ( ..) ) => {
1534+ self . fuzzy_match_tys ( a, b, true ) . is_some ( )
15061535 }
1507- _ => false ,
1508- } ,
1509- // infer and error can be equated to all types
1510- _ => true ,
1536+ _ => true ,
1537+ }
1538+ . then_some ( CandidateSimilarity :: Fuzzy { ignoring_lifetimes } )
1539+ } else if ignoring_lifetimes {
1540+ None
1541+ } else {
1542+ self . fuzzy_match_tys ( a, b, true )
15111543 }
15121544 }
15131545
@@ -1533,22 +1565,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
15331565
15341566 let imp = self . tcx . impl_trait_ref ( def_id) . unwrap ( ) ;
15351567
1536- // Check for exact match.
1537- if trait_ref. skip_binder ( ) . self_ty ( ) == imp. self_ty ( ) {
1538- return Some ( ImplCandidate {
1539- trait_ref : imp,
1540- similarity : CandidateSimilarity :: Exact ,
1541- } ) ;
1542- }
1543-
1544- if self . fuzzy_match_tys ( trait_ref. skip_binder ( ) . self_ty ( ) , imp. self_ty ( ) ) {
1545- return Some ( ImplCandidate {
1546- trait_ref : imp,
1547- similarity : CandidateSimilarity :: Fuzzy ,
1548- } ) ;
1549- }
1550-
1551- None
1568+ self . fuzzy_match_tys ( trait_ref. skip_binder ( ) . self_ty ( ) , imp. self_ty ( ) , false )
1569+ . map ( |similarity| ImplCandidate { trait_ref : imp, similarity } )
15521570 } )
15531571 . collect ( )
15541572 }
0 commit comments