diff options
-rw-r--r-- | crates/ra_hir/src/ty.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/autoderef.rs | 24 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 31 |
3 files changed, 61 insertions, 5 deletions
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index d2f92a1f2..4ed19f860 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -474,6 +474,17 @@ impl Ty { | |||
474 | _ => None, | 474 | _ => None, |
475 | } | 475 | } |
476 | } | 476 | } |
477 | |||
478 | /// Shifts up `Ty::Bound` vars by `n`. | ||
479 | pub fn shift_bound_vars(self, n: i32) -> Ty { | ||
480 | self.fold(&mut |ty| match ty { | ||
481 | Ty::Bound(idx) => { | ||
482 | assert!(idx as i32 >= -n); | ||
483 | Ty::Bound((idx as i32 + n) as u32) | ||
484 | } | ||
485 | ty => ty, | ||
486 | }) | ||
487 | } | ||
477 | } | 488 | } |
478 | 489 | ||
479 | impl HirDisplay for &Ty { | 490 | impl HirDisplay for &Ty { |
diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index bee756d80..11f489489 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | 5 | ||
6 | use std::iter::successors; | 6 | use std::iter::successors; |
7 | 7 | ||
8 | use log::info; | 8 | use log::{info, warn}; |
9 | 9 | ||
10 | use crate::{HirDatabase, Name, Resolver}; | 10 | use crate::{HirDatabase, Name, Resolver}; |
11 | use super::{traits::Solution, Ty, Canonical}; | 11 | use super::{traits::Solution, Ty, Canonical}; |
@@ -43,15 +43,13 @@ fn deref_by_trait( | |||
43 | let target = deref_trait.associated_type_by_name(db, Name::target())?; | 43 | let target = deref_trait.associated_type_by_name(db, Name::target())?; |
44 | 44 | ||
45 | // FIXME we should check that Deref has no type parameters, because we assume it below | 45 | // FIXME we should check that Deref has no type parameters, because we assume it below |
46 | |||
47 | // FIXME make the Canonical handling nicer | 46 | // FIXME make the Canonical handling nicer |
48 | // TODO shift inference variables in ty | ||
49 | 47 | ||
50 | let projection = super::traits::ProjectionPredicate { | 48 | let projection = super::traits::ProjectionPredicate { |
51 | ty: Ty::Bound(0), | 49 | ty: Ty::Bound(0), |
52 | projection_ty: super::ProjectionTy { | 50 | projection_ty: super::ProjectionTy { |
53 | associated_ty: target, | 51 | associated_ty: target, |
54 | parameters: vec![ty.value.clone()].into(), | 52 | parameters: vec![ty.value.clone().shift_bound_vars(1)].into(), |
55 | }, | 53 | }, |
56 | }; | 54 | }; |
57 | 55 | ||
@@ -61,10 +59,26 @@ fn deref_by_trait( | |||
61 | 59 | ||
62 | match &solution { | 60 | match &solution { |
63 | Solution::Unique(vars) => { | 61 | Solution::Unique(vars) => { |
62 | // FIXME: vars may contain solutions for any inference variables | ||
63 | // that happened to be inside ty. To correctly handle these, we | ||
64 | // would have to pass the solution up to the inference context, but | ||
65 | // that requires a larger refactoring (especially if the deref | ||
66 | // happens during method resolution). So for the moment, we just | ||
67 | // check that we're not in the situation we're we would actually | ||
68 | // need to handle the values of the additional variables, i.e. | ||
69 | // they're just being 'passed through'. In the 'standard' case where | ||
70 | // we have `impl<T> Deref for Foo<T> { Target = T }`, that should be | ||
71 | // the case. | ||
72 | for i in 1..vars.0.num_vars { | ||
73 | if vars.0.value[i] != Ty::Bound((i - 1) as u32) { | ||
74 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty, solution); | ||
75 | return None; | ||
76 | } | ||
77 | } | ||
64 | Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars }) | 78 | Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars }) |
65 | } | 79 | } |
66 | Solution::Ambig(_) => { | 80 | Solution::Ambig(_) => { |
67 | info!("Ambiguous solution for deref: {:?}", solution); | 81 | info!("Ambiguous solution for derefing {:?}: {:?}", ty, solution); |
68 | None | 82 | None |
69 | } | 83 | } |
70 | } | 84 | } |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e587dca31..059a73900 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -2766,6 +2766,37 @@ fn test(s: Arc<S>) { | |||
2766 | assert_eq!(t, "(S, u128)"); | 2766 | assert_eq!(t, "(S, u128)"); |
2767 | } | 2767 | } |
2768 | 2768 | ||
2769 | #[test] | ||
2770 | fn deref_trait_with_inference_var() { | ||
2771 | // std::env::set_var("RUST_BACKTRACE", "1"); | ||
2772 | let t = type_at( | ||
2773 | r#" | ||
2774 | //- /main.rs | ||
2775 | #[lang = "deref"] | ||
2776 | trait Deref { | ||
2777 | type Target; | ||
2778 | fn deref(&self) -> &Self::Target; | ||
2779 | } | ||
2780 | |||
2781 | struct Arc<T>; | ||
2782 | fn new_arc<T>() -> Arc<T> {} | ||
2783 | impl<T> Deref for Arc<T> { | ||
2784 | type Target = T; | ||
2785 | } | ||
2786 | |||
2787 | struct S; | ||
2788 | fn foo(a: Arc<S>) {} | ||
2789 | |||
2790 | fn test() { | ||
2791 | let a = new_arc(); | ||
2792 | let b = (*a)<|>; | ||
2793 | foo(a); | ||
2794 | } | ||
2795 | "#, | ||
2796 | ); | ||
2797 | assert_eq!(t, "S"); | ||
2798 | } | ||
2799 | |||
2769 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { | 2800 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { |
2770 | let file = db.parse(pos.file_id).ok().unwrap(); | 2801 | let file = db.parse(pos.file_id).ok().unwrap(); |
2771 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); | 2802 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); |