about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs64
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.rs22
-rw-r--r--tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.stderr57
5 files changed, 142 insertions, 4 deletions
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index b3fdafe36e6..7dbc156c546 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 9394fcafd76..5d1f4143ea4 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<Pick<'tcx>> {
+        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 9734926810a..34ecc18a6c6 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 00000000000..218d2eefeaf
--- /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<T: Trait>(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 00000000000..5669073f4ab
--- /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`.