Avoid cycle when lowering predicates for associated item lookup

This commit is contained in:
Jonas Schievink 2021-10-04 17:39:55 +02:00
parent 9724ca7af1
commit 3aa37d7f80
6 changed files with 65 additions and 17 deletions

View file

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

View file

@ -2024,7 +2024,7 @@ impl TypeParam {
} }
pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> { pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
db.generic_predicates_for_param(self.id) db.generic_predicates_for_param(self.id, None)
.iter() .iter()
.filter_map(|pred| match &pred.skip_binders().skip_binders() { .filter_map(|pred| match &pred.skip_binders().skip_binders() {
hir_ty::WhereClause::Implemented(trait_ref) => { hir_ty::WhereClause::Implemented(trait_ref) => {

View file

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

View file

@ -30,6 +30,7 @@ use smallvec::SmallVec;
use stdx::impl_from; use stdx::impl_from;
use syntax::ast; use syntax::ast;
use crate::all_super_traits;
use crate::{ use crate::{
consteval, consteval,
db::HirDatabase, db::HirDatabase,
@ -531,9 +532,10 @@ impl<'a> TyLoweringContext<'a> {
fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty { fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
if let Some(res) = res { if let Some(res) = res {
let ty = associated_type_shorthand_candidates( let ty = named_associated_type_shorthand_candidates(
self.db, self.db,
res, res,
Some(segment.name.clone()),
move |name, t, associated_ty| { move |name, t, associated_ty| {
if name == segment.name { if name == segment.name {
let substs = match self.type_param_mode { let substs = match self.type_param_mode {
@ -555,16 +557,16 @@ impl<'a> TyLoweringContext<'a> {
// associated_type_shorthand_candidates does not do that // associated_type_shorthand_candidates does not do that
let substs = substs.shifted_in_from(&Interner, self.in_binders); let substs = substs.shifted_in_from(&Interner, self.in_binders);
// FIXME handle type parameters on the segment // FIXME handle type parameters on the segment
return Some( Some(
TyKind::Alias(AliasTy::Projection(ProjectionTy { TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty), associated_ty_id: to_assoc_type_id(associated_ty),
substitution: substs, substitution: substs,
})) }))
.intern(&Interner), .intern(&Interner),
); )
} } else {
None None
}
}, },
); );
@ -935,6 +937,15 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig
pub fn associated_type_shorthand_candidates<R>( pub fn associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase, db: &dyn HirDatabase,
res: TypeNs, res: TypeNs,
cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
) -> Option<R> {
named_associated_type_shorthand_candidates(db, res, None, cb)
}
fn named_associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase,
res: TypeNs,
assoc_name: Option<Name>,
mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
) -> Option<R> { ) -> Option<R> {
let mut search = |t| { let mut search = |t| {
@ -959,7 +970,7 @@ pub fn associated_type_shorthand_candidates<R>(
db.impl_trait(impl_id)?.into_value_and_skipped_binders().0, db.impl_trait(impl_id)?.into_value_and_skipped_binders().0,
), ),
TypeNs::GenericParam(param_id) => { TypeNs::GenericParam(param_id) => {
let predicates = db.generic_predicates_for_param(param_id); let predicates = db.generic_predicates_for_param(param_id, assoc_name);
let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() { let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() {
// FIXME: how to correctly handle higher-ranked bounds here? // FIXME: how to correctly handle higher-ranked bounds here?
WhereClause::Implemented(tr) => search( WhereClause::Implemented(tr) => search(
@ -1022,6 +1033,7 @@ pub(crate) fn field_types_query(
pub(crate) fn generic_predicates_for_param_query( pub(crate) fn generic_predicates_for_param_query(
db: &dyn HirDatabase, db: &dyn HirDatabase,
param_id: TypeParamId, param_id: TypeParamId,
assoc_name: Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]> { ) -> Arc<[Binders<QuantifiedWhereClause>]> {
let resolver = param_id.parent.resolver(db.upcast()); let resolver = param_id.parent.resolver(db.upcast());
let ctx = let ctx =
@ -1031,13 +1043,46 @@ pub(crate) fn generic_predicates_for_param_query(
.where_predicates_in_scope() .where_predicates_in_scope()
// we have to filter out all other predicates *first*, before attempting to lower them // we have to filter out all other predicates *first*, before attempting to lower them
.filter(|pred| match pred { .filter(|pred| match pred {
WherePredicate::ForLifetime { target, .. } WherePredicate::ForLifetime { target, bound, .. }
| WherePredicate::TypeBound { target, .. } => match target { | WherePredicate::TypeBound { target, bound, .. } => {
match target {
WherePredicateTypeTarget::TypeRef(type_ref) => { WherePredicateTypeTarget::TypeRef(type_ref) => {
ctx.lower_ty_only_param(type_ref) == Some(param_id) if ctx.lower_ty_only_param(type_ref) != Some(param_id) {
return false;
}
}
WherePredicateTypeTarget::TypeParam(local_id) => {
if *local_id != param_id.local_id {
return false;
}
}
};
match &**bound {
TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => {
// Only lower the bound if the trait could possibly define the associated
// type we're looking for.
let assoc_name = match &assoc_name {
Some(it) => it,
None => return true,
};
let tr = match resolver
.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
{
Some(TypeNs::TraitId(tr)) => tr,
_ => return false,
};
all_super_traits(db.upcast(), tr).iter().any(|tr| {
db.trait_data(*tr).items.iter().any(|(name, item)| {
matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name
})
})
}
TypeBound::Lifetime(_) | TypeBound::Error => false,
}
} }
WherePredicateTypeTarget::TypeParam(local_id) => *local_id == param_id.local_id,
},
WherePredicate::Lifetime { .. } => false, WherePredicate::Lifetime { .. } => false,
}) })
.flat_map(|pred| ctx.lower_where_predicate(pred, true).map(|p| make_binders(&generics, p))) .flat_map(|pred| ctx.lower_where_predicate(pred, true).map(|p| make_binders(&generics, p)))
@ -1056,6 +1101,7 @@ pub(crate) fn generic_predicates_for_param_recover(
_db: &dyn HirDatabase, _db: &dyn HirDatabase,
_cycle: &[String], _cycle: &[String],
_param_id: &TypeParamId, _param_id: &TypeParamId,
_assoc_name: &Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]> { ) -> Arc<[Binders<QuantifiedWhereClause>]> {
Arc::new([]) Arc::new([])
} }

View file

@ -2100,7 +2100,8 @@ fn test() {
#[test] #[test]
fn unselected_projection_in_trait_env_cycle_1() { fn unselected_projection_in_trait_env_cycle_1() {
// this is a legitimate cycle // This is not a cycle, because the `T: Trait2<T::Item>` bound depends only on the `T: Trait`
// bound, not on itself (since only `Trait` can define `Item`).
check_types( check_types(
r#" r#"
trait Trait { trait Trait {
@ -2111,7 +2112,7 @@ trait Trait2<T> {}
fn test<T: Trait>() where T: Trait2<T::Item> { fn test<T: Trait>() where T: Trait2<T::Item> {
let x: T::Item = no_matter; let x: T::Item = no_matter;
} //^^^^^^^^^ {unknown} } //^^^^^^^^^ Trait::Item<T>
"#, "#,
); );
} }

View file

@ -79,7 +79,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 }, Some(p) => TypeParamId { parent: trait_ref.hir_trait_id().into(), local_id: p },
None => return Vec::new(), None => return Vec::new(),
}; };
db.generic_predicates_for_param(trait_self) db.generic_predicates_for_param(trait_self, None)
.iter() .iter()
.filter_map(|pred| { .filter_map(|pred| {
pred.as_ref().filter_map(|pred| match pred.skip_binders() { pred.as_ref().filter_map(|pred| match pred.skip_binders() {