@@ -40,6 +40,21 @@ use suggestions::InferCtxtExt as _;
4040
4141pub use rustc_infer:: traits:: error_reporting:: * ;
4242
43+ // When outputting impl candidates, prefer showing those that are more similar.
44+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
45+ pub enum CandidateSimilarity {
46+ Exact ,
47+ Simplified ,
48+ Fuzzy ,
49+ Unknown ,
50+ }
51+
52+ #[ derive( Debug , Clone , Copy ) ]
53+ pub struct ImplCandidate < ' tcx > {
54+ pub trait_ref : ty:: TraitRef < ' tcx > ,
55+ pub similarity : CandidateSimilarity ,
56+ }
57+
4358pub trait InferCtxtExt < ' tcx > {
4459 fn report_fulfillment_errors (
4560 & self ,
@@ -1084,18 +1099,18 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
10841099 error : & MismatchedProjectionTypes < ' tcx > ,
10851100 ) ;
10861101
1087- fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> bool ;
1102+ fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > , strip_references : StripReferences ) -> bool ;
10881103
10891104 fn describe_generator ( & self , body_id : hir:: BodyId ) -> Option < & ' static str > ;
10901105
10911106 fn find_similar_impl_candidates (
10921107 & self ,
10931108 trait_ref : ty:: PolyTraitRef < ' tcx > ,
1094- ) -> Vec < ty :: TraitRef < ' tcx > > ;
1109+ ) -> Vec < ImplCandidate < ' tcx > > ;
10951110
10961111 fn report_similar_impl_candidates (
10971112 & self ,
1098- impl_candidates : Vec < ty :: TraitRef < ' tcx > > ,
1113+ impl_candidates : Vec < ImplCandidate < ' tcx > > ,
10991114 err : & mut DiagnosticBuilder < ' _ > ,
11001115 ) ;
11011116
@@ -1389,7 +1404,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
13891404 } ) ;
13901405 }
13911406
1392- fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> bool {
1407+ fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > , strip_references : StripReferences ) -> bool {
13931408 /// returns the fuzzy category of a given type, or None
13941409 /// if the type can be equated to any type.
13951410 fn type_category ( t : Ty < ' _ > ) -> Option < u32 > {
@@ -1421,6 +1436,23 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
14211436 }
14221437 }
14231438
1439+ let strip_reference = |mut t : Ty < ' tcx > | -> Ty < ' tcx > {
1440+ loop {
1441+ match t. kind ( ) {
1442+ ty:: Ref ( _, inner, _) | ty:: RawPtr ( ty:: TypeAndMut { ty : inner, .. } ) => {
1443+ t = inner
1444+ }
1445+ _ => break t,
1446+ }
1447+ }
1448+ } ;
1449+
1450+ let ( a, b) = if strip_references == StripReferences :: Yes {
1451+ ( strip_reference ( a) , strip_reference ( b) )
1452+ } else {
1453+ ( a, b)
1454+ } ;
1455+
14241456 match ( type_category ( a) , type_category ( b) ) {
14251457 ( Some ( cat_a) , Some ( cat_b) ) => match ( a. kind ( ) , b. kind ( ) ) {
14261458 ( & ty:: Adt ( def_a, _) , & ty:: Adt ( def_b, _) ) => def_a == def_b,
@@ -1443,7 +1475,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
14431475 fn find_similar_impl_candidates (
14441476 & self ,
14451477 trait_ref : ty:: PolyTraitRef < ' tcx > ,
1446- ) -> Vec < ty :: TraitRef < ' tcx > > {
1478+ ) -> Vec < ImplCandidate < ' tcx > > {
14471479 // We simplify params and strip references here.
14481480 //
14491481 // This both removes a lot of unhelpful suggestions, e.g.
@@ -1461,40 +1493,75 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
14611493 let all_impls = self . tcx . all_impls ( trait_ref. def_id ( ) ) ;
14621494
14631495 match simp {
1464- Some ( simp) => all_impls
1465- . filter_map ( |def_id| {
1466- let imp = self . tcx . impl_trait_ref ( def_id) . unwrap ( ) ;
1467- let imp_simp = fast_reject:: simplify_type (
1468- self . tcx ,
1469- imp. self_ty ( ) ,
1470- SimplifyParams :: Yes ,
1471- StripReferences :: Yes ,
1472- ) ;
1473- if let Some ( imp_simp) = imp_simp {
1474- if simp != imp_simp {
1496+ Some ( simp) => {
1497+ all_impls
1498+ . filter_map ( |def_id| {
1499+ if self . tcx . impl_polarity ( def_id) == ty:: ImplPolarity :: Negative {
14751500 return None ;
14761501 }
1477- }
1478- if self . tcx . impl_polarity ( def_id) == ty:: ImplPolarity :: Negative {
1479- return None ;
1480- }
1481- Some ( imp)
1482- } )
1483- . collect ( ) ,
1502+
1503+ let imp = self . tcx . impl_trait_ref ( def_id) . unwrap ( ) ;
1504+
1505+ // Check for exact match.
1506+ if trait_ref. skip_binder ( ) . self_ty ( ) == imp. self_ty ( ) {
1507+ return Some ( ImplCandidate {
1508+ trait_ref : imp,
1509+ similarity : CandidateSimilarity :: Exact ,
1510+ } ) ;
1511+ }
1512+
1513+ // Check for match between simplified types.
1514+ let imp_simp = fast_reject:: simplify_type (
1515+ self . tcx ,
1516+ imp. self_ty ( ) ,
1517+ SimplifyParams :: Yes ,
1518+ StripReferences :: Yes ,
1519+ ) ;
1520+ if let Some ( imp_simp) = imp_simp {
1521+ if simp == imp_simp {
1522+ return Some ( ImplCandidate {
1523+ trait_ref : imp,
1524+ similarity : CandidateSimilarity :: Simplified ,
1525+ } ) ;
1526+ }
1527+ }
1528+
1529+ // Check for fuzzy match.
1530+ // Pass `StripReferences::Yes` because although we do want to
1531+ // be fuzzier than `simplify_type`, we don't want to be
1532+ // *too* fuzzy.
1533+ if self . fuzzy_match_tys (
1534+ trait_ref. skip_binder ( ) . self_ty ( ) ,
1535+ imp. self_ty ( ) ,
1536+ StripReferences :: Yes ,
1537+ ) {
1538+ return Some ( ImplCandidate {
1539+ trait_ref : imp,
1540+ similarity : CandidateSimilarity :: Fuzzy ,
1541+ } ) ;
1542+ }
1543+
1544+ None
1545+ } )
1546+ . collect ( )
1547+ }
14841548 None => all_impls
14851549 . filter_map ( |def_id| {
14861550 if self . tcx . impl_polarity ( def_id) == ty:: ImplPolarity :: Negative {
14871551 return None ;
14881552 }
1489- self . tcx . impl_trait_ref ( def_id)
1553+ self . tcx . impl_trait_ref ( def_id) . map ( |trait_ref| ImplCandidate {
1554+ trait_ref,
1555+ similarity : CandidateSimilarity :: Unknown ,
1556+ } )
14901557 } )
14911558 . collect ( ) ,
14921559 }
14931560 }
14941561
14951562 fn report_similar_impl_candidates (
14961563 & self ,
1497- impl_candidates : Vec < ty :: TraitRef < ' tcx > > ,
1564+ impl_candidates : Vec < ImplCandidate < ' tcx > > ,
14981565 err : & mut DiagnosticBuilder < ' _ > ,
14991566 ) {
15001567 if impl_candidates. is_empty ( ) {
@@ -1518,13 +1585,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
15181585 } ;
15191586
15201587 // Sort impl candidates so that ordering is consistent for UI tests.
1521- let mut normalized_impl_candidates =
1522- impl_candidates. iter ( ) . copied ( ) . map ( normalize) . collect :: < Vec < String > > ( ) ;
1523-
1524- // Sort before taking the `..end` range,
15251588 // because the ordering of `impl_candidates` may not be deterministic:
15261589 // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507
1527- normalized_impl_candidates. sort ( ) ;
1590+ //
1591+ // Prefer more similar candidates first, then sort lexicographically
1592+ // by their normalized string representation.
1593+ let mut normalized_impl_candidates_and_similarities = impl_candidates
1594+ . into_iter ( )
1595+ . map ( |ImplCandidate { trait_ref, similarity } | {
1596+ let normalized = normalize ( trait_ref) ;
1597+ ( similarity, normalized)
1598+ } )
1599+ . collect :: < Vec < _ > > ( ) ;
1600+ normalized_impl_candidates_and_similarities. sort ( ) ;
1601+
1602+ let normalized_impl_candidates = normalized_impl_candidates_and_similarities
1603+ . into_iter ( )
1604+ . map ( |( _, normalized) | normalized)
1605+ . collect :: < Vec < _ > > ( ) ;
15281606
15291607 err. help ( & format ! (
15301608 "the following implementations were found:{}{}" ,
@@ -1688,7 +1766,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
16881766 return ;
16891767 }
16901768
1691- let impl_candidates = self . find_similar_impl_candidates ( trait_ref) ;
1769+ let impl_candidates = self
1770+ . find_similar_impl_candidates ( trait_ref)
1771+ . into_iter ( )
1772+ . map ( |candidate| candidate. trait_ref )
1773+ . collect ( ) ;
16921774 let mut err = self . emit_inference_failure_err (
16931775 body_id,
16941776 span,
0 commit comments