Prevent cycles when lowering from supertrait elaboration
Have a separate query for it.
This commit is contained in:
parent
c53d127990
commit
b4f36ce8f4
4 changed files with 125 additions and 24 deletions
|
|
@ -831,6 +831,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
|
|||
let mut ordered_associated_types = vec![];
|
||||
|
||||
if let Some(principal_trait) = principal {
|
||||
// Generally we should not elaborate in lowering as this can lead to cycles, but
|
||||
// here rustc cycles as well.
|
||||
for clause in elaborate::elaborate(
|
||||
interner,
|
||||
[Clause::upcast_from(
|
||||
|
|
@ -2590,11 +2592,13 @@ pub(crate) fn associated_type_by_name_including_super_traits<'db>(
|
|||
) -> Option<(TraitRef<'db>, TypeAliasId)> {
|
||||
let module = trait_ref.def_id.0.module(db);
|
||||
let interner = DbInterner::new_with(db, module.krate(db));
|
||||
rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| {
|
||||
let trait_id = t.as_ref().skip_binder().def_id.0;
|
||||
let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?;
|
||||
Some((t.skip_binder(), assoc_type))
|
||||
})
|
||||
all_supertraits_trait_refs(db, trait_ref.def_id.0)
|
||||
.map(|t| t.instantiate(interner, trait_ref.args))
|
||||
.find_map(|t| {
|
||||
let trait_id = t.def_id.0;
|
||||
let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?;
|
||||
Some((t, assoc_type))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn associated_type_shorthand_candidates(
|
||||
|
|
@ -2723,3 +2727,96 @@ fn named_associated_type_shorthand_candidates<'db, R>(
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// During lowering, elaborating supertraits can cause cycles. To avoid that, we have a separate query
|
||||
/// to only collect supertraits.
|
||||
///
|
||||
/// Technically, it is possible to avoid even more cycles by only collecting the `TraitId` of supertraits
|
||||
/// without their args. However rustc doesn't do that, so we don't either.
|
||||
pub(crate) fn all_supertraits_trait_refs(
|
||||
db: &dyn HirDatabase,
|
||||
trait_: TraitId,
|
||||
) -> impl ExactSizeIterator<Item = EarlyBinder<'_, TraitRef<'_>>> {
|
||||
let interner = DbInterner::new_no_crate(db);
|
||||
return all_supertraits_trait_refs_query(db, trait_).iter().map(move |trait_ref| {
|
||||
trait_ref.get_with(|(trait_, args)| {
|
||||
TraitRef::new_from_args(interner, (*trait_).into(), args.as_ref())
|
||||
})
|
||||
});
|
||||
|
||||
#[salsa_macros::tracked(returns(deref), cycle_result = all_supertraits_trait_refs_cycle_result)]
|
||||
pub(crate) fn all_supertraits_trait_refs_query(
|
||||
db: &dyn HirDatabase,
|
||||
trait_: TraitId,
|
||||
) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> {
|
||||
let resolver = trait_.resolver(db);
|
||||
let signature = db.trait_signature(trait_);
|
||||
let mut ctx = TyLoweringContext::new(
|
||||
db,
|
||||
&resolver,
|
||||
&signature.store,
|
||||
trait_.into(),
|
||||
LifetimeElisionKind::AnonymousReportError,
|
||||
);
|
||||
let interner = ctx.interner;
|
||||
|
||||
let self_param_ty = Ty::new_param(
|
||||
interner,
|
||||
TypeParamId::from_unchecked(TypeOrConstParamId {
|
||||
parent: trait_.into(),
|
||||
local_id: Idx::from_raw(la_arena::RawIdx::from_u32(0)),
|
||||
}),
|
||||
0,
|
||||
);
|
||||
|
||||
let mut supertraits = FxHashSet::default();
|
||||
supertraits.insert(StoredEarlyBinder::bind((
|
||||
trait_,
|
||||
GenericArgs::identity_for_item(interner, trait_.into()).store(),
|
||||
)));
|
||||
|
||||
for pred in signature.generic_params.where_predicates() {
|
||||
let WherePredicate::TypeBound { target, bound } = pred else {
|
||||
continue;
|
||||
};
|
||||
let target = &signature.store[*target];
|
||||
if let TypeRef::TypeParam(param_id) = target
|
||||
&& param_id.local_id().into_raw().into_u32() == 0
|
||||
{
|
||||
// This is `Self`.
|
||||
} else if let TypeRef::Path(path) = target
|
||||
&& path.is_self_type()
|
||||
{
|
||||
// Also `Self`.
|
||||
} else {
|
||||
// Not `Self`!
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx.lower_type_bound(bound, self_param_ty, true).for_each(|(clause, _)| {
|
||||
if let ClauseKind::Trait(trait_ref) = clause.kind().skip_binder() {
|
||||
supertraits.extend(
|
||||
all_supertraits_trait_refs(db, trait_ref.trait_ref.def_id.0).map(|t| {
|
||||
let trait_ref = t.instantiate(interner, trait_ref.trait_ref.args);
|
||||
StoredEarlyBinder::bind((trait_ref.def_id.0, trait_ref.args.store()))
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Box::from_iter(supertraits)
|
||||
}
|
||||
|
||||
pub(crate) fn all_supertraits_trait_refs_cycle_result(
|
||||
db: &dyn HirDatabase,
|
||||
_: salsa::Id,
|
||||
trait_: TraitId,
|
||||
) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> {
|
||||
let interner = DbInterner::new_no_crate(db);
|
||||
Box::new([StoredEarlyBinder::bind((
|
||||
trait_,
|
||||
GenericArgs::identity_for_item(interner, trait_.into()).store(),
|
||||
))])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2674,3 +2674,16 @@ pub use hresult::HRESULT;
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_21577() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
pub trait FilterT<F: FilterT<F, V = Self::V> = Self> {
|
||||
type V;
|
||||
|
||||
fn foo() {}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use crate::{
|
|||
TargetFeatures,
|
||||
db::HirDatabase,
|
||||
layout::{Layout, TagEncoding},
|
||||
lower::all_supertraits_trait_refs,
|
||||
mir::pad16,
|
||||
};
|
||||
|
||||
|
|
@ -62,23 +63,13 @@ pub fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[T
|
|||
|
||||
/// Returns an iterator over the whole super trait hierarchy (including the
|
||||
/// trait itself).
|
||||
pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
|
||||
// we need to take care a bit here to avoid infinite loops in case of cycles
|
||||
// (i.e. if we have `trait A: B; trait B: A;`)
|
||||
|
||||
let mut result = smallvec![trait_];
|
||||
let mut i = 0;
|
||||
while let Some(&t) = result.get(i) {
|
||||
// yeah this is quadratic, but trait hierarchies should be flat
|
||||
// enough that this doesn't matter
|
||||
direct_super_traits_cb(db, t, |tt| {
|
||||
if !result.contains(&tt) {
|
||||
result.push(tt);
|
||||
}
|
||||
});
|
||||
i += 1;
|
||||
}
|
||||
result
|
||||
pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
|
||||
let mut supertraits = all_supertraits_trait_refs(db, trait_)
|
||||
.map(|trait_ref| trait_ref.skip_binder().def_id.0)
|
||||
.collect::<SmallVec<[_; _]>>();
|
||||
supertraits.sort_unstable();
|
||||
supertraits.dedup();
|
||||
supertraits
|
||||
}
|
||||
|
||||
fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) {
|
||||
|
|
|
|||
|
|
@ -1975,8 +1975,8 @@ trait Sub: Super + Super {
|
|||
fn f() -> impl Sub<$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
trait Sub<SubTy = …, SuperTy = …>
|
||||
^^^^^^^^^ -----------
|
||||
trait Sub<SuperTy = …, SubTy = …>
|
||||
^^^^^^^^^^^ ---------
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue