aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2020-02-22 12:14:39 +0000
committerFlorian Diebold <[email protected]>2020-02-22 12:14:39 +0000
commit3e106c77ff76c39be49444165eac805d32666e41 (patch)
tree7f136356bb7edb610233a2821be0b755725ed03a
parentc2000257941956cd4c4365d6eb6cdbc1b16e929c (diff)
Rework find_super_trait_path to protect against cycles
-rw-r--r--crates/ra_hir_ty/src/lib.rs17
-rw-r--r--crates/ra_hir_ty/src/lower.rs3
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs38
-rw-r--r--crates/ra_hir_ty/src/traits/builtin.rs6
-rw-r--r--crates/ra_hir_ty/src/utils.rs33
5 files changed, 74 insertions, 23 deletions
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 182f847f1..0009c426c 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -814,13 +814,16 @@ pub trait TypeWalk {
814 where 814 where
815 Self: Sized, 815 Self: Sized,
816 { 816 {
817 self.fold_binders(&mut |ty, binders| match ty { 817 self.fold_binders(
818 Ty::Bound(idx) if idx as usize >= binders => { 818 &mut |ty, binders| match ty {
819 assert!(idx as i32 >= -n); 819 Ty::Bound(idx) if idx as usize >= binders => {
820 Ty::Bound((idx as i32 + n) as u32) 820 assert!(idx as i32 >= -n);
821 } 821 Ty::Bound((idx as i32 + n) as u32)
822 ty => ty, 822 }
823 }, 0) 823 ty => ty,
824 },
825 0,
826 )
824 } 827 }
825} 828}
826 829
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 0e6efa971..092977e93 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -241,7 +241,8 @@ impl Ty {
241 TypeNs::TraitId(trait_) => { 241 TypeNs::TraitId(trait_) => {
242 // if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there 242 // if this is a bare dyn Trait, we'll directly put the required ^0 for the self type in there
243 let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None }; 243 let self_ty = if remaining_segments.len() == 0 { Some(Ty::Bound(0)) } else { None };
244 let trait_ref = TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty); 244 let trait_ref =
245 TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty);
245 return if remaining_segments.len() == 1 { 246 return if remaining_segments.len() == 1 {
246 let segment = remaining_segments.first().unwrap(); 247 let segment = remaining_segments.first().unwrap();
247 let associated_ty = associated_type_by_name_including_super_traits( 248 let associated_ty = associated_type_by_name_including_super_traits(
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 5594ed394..60ad6e9be 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -622,6 +622,44 @@ fn test() {
622 ); 622 );
623} 623}
624 624
625#[test]
626fn coerce_unsize_super_trait_cycle() {
627 assert_snapshot!(
628 infer_with_mismatches(r#"
629#[lang = "unsize"]
630pub trait Unsize<T> {}
631#[lang = "coerce_unsized"]
632pub trait CoerceUnsized<T> {}
633
634impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
635
636trait A {}
637trait B: C + A {}
638trait C: B {}
639trait D: C
640
641struct S;
642impl A for S {}
643impl B for S {}
644impl C for S {}
645impl D for S {}
646
647fn test() {
648 let obj: &dyn D = &S;
649 let obj: &dyn A = obj;
650}
651"#, true),
652 @r###"
653 [292; 348) '{ ...obj; }': ()
654 [302; 305) 'obj': &dyn D
655 [316; 318) '&S': &S
656 [317; 318) 'S': S
657 [328; 331) 'obj': &dyn A
658 [342; 345) 'obj': &dyn D
659 "###
660 );
661}
662
625#[ignore] 663#[ignore]
626#[test] 664#[test]
627fn coerce_unsize_generic() { 665fn coerce_unsize_generic() {
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs
index 19e533cee..df0645717 100644
--- a/crates/ra_hir_ty/src/traits/builtin.rs
+++ b/crates/ra_hir_ty/src/traits/builtin.rs
@@ -316,12 +316,10 @@ fn super_trait_object_unsize_impl_datum(
316 let self_bounds = vec![GenericPredicate::Implemented(self_trait_ref.clone())]; 316 let self_bounds = vec![GenericPredicate::Implemented(self_trait_ref.clone())];
317 317
318 // we need to go from our trait to the super trait, substituting type parameters 318 // we need to go from our trait to the super trait, substituting type parameters
319 let mut path = crate::utils::find_super_trait_path(db, data.super_trait, data.trait_); 319 let path = crate::utils::find_super_trait_path(db, data.trait_, data.super_trait);
320 path.pop(); // the last one is our current trait, we don't need that
321 path.reverse(); // we want to go from trait to super trait
322 320
323 let mut current_trait_ref = self_trait_ref; 321 let mut current_trait_ref = self_trait_ref;
324 for t in path { 322 for t in path.into_iter().skip(1) {
325 let bounds = db.generic_predicates(current_trait_ref.trait_.into()); 323 let bounds = db.generic_predicates(current_trait_ref.trait_.into());
326 let super_trait_ref = bounds 324 let super_trait_ref = bounds
327 .iter() 325 .iter()
diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs
index 0d1583c39..463fd65b4 100644
--- a/crates/ra_hir_ty/src/utils.rs
+++ b/crates/ra_hir_ty/src/utils.rs
@@ -62,25 +62,36 @@ pub(super) fn all_super_traits(db: &impl DefDatabase, trait_: TraitId) -> Vec<Tr
62 result 62 result
63} 63}
64 64
65/// Finds a path from a trait to one of its descendant traits. Returns an empty 65/// Finds a path from a trait to one of its super traits. Returns an empty
66/// vector if there is no path. 66/// vector if there is no path.
67pub(super) fn find_super_trait_path( 67pub(super) fn find_super_trait_path(
68 db: &impl DefDatabase, 68 db: &impl DefDatabase,
69 super_trait: TraitId,
70 trait_: TraitId, 69 trait_: TraitId,
70 super_trait: TraitId,
71) -> Vec<TraitId> { 71) -> Vec<TraitId> {
72 if trait_ == super_trait { 72 let mut result = Vec::with_capacity(2);
73 return vec![trait_]; 73 result.push(trait_);
74 } 74 return if go(db, super_trait, &mut result) { result } else { Vec::new() };
75
76 fn go(db: &impl DefDatabase, super_trait: TraitId, path: &mut Vec<TraitId>) -> bool {
77 let trait_ = *path.last().unwrap();
78 if trait_ == super_trait {
79 return true;
80 }
75 81
76 for tt in direct_super_traits(db, trait_) { 82 for tt in direct_super_traits(db, trait_) {
77 let mut path = find_super_trait_path(db, super_trait, tt); 83 if path.contains(&tt) {
78 if !path.is_empty() { 84 continue;
79 path.push(trait_); 85 }
80 return path; 86 path.push(tt);
87 if go(db, super_trait, path) {
88 return true;
89 } else {
90 path.pop();
91 }
81 } 92 }
93 false
82 } 94 }
83 Vec::new()
84} 95}
85 96
86pub(super) fn associated_type_by_name_including_super_traits( 97pub(super) fn associated_type_by_name_including_super_traits(