-
-
Notifications
You must be signed in to change notification settings - Fork 14.8k
Add supertrait item shadowing for type-level path resolution #152225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ use std::{assert_matches, slice}; | |
| use rustc_abi::FIRST_VARIANT; | ||
| use rustc_ast::LitKind; | ||
| use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; | ||
| use rustc_data_structures::sso::SsoHashSet; | ||
| use rustc_errors::codes::*; | ||
| use rustc_errors::{ | ||
| Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, FatalError, Level, StashKey, | ||
|
|
@@ -1201,6 +1202,69 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
| ) | ||
| } | ||
|
|
||
| /// When there are multiple traits which contain an identically named | ||
| /// associated item, this function eliminates any traits which are a | ||
| /// supertrait of another candidate trait. | ||
| /// | ||
| /// This implements RFC #3624. | ||
| fn collapse_candidates_to_subtrait_pick( | ||
| &self, | ||
| matching_candidates: &[ty::PolyTraitRef<'tcx>], | ||
| ) -> Option<ty::PolyTraitRef<'tcx>> { | ||
| if !self.tcx().features().supertrait_item_shadowing() { | ||
| return None; | ||
| } | ||
|
|
||
| let mut child_trait = matching_candidates[0]; | ||
| let mut supertraits: SsoHashSet<_> = | ||
| traits::supertrait_def_ids(self.tcx(), child_trait.def_id()).collect(); | ||
|
|
||
| let mut remaining_candidates: Vec<_> = matching_candidates[1..].iter().copied().collect(); | ||
| while !remaining_candidates.is_empty() { | ||
| let mut made_progress = false; | ||
| let mut next_round = vec![]; | ||
|
|
||
| for remaining_trait in remaining_candidates { | ||
| if supertraits.contains(&remaining_trait.def_id()) { | ||
| made_progress = true; | ||
| continue; | ||
| } | ||
|
|
||
| // This pick is not a supertrait of the `child_pick`. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a commit with some cleanup that I'm about to push - but |
||
| // Check if it's a subtrait of the `child_pick`, instead. | ||
| // If it is, then it must have been a subtrait of every | ||
| // other pick we've eliminated at this point. It will | ||
| // take over at this point. | ||
| let remaining_trait_supertraits: SsoHashSet<_> = | ||
| traits::supertrait_def_ids(self.tcx(), remaining_trait.def_id()).collect(); | ||
| if remaining_trait_supertraits.contains(&child_trait.def_id()) { | ||
| child_trait = remaining_trait; | ||
| supertraits = remaining_trait_supertraits; | ||
| made_progress = true; | ||
| continue; | ||
| } | ||
|
|
||
| // `child_pick` is not a supertrait of this pick. | ||
| // Don't bail here, since we may be comparing two supertraits | ||
| // of a common subtrait. These two supertraits won't be related | ||
| // at all, but we will pick them up next round when we find their | ||
| // child as we continue iterating in this round. | ||
| next_round.push(remaining_trait); | ||
| } | ||
|
|
||
| if made_progress { | ||
| // If we've made progress, iterate again. | ||
| remaining_candidates = next_round; | ||
| } else { | ||
| // Otherwise, we must have at least two candidates which | ||
| // are not related to each other at all.; | ||
| return None; | ||
| } | ||
| } | ||
|
|
||
| Some(child_trait) | ||
| } | ||
|
|
||
| /// Search for a single trait bound whose trait defines the associated item given by | ||
| /// `assoc_ident`. | ||
| /// | ||
|
|
@@ -1240,6 +1304,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
| if let Some(bound2) = matching_candidates.next() { | ||
| debug!(?bound2); | ||
|
|
||
| // If the other matching candidates are all from supertraits of one | ||
| // trait, pick the subtrait. | ||
| let matching_candidates: Vec<_> = | ||
| [bound, bound2].into_iter().chain(matching_candidates).collect(); | ||
| if let Some(bound) = self.collapse_candidates_to_subtrait_pick(&matching_candidates) { | ||
| return Ok(bound); | ||
| } | ||
|
|
||
| let assoc_kind_str = errors::assoc_tag_str(assoc_tag); | ||
| let qself_str = qself.to_string(tcx); | ||
| let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { | ||
|
|
@@ -1263,7 +1335,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { | |
| // predicates!). | ||
| // FIXME: Turn this into a structured, translatable & more actionable suggestion. | ||
| let mut where_bounds = vec![]; | ||
| for bound in [bound, bound2].into_iter().chain(matching_candidates) { | ||
| for bound in matching_candidates { | ||
| let bound_id = bound.def_id(); | ||
| let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( | ||
| tcx, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| //@ run-pass | ||
| //@ check-run-results | ||
|
|
||
| #![feature(supertrait_item_shadowing)] | ||
| #![feature(min_generic_const_args)] | ||
| #![allow(dead_code)] | ||
|
|
||
| trait A { | ||
| const CONST: i32; | ||
| } | ||
| impl<T> A for T { | ||
| const CONST: i32 = 1; | ||
| } | ||
|
|
||
| trait B: A { | ||
| type const CONST: i32; | ||
| } | ||
| impl<T> B for T { | ||
| type const CONST: i32 = 2; | ||
| } | ||
|
|
||
| trait C: B {} | ||
| impl<T> C for T {} | ||
|
|
||
| fn main() { | ||
| println!("{}", i32::CONST); | ||
| generic::<u32>(); | ||
| } | ||
|
|
||
| fn generic<T: C<CONST = 2>>() { | ||
| println!("{}", T::CONST); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| 2 | ||
| 2 |
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, this test probably doesn't make sense here, right? This is not about method resolution. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| //@ run-pass | ||
| //@ check-run-results | ||
|
|
||
| #![feature(supertrait_item_shadowing)] | ||
| #![allow(dead_code)] | ||
|
|
||
| use std::mem::size_of; | ||
|
|
||
| trait A { | ||
| type T; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use a different name for the associated type to name lexically clash with the param. |
||
| } | ||
| impl<T> A for T { | ||
| type T = i8; | ||
| } | ||
|
|
||
| trait B: A { | ||
| type T; | ||
| } | ||
| impl<T> B for T { | ||
| type T = i16; | ||
| } | ||
|
|
||
| trait C: B {} | ||
| impl<T> C for T {} | ||
|
|
||
| fn main() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have |
||
| generic::<u32>(); | ||
| generic2::<u32>(); | ||
| generic3::<u32>(); | ||
| generic4::<u32>(); | ||
| } | ||
|
|
||
| fn generic<U: B>() { | ||
| println!("{}", size_of::<U::T>()); | ||
| } | ||
|
|
||
| fn generic2<U: A<T = i8>>() { | ||
| println!("{}", size_of::<U::T>()); | ||
| } | ||
|
|
||
| fn generic3<U: B<T = i16>>() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is missing here is a test of |
||
| println!("{}", size_of::<U::T>()); | ||
| } | ||
|
|
||
| fn generic4<U: C<T = i16>>() { | ||
| println!("{}", size_of::<U::T>()); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| 2 | ||
| 1 | ||
| 2 | ||
| 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think docs here and for
collapse_candidates_to_subtrait_pickinmethod::probeshould link to each other to help ensure they are kept up to date.It's kind of unfortunate we can't have some shared code here.