Fix assoc type shorthand from method bounds

In code like this:
```rust
impl<T> Option<T> {
    fn as_deref(&self) -> T::Target where T: Deref {}
}
```

when trying to resolve the associated type `T::Target`, we were only
looking at the bounds on the impl (where the type parameter is defined),
but the method can add additional bounds that can also be used to refer
to associated types. Hence, when resolving such an associated type, it's
not enough to just know the type parameter T, we also need to know
exactly where we are currently.

This fixes #11364 (beta apparently switched some bounds around).
This commit is contained in:
Florian Diebold 2022-02-03 12:43:15 +01:00
parent 9cb6e3a190
commit 4ed5fe1554
9 changed files with 107 additions and 67 deletions

View File

@ -239,7 +239,7 @@ impl HirDisplay for TypeParam {
return Ok(());
}
let bounds = f.db.generic_predicates_for_param(self.id, None);
let bounds = f.db.generic_predicates_for_param(self.id.parent, self.id, None);
let substs = TyBuilder::type_params_subst(f.db, self.id.parent);
let predicates: Vec<_> =
bounds.iter().cloned().map(|b| b.substitute(Interner, &substs)).collect();

View File

@ -2235,8 +2235,11 @@ impl TypeParam {
Type::new_with_resolver_inner(db, krate, &resolver, ty)
}
/// FIXME: this only lists trait bounds from the item defining the type
/// parameter, not additional bounds that might be added e.g. by a method if
/// the parameter comes from an impl!
pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
db.generic_predicates_for_param(self.id, None)
db.generic_predicates_for_param(self.id.parent, self.id, None)
.iter()
.filter_map(|pred| match &pred.skip_binders().skip_binders() {
hir_ty::WhereClause::Implemented(trait_ref) => {

View File

@ -12,7 +12,7 @@ use hir_def::{
AsMacroCall, FunctionId, TraitId, VariantId,
};
use hir_expand::{name::AsName, ExpansionInfo, MacroCallId};
use hir_ty::{associated_type_shorthand_candidates, Interner};
use hir_ty::Interner;
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
@ -50,7 +50,7 @@ pub enum PathResolution {
}
impl PathResolution {
fn in_type_ns(&self) -> Option<TypeNs> {
pub(crate) fn in_type_ns(&self) -> Option<TypeNs> {
match self {
PathResolution::Def(ModuleDef::Adt(adt)) => Some(TypeNs::AdtId((*adt).into())),
PathResolution::Def(ModuleDef::BuiltinType(builtin)) => {
@ -80,18 +80,6 @@ impl PathResolution {
}
}
}
/// Returns an iterator over associated types that may be specified after this path (using
/// `Ty::Assoc` syntax).
pub fn assoc_type_shorthand_candidates<R>(
&self,
db: &dyn HirDatabase,
mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
) -> Option<R> {
associated_type_shorthand_candidates(db, self.in_type_ns()?, |name, _, id| {
cb(name, id.into())
})
}
}
#[derive(Debug)]
@ -1314,4 +1302,20 @@ impl<'a> SemanticsScope<'a> {
let path = Path::from_src(path.clone(), &ctx)?;
resolve_hir_path(self.db, &self.resolver, &path)
}
/// Iterates over associated types that may be specified after the given path (using
/// `Ty::Assoc` syntax).
pub fn assoc_type_shorthand_candidates<R>(
&self,
resolution: &PathResolution,
mut cb: impl FnMut(&Name, TypeAlias) -> Option<R>,
) -> Option<R> {
let def = self.resolver.generic_def()?;
hir_ty::associated_type_shorthand_candidates(
self.db,
def,
resolution.in_type_ns()?,
|name, _, id| cb(name, id.into()),
)
}
}

View File

@ -619,9 +619,15 @@ fn resolve_hir_path_(
TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()),
};
match unresolved {
Some(unresolved) => res
.assoc_type_shorthand_candidates(db, |name, alias| {
(name == unresolved.name).then(|| alias)
Some(unresolved) => resolver
.generic_def()
.and_then(|def| {
hir_ty::associated_type_shorthand_candidates(
db,
def,
res.in_type_ns()?,
|name, _, id| (name == unresolved.name).then(|| id),
)
})
.map(TypeAlias::from)
.map(Into::into)

View File

@ -60,6 +60,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
fn generic_predicates_for_param(
&self,
def: GenericDefId,
param_id: TypeParamId,
assoc_name: Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]>;

View File

@ -530,49 +530,50 @@ impl<'a> TyLoweringContext<'a> {
}
fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
if let Some(res) = res {
let ty = named_associated_type_shorthand_candidates(
self.db,
res,
Some(segment.name.clone()),
move |name, t, associated_ty| {
if name == segment.name {
let substs = match self.type_param_mode {
TypeParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let generics = generics(
self.db.upcast(),
self.resolver.generic_def().expect(
"there should be generics if there's a generic param",
),
);
let s = generics.type_params_subst(self.db);
s.apply(t.substitution.clone(), Interner)
}
TypeParamLoweringMode::Variable => t.substitution.clone(),
};
// We need to shift in the bound vars, since
// associated_type_shorthand_candidates does not do that
let substs = substs.shifted_in_from(Interner, self.in_binders);
// FIXME handle type parameters on the segment
Some(
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: substs,
}))
.intern(Interner),
)
} else {
None
}
},
);
let (def, res) = match (self.resolver.generic_def(), res) {
(Some(def), Some(res)) => (def, res),
_ => return TyKind::Error.intern(Interner),
};
let ty = named_associated_type_shorthand_candidates(
self.db,
def,
res,
Some(segment.name.clone()),
move |name, t, associated_ty| {
if name == segment.name {
let substs = match self.type_param_mode {
TypeParamLoweringMode::Placeholder => {
// if we're lowering to placeholders, we have to put
// them in now
let generics = generics(
self.db.upcast(),
self.resolver
.generic_def()
.expect("there should be generics if there's a generic param"),
);
let s = generics.type_params_subst(self.db);
s.apply(t.substitution.clone(), Interner)
}
TypeParamLoweringMode::Variable => t.substitution.clone(),
};
// We need to shift in the bound vars, since
// associated_type_shorthand_candidates does not do that
let substs = substs.shifted_in_from(Interner, self.in_binders);
// FIXME handle type parameters on the segment
Some(
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution: substs,
}))
.intern(Interner),
)
} else {
None
}
},
);
ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
} else {
TyKind::Error.intern(Interner)
}
ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
}
fn lower_path_inner(
@ -934,14 +935,18 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig
pub fn associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase,
def: GenericDefId,
res: TypeNs,
cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
) -> Option<R> {
named_associated_type_shorthand_candidates(db, res, None, cb)
named_associated_type_shorthand_candidates(db, def, res, None, cb)
}
fn named_associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase,
// If the type parameter is defined in an impl and we're in a method, there
// might be additional where clauses to consider
def: GenericDefId,
res: TypeNs,
assoc_name: Option<Name>,
mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
@ -968,7 +973,7 @@ fn named_associated_type_shorthand_candidates<R>(
db.impl_trait(impl_id)?.into_value_and_skipped_binders().0,
),
TypeNs::GenericParam(param_id) => {
let predicates = db.generic_predicates_for_param(param_id, assoc_name);
let predicates = db.generic_predicates_for_param(def, param_id, assoc_name);
let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() {
// FIXME: how to correctly handle higher-ranked bounds here?
WhereClause::Implemented(tr) => search(
@ -1030,13 +1035,14 @@ pub(crate) fn field_types_query(
/// these are fine: `T: Foo<U::Item>, U: Foo<()>`.
pub(crate) fn generic_predicates_for_param_query(
db: &dyn HirDatabase,
def: GenericDefId,
param_id: TypeParamId,
assoc_name: Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]> {
let resolver = param_id.parent.resolver(db.upcast());
let resolver = def.resolver(db.upcast());
let ctx =
TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
let generics = generics(db.upcast(), param_id.parent);
let generics = generics(db.upcast(), def);
let mut predicates: Vec<_> = resolver
.where_predicates_in_scope()
// we have to filter out all other predicates *first*, before attempting to lower them
@ -1098,6 +1104,7 @@ pub(crate) fn generic_predicates_for_param_query(
pub(crate) fn generic_predicates_for_param_recover(
_db: &dyn HirDatabase,
_cycle: &[String],
_def: &GenericDefId,
_param_id: &TypeParamId,
_assoc_name: &Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]> {

View File

@ -392,6 +392,25 @@ fn test() {
);
}
#[test]
fn associated_type_shorthand_from_method_bound() {
check_types(
r#"
trait Iterable {
type Item;
}
struct S<T>;
impl<T> S<T> {
fn foo(self) -> T::Item where T: Iterable { loop {} }
}
fn test<T: Iterable>() {
let s: S<T>;
s.foo();
// ^^^^^^^ Iterable::Item<T>
}"#,
);
}
#[test]
fn infer_associated_type_bound() {
check_types(

View File

@ -83,7 +83,7 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<Tr
Some(p) => TypeParamId { parent: trait_ref.hir_trait_id().into(), local_id: p },
None => return Vec::new(),
};
db.generic_predicates_for_param(trait_self, None)
db.generic_predicates_for_param(trait_self.parent, trait_self, None)
.iter()
.filter_map(|pred| {
pred.as_ref().filter_map(|pred| match pred.skip_binders() {

View File

@ -119,7 +119,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
if !matches!(kind, Some(PathKind::Pat)) {
// Add associated types on type parameters and `Self`.
resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
ctx.scope.assoc_type_shorthand_candidates(&resolution, |_, alias| {
acc.add_type_alias(ctx, alias);
None::<()>
});