Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub(crate) struct ProbeContext<'a, 'tcx> {
steps: &'tcx [CandidateStep<'tcx>],

inherent_candidates: Vec<Candidate<'tcx>>,
dyn_extension_candidates: Vec<Candidate<'tcx>>,
extension_candidates: Vec<Candidate<'tcx>>,
impl_dups: FxHashSet<DefId>,

Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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())
},
Expand Down Expand Up @@ -576,7 +591,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
),
import_ids: &[],
},
false,
InherentOrExtension::Extension,
);
}
};
Expand Down Expand Up @@ -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,
Expand All @@ -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();
Expand All @@ -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);
Expand All @@ -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);
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand All @@ -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
},
);
},
);
Expand Down Expand Up @@ -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,
);
});
}
Expand Down Expand Up @@ -1115,7 +1137,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
import_ids,
kind: TraitCandidate(bound_trait_ref, lint_ambiguous),
},
false,
InherentOrExtension::Extension,
);
}
}
Expand All @@ -1138,7 +1160,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
import_ids,
kind: TraitCandidate(ty::Binder::dummy(trait_ref), lint_ambiguous),
},
false,
InherentOrExtension::Extension,
);
}
}
Expand All @@ -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| {
Expand Down Expand Up @@ -1677,9 +1700,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
) -> Option<PickResult<'tcx>> {
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,
Expand Down
32 changes: 32 additions & 0 deletions tests/ui/methods/call-ambig-trait-object-method-supertrait.rs
Original file line number Diff line number Diff line change
@@ -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!(<dyn Sub>::foo(x), 42);
assert_eq!(<dyn Sub as Super>::foo(x), 27);

let x: &(dyn Sub + Send) = &0i32;
assert_eq!(x.foo(), 27);
}
48 changes: 41 additions & 7 deletions tests/ui/methods/call-ambig-trait-object-method.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,54 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/18446>.
//! 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!(<dyn T>::foo(x), 1);
//~^ ERROR multiple applicable items in scope
assert_eq!(<dyn T as T>::foo(x), 0);
assert_eq!(<dyn T>::bar(x), ());
//~^ ERROR multiple applicable items in scope
assert_eq!(<dyn T as T>::bar(x), 0);
assert_eq!(<dyn T as OtherTrait>::foo(x), i32::MIN);

let x: &(dyn T + Send) = &0i32;
assert_eq!(x.foo(), 0);
Comment on lines +52 to +53

@Jules-Bertholet Jules-Bertholet Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is not so nice… ideally this would use the inherent impl too.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into making this work, but it seems non-trivial. The inherent_impls query is based on DefIds, so information about auto traits is lost

}
88 changes: 77 additions & 11 deletions tests/ui/methods/call-ambig-trait-object-method.stderr
Original file line number Diff line number Diff line change
@@ -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!(<dyn T>::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!(<dyn T>::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!(<dyn T>::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!(<dyn T>::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`.
Loading