Skip to content

Commit a670ff8

Browse files
committed
Auto merge of rust-lang#13074 - lowr:fix/method-resolution-with-impl-predicate, r=lowr
Consider bounds on inherent impl in method resolution There are three type-related things we should consider in method resolution: `Self` type, receiver type, and impl bounds. While we check the first two and impl bounds on trait impls, we've been ignoring the impl bounds on inherent impls. With this patch rust-analyzer now takes them into account and is able to select the appropriate inherent method. Resolves rust-lang#5441 Resolves rust-lang#12308
2 parents e162363 + dd22aa4 commit a670ff8

File tree

2 files changed

+140
-45
lines changed

2 files changed

+140
-45
lines changed

crates/hir-ty/src/method_resolution.rs

+97-45
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,14 @@ pub fn resolve_indexing_op(
10641064
None
10651065
}
10661066

1067+
macro_rules! check_that {
1068+
($cond:expr) => {
1069+
if !$cond {
1070+
return false;
1071+
}
1072+
};
1073+
}
1074+
10671075
fn is_valid_candidate(
10681076
table: &mut InferenceTable<'_>,
10691077
name: Option<&Name>,
@@ -1072,54 +1080,10 @@ fn is_valid_candidate(
10721080
self_ty: &Ty,
10731081
visible_from_module: Option<ModuleId>,
10741082
) -> bool {
1075-
macro_rules! check_that {
1076-
($cond:expr) => {
1077-
if !$cond {
1078-
return false;
1079-
}
1080-
};
1081-
}
1082-
10831083
let db = table.db;
10841084
match item {
10851085
AssocItemId::FunctionId(m) => {
1086-
let data = db.function_data(m);
1087-
1088-
check_that!(name.map_or(true, |n| n == &data.name));
1089-
check_that!(visible_from_module.map_or(true, |from_module| {
1090-
let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module);
1091-
if !v {
1092-
cov_mark::hit!(autoderef_candidate_not_visible);
1093-
}
1094-
v
1095-
}));
1096-
1097-
table.run_in_snapshot(|table| {
1098-
let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
1099-
let expect_self_ty = match m.lookup(db.upcast()).container {
1100-
ItemContainerId::TraitId(_) => {
1101-
subst.at(Interner, 0).assert_ty_ref(Interner).clone()
1102-
}
1103-
ItemContainerId::ImplId(impl_id) => {
1104-
subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
1105-
}
1106-
// We should only get called for associated items (impl/trait)
1107-
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
1108-
unreachable!()
1109-
}
1110-
};
1111-
check_that!(table.unify(&expect_self_ty, self_ty));
1112-
if let Some(receiver_ty) = receiver_ty {
1113-
check_that!(data.has_self_param());
1114-
1115-
let sig = db.callable_item_signature(m.into());
1116-
let expected_receiver =
1117-
sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
1118-
1119-
check_that!(table.unify(&receiver_ty, &expected_receiver));
1120-
}
1121-
true
1122-
})
1086+
is_valid_fn_candidate(table, m, name, receiver_ty, self_ty, visible_from_module)
11231087
}
11241088
AssocItemId::ConstId(c) => {
11251089
let data = db.const_data(c);
@@ -1152,6 +1116,94 @@ fn is_valid_candidate(
11521116
}
11531117
}
11541118

1119+
fn is_valid_fn_candidate(
1120+
table: &mut InferenceTable<'_>,
1121+
fn_id: FunctionId,
1122+
name: Option<&Name>,
1123+
receiver_ty: Option<&Ty>,
1124+
self_ty: &Ty,
1125+
visible_from_module: Option<ModuleId>,
1126+
) -> bool {
1127+
let db = table.db;
1128+
let data = db.function_data(fn_id);
1129+
1130+
check_that!(name.map_or(true, |n| n == &data.name));
1131+
check_that!(visible_from_module.map_or(true, |from_module| {
1132+
let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module);
1133+
if !v {
1134+
cov_mark::hit!(autoderef_candidate_not_visible);
1135+
}
1136+
v
1137+
}));
1138+
1139+
table.run_in_snapshot(|table| {
1140+
let container = fn_id.lookup(db.upcast()).container;
1141+
let impl_subst = match container {
1142+
ItemContainerId::ImplId(it) => {
1143+
TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build()
1144+
}
1145+
ItemContainerId::TraitId(it) => {
1146+
TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build()
1147+
}
1148+
_ => unreachable!(),
1149+
};
1150+
1151+
let fn_subst = TyBuilder::subst_for_def(db, fn_id)
1152+
.use_parent_substs(&impl_subst)
1153+
.fill_with_inference_vars(table)
1154+
.build();
1155+
1156+
let expect_self_ty = match container {
1157+
ItemContainerId::TraitId(_) => fn_subst.at(Interner, 0).assert_ty_ref(Interner).clone(),
1158+
ItemContainerId::ImplId(impl_id) => {
1159+
fn_subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
1160+
}
1161+
// We should only get called for associated items (impl/trait)
1162+
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
1163+
unreachable!()
1164+
}
1165+
};
1166+
check_that!(table.unify(&expect_self_ty, self_ty));
1167+
1168+
if let Some(receiver_ty) = receiver_ty {
1169+
check_that!(data.has_self_param());
1170+
1171+
let sig = db.callable_item_signature(fn_id.into());
1172+
let expected_receiver =
1173+
sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
1174+
1175+
check_that!(table.unify(&receiver_ty, &expected_receiver));
1176+
}
1177+
1178+
if let ItemContainerId::ImplId(impl_id) = container {
1179+
// We need to consider the bounds on the impl to distinguish functions of the same name
1180+
// for a type.
1181+
let predicates = db.generic_predicates(impl_id.into());
1182+
predicates
1183+
.iter()
1184+
.map(|predicate| {
1185+
let (p, b) = predicate
1186+
.clone()
1187+
.substitute(Interner, &impl_subst)
1188+
// Skipping the inner binders is ok, as we don't handle quantified where
1189+
// clauses yet.
1190+
.into_value_and_skipped_binders();
1191+
stdx::always!(b.len(Interner) == 0);
1192+
p
1193+
})
1194+
// It's ok to get ambiguity here, as we may not have enough information to prove
1195+
// obligations. We'll check if the user is calling the selected method properly
1196+
// later anyway.
1197+
.all(|p| table.try_obligation(p.cast(Interner)).is_some())
1198+
} else {
1199+
// For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in
1200+
// `iterate_trait_method_candidates()`.
1201+
// For others, this function shouldn't be called.
1202+
true
1203+
}
1204+
})
1205+
}
1206+
11551207
pub fn implements_trait(
11561208
ty: &Canonical<Ty>,
11571209
db: &dyn HirDatabase,

crates/hir-ty/src/tests/method_resolution.rs

+43
Original file line numberDiff line numberDiff line change
@@ -1790,3 +1790,46 @@ impl u16 {
17901790
"#,
17911791
)
17921792
}
1793+
1794+
#[test]
1795+
fn with_impl_bounds() {
1796+
check_types(
1797+
r#"
1798+
trait Trait {}
1799+
struct Foo<T>(T);
1800+
impl Trait for isize {}
1801+
1802+
impl<T: Trait> Foo<T> {
1803+
fn foo() -> isize { 0 }
1804+
fn bar(&self) -> isize { 0 }
1805+
}
1806+
1807+
impl Foo<()> {
1808+
fn foo() {}
1809+
fn bar(&self) {}
1810+
}
1811+
1812+
fn f() {
1813+
let _ = Foo::<isize>::foo();
1814+
//^isize
1815+
let _ = Foo(0isize).bar();
1816+
//^isize
1817+
let _ = Foo::<()>::foo();
1818+
//^()
1819+
let _ = Foo(()).bar();
1820+
//^()
1821+
let _ = Foo::<usize>::foo();
1822+
//^{unknown}
1823+
let _ = Foo(0usize).bar();
1824+
//^{unknown}
1825+
}
1826+
1827+
fn g<T: Trait>(a: T) {
1828+
let _ = Foo::<T>::foo();
1829+
//^isize
1830+
let _ = Foo(a).bar();
1831+
//^isize
1832+
}
1833+
"#,
1834+
);
1835+
}

0 commit comments

Comments
 (0)