From 0c85044a5d5ab1aef40153a3de48eb67804f6677 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 30 May 2024 13:29:54 -0400 Subject: [PATCH] Implement RFC 3624 supertrait_item_shadowing --- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir_typeck/src/method/probe.rs | 64 +++++++++++++++++-- compiler/rustc_span/src/symbol.rs | 1 + .../feature-gate-supertrait-item-shadowing.rs | 22 +++++++ ...ture-gate-supertrait-item-shadowing.stderr | 57 +++++++++++++++++ 5 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.rs create mode 100644 tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.stderr diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index b3fdafe36e61..7dbc156c5466 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -633,6 +633,8 @@ declare_features! ( (unstable, strict_provenance_lints, "1.61.0", Some(130351)), /// Allows string patterns to dereference values to match them. (unstable, string_deref_patterns, "1.67.0", Some(87121)), + /// Allows subtrait items to shadow supertrait items. + (unstable, supertrait_item_shadowing, "CURRENT_RUSTC_VERSION", Some(89151)), /// Allows using `#[thread_local]` on `static` items. (unstable, thread_local, "1.0.0", Some(29594)), /// Allows defining `trait X = A + B;` alias items. diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 9394fcafd767..5d1f4143ea45 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,6 +3,7 @@ use std::cmp::max; use std::ops::Deref; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sso::SsoHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::HirId; @@ -33,6 +34,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{ CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult, }; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; +use rustc_type_ir::elaborate::supertrait_def_ids; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -1614,10 +1616,18 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { debug!("applicable_candidates: {:?}", applicable_candidates); if applicable_candidates.len() > 1 { - if let Some(pick) = - self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates) - { - return Some(Ok(pick)); + if self.tcx.features().supertrait_item_shadowing() { + if let Some(pick) = + self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates) + { + return Some(Ok(pick)); + } + } else { + if let Some(pick) = + self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates) + { + return Some(Ok(pick)); + } } } @@ -2084,6 +2094,52 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }) } + /// Much like `collapse_candidates_to_trait_pick`, this method allows us to collapse + /// multiple conflicting picks if there is one pick whose trait container is a subtrait + /// of the trait containers of all of the other picks. + /// + /// This implements RFC #3624. + fn collapse_candidates_to_subtrait_pick( + &self, + self_ty: Ty<'tcx>, + probes: &[(&Candidate<'tcx>, ProbeResult)], + ) -> Option> { + let mut child_pick = probes[0].0; + let mut supertraits: SsoHashSet<_> = + supertrait_def_ids(self.tcx, child_pick.item.trait_container(self.tcx)?).collect(); + + // All other picks should be a supertrait of the `child_pick`. + // If it's not, then we update the `child_pick` and the `supertraits` + // list. + for (p, _) in &probes[1..] { + let p_container = p.item.trait_container(self.tcx)?; + if !supertraits.contains(&p_container) { + // This pick is not a supertrait of the `child_pick`. + // Check if it's a subtrait of the `child_pick`, which + // is sufficient to imply that all of the previous picks + // are also supertraits of this pick. + supertraits = supertrait_def_ids(self.tcx, p_container).collect(); + if supertraits.contains(&child_pick.item.trait_container(self.tcx).unwrap()) { + child_pick = *p; + } else { + // `child_pick` is not a supertrait of this pick. Bail. + return None; + } + } + } + + Some(Pick { + item: child_pick.item, + kind: TraitPick, + import_ids: child_pick.import_ids.clone(), + autoderefs: 0, + autoref_or_ptr_adjustment: None, + self_ty, + unstable_candidates: vec![], + receiver_steps: None, + }) + } + /// Similarly to `probe_for_return_type`, this method attempts to find the best matching /// candidate method where the method name may have been misspelled. Similarly to other /// edit distance based suggestions, we provide at most one such suggestion. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9734926810ab..34ecc18a6c60 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1995,6 +1995,7 @@ symbols! { sub_assign, sub_with_overflow, suggestion, + supertrait_item_shadowing, surface_async_drop_in_place, sym, sync, diff --git a/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.rs b/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.rs new file mode 100644 index 000000000000..218d2eefeaff --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.rs @@ -0,0 +1,22 @@ +trait Sup { + fn method(&self) {} +} + +trait Trait: Sup { + fn method(&self) {} +} + +impl Sup for i32 {} +impl Trait for i32 {} + +fn poly(x: T) { + x.method(); + //~^ ERROR multiple applicable items in scope +} + +fn concrete() { + 1.method(); + //~^ ERROR multiple applicable items in scope +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.stderr b/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.stderr new file mode 100644 index 000000000000..5669073f4abb --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.stderr @@ -0,0 +1,57 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/feature-gate-supertrait-item-shadowing.rs:13:7 + | +LL | x.method(); + | ^^^^^^ multiple `method` found + | +note: candidate #1 is defined in the trait `Sup` + --> $DIR/feature-gate-supertrait-item-shadowing.rs:2:5 + | +LL | fn method(&self) {} + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `Trait` + --> $DIR/feature-gate-supertrait-item-shadowing.rs:6:5 + | +LL | fn method(&self) {} + | ^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - x.method(); +LL + Sup::method(&x); + | +help: disambiguate the method for candidate #2 + | +LL - x.method(); +LL + Trait::method(&x); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/feature-gate-supertrait-item-shadowing.rs:18:7 + | +LL | 1.method(); + | ^^^^^^ multiple `method` found + | +note: candidate #1 is defined in an impl of the trait `Sup` for the type `i32` + --> $DIR/feature-gate-supertrait-item-shadowing.rs:2:5 + | +LL | fn method(&self) {} + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `Trait` for the type `i32` + --> $DIR/feature-gate-supertrait-item-shadowing.rs:6:5 + | +LL | fn method(&self) {} + | ^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - 1.method(); +LL + Sup::method(&1); + | +help: disambiguate the method for candidate #2 + | +LL - 1.method(); +LL + Trait::method(&1); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0034`.