mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
Avoid cycle when lowering predicates for associated item lookup
This commit is contained in:
parent
9724ca7af1
commit
3aa37d7f80
6 changed files with 65 additions and 17 deletions
|
@ -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();
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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, .. } => {
|
||||||
WherePredicateTypeTarget::TypeRef(type_ref) => {
|
match target {
|
||||||
ctx.lower_ty_only_param(type_ref) == Some(param_id)
|
WherePredicateTypeTarget::TypeRef(type_ref) => {
|
||||||
|
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([])
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue