mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 22:54:58 +00:00
Rework find_super_trait_path to protect against cycles
This commit is contained in:
parent
c200025794
commit
3e106c77ff
5 changed files with 74 additions and 23 deletions
|
@ -814,13 +814,16 @@ pub trait TypeWalk {
|
|||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.fold_binders(&mut |ty, binders| match ty {
|
||||
Ty::Bound(idx) if idx as usize >= binders => {
|
||||
assert!(idx as i32 >= -n);
|
||||
Ty::Bound((idx as i32 + n) as u32)
|
||||
}
|
||||
ty => ty,
|
||||
}, 0)
|
||||
self.fold_binders(
|
||||
&mut |ty, binders| match ty {
|
||||
Ty::Bound(idx) if idx as usize >= binders => {
|
||||
assert!(idx as i32 >= -n);
|
||||
Ty::Bound((idx as i32 + n) as u32)
|
||||
}
|
||||
ty => ty,
|
||||
},
|
||||
0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -241,7 +241,8 @@ impl Ty {
|
|||
TypeNs::TraitId(trait_) => {
|
||||
// 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 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 {
|
||||
let segment = remaining_segments.first().unwrap();
|
||||
let associated_ty = associated_type_by_name_including_super_traits(
|
||||
|
|
|
@ -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]
|
||||
#[test]
|
||||
fn coerce_unsize_generic() {
|
||||
|
|
|
@ -316,12 +316,10 @@ fn super_trait_object_unsize_impl_datum(
|
|||
let self_bounds = vec![GenericPredicate::Implemented(self_trait_ref.clone())];
|
||||
|
||||
// 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_);
|
||||
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 path = crate::utils::find_super_trait_path(db, data.trait_, data.super_trait);
|
||||
|
||||
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 super_trait_ref = bounds
|
||||
.iter()
|
||||
|
|
|
@ -62,25 +62,36 @@ pub(super) fn all_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec<Tr
|
|||
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.
|
||||
pub(super) fn find_super_trait_path(
|
||||
db: &impl DefDatabase,
|
||||
super_trait: TraitId,
|
||||
trait_: TraitId,
|
||||
super_trait: TraitId,
|
||||
) -> Vec<TraitId> {
|
||||
if trait_ == super_trait {
|
||||
return vec![trait_];
|
||||
}
|
||||
let mut result = Vec::with_capacity(2);
|
||||
result.push(trait_);
|
||||
return if go(db, super_trait, &mut result) { result } else { Vec::new() };
|
||||
|
||||
for tt in direct_super_traits(db, trait_) {
|
||||
let mut path = find_super_trait_path(db, super_trait, tt);
|
||||
if !path.is_empty() {
|
||||
path.push(trait_);
|
||||
return path;
|
||||
fn go(db: &impl DefDatabase, super_trait: TraitId, path: &mut Vec<TraitId>) -> bool {
|
||||
let trait_ = *path.last().unwrap();
|
||||
if trait_ == super_trait {
|
||||
return true;
|
||||
}
|
||||
|
||||
for tt in direct_super_traits(db, trait_) {
|
||||
if path.contains(&tt) {
|
||||
continue;
|
||||
}
|
||||
path.push(tt);
|
||||
if go(db, super_trait, path) {
|
||||
return true;
|
||||
} else {
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub(super) fn associated_type_by_name_including_super_traits(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue