@@ -13,7 +13,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
1313use rustc_data_structures:: sorted_map:: SortedMap ;
1414use rustc_data_structures:: unord:: UnordSet ;
1515use rustc_errors:: codes:: * ;
16- use rustc_errors:: { Applicability , Diag , MultiSpan , StashKey , pluralize, struct_span_code_err} ;
16+ use rustc_errors:: {
17+ Applicability , Diag , MultiSpan , StashKey , listify, pluralize, struct_span_code_err,
18+ } ;
1719use rustc_hir:: attrs:: AttributeKind ;
1820use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
1921use rustc_hir:: def_id:: DefId ;
@@ -50,6 +52,51 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
5052use crate :: method:: probe:: UnsatisfiedPredicates ;
5153use crate :: { Expectation , FnCtxt } ;
5254
55+ /// Tracks trait bounds and detects duplicates between ref and non-ref versions of self types.
56+ /// This is used to condense error messages when the same trait bound appears for both
57+ /// `T` and `&T` (or `&mut T`).
58+ struct TraitBoundDuplicateTracker {
59+ trait_def_ids : FxIndexSet < DefId > ,
60+ seen_ref : FxIndexSet < DefId > ,
61+ seen_non_ref : FxIndexSet < DefId > ,
62+ has_ref_dupes : bool ,
63+ }
64+
65+ impl TraitBoundDuplicateTracker {
66+ fn new ( ) -> Self {
67+ Self {
68+ trait_def_ids : FxIndexSet :: default ( ) ,
69+ seen_ref : FxIndexSet :: default ( ) ,
70+ seen_non_ref : FxIndexSet :: default ( ) ,
71+ has_ref_dupes : false ,
72+ }
73+ }
74+
75+ /// Track a trait bound. `is_ref` indicates whether the self type is a reference.
76+ fn track ( & mut self , def_id : DefId , is_ref : bool ) {
77+ self . trait_def_ids . insert ( def_id) ;
78+ if is_ref {
79+ if self . seen_non_ref . contains ( & def_id) {
80+ self . has_ref_dupes = true ;
81+ }
82+ self . seen_ref . insert ( def_id) ;
83+ } else {
84+ if self . seen_ref . contains ( & def_id) {
85+ self . has_ref_dupes = true ;
86+ }
87+ self . seen_non_ref . insert ( def_id) ;
88+ }
89+ }
90+
91+ fn has_ref_dupes ( & self ) -> bool {
92+ self . has_ref_dupes
93+ }
94+
95+ fn into_trait_def_ids ( self ) -> FxIndexSet < DefId > {
96+ self . trait_def_ids
97+ }
98+ }
99+
53100impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
54101 fn is_slice_ty ( & self , ty : Ty < ' tcx > , span : Span ) -> bool {
55102 self . autoderef ( span, ty)
@@ -1004,6 +1051,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10041051 item_ident : Ident ,
10051052 item_kind : & str ,
10061053 bound_spans : SortedMap < Span , Vec < String > > ,
1054+ unsatisfied_predicates : & UnsatisfiedPredicates < ' tcx > ,
10071055 ) {
10081056 let mut ty_span = match rcvr_ty. kind ( ) {
10091057 ty:: Param ( param_type) => {
@@ -1012,13 +1060,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10121060 ty:: Adt ( def, _) if def. did ( ) . is_local ( ) => Some ( self . tcx . def_span ( def. did ( ) ) ) ,
10131061 _ => None ,
10141062 } ;
1063+ let rcvr_ty_str = self . tcx . short_string ( rcvr_ty, err. long_ty_path ( ) ) ;
1064+ let mut tracker = TraitBoundDuplicateTracker :: new ( ) ;
1065+ for ( predicate, _parent_pred, _cause) in unsatisfied_predicates {
1066+ if let ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Trait ( pred) ) =
1067+ predicate. kind ( ) . skip_binder ( )
1068+ && let self_ty = pred. trait_ref . self_ty ( )
1069+ && self_ty. peel_refs ( ) == rcvr_ty
1070+ {
1071+ let is_ref = matches ! ( self_ty. kind( ) , ty:: Ref ( ..) ) ;
1072+ tracker. track ( pred. trait_ref . def_id , is_ref) ;
1073+ }
1074+ }
1075+ let has_ref_dupes = tracker. has_ref_dupes ( ) ;
1076+ let mut missing_trait_names = tracker
1077+ . into_trait_def_ids ( )
1078+ . into_iter ( )
1079+ . map ( |def_id| format ! ( "`{}`" , self . tcx. def_path_str( def_id) ) )
1080+ . collect :: < Vec < _ > > ( ) ;
1081+ missing_trait_names. sort ( ) ;
1082+ let should_condense =
1083+ has_ref_dupes && missing_trait_names. len ( ) > 1 && matches ! ( rcvr_ty. kind( ) , ty:: Adt ( ..) ) ;
1084+ let missing_trait_list = if should_condense {
1085+ Some ( match missing_trait_names. as_slice ( ) {
1086+ [ only] => only. clone ( ) ,
1087+ [ first, second] => format ! ( "{first} or {second}" ) ,
1088+ [ rest @ .., last] => format ! ( "{} or {last}" , rest. join( ", " ) ) ,
1089+ [ ] => String :: new ( ) ,
1090+ } )
1091+ } else {
1092+ None
1093+ } ;
10151094 for ( span, mut bounds) in bound_spans {
10161095 if !self . tcx . sess . source_map ( ) . is_span_accessible ( span) {
10171096 continue ;
10181097 }
10191098 bounds. sort ( ) ;
10201099 bounds. dedup ( ) ;
1021- let pre = if Some ( span) == ty_span {
1100+ let is_ty_span = Some ( span) == ty_span;
1101+ if is_ty_span && should_condense {
1102+ ty_span. take ( ) ;
1103+ let label = if let Some ( missing_trait_list) = & missing_trait_list {
1104+ format ! (
1105+ "{item_kind} `{item_ident}` not found for this {} because `{rcvr_ty_str}` doesn't implement {missing_trait_list}" ,
1106+ rcvr_ty. prefix_string( self . tcx)
1107+ )
1108+ } else {
1109+ format ! (
1110+ "{item_kind} `{item_ident}` not found for this {}" ,
1111+ rcvr_ty. prefix_string( self . tcx)
1112+ )
1113+ } ;
1114+ err. span_label ( span, label) ;
1115+ continue ;
1116+ }
1117+ let pre = if is_ty_span {
10221118 ty_span. take ( ) ;
10231119 format ! (
10241120 "{item_kind} `{item_ident}` not found for this {} because it " ,
@@ -1248,6 +1344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12481344 item_ident,
12491345 item_kind,
12501346 bound_spans,
1347+ unsatisfied_predicates,
12511348 ) ;
12521349
12531350 self . note_derefed_ty_has_method ( & mut err, source, rcvr_ty, item_ident, expected) ;
@@ -1507,6 +1604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15071604 bound_spans : & mut SortedMap < Span , Vec < String > > ,
15081605 ) {
15091606 let tcx = self . tcx ;
1607+ let rcvr_ty_str = self . tcx . short_string ( rcvr_ty, err. long_ty_path ( ) ) ;
15101608 let mut type_params = FxIndexMap :: default ( ) ;
15111609
15121610 // Pick out the list of unimplemented traits on the receiver.
@@ -1798,14 +1896,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17981896 let mut spanned_predicates: Vec < _ > = spanned_predicates. into_iter ( ) . collect ( ) ;
17991897 spanned_predicates. sort_by_key ( |( span, _) | * span) ;
18001898 for ( _, ( primary_spans, span_labels, predicates) ) in spanned_predicates {
1899+ let mut tracker = TraitBoundDuplicateTracker :: new ( ) ;
1900+ let mut all_trait_bounds_for_rcvr = true ;
1901+ for pred in & predicates {
1902+ match pred. kind ( ) . skip_binder ( ) {
1903+ ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Trait ( pred) ) => {
1904+ let self_ty = pred. trait_ref . self_ty ( ) ;
1905+ if self_ty. peel_refs ( ) != rcvr_ty {
1906+ all_trait_bounds_for_rcvr = false ;
1907+ break ;
1908+ }
1909+ let is_ref = matches ! ( self_ty. kind( ) , ty:: Ref ( ..) ) ;
1910+ tracker. track ( pred. trait_ref . def_id , is_ref) ;
1911+ }
1912+ _ => {
1913+ all_trait_bounds_for_rcvr = false ;
1914+ break ;
1915+ }
1916+ }
1917+ }
1918+ let has_ref_dupes = tracker. has_ref_dupes ( ) ;
1919+ let trait_def_ids = tracker. into_trait_def_ids ( ) ;
18011920 let mut preds: Vec < _ > = predicates
18021921 . iter ( )
18031922 . filter_map ( |pred| format_pred ( * * pred) )
18041923 . map ( |( p, _) | format ! ( "`{p}`" ) )
18051924 . collect ( ) ;
18061925 preds. sort ( ) ;
18071926 preds. dedup ( ) ;
1808- let msg = if let [ pred] = & preds[ ..] {
1927+ let availability_note = if all_trait_bounds_for_rcvr
1928+ && has_ref_dupes
1929+ && trait_def_ids. len ( ) > 1
1930+ && matches ! ( rcvr_ty. kind( ) , ty:: Adt ( ..) )
1931+ {
1932+ let mut trait_names = trait_def_ids
1933+ . into_iter ( )
1934+ . map ( |def_id| format ! ( "`{}`" , tcx. def_path_str( def_id) ) )
1935+ . collect :: < Vec < _ > > ( ) ;
1936+ trait_names. sort ( ) ;
1937+ listify ( & trait_names, |name| name. to_string ( ) ) . map ( |traits| {
1938+ format ! (
1939+ "for `{item_ident}` to be available, `{rcvr_ty_str}` must implement {traits}"
1940+ )
1941+ } )
1942+ } else {
1943+ None
1944+ } ;
1945+ let msg = if let Some ( availability_note) = availability_note {
1946+ availability_note
1947+ } else if let [ pred] = & preds[ ..] {
18091948 format ! ( "trait bound {pred} was not satisfied" )
18101949 } else {
18111950 format ! ( "the following trait bounds were not satisfied:\n {}" , preds. join( "\n " ) , )
0 commit comments