Rework find_super_trait_path to protect against cycles

This commit is contained in:
Florian Diebold 2020-02-22 13:14:39 +01:00
parent c200025794
commit 3e106c77ff
5 changed files with 74 additions and 23 deletions

View file

@ -814,13 +814,16 @@ pub trait TypeWalk {
where where
Self: Sized, Self: Sized,
{ {
self.fold_binders(&mut |ty, binders| match ty { self.fold_binders(
&mut |ty, binders| match ty {
Ty::Bound(idx) if idx as usize >= binders => { Ty::Bound(idx) if idx as usize >= binders => {
assert!(idx as i32 >= -n); assert!(idx as i32 >= -n);
Ty::Bound((idx as i32 + n) as u32) Ty::Bound((idx as i32 + n) as u32)
} }
ty => ty, ty => ty,
}, 0) },
0,
)
} }
} }

View file

@ -241,7 +241,8 @@ impl Ty {
TypeNs::TraitId(trait_) => { TypeNs::TraitId(trait_) => {
// if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there // if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there
let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None }; let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None };
let trait_ref = TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty); let trait_ref =
TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty);
return if remaining_segments.len() == 1 { return if remaining_segments.len() == 1 {
let segment = remaining_segments.first().unwrap(); let segment = remaining_segments.first().unwrap();
let associated_ty = associated_type_by_name_including_super_traits( let associated_ty = associated_type_by_name_including_super_traits(

View file

@ -622,6 +622,44 @@ fn test() {
); );
} }
#[test]
fn coerce_unsize_super_trait_cycle() {
assert_snapshot!(
infer_with_mismatches(r#"
#[lang = "unsize"]
pub trait Unsize<T> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}
impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
trait A {}
trait B: C + A {}
trait C: B {}
trait D: C
struct S;
impl A for S {}
impl B for S {}
impl C for S {}
impl D for S {}
fn test() {
let obj: &dyn D = &S;
let obj: &dyn A = obj;
}
"#, true),
@r###"
[292; 348) '{ ...obj; }': ()
[302; 305) 'obj': &dyn D
[316; 318) '&S': &S
[317; 318) 'S': S
[328; 331) 'obj': &dyn A
[342; 345) 'obj': &dyn D
"###
);
}
#[ignore] #[ignore]
#[test] #[test]
fn coerce_unsize_generic() { fn coerce_unsize_generic() {

View file

@ -316,12 +316,10 @@ fn super_trait_object_unsize_impl_datum(
let self_bounds = vec![GenericPredicate::Implemented(self_trait_ref.clone())]; let self_bounds = vec![GenericPredicate::Implemented(self_trait_ref.clone())];
// we need to go from our trait to the super trait, substituting type parameters // we need to go from our trait to the super trait, substituting type parameters
let mut path = crate::utils::find_super_trait_path(db, data.super_trait, data.trait_); let path = crate::utils::find_super_trait_path(db, data.trait_, data.super_trait);
path.pop(); // the last one is our current trait, we don't need that
path.reverse(); // we want to go from trait to super trait
let mut current_trait_ref = self_trait_ref; let mut current_trait_ref = self_trait_ref;
for t in path { for t in path.into_iter().skip(1) {
let bounds = db.generic_predicates(current_trait_ref.trait_.into()); let bounds = db.generic_predicates(current_trait_ref.trait_.into());
let super_trait_ref = bounds let super_trait_ref = bounds
.iter() .iter()

View file

@ -62,25 +62,36 @@ pub(super) fn all_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec<Tr
result result
} }
/// Finds a path from a trait to one of its descendant traits. Returns an empty /// Finds a path from a trait to one of its super traits. Returns an empty
/// vector if there is no path. /// vector if there is no path.
pub(super) fn find_super_trait_path( pub(super) fn find_super_trait_path(
db: &impl DefDatabase, db: &impl DefDatabase,
super_trait: TraitId,
trait_: TraitId, trait_: TraitId,
super_trait: TraitId,
) -> Vec<TraitId> { ) -> Vec<TraitId> {
let mut result = Vec::with_capacity(2);
result.push(trait_);
return if go(db, super_trait, &mut result) { result } else { Vec::new() };
fn go(db: &impl DefDatabase, super_trait: TraitId, path: &mut Vec<TraitId>) -> bool {
let trait_ = *path.last().unwrap();
if trait_ == super_trait { if trait_ == super_trait {
return vec![trait_]; return true;
} }
for tt in direct_super_traits(db, trait_) { for tt in direct_super_traits(db, trait_) {
let mut path = find_super_trait_path(db, super_trait, tt); if path.contains(&tt) {
if !path.is_empty() { continue;
path.push(trait_); }
return path; path.push(tt);
if go(db, super_trait, path) {
return true;
} else {
path.pop();
} }
} }
Vec::new() false
}
} }
pub(super) fn associated_type_by_name_including_super_traits( pub(super) fn associated_type_by_name_including_super_traits(