Skip to content

Commit 5bdae82

Browse files
Merge #10456
10456: fix: Avoid cycle when lowering predicates for associated item lookup r=flodiebold a=jonas-schievink Fixes #10386 (the salsa bug persists, but this lets us avoid it by fixing the underlying bug) This reimplements the rustc logic in https://github.com/rust-lang/rust/blob/b27661eb33c74cb514dba059b47d86b6582ac1c2/compiler/rustc_typeck/src/collect.rs#L556: When resolving an associated type `T::Item`, we've previously lowered all predicates that could affect `T`, but we actually have to look only at those predicates whose traits define an associated type of the right name. Co-authored-by: Jonas Schievink <[email protected]>
2 parents cf1ea9d + 3aa37d7 commit 5bdae82

File tree

6 files changed

+65
-17
lines changed

6 files changed

+65
-17
lines changed

crates/hir/src/display.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ impl HirDisplay for TypeParam {
241241
return Ok(());
242242
}
243243

244-
let bounds = f.db.generic_predicates_for_param(self.id);
244+
let bounds = f.db.generic_predicates_for_param(self.id, None);
245245
let substs = TyBuilder::type_params_subst(f.db, self.id.parent);
246246
let predicates: Vec<_> =
247247
bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect();

crates/hir/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2024,7 +2024,7 @@ impl TypeParam {
20242024
}
20252025

20262026
pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
2027-
db.generic_predicates_for_param(self.id)
2027+
db.generic_predicates_for_param(self.id, None)
20282028
.iter()
20292029
.filter_map(|pred| match &pred.skip_binders().skip_binders() {
20302030
hir_ty::WhereClause::Implemented(trait_ref) => {

crates/hir_ty/src/db.rs

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
6161
fn generic_predicates_for_param(
6262
&self,
6363
param_id: TypeParamId,
64+
assoc_name: Option<Name>,
6465
) -> Arc<[Binders<QuantifiedWhereClause>]>;
6566

6667
#[salsa::invoke(crate::lower::generic_predicates_query)]

crates/hir_ty/src/lower.rs

+58-12
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use smallvec::SmallVec;
3030
use stdx::impl_from;
3131
use syntax::ast;
3232

33+
use crate::all_super_traits;
3334
use crate::{
3435
consteval,
3536
db::HirDatabase,
@@ -531,9 +532,10 @@ impl<'a> TyLoweringContext<'a> {
531532

532533
fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
533534
if let Some(res) = res {
534-
let ty = associated_type_shorthand_candidates(
535+
let ty = named_associated_type_shorthand_candidates(
535536
self.db,
536537
res,
538+
Some(segment.name.clone()),
537539
move |name, t, associated_ty| {
538540
if name == segment.name {
539541
let substs = match self.type_param_mode {
@@ -555,16 +557,16 @@ impl<'a> TyLoweringContext<'a> {
555557
// associated_type_shorthand_candidates does not do that
556558
let substs = substs.shifted_in_from(&Interner, self.in_binders);
557559
// FIXME handle type parameters on the segment
558-
return Some(
560+
Some(
559561
TyKind::Alias(AliasTy::Projection(ProjectionTy {
560562
associated_ty_id: to_assoc_type_id(associated_ty),
561563
substitution: substs,
562564
}))
563565
.intern(&Interner),
564-
);
566+
)
567+
} else {
568+
None
565569
}
566-
567-
None
568570
},
569571
);
570572

@@ -935,6 +937,15 @@ pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig
935937
pub fn associated_type_shorthand_candidates<R>(
936938
db: &dyn HirDatabase,
937939
res: TypeNs,
940+
cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
941+
) -> Option<R> {
942+
named_associated_type_shorthand_candidates(db, res, None, cb)
943+
}
944+
945+
fn named_associated_type_shorthand_candidates<R>(
946+
db: &dyn HirDatabase,
947+
res: TypeNs,
948+
assoc_name: Option<Name>,
938949
mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>,
939950
) -> Option<R> {
940951
let mut search = |t| {
@@ -959,7 +970,7 @@ pub fn associated_type_shorthand_candidates<R>(
959970
db.impl_trait(impl_id)?.into_value_and_skipped_binders().0,
960971
),
961972
TypeNs::GenericParam(param_id) => {
962-
let predicates = db.generic_predicates_for_param(param_id);
973+
let predicates = db.generic_predicates_for_param(param_id, assoc_name);
963974
let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() {
964975
// FIXME: how to correctly handle higher-ranked bounds here?
965976
WhereClause::Implemented(tr) => search(
@@ -1022,6 +1033,7 @@ pub(crate) fn field_types_query(
10221033
pub(crate) fn generic_predicates_for_param_query(
10231034
db: &dyn HirDatabase,
10241035
param_id: TypeParamId,
1036+
assoc_name: Option<Name>,
10251037
) -> Arc<[Binders<QuantifiedWhereClause>]> {
10261038
let resolver = param_id.parent.resolver(db.upcast());
10271039
let ctx =
@@ -1031,13 +1043,46 @@ pub(crate) fn generic_predicates_for_param_query(
10311043
.where_predicates_in_scope()
10321044
// we have to filter out all other predicates *first*, before attempting to lower them
10331045
.filter(|pred| match pred {
1034-
WherePredicate::ForLifetime { target, .. }
1035-
| WherePredicate::TypeBound { target, .. } => match target {
1036-
WherePredicateTypeTarget::TypeRef(type_ref) => {
1037-
ctx.lower_ty_only_param(type_ref) == Some(param_id)
1046+
WherePredicate::ForLifetime { target, bound, .. }
1047+
| WherePredicate::TypeBound { target, bound, .. } => {
1048+
match target {
1049+
WherePredicateTypeTarget::TypeRef(type_ref) => {
1050+
if ctx.lower_ty_only_param(type_ref) != Some(param_id) {
1051+
return false;
1052+
}
1053+
}
1054+
WherePredicateTypeTarget::TypeParam(local_id) => {
1055+
if *local_id != param_id.local_id {
1056+
return false;
1057+
}
1058+
}
1059+
};
1060+
1061+
match &**bound {
1062+
TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => {
1063+
// Only lower the bound if the trait could possibly define the associated
1064+
// type we're looking for.
1065+
1066+
let assoc_name = match &assoc_name {
1067+
Some(it) => it,
1068+
None => return true,
1069+
};
1070+
let tr = match resolver
1071+
.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
1072+
{
1073+
Some(TypeNs::TraitId(tr)) => tr,
1074+
_ => return false,
1075+
};
1076+
1077+
all_super_traits(db.upcast(), tr).iter().any(|tr| {
1078+
db.trait_data(*tr).items.iter().any(|(name, item)| {
1079+
matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name
1080+
})
1081+
})
1082+
}
1083+
TypeBound::Lifetime(_) | TypeBound::Error => false,
10381084
}
1039-
WherePredicateTypeTarget::TypeParam(local_id) => *local_id == param_id.local_id,
1040-
},
1085+
}
10411086
WherePredicate::Lifetime { .. } => false,
10421087
})
10431088
.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(
10561101
_db: &dyn HirDatabase,
10571102
_cycle: &[String],
10581103
_param_id: &TypeParamId,
1104+
_assoc_name: &Option<Name>,
10591105
) -> Arc<[Binders<QuantifiedWhereClause>]> {
10601106
Arc::new([])
10611107
}

crates/hir_ty/src/tests/traits.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2100,7 +2100,8 @@ fn test() {
21002100

21012101
#[test]
21022102
fn unselected_projection_in_trait_env_cycle_1() {
2103-
// this is a legitimate cycle
2103+
// This is not a cycle, because the `T: Trait2<T::Item>` bound depends only on the `T: Trait`
2104+
// bound, not on itself (since only `Trait` can define `Item`).
21042105
check_types(
21052106
r#"
21062107
trait Trait {
@@ -2111,7 +2112,7 @@ trait Trait2<T> {}
21112112
21122113
fn test<T: Trait>() where T: Trait2<T::Item> {
21132114
let x: T::Item = no_matter;
2114-
} //^^^^^^^^^ {unknown}
2115+
} //^^^^^^^^^ Trait::Item<T>
21152116
"#,
21162117
);
21172118
}

crates/hir_ty/src/utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<Tr
7979
Some(p) => TypeParamId { parent: trait_ref.hir_trait_id().into(), local_id: p },
8080
None => return Vec::new(),
8181
};
82-
db.generic_predicates_for_param(trait_self)
82+
db.generic_predicates_for_param(trait_self, None)
8383
.iter()
8484
.filter_map(|pred| {
8585
pred.as_ref().filter_map(|pred| match pred.skip_binders() {

0 commit comments

Comments
 (0)