Skip to content

Commit

Permalink
fix: handle trait methods as inherent methods for placeholders
Browse files Browse the repository at this point in the history
  • Loading branch information
lowr committed Aug 30, 2022
1 parent f9e2ac5 commit 484d5b6
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 27 deletions.
87 changes: 60 additions & 27 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -914,19 +914,10 @@ fn iterate_trait_method_candidates(
let db = table.db;
let env = table.trait_env.clone();
let self_is_array = matches!(self_ty.kind(Interner), chalk_ir::TyKind::Array(..));
let env_traits = matches!(self_ty.kind(Interner), TyKind::Placeholder(_))
// if we have `T: Trait` in the param env, the trait doesn't need to be in scope
.then(|| {
env.traits_in_scope_from_clauses(self_ty.clone())
.flat_map(|t| all_super_traits(db.upcast(), t))
})
.into_iter()
.flatten();
let traits = env_traits.chain(traits_in_scope.iter().copied());

let canonical_self_ty = table.canonicalize(self_ty.clone()).value;

'traits: for t in traits {
'traits: for &t in traits_in_scope {
let data = db.trait_data(t);

// Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
Expand Down Expand Up @@ -976,6 +967,43 @@ fn iterate_inherent_methods(
) -> ControlFlow<()> {
let db = table.db;
let env = table.trait_env.clone();

// For trait object types and placeholder types with trait bounds, the methods of the trait and
// its super traits are considered inherent methods. This matters because these methods have
// higher priority than the other traits' methods, which would be considered in
// `iterate_trait_method_candidates()` only after this function.
match self_ty.kind(Interner) {
TyKind::Placeholder(_) => {
let env = table.trait_env.clone();
let traits = env
.traits_in_scope_from_clauses(self_ty.clone())
.flat_map(|t| all_super_traits(db.upcast(), t));
iterate_inherent_trait_methods(
self_ty,
table,
name,
receiver_ty,
receiver_adjustments.clone(),
callback,
traits,
)?;
}
TyKind::Dyn(_) => {
let principal_trait = self_ty.dyn_trait().unwrap();
let traits = all_super_traits(db.upcast(), principal_trait);
iterate_inherent_trait_methods(
self_ty,
table,
name,
receiver_ty,
receiver_adjustments.clone(),
callback,
traits.into_iter(),
)?;
}
_ => {}
}

let def_crates = match def_crates(db, self_ty, env.krate) {
Some(k) => k,
None => return ControlFlow::Continue(()),
Expand All @@ -987,23 +1015,6 @@ fn iterate_inherent_methods(
VisibleFromModule::None => (None, None),
};

// For trait object types, methods of the trait and its super traits are considered inherent
// methods. This matters because these trait methods have higher priority than the other
// traits' methods, which would be considered in `iterate_trait_method_candidates()` after this
// function.
let inherent_traits =
self_ty.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
for t in inherent_traits {
let data = db.trait_data(t);
for &(_, item) in data.items.iter() {
// We don't pass `visible_from_module` as all trait items should be visible from the
// trait object.
if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
}
}
}

if let Some(block_id) = block {
if let Some(impls) = db.inherent_impls_in_block(block_id) {
impls_for_self_ty(
Expand Down Expand Up @@ -1034,6 +1045,28 @@ fn iterate_inherent_methods(
}
return ControlFlow::Continue(());

fn iterate_inherent_trait_methods(
self_ty: &Ty,
table: &mut InferenceTable<'_>,
name: Option<&Name>,
receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
traits: impl Iterator<Item = TraitId>,
) -> ControlFlow<()> {
let db = table.db;
for t in traits {
let data = db.trait_data(t);
for &(_, item) in data.items.iter() {
// We don't pass `visible_from_module` as all trait items should be visible.
if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
}
}
}
ControlFlow::Continue(())
}

fn impls_for_self_ty(
impls: &InherentImpls,
self_ty: &Ty,
Expand Down
17 changes: 17 additions & 0 deletions crates/hir-ty/src/tests/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,23 @@ fn foo(a: &dyn Trait) {
);
}

#[test]
fn trait_method_priority_for_placeholder_type() {
check_types(
r#"
//- minicore: from
trait Trait {
fn into(&self) -> usize { 0 }
}
fn foo<T: Trait>(a: &T) {
let _ = a.into();
//^usize
}
"#,
);
}

#[test]
fn autoderef_visibility_field() {
check(
Expand Down

0 comments on commit 484d5b6

Please sign in to comment.