@@ -24,6 +24,7 @@ use std::{assert_matches, slice};
2424use rustc_abi:: FIRST_VARIANT ;
2525use rustc_ast:: LitKind ;
2626use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , FxIndexSet } ;
27+ use rustc_data_structures:: sso:: SsoHashSet ;
2728use rustc_errors:: codes:: * ;
2829use rustc_errors:: {
2930 Applicability , Diag , DiagCtxtHandle , Diagnostic , ErrorGuaranteed , FatalError , Level , StashKey ,
@@ -1201,6 +1202,69 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
12011202 )
12021203 }
12031204
1205+ /// When there are multiple traits which contain an identically named
1206+ /// associated item, this function eliminates any traits which are a
1207+ /// supertrait of another candidate trait.
1208+ ///
1209+ /// This implements RFC #3624.
1210+ fn collapse_candidates_to_subtrait_pick (
1211+ & self ,
1212+ matching_candidates : & [ ty:: PolyTraitRef < ' tcx > ] ,
1213+ ) -> Option < ty:: PolyTraitRef < ' tcx > > {
1214+ if !self . tcx ( ) . features ( ) . supertrait_item_shadowing ( ) {
1215+ return None ;
1216+ }
1217+
1218+ let mut child_trait = matching_candidates[ 0 ] ;
1219+ let mut supertraits: SsoHashSet < _ > =
1220+ traits:: supertrait_def_ids ( self . tcx ( ) , child_trait. def_id ( ) ) . collect ( ) ;
1221+
1222+ let mut remaining_candidates: Vec < _ > = matching_candidates[ 1 ..] . iter ( ) . copied ( ) . collect ( ) ;
1223+ while !remaining_candidates. is_empty ( ) {
1224+ let mut made_progress = false ;
1225+ let mut next_round = vec ! [ ] ;
1226+
1227+ for remaining_trait in remaining_candidates {
1228+ if supertraits. contains ( & remaining_trait. def_id ( ) ) {
1229+ made_progress = true ;
1230+ continue ;
1231+ }
1232+
1233+ // This pick is not a supertrait of the `child_pick`.
1234+ // Check if it's a subtrait of the `child_pick`, instead.
1235+ // If it is, then it must have been a subtrait of every
1236+ // other pick we've eliminated at this point. It will
1237+ // take over at this point.
1238+ let remaining_trait_supertraits: SsoHashSet < _ > =
1239+ traits:: supertrait_def_ids ( self . tcx ( ) , remaining_trait. def_id ( ) ) . collect ( ) ;
1240+ if remaining_trait_supertraits. contains ( & child_trait. def_id ( ) ) {
1241+ child_trait = remaining_trait;
1242+ supertraits = remaining_trait_supertraits;
1243+ made_progress = true ;
1244+ continue ;
1245+ }
1246+
1247+ // `child_pick` is not a supertrait of this pick.
1248+ // Don't bail here, since we may be comparing two supertraits
1249+ // of a common subtrait. These two supertraits won't be related
1250+ // at all, but we will pick them up next round when we find their
1251+ // child as we continue iterating in this round.
1252+ next_round. push ( remaining_trait) ;
1253+ }
1254+
1255+ if made_progress {
1256+ // If we've made progress, iterate again.
1257+ remaining_candidates = next_round;
1258+ } else {
1259+ // Otherwise, we must have at least two candidates which
1260+ // are not related to each other at all.;
1261+ return None ;
1262+ }
1263+ }
1264+
1265+ Some ( child_trait)
1266+ }
1267+
12041268 /// Search for a single trait bound whose trait defines the associated item given by
12051269 /// `assoc_ident`.
12061270 ///
@@ -1240,6 +1304,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
12401304 if let Some ( bound2) = matching_candidates. next ( ) {
12411305 debug ! ( ?bound2) ;
12421306
1307+ // If the other matching candidates are all from supertraits of one
1308+ // trait, pick the subtrait.
1309+ let matching_candidates: Vec < _ > =
1310+ [ bound, bound2] . into_iter ( ) . chain ( matching_candidates) . collect ( ) ;
1311+ if let Some ( bound) = self . collapse_candidates_to_subtrait_pick ( & matching_candidates) {
1312+ return Ok ( bound) ;
1313+ }
1314+
12431315 let assoc_kind_str = errors:: assoc_tag_str ( assoc_tag) ;
12441316 let qself_str = qself. to_string ( tcx) ;
12451317 let mut err = self . dcx ( ) . create_err ( crate :: errors:: AmbiguousAssocItem {
@@ -1263,7 +1335,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
12631335 // predicates!).
12641336 // FIXME: Turn this into a structured, translatable & more actionable suggestion.
12651337 let mut where_bounds = vec ! [ ] ;
1266- for bound in [ bound , bound2 ] . into_iter ( ) . chain ( matching_candidates) {
1338+ for bound in matching_candidates {
12671339 let bound_id = bound. def_id ( ) ;
12681340 let assoc_item = tcx. associated_items ( bound_id) . find_by_ident_and_kind (
12691341 tcx,
0 commit comments