@@ -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 ,
@@ -1143,18 +1158,18 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
11431158 error : & MismatchedProjectionTypes < ' tcx > ,
11441159 ) ;
11451160
1146- fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> bool ;
1161+ fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > , strip_references : StripReferences ) -> bool ;
11471162
11481163 fn describe_generator ( & self , body_id : hir:: BodyId ) -> Option < & ' static str > ;
11491164
11501165 fn find_similar_impl_candidates (
11511166 & self ,
11521167 trait_ref : ty:: PolyTraitRef < ' tcx > ,
1153- ) -> Vec < ty :: TraitRef < ' tcx > > ;
1168+ ) -> Vec < ImplCandidate < ' tcx > > ;
11541169
11551170 fn report_similar_impl_candidates (
11561171 & self ,
1157- impl_candidates : Vec < ty :: TraitRef < ' tcx > > ,
1172+ impl_candidates : Vec < ImplCandidate < ' tcx > > ,
11581173 err : & mut DiagnosticBuilder < ' _ > ,
11591174 ) ;
11601175
@@ -1446,7 +1461,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
14461461 } ) ;
14471462 }
14481463
1449- fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> bool {
1464+ fn fuzzy_match_tys ( & self , a : Ty < ' tcx > , b : Ty < ' tcx > , strip_references : StripReferences ) -> bool {
14501465 /// returns the fuzzy category of a given type, or None
14511466 /// if the type can be equated to any type.
14521467 fn type_category ( t : Ty < ' _ > ) -> Option < u32 > {
@@ -1478,6 +1493,23 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
14781493 }
14791494 }
14801495
1496+ let strip_reference = |mut t : Ty < ' tcx > | -> Ty < ' tcx > {
1497+ loop {
1498+ match t. kind ( ) {
1499+ ty:: Ref ( _, inner, _) | ty:: RawPtr ( ty:: TypeAndMut { ty : inner, .. } ) => {
1500+ t = inner
1501+ }
1502+ _ => break t,
1503+ }
1504+ }
1505+ } ;
1506+
1507+ let ( a, b) = if strip_references == StripReferences :: Yes {
1508+ ( strip_reference ( a) , strip_reference ( b) )
1509+ } else {
1510+ ( a, b)
1511+ } ;
1512+
14811513 match ( type_category ( a) , type_category ( b) ) {
14821514 ( Some ( cat_a) , Some ( cat_b) ) => match ( a. kind ( ) , b. kind ( ) ) {
14831515 ( & ty:: Adt ( def_a, _) , & ty:: Adt ( def_b, _) ) => def_a == def_b,
@@ -1500,7 +1532,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
15001532 fn find_similar_impl_candidates (
15011533 & self ,
15021534 trait_ref : ty:: PolyTraitRef < ' tcx > ,
1503- ) -> Vec < ty :: TraitRef < ' tcx > > {
1535+ ) -> Vec < ImplCandidate < ' tcx > > {
15041536 // We simplify params and strip references here.
15051537 //
15061538 // This both removes a lot of unhelpful suggestions, e.g.
@@ -1518,40 +1550,75 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
15181550 let all_impls = self . tcx . all_impls ( trait_ref. def_id ( ) ) ;
15191551
15201552 match simp {
1521- Some ( simp) => all_impls
1522- . filter_map ( |def_id| {
1523- let imp = self . tcx . impl_trait_ref ( def_id) . unwrap ( ) ;
1524- let imp_simp = fast_reject:: simplify_type (
1525- self . tcx ,
1526- imp. self_ty ( ) ,
1527- SimplifyParams :: Yes ,
1528- StripReferences :: Yes ,
1529- ) ;
1530- if let Some ( imp_simp) = imp_simp {
1531- if simp != imp_simp {
1553+ Some ( simp) => {
1554+ all_impls
1555+ . filter_map ( |def_id| {
1556+ if self . tcx . impl_polarity ( def_id) == ty:: ImplPolarity :: Negative {
15321557 return None ;
15331558 }
1534- }
1535- if self . tcx . impl_polarity ( def_id) == ty:: ImplPolarity :: Negative {
1536- return None ;
1537- }
1538- Some ( imp)
1539- } )
1540- . collect ( ) ,
1559+
1560+ let imp = self . tcx . impl_trait_ref ( def_id) . unwrap ( ) ;
1561+
1562+ // Check for exact match.
1563+ if trait_ref. skip_binder ( ) . self_ty ( ) == imp. self_ty ( ) {
1564+ return Some ( ImplCandidate {
1565+ trait_ref : imp,
1566+ similarity : CandidateSimilarity :: Exact ,
1567+ } ) ;
1568+ }
1569+
1570+ // Check for match between simplified types.
1571+ let imp_simp = fast_reject:: simplify_type (
1572+ self . tcx ,
1573+ imp. self_ty ( ) ,
1574+ SimplifyParams :: Yes ,
1575+ StripReferences :: Yes ,
1576+ ) ;
1577+ if let Some ( imp_simp) = imp_simp {
1578+ if simp == imp_simp {
1579+ return Some ( ImplCandidate {
1580+ trait_ref : imp,
1581+ similarity : CandidateSimilarity :: Simplified ,
1582+ } ) ;
1583+ }
1584+ }
1585+
1586+ // Check for fuzzy match.
1587+ // Pass `StripReferences::Yes` because although we do want to
1588+ // be fuzzier than `simplify_type`, we don't want to be
1589+ // *too* fuzzy.
1590+ if self . fuzzy_match_tys (
1591+ trait_ref. skip_binder ( ) . self_ty ( ) ,
1592+ imp. self_ty ( ) ,
1593+ StripReferences :: Yes ,
1594+ ) {
1595+ return Some ( ImplCandidate {
1596+ trait_ref : imp,
1597+ similarity : CandidateSimilarity :: Fuzzy ,
1598+ } ) ;
1599+ }
1600+
1601+ None
1602+ } )
1603+ . collect ( )
1604+ }
15411605 None => all_impls
15421606 . filter_map ( |def_id| {
15431607 if self . tcx . impl_polarity ( def_id) == ty:: ImplPolarity :: Negative {
15441608 return None ;
15451609 }
1546- self . tcx . impl_trait_ref ( def_id)
1610+ self . tcx . impl_trait_ref ( def_id) . map ( |trait_ref| ImplCandidate {
1611+ trait_ref,
1612+ similarity : CandidateSimilarity :: Unknown ,
1613+ } )
15471614 } )
15481615 . collect ( ) ,
15491616 }
15501617 }
15511618
15521619 fn report_similar_impl_candidates (
15531620 & self ,
1554- impl_candidates : Vec < ty :: TraitRef < ' tcx > > ,
1621+ impl_candidates : Vec < ImplCandidate < ' tcx > > ,
15551622 err : & mut DiagnosticBuilder < ' _ > ,
15561623 ) {
15571624 if impl_candidates. is_empty ( ) {
@@ -1575,13 +1642,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
15751642 } ;
15761643
15771644 // Sort impl candidates so that ordering is consistent for UI tests.
1578- let mut normalized_impl_candidates =
1579- impl_candidates. iter ( ) . copied ( ) . map ( normalize) . collect :: < Vec < String > > ( ) ;
1580-
1581- // Sort before taking the `..end` range,
15821645 // because the ordering of `impl_candidates` may not be deterministic:
15831646 // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507
1584- normalized_impl_candidates. sort ( ) ;
1647+ //
1648+ // Prefer more similar candidates first, then sort lexicographically
1649+ // by their normalized string representation.
1650+ let mut normalized_impl_candidates_and_similarities = impl_candidates
1651+ . into_iter ( )
1652+ . map ( |ImplCandidate { trait_ref, similarity } | {
1653+ let normalized = normalize ( trait_ref) ;
1654+ ( similarity, normalized)
1655+ } )
1656+ . collect :: < Vec < _ > > ( ) ;
1657+ normalized_impl_candidates_and_similarities. sort ( ) ;
1658+
1659+ let normalized_impl_candidates = normalized_impl_candidates_and_similarities
1660+ . into_iter ( )
1661+ . map ( |( _, normalized) | normalized)
1662+ . collect :: < Vec < _ > > ( ) ;
15851663
15861664 err. help ( & format ! (
15871665 "the following implementations were found:{}{}" ,
@@ -1744,7 +1822,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
17441822 return ;
17451823 }
17461824
1747- let impl_candidates = self . find_similar_impl_candidates ( trait_ref) ;
1825+ let impl_candidates = self
1826+ . find_similar_impl_candidates ( trait_ref)
1827+ . into_iter ( )
1828+ . map ( |candidate| candidate. trait_ref )
1829+ . collect ( ) ;
17481830 let mut err = self . emit_inference_failure_err (
17491831 body_id,
17501832 span,
0 commit comments