diff options
author | Florian Diebold <[email protected]> | 2020-02-22 12:14:39 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2020-02-22 12:14:39 +0000 |
commit | 3e106c77ff76c39be49444165eac805d32666e41 (patch) | |
tree | 7f136356bb7edb610233a2821be0b755725ed03a | |
parent | c2000257941956cd4c4365d6eb6cdbc1b16e929c (diff) |
Rework find_super_trait_path to protect against cycles
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 17 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lower.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/coercion.rs | 38 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/traits/builtin.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/utils.rs | 33 |
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] | ||
626 | fn coerce_unsize_super_trait_cycle() { | ||
627 | assert_snapshot!( | ||
628 | infer_with_mismatches(r#" | ||
629 | #[lang = "unsize"] | ||
630 | pub trait Unsize<T> {} | ||
631 | #[lang = "coerce_unsized"] | ||
632 | pub trait CoerceUnsized<T> {} | ||
633 | |||
634 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
635 | |||
636 | trait A {} | ||
637 | trait B: C + A {} | ||
638 | trait C: B {} | ||
639 | trait D: C | ||
640 | |||
641 | struct S; | ||
642 | impl A for S {} | ||
643 | impl B for S {} | ||
644 | impl C for S {} | ||
645 | impl D for S {} | ||
646 | |||
647 | fn 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] |
627 | fn coerce_unsize_generic() { | 665 | fn 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. |
67 | pub(super) fn find_super_trait_path( | 67 | pub(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 | ||
86 | pub(super) fn associated_type_by_name_including_super_traits( | 97 | pub(super) fn associated_type_by_name_including_super_traits( |