Ignore type bindings in generic_predicates_for_param

This allows us to handle more cases without a query cycle, which
includes certain cases that rustc accepted. That in turn means we avoid
triggering salsa-rs/salsa#257 on valid code (it will still happen if the
user writes an actual cycle).

We actually accept more definitions than rustc now; that's because rustc
only ignores bindings when looking up super traits, whereas we now also
ignore them when looking for predicates to disambiguate associated type
shorthand. We could introduce a separate query for super traits if
necessary, but for now I think this should be fine.
This commit is contained in:
Florian Diebold 2021-03-20 20:07:36 +01:00
parent 0623bb4d71
commit d8f8b495ad
6 changed files with 52 additions and 19 deletions

View file

@ -2068,7 +2068,10 @@ impl Type {
match pred { match pred {
WhereClause::Implemented(trait_ref) => { WhereClause::Implemented(trait_ref) => {
cb(type_.clone()); cb(type_.clone());
walk_substs(db, type_, &trait_ref.substitution, cb); // skip the self type. it's likely the type we just got the bounds from
for ty in trait_ref.substitution.iter().skip(1) {
walk_type(db, &type_.derived(ty.clone()), cb);
}
} }
_ => (), _ => (),
} }

View file

@ -571,13 +571,22 @@ impl HirDisplay for Ty {
write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))? write!(f, "{}", param_data.name.clone().unwrap_or_else(Name::missing))?
} }
TypeParamProvenance::ArgumentImplTrait => { TypeParamProvenance::ArgumentImplTrait => {
let bounds = f.db.generic_predicates_for_param(id);
let substs = Substitution::type_params_for_generics(f.db, &generics); let substs = Substitution::type_params_for_generics(f.db, &generics);
write_bounds_like_dyn_trait_with_prefix( let bounds = f
"impl", .db
&bounds.iter().map(|b| b.clone().subst(&substs)).collect::<Vec<_>>(), .generic_predicates(id.parent)
f, .into_iter()
)?; .map(|pred| pred.clone().subst(&substs))
.filter(|wc| match &wc {
WhereClause::Implemented(tr) => tr.self_type_parameter() == self,
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj),
ty: _,
}) => proj.self_type_parameter() == self,
_ => false,
})
.collect::<Vec<_>>();
write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?;
} }
} }
} }

View file

@ -940,10 +940,19 @@ impl Ty {
let param_data = &generic_params.types[id.local_id]; let param_data = &generic_params.types[id.local_id];
match param_data.provenance { match param_data.provenance {
hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
let substs = Substitution::type_params(db, id.parent);
let predicates = db let predicates = db
.generic_predicates_for_param(id) .generic_predicates(id.parent)
.into_iter() .into_iter()
.map(|pred| pred.value.clone()) .map(|pred| pred.clone().subst(&substs))
.filter(|wc| match &wc {
WhereClause::Implemented(tr) => tr.self_type_parameter() == self,
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj),
ty: _,
}) => proj.self_type_parameter() == self,
_ => false,
})
.collect_vec(); .collect_vec();
Some(predicates) Some(predicates)

View file

@ -189,7 +189,10 @@ impl<'a> TyLoweringContext<'a> {
let self_ty = let self_ty =
TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner); TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner);
let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone())).collect() bounds
.iter()
.flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false))
.collect()
}); });
TyKind::Dyn(predicates).intern(&Interner) TyKind::Dyn(predicates).intern(&Interner)
} }
@ -666,6 +669,7 @@ impl<'a> TyLoweringContext<'a> {
pub(crate) fn lower_where_predicate( pub(crate) fn lower_where_predicate(
&'a self, &'a self,
where_predicate: &'a WherePredicate, where_predicate: &'a WherePredicate,
ignore_bindings: bool,
) -> impl Iterator<Item = WhereClause> + 'a { ) -> impl Iterator<Item = WhereClause> + 'a {
match where_predicate { match where_predicate {
WherePredicate::ForLifetime { target, bound, .. } WherePredicate::ForLifetime { target, bound, .. }
@ -688,7 +692,9 @@ impl<'a> TyLoweringContext<'a> {
.intern(&Interner) .intern(&Interner)
} }
}; };
self.lower_type_bound(bound, self_ty).collect::<Vec<_>>().into_iter() self.lower_type_bound(bound, self_ty, ignore_bindings)
.collect::<Vec<_>>()
.into_iter()
} }
WherePredicate::Lifetime { .. } => vec![].into_iter(), WherePredicate::Lifetime { .. } => vec![].into_iter(),
} }
@ -698,6 +704,7 @@ impl<'a> TyLoweringContext<'a> {
&'a self, &'a self,
bound: &'a TypeBound, bound: &'a TypeBound,
self_ty: Ty, self_ty: Ty,
ignore_bindings: bool,
) -> impl Iterator<Item = WhereClause> + 'a { ) -> impl Iterator<Item = WhereClause> + 'a {
let mut bindings = None; let mut bindings = None;
let trait_ref = match bound { let trait_ref = match bound {
@ -711,6 +718,7 @@ impl<'a> TyLoweringContext<'a> {
trait_ref.into_iter().chain( trait_ref.into_iter().chain(
bindings bindings
.into_iter() .into_iter()
.filter(move |_| !ignore_bindings)
.flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)), .flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)),
) )
} }
@ -755,6 +763,7 @@ impl<'a> TyLoweringContext<'a> {
preds.extend(self.lower_type_bound( preds.extend(self.lower_type_bound(
bound, bound,
TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(&Interner), TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(&Interner),
false,
)); ));
} }
preds preds
@ -766,7 +775,7 @@ impl<'a> TyLoweringContext<'a> {
let self_ty = let self_ty =
TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner); TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(&Interner);
let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone())).collect() bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)).collect()
}); });
ReturnTypeImplTrait { bounds: Binders::new(1, predicates) } ReturnTypeImplTrait { bounds: Binders::new(1, predicates) }
} }
@ -896,7 +905,9 @@ pub(crate) fn generic_predicates_for_param_query(
}, },
WherePredicate::Lifetime { .. } => false, WherePredicate::Lifetime { .. } => false,
}) })
.flat_map(|pred| ctx.lower_where_predicate(pred).map(|p| Binders::new(generics.len(), p))) .flat_map(|pred| {
ctx.lower_where_predicate(pred, true).map(|p| Binders::new(generics.len(), p))
})
.collect() .collect()
} }
@ -918,7 +929,7 @@ pub(crate) fn trait_environment_query(
let mut traits_in_scope = Vec::new(); let mut traits_in_scope = Vec::new();
let mut clauses = Vec::new(); let mut clauses = Vec::new();
for pred in resolver.where_predicates_in_scope() { for pred in resolver.where_predicates_in_scope() {
for pred in ctx.lower_where_predicate(pred) { for pred in ctx.lower_where_predicate(pred, false) {
if let WhereClause::Implemented(tr) = &pred { if let WhereClause::Implemented(tr) = &pred {
traits_in_scope.push((tr.self_type_parameter().clone(), tr.hir_trait_id())); traits_in_scope.push((tr.self_type_parameter().clone(), tr.hir_trait_id()));
} }
@ -967,7 +978,9 @@ pub(crate) fn generic_predicates_query(
let generics = generics(db.upcast(), def); let generics = generics(db.upcast(), def);
resolver resolver
.where_predicates_in_scope() .where_predicates_in_scope()
.flat_map(|pred| ctx.lower_where_predicate(pred).map(|p| Binders::new(generics.len(), p))) .flat_map(|pred| {
ctx.lower_where_predicate(pred, false).map(|p| Binders::new(generics.len(), p))
})
.collect() .collect()
} }

View file

@ -2273,8 +2273,7 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
#[test] #[test]
fn unselected_projection_in_trait_env_cycle_3() { fn unselected_projection_in_trait_env_cycle_3() {
// this is a cycle, although it would be possible to handle if we didn't go // this is a cycle for rustc; we currently accept it
// into bindings when looking for traits
check_types( check_types(
r#" r#"
//- /main.rs //- /main.rs
@ -2285,7 +2284,7 @@ trait Trait {
fn test<T>() where T: Trait<OtherItem = T::Item> { fn test<T>() where T: Trait<OtherItem = T::Item> {
let x: T::Item = no_matter; let x: T::Item = no_matter;
} //^ {unknown} } //^ Trait::Item<T>
"#, "#,
); );
} }

View file

@ -395,7 +395,7 @@ pub(crate) fn associated_ty_data_query(
let bounds = type_alias_data let bounds = type_alias_data
.bounds .bounds
.iter() .iter()
.flat_map(|bound| ctx.lower_type_bound(bound, self_ty.clone())) .flat_map(|bound| ctx.lower_type_bound(bound, self_ty.clone(), false))
.filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty)) .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty))
.map(|bound| make_binders(bound.shifted_in(&Interner), 0)) .map(|bound| make_binders(bound.shifted_in(&Interner), 0))
.collect(); .collect();