Fallback to invisible associated functions and constants if no visible resolutions are found

This commit is contained in:
Lukas Wirth 2022-12-30 23:56:08 +01:00
parent 0d76b94c90
commit 5d54c550e7
3 changed files with 94 additions and 54 deletions

View file

@ -234,7 +234,8 @@ impl<'a> InferenceContext<'a> {
let canonical_ty = self.canonicalize(ty.clone()); let canonical_ty = self.canonicalize(ty.clone());
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
method_resolution::iterate_method_candidates( let mut not_visible = None;
let res = method_resolution::iterate_method_candidates(
&canonical_ty.value, &canonical_ty.value,
self.db, self.db,
self.table.trait_env.clone(), self.table.trait_env.clone(),
@ -242,7 +243,7 @@ impl<'a> InferenceContext<'a> {
VisibleFromModule::Filter(self.resolver.module()), VisibleFromModule::Filter(self.resolver.module()),
Some(name), Some(name),
method_resolution::LookupMode::Path, method_resolution::LookupMode::Path,
move |_ty, item| { |_ty, item, visible| {
let (def, container) = match item { let (def, container) = match item {
AssocItemId::FunctionId(f) => { AssocItemId::FunctionId(f) => {
(ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container) (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container)
@ -277,10 +278,21 @@ impl<'a> InferenceContext<'a> {
} }
}; };
self.write_assoc_resolution(id, item, substs.clone()); if visible {
Some((def, Some(substs))) Some((def, item, Some(substs)))
} else {
if not_visible.is_none() {
not_visible = Some((def, item, Some(substs)));
}
None
}
}, },
) );
let res = res.or(not_visible);
if let Some((_, item, Some(ref substs))) = res {
self.write_assoc_resolution(id, item, substs.clone());
}
res.map(|(def, _, substs)| (def, substs))
} }
fn resolve_enum_variant_on_ty( fn resolve_enum_variant_on_ty(

View file

@ -495,7 +495,8 @@ pub(crate) fn lookup_method(
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: &Name, name: &Name,
) -> Option<(ReceiverAdjustments, FunctionId)> { ) -> Option<(ReceiverAdjustments, FunctionId)> {
iterate_method_candidates( let mut not_visible = None;
let res = iterate_method_candidates(
ty, ty,
db, db,
env, env,
@ -503,11 +504,16 @@ pub(crate) fn lookup_method(
visible_from_module, visible_from_module,
Some(name), Some(name),
LookupMode::MethodCall, LookupMode::MethodCall,
|adjustments, f| match f { |adjustments, f, visible| match f {
AssocItemId::FunctionId(f) => Some((adjustments, f)), AssocItemId::FunctionId(f) if visible => Some((adjustments, f)),
AssocItemId::FunctionId(f) if not_visible.is_none() => {
not_visible = Some((adjustments, f));
None
}
_ => None, _ => None,
}, },
) );
res.or(not_visible)
} }
/// Whether we're looking up a dotted method call (like `v.len()`) or a path /// Whether we're looking up a dotted method call (like `v.len()`) or a path
@ -619,7 +625,7 @@ pub(crate) fn iterate_method_candidates<T>(
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mode: LookupMode, mode: LookupMode,
mut callback: impl FnMut(ReceiverAdjustments, AssocItemId) -> Option<T>, mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>,
) -> Option<T> { ) -> Option<T> {
let mut slot = None; let mut slot = None;
iterate_method_candidates_dyn( iterate_method_candidates_dyn(
@ -630,9 +636,9 @@ pub(crate) fn iterate_method_candidates<T>(
visible_from_module, visible_from_module,
name, name,
mode, mode,
&mut |adj, item| { &mut |adj, item, visible| {
assert!(slot.is_none()); assert!(slot.is_none());
if let Some(it) = callback(adj, item) { if let Some(it) = callback(adj, item, visible) {
slot = Some(it); slot = Some(it);
return ControlFlow::Break(()); return ControlFlow::Break(());
} }
@ -771,7 +777,7 @@ pub fn iterate_path_candidates(
name, name,
LookupMode::Path, LookupMode::Path,
// the adjustments are not relevant for path lookup // the adjustments are not relevant for path lookup
&mut |_, id| callback(id), &mut |_, id, _| callback(id),
) )
} }
@ -783,7 +789,7 @@ pub fn iterate_method_candidates_dyn(
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mode: LookupMode, mode: LookupMode,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
match mode { match mode {
LookupMode::MethodCall => { LookupMode::MethodCall => {
@ -847,7 +853,7 @@ fn iterate_method_candidates_with_autoref(
traits_in_scope: &FxHashSet<TraitId>, traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) { if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) {
// don't try to resolve methods on unknown types // don't try to resolve methods on unknown types
@ -908,7 +914,7 @@ fn iterate_method_candidates_by_receiver(
traits_in_scope: &FxHashSet<TraitId>, traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let mut table = InferenceTable::new(db, env); let mut table = InferenceTable::new(db, env);
let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); let receiver_ty = table.instantiate_canonical(receiver_ty.clone());
@ -954,7 +960,7 @@ fn iterate_method_candidates_for_self_ty(
traits_in_scope: &FxHashSet<TraitId>, traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
name: Option<&Name>, name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let mut table = InferenceTable::new(db, env); let mut table = InferenceTable::new(db, env);
let self_ty = table.instantiate_canonical(self_ty.clone()); let self_ty = table.instantiate_canonical(self_ty.clone());
@ -985,7 +991,7 @@ fn iterate_trait_method_candidates(
name: Option<&Name>, name: Option<&Name>,
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>, receiver_adjustments: Option<ReceiverAdjustments>,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let db = table.db; let db = table.db;
let env = table.trait_env.clone(); let env = table.trait_env.clone();
@ -1016,9 +1022,11 @@ fn iterate_trait_method_candidates(
for &(_, item) in data.items.iter() { for &(_, item) in data.items.iter() {
// Don't pass a `visible_from_module` down to `is_valid_candidate`, // Don't pass a `visible_from_module` down to `is_valid_candidate`,
// since only inherent methods should be included into visibility checking. // since only inherent methods should be included into visibility checking.
if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { let visible = match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
continue; IsValidCandidate::Yes => true,
} IsValidCandidate::NotVisible => false,
IsValidCandidate::No => continue,
};
if !known_implemented { if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty); let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty);
if db.trait_solve(env.krate, goal.cast(Interner)).is_none() { if db.trait_solve(env.krate, goal.cast(Interner)).is_none() {
@ -1026,7 +1034,7 @@ fn iterate_trait_method_candidates(
} }
} }
known_implemented = true; known_implemented = true;
callback(receiver_adjustments.clone().unwrap_or_default(), item)?; callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
} }
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -1039,7 +1047,7 @@ fn iterate_inherent_methods(
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>, receiver_adjustments: Option<ReceiverAdjustments>,
visible_from_module: VisibleFromModule, visible_from_module: VisibleFromModule,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let db = table.db; let db = table.db;
let env = table.trait_env.clone(); let env = table.trait_env.clone();
@ -1128,7 +1136,7 @@ fn iterate_inherent_methods(
name: Option<&Name>, name: Option<&Name>,
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>, receiver_adjustments: Option<ReceiverAdjustments>,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
traits: impl Iterator<Item = TraitId>, traits: impl Iterator<Item = TraitId>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let db = table.db; let db = table.db;
@ -1136,9 +1144,13 @@ fn iterate_inherent_methods(
let data = db.trait_data(t); let data = db.trait_data(t);
for &(_, item) in data.items.iter() { for &(_, item) in data.items.iter() {
// We don't pass `visible_from_module` as all trait items should be visible. // 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) { let visible =
callback(receiver_adjustments.clone().unwrap_or_default(), item)?; match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
} IsValidCandidate::Yes => true,
IsValidCandidate::NotVisible => false,
IsValidCandidate::No => continue,
};
callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
} }
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -1152,17 +1164,25 @@ fn iterate_inherent_methods(
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
receiver_adjustments: Option<ReceiverAdjustments>, receiver_adjustments: Option<ReceiverAdjustments>,
visible_from_module: Option<ModuleId>, visible_from_module: Option<ModuleId>,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
let db = table.db; let db = table.db;
let impls_for_self_ty = impls.for_self_ty(self_ty); let impls_for_self_ty = impls.for_self_ty(self_ty);
for &impl_def in impls_for_self_ty { for &impl_def in impls_for_self_ty {
for &item in &db.impl_data(impl_def).items { for &item in &db.impl_data(impl_def).items {
if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module) let visible = match is_valid_candidate(
{ table,
continue; name,
} receiver_ty,
callback(receiver_adjustments.clone().unwrap_or_default(), item)?; item,
self_ty,
visible_from_module,
) {
IsValidCandidate::Yes => true,
IsValidCandidate::NotVisible => false,
IsValidCandidate::No => continue,
};
callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
} }
} }
ControlFlow::Continue(()) ControlFlow::Continue(())
@ -1191,7 +1211,7 @@ pub fn resolve_indexing_op(
macro_rules! check_that { macro_rules! check_that {
($cond:expr) => { ($cond:expr) => {
if !$cond { if !$cond {
return false; return IsValidCandidate::No;
} }
}; };
} }
@ -1203,7 +1223,7 @@ fn is_valid_candidate(
item: AssocItemId, item: AssocItemId,
self_ty: &Ty, self_ty: &Ty,
visible_from_module: Option<ModuleId>, visible_from_module: Option<ModuleId>,
) -> bool { ) -> IsValidCandidate {
let db = table.db; let db = table.db;
match item { match item {
AssocItemId::FunctionId(m) => { AssocItemId::FunctionId(m) => {
@ -1214,13 +1234,13 @@ fn is_valid_candidate(
check_that!(receiver_ty.is_none()); check_that!(receiver_ty.is_none());
check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n))); check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
check_that!(visible_from_module.map_or(true, |from_module| {
let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module); if let Some(from_module) = visible_from_module {
if !v { if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
cov_mark::hit!(const_candidate_not_visible); cov_mark::hit!(const_candidate_not_visible);
return IsValidCandidate::NotVisible;
} }
v }
}));
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container { if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
let self_ty_matches = table.run_in_snapshot(|table| { let self_ty_matches = table.run_in_snapshot(|table| {
let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id) let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id)
@ -1230,15 +1250,21 @@ fn is_valid_candidate(
}); });
if !self_ty_matches { if !self_ty_matches {
cov_mark::hit!(const_candidate_self_type_mismatch); cov_mark::hit!(const_candidate_self_type_mismatch);
return false; return IsValidCandidate::No;
} }
} }
true IsValidCandidate::Yes
} }
_ => false, _ => IsValidCandidate::No,
} }
} }
enum IsValidCandidate {
Yes,
No,
NotVisible,
}
fn is_valid_fn_candidate( fn is_valid_fn_candidate(
table: &mut InferenceTable<'_>, table: &mut InferenceTable<'_>,
fn_id: FunctionId, fn_id: FunctionId,
@ -1246,19 +1272,17 @@ fn is_valid_fn_candidate(
receiver_ty: Option<&Ty>, receiver_ty: Option<&Ty>,
self_ty: &Ty, self_ty: &Ty,
visible_from_module: Option<ModuleId>, visible_from_module: Option<ModuleId>,
) -> bool { ) -> IsValidCandidate {
let db = table.db; let db = table.db;
let data = db.function_data(fn_id); let data = db.function_data(fn_id);
check_that!(name.map_or(true, |n| n == &data.name)); check_that!(name.map_or(true, |n| n == &data.name));
check_that!(visible_from_module.map_or(true, |from_module| { if let Some(from_module) = visible_from_module {
let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module); if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) {
if !v {
cov_mark::hit!(autoderef_candidate_not_visible); cov_mark::hit!(autoderef_candidate_not_visible);
return IsValidCandidate::NotVisible;
} }
v }
}));
table.run_in_snapshot(|table| { table.run_in_snapshot(|table| {
let container = fn_id.lookup(db.upcast()).container; let container = fn_id.lookup(db.upcast()).container;
let (impl_subst, expect_self_ty) = match container { let (impl_subst, expect_self_ty) = match container {
@ -1297,7 +1321,7 @@ fn is_valid_fn_candidate(
// We need to consider the bounds on the impl to distinguish functions of the same name // We need to consider the bounds on the impl to distinguish functions of the same name
// for a type. // for a type.
let predicates = db.generic_predicates(impl_id.into()); let predicates = db.generic_predicates(impl_id.into());
predicates let valid = predicates
.iter() .iter()
.map(|predicate| { .map(|predicate| {
let (p, b) = predicate let (p, b) = predicate
@ -1312,12 +1336,16 @@ fn is_valid_fn_candidate(
// It's ok to get ambiguity here, as we may not have enough information to prove // It's ok to get ambiguity here, as we may not have enough information to prove
// obligations. We'll check if the user is calling the selected method properly // obligations. We'll check if the user is calling the selected method properly
// later anyway. // later anyway.
.all(|p| table.try_obligation(p.cast(Interner)).is_some()) .all(|p| table.try_obligation(p.cast(Interner)).is_some());
match valid {
true => IsValidCandidate::Yes,
false => IsValidCandidate::No,
}
} else { } else {
// For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in
// `iterate_trait_method_candidates()`. // `iterate_trait_method_candidates()`.
// For others, this function shouldn't be called. // For others, this function shouldn't be called.
true IsValidCandidate::Yes
} }
}) })
} }

View file

@ -3274,7 +3274,7 @@ impl Type {
with_local_impls.and_then(|b| b.id.containing_block()).into(), with_local_impls.and_then(|b| b.id.containing_block()).into(),
name, name,
method_resolution::LookupMode::MethodCall, method_resolution::LookupMode::MethodCall,
&mut |_adj, id| callback(id), &mut |_adj, id, _| callback(id),
); );
} }