@@ -40,6 +40,21 @@ use suggestions::InferCtxtExt as _;
40
40
41
41
pub use rustc_infer:: traits:: error_reporting:: * ;
42
42
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
+
43
58
pub trait InferCtxtExt < ' tcx > {
44
59
fn report_fulfillment_errors (
45
60
& self ,
@@ -1084,18 +1099,18 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
1084
1099
error : & MismatchedProjectionTypes < ' tcx > ,
1085
1100
) ;
1086
1101
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 ;
1088
1103
1089
1104
fn describe_generator ( & self , body_id : hir:: BodyId ) -> Option < & ' static str > ;
1090
1105
1091
1106
fn find_similar_impl_candidates (
1092
1107
& self ,
1093
1108
trait_ref : ty:: PolyTraitRef < ' tcx > ,
1094
- ) -> Vec < ty :: TraitRef < ' tcx > > ;
1109
+ ) -> Vec < ImplCandidate < ' tcx > > ;
1095
1110
1096
1111
fn report_similar_impl_candidates (
1097
1112
& self ,
1098
- impl_candidates : Vec < ty :: TraitRef < ' tcx > > ,
1113
+ impl_candidates : Vec < ImplCandidate < ' tcx > > ,
1099
1114
err : & mut DiagnosticBuilder < ' _ > ,
1100
1115
) ;
1101
1116
@@ -1389,7 +1404,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
1389
1404
} ) ;
1390
1405
}
1391
1406
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 {
1393
1408
/// returns the fuzzy category of a given type, or None
1394
1409
/// if the type can be equated to any type.
1395
1410
fn type_category ( t : Ty < ' _ > ) -> Option < u32 > {
@@ -1421,6 +1436,23 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
1421
1436
}
1422
1437
}
1423
1438
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
+
1424
1456
match ( type_category ( a) , type_category ( b) ) {
1425
1457
( Some ( cat_a) , Some ( cat_b) ) => match ( a. kind ( ) , b. kind ( ) ) {
1426
1458
( & 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> {
1443
1475
fn find_similar_impl_candidates (
1444
1476
& self ,
1445
1477
trait_ref : ty:: PolyTraitRef < ' tcx > ,
1446
- ) -> Vec < ty :: TraitRef < ' tcx > > {
1478
+ ) -> Vec < ImplCandidate < ' tcx > > {
1447
1479
// We simplify params and strip references here.
1448
1480
//
1449
1481
// This both removes a lot of unhelpful suggestions, e.g.
@@ -1461,40 +1493,75 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
1461
1493
let all_impls = self . tcx . all_impls ( trait_ref. def_id ( ) ) ;
1462
1494
1463
1495
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 {
1475
1500
return None ;
1476
1501
}
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
+ }
1484
1548
None => all_impls
1485
1549
. filter_map ( |def_id| {
1486
1550
if self . tcx . impl_polarity ( def_id) == ty:: ImplPolarity :: Negative {
1487
1551
return None ;
1488
1552
}
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
+ } )
1490
1557
} )
1491
1558
. collect ( ) ,
1492
1559
}
1493
1560
}
1494
1561
1495
1562
fn report_similar_impl_candidates (
1496
1563
& self ,
1497
- impl_candidates : Vec < ty :: TraitRef < ' tcx > > ,
1564
+ impl_candidates : Vec < ImplCandidate < ' tcx > > ,
1498
1565
err : & mut DiagnosticBuilder < ' _ > ,
1499
1566
) {
1500
1567
if impl_candidates. is_empty ( ) {
@@ -1518,13 +1585,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
1518
1585
} ;
1519
1586
1520
1587
// 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,
1525
1588
// because the ordering of `impl_candidates` may not be deterministic:
1526
1589
// 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 < _ > > ( ) ;
1528
1606
1529
1607
err. help ( & format ! (
1530
1608
"the following implementations were found:{}{}" ,
@@ -1688,7 +1766,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
1688
1766
return ;
1689
1767
}
1690
1768
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 ( ) ;
1692
1774
let mut err = self . emit_inference_failure_err (
1693
1775
body_id,
1694
1776
span,
0 commit comments