diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4258896deec70..c9eeab98ceae4 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -62,6 +62,7 @@ pub(crate) struct ProbeContext<'a, 'tcx> { steps: &'tcx [CandidateStep<'tcx>], inherent_candidates: Vec>, + dyn_extension_candidates: Vec>, extension_candidates: Vec>, impl_dups: FxHashSet, @@ -102,6 +103,19 @@ pub(crate) struct Candidate<'tcx> { pub(crate) import_ids: &'tcx [LocalDefId], } +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +enum InherentOrExtension { + /// Inherent candidates + Inherent, + /// Candidates for a trait object's supertraits + /// (but not the trait itself). + /// Take precedence over other extension candidates, + /// but not inherent candidates + DynExtension, + /// Extension candidates + Extension, +} + #[derive(Debug, Clone)] pub(crate) enum CandidateKind<'tcx> { InherentImplCandidate { impl_def_id: DefId, receiver_steps: usize }, @@ -369,6 +383,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(probe_cx .inherent_candidates .into_iter() + .chain(probe_cx.dyn_extension_candidates) .chain(probe_cx.extension_candidates) .collect()) }, @@ -576,7 +591,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), import_ids: &[], }, - false, + InherentOrExtension::Extension, ); } }; @@ -772,6 +787,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { method_name, return_type, inherent_candidates: Vec::new(), + dyn_extension_candidates: Vec::new(), extension_candidates: Vec::new(), impl_dups: FxHashSet::default(), orig_steps_var_values, @@ -787,6 +803,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn reset(&mut self) { self.inherent_candidates.clear(); + self.dyn_extension_candidates.clear(); self.extension_candidates.clear(); self.impl_dups.clear(); self.private_candidates.clear(); @@ -807,7 +824,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /////////////////////////////////////////////////////////////////////////// // CANDIDATE ASSEMBLY - fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) { + fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: InherentOrExtension) { let is_accessible = if let Some(name) = self.method_name { let item = candidate.item; let hir_id = self.tcx.local_def_id_to_hir_id(self.body_id); @@ -818,11 +835,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { true }; if is_accessible { - if is_inherent { - self.inherent_candidates.push(candidate); - } else { - self.extension_candidates.push(candidate); - } + let list_to_push_to = match is_inherent { + InherentOrExtension::Inherent => &mut self.inherent_candidates, + InherentOrExtension::DynExtension => &mut self.dyn_extension_candidates, + InherentOrExtension::Extension => &mut self.extension_candidates, + }; + list_to_push_to.push(candidate); } else { self.private_candidates.push(candidate); } @@ -863,9 +881,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) = self.fcx.instantiate_canonical(self.span, self_ty); - self.assemble_inherent_candidates_from_object(generalized_self_ty); self.assemble_inherent_impl_candidates_for_type(p.def_id(), receiver_steps); self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps); + self.assemble_dyn_extension_candidates(generalized_self_ty); } ty::Adt(def, _) => { let def_id = def.did(); @@ -948,13 +966,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { kind: InherentImplCandidate { impl_def_id, receiver_steps }, import_ids: &[], }, - true, + InherentOrExtension::Inherent, ); } } #[instrument(level = "debug", skip(self))] - fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) { + fn assemble_dyn_extension_candidates(&mut self, self_ty: Ty<'tcx>) { let principal = match self_ty.kind() { ty::Dynamic(data, ..) => Some(data), _ => None, @@ -980,7 +998,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { |this, new_trait_ref, item| { this.push_candidate( Candidate { item, kind: ObjectCandidate(new_trait_ref), import_ids: &[] }, - true, + if new_trait_ref == trait_ref { + InherentOrExtension::Inherent + } else { + InherentOrExtension::DynExtension + }, ); }, ); @@ -1015,7 +1037,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_candidates_for_bounds(bounds, |this, poly_trait_ref, item| { this.push_candidate( Candidate { item, kind: WhereClauseCandidate(poly_trait_ref), import_ids: &[] }, - true, + InherentOrExtension::Inherent, ); }); } @@ -1115,7 +1137,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { import_ids, kind: TraitCandidate(bound_trait_ref, lint_ambiguous), }, - false, + InherentOrExtension::Extension, ); } } @@ -1138,7 +1160,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { import_ids, kind: TraitCandidate(ty::Binder::dummy(trait_ref), lint_ambiguous), }, - false, + InherentOrExtension::Extension, ); } } @@ -1152,6 +1174,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let mut names: Vec<_> = self .inherent_candidates .iter() + .chain(&self.dyn_extension_candidates) .chain(&self.extension_candidates) .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { @@ -1677,9 +1700,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ) -> Option> { debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); - for (kind, candidates) in - [("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] - { + for (kind, candidates) in [ + ("inherent", &self.inherent_candidates), + ("dyn extension", &self.dyn_extension_candidates), + ("extension", &self.extension_candidates), + ] { debug!("searching {} candidates", kind); let res = self.consider_candidates( self_ty, diff --git a/tests/ui/methods/call-ambig-trait-object-method-supertrait.rs b/tests/ui/methods/call-ambig-trait-object-method-supertrait.rs new file mode 100644 index 0000000000000..a2af5f66ba308 --- /dev/null +++ b/tests/ui/methods/call-ambig-trait-object-method-supertrait.rs @@ -0,0 +1,32 @@ +//@ edition: 2024 +//@ run-pass + +trait Super { + fn foo(&self) -> i32 { + 27 + } +} + +trait Sub: Super {} + +impl<'a> dyn Sub + 'a { + fn foo(&self) -> i32 { + 42 + } +} + +impl Super for i32 {} +impl Sub for i32 {} + +fn main() { + let x = &0i32; + assert_eq!(x.foo(), 27); + + let x: &dyn Sub = &0i32; + assert_eq!(x.foo(), 42); + assert_eq!(::foo(x), 42); + assert_eq!(::foo(x), 27); + + let x: &(dyn Sub + Send) = &0i32; + assert_eq!(x.foo(), 27); +} diff --git a/tests/ui/methods/call-ambig-trait-object-method.rs b/tests/ui/methods/call-ambig-trait-object-method.rs index 9e2f5c5aebe8b..be80f76ec630d 100644 --- a/tests/ui/methods/call-ambig-trait-object-method.rs +++ b/tests/ui/methods/call-ambig-trait-object-method.rs @@ -1,20 +1,54 @@ -//! Regression test for . -//! Test that name clashes between the method in an impl for the type -//! and the method in the trait when both are in the same scope. +//@ edition: 2024 trait T { - fn foo(&self); + fn foo(&self) -> i32; + fn bar(&self) -> i32; } impl<'a> dyn T + 'a { - fn foo(&self) {} + fn foo(&self) -> i32 { + 1 + } + + fn bar(&self) {} } impl T for i32 { - fn foo(&self) {} + fn foo(&self) -> i32 { + 0 + } + + fn bar(&self) -> i32 { + 0 + } +} + +trait OtherTrait { + fn foo(&self) -> i32 { + i32::MIN + } } +impl OtherTrait for dyn T {} + fn main() { + let x = &0i32; + assert_eq!(x.foo(), 0); + assert_eq!(x.bar(), 0); + let x: &dyn T = &0i32; - x.foo(); //~ ERROR multiple applicable items in scope [E0034] + assert_eq!(x.foo(), 1); + //~^ ERROR multiple applicable items in scope + assert_eq!(x.bar(), ()); + //~^ ERROR multiple applicable items in scope + assert_eq!(::foo(x), 1); + //~^ ERROR multiple applicable items in scope + assert_eq!(::foo(x), 0); + assert_eq!(::bar(x), ()); + //~^ ERROR multiple applicable items in scope + assert_eq!(::bar(x), 0); + assert_eq!(::foo(x), i32::MIN); + + let x: &(dyn T + Send) = &0i32; + assert_eq!(x.foo(), 0); } diff --git a/tests/ui/methods/call-ambig-trait-object-method.stderr b/tests/ui/methods/call-ambig-trait-object-method.stderr index b76f107160011..16038fb3c9292 100644 --- a/tests/ui/methods/call-ambig-trait-object-method.stderr +++ b/tests/ui/methods/call-ambig-trait-object-method.stderr @@ -1,25 +1,91 @@ error[E0034]: multiple applicable items in scope - --> $DIR/call-ambig-trait-object-method.rs:19:7 + --> $DIR/call-ambig-trait-object-method.rs:40:18 | -LL | x.foo(); - | ^^^ multiple `foo` found +LL | assert_eq!(x.foo(), 1); + | ^^^ multiple `foo` found | note: candidate #1 is defined in the trait `T` - --> $DIR/call-ambig-trait-object-method.rs:6:5 + --> $DIR/call-ambig-trait-object-method.rs:4:5 | -LL | fn foo(&self); - | ^^^^^^^^^^^^^^ +LL | fn foo(&self) -> i32; + | ^^^^^^^^^^^^^^^^^^^^^ note: candidate #2 is defined in an impl for the type `(dyn T + 'a)` - --> $DIR/call-ambig-trait-object-method.rs:10:5 + --> $DIR/call-ambig-trait-object-method.rs:9:5 | -LL | fn foo(&self) {} +LL | fn foo(&self) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - assert_eq!(x.foo(), 1); +LL + assert_eq!(T::foo(&x), 1); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/call-ambig-trait-object-method.rs:42:18 + | +LL | assert_eq!(x.bar(), ()); + | ^^^ multiple `bar` found + | +note: candidate #1 is defined in the trait `T` + --> $DIR/call-ambig-trait-object-method.rs:5:5 + | +LL | fn bar(&self) -> i32; + | ^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl for the type `(dyn T + 'a)` + --> $DIR/call-ambig-trait-object-method.rs:13:5 + | +LL | fn bar(&self) {} | ^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL - x.foo(); -LL + T::foo(&x); +LL - assert_eq!(x.bar(), ()); +LL + assert_eq!(T::bar(&x), ()); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/call-ambig-trait-object-method.rs:44:25 + | +LL | assert_eq!(::foo(x), 1); + | ^^^ multiple `foo` found + | +note: candidate #1 is defined in the trait `T` + --> $DIR/call-ambig-trait-object-method.rs:4:5 + | +LL | fn foo(&self) -> i32; + | ^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl for the type `(dyn T + 'a)` + --> $DIR/call-ambig-trait-object-method.rs:9:5 + | +LL | fn foo(&self) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - assert_eq!(::foo(x), 1); +LL + assert_eq!(T::foo(x), 1); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/call-ambig-trait-object-method.rs:47:25 + | +LL | assert_eq!(::bar(x), ()); + | ^^^ multiple `bar` found + | +note: candidate #1 is defined in the trait `T` + --> $DIR/call-ambig-trait-object-method.rs:5:5 + | +LL | fn bar(&self) -> i32; + | ^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl for the type `(dyn T + 'a)` + --> $DIR/call-ambig-trait-object-method.rs:13:5 + | +LL | fn bar(&self) {} + | ^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - assert_eq!(::bar(x), ()); +LL + assert_eq!(T::bar(x), ()); | -error: aborting due to 1 previous error +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0034`.