diff options
Diffstat (limited to 'crates/ra_hir/src/ty/autoderef.rs')
-rw-r--r-- | crates/ra_hir/src/ty/autoderef.rs | 89 |
1 files changed, 80 insertions, 9 deletions
diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs index a442a856c..1f443d49b 100644 --- a/crates/ra_hir/src/ty/autoderef.rs +++ b/crates/ra_hir/src/ty/autoderef.rs | |||
@@ -5,17 +5,88 @@ | |||
5 | 5 | ||
6 | use std::iter::successors; | 6 | use std::iter::successors; |
7 | 7 | ||
8 | use crate::HirDatabase; | 8 | use log::{info, warn}; |
9 | use super::Ty; | ||
10 | 9 | ||
11 | impl Ty { | 10 | use crate::{HirDatabase, Name, Resolver, HasGenericParams}; |
12 | /// Iterates over the possible derefs of `ty`. | 11 | use super::{traits::Solution, Ty, Canonical}; |
13 | pub fn autoderef<'a>(self, db: &'a impl HirDatabase) -> impl Iterator<Item = Ty> + 'a { | 12 | |
14 | successors(Some(self), move |ty| ty.autoderef_step(db)) | 13 | const AUTODEREF_RECURSION_LIMIT: usize = 10; |
14 | |||
15 | pub(crate) fn autoderef<'a>( | ||
16 | db: &'a impl HirDatabase, | ||
17 | resolver: &'a Resolver, | ||
18 | ty: Canonical<Ty>, | ||
19 | ) -> impl Iterator<Item = Canonical<Ty>> + 'a { | ||
20 | successors(Some(ty), move |ty| deref(db, resolver, ty)).take(AUTODEREF_RECURSION_LIMIT) | ||
21 | } | ||
22 | |||
23 | pub(crate) fn deref( | ||
24 | db: &impl HirDatabase, | ||
25 | resolver: &Resolver, | ||
26 | ty: &Canonical<Ty>, | ||
27 | ) -> Option<Canonical<Ty>> { | ||
28 | if let Some(derefed) = ty.value.builtin_deref() { | ||
29 | Some(Canonical { value: derefed, num_vars: ty.num_vars }) | ||
30 | } else { | ||
31 | deref_by_trait(db, resolver, ty) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | fn deref_by_trait( | ||
36 | db: &impl HirDatabase, | ||
37 | resolver: &Resolver, | ||
38 | ty: &Canonical<Ty>, | ||
39 | ) -> Option<Canonical<Ty>> { | ||
40 | let krate = resolver.krate()?; | ||
41 | let deref_trait = match db.lang_item(krate, "deref".into())? { | ||
42 | crate::lang_item::LangItemTarget::Trait(t) => t, | ||
43 | _ => return None, | ||
44 | }; | ||
45 | let target = deref_trait.associated_type_by_name(db, Name::target())?; | ||
46 | |||
47 | if target.generic_params(db).count_params_including_parent() != 1 { | ||
48 | // the Target type + Deref trait should only have one generic parameter, | ||
49 | // namely Deref's Self type | ||
50 | return None; | ||
15 | } | 51 | } |
16 | 52 | ||
17 | fn autoderef_step(&self, _db: &impl HirDatabase) -> Option<Ty> { | 53 | // FIXME make the Canonical handling nicer |
18 | // FIXME Deref::deref | 54 | |
19 | self.builtin_deref() | 55 | let projection = super::traits::ProjectionPredicate { |
56 | ty: Ty::Bound(0), | ||
57 | projection_ty: super::ProjectionTy { | ||
58 | associated_ty: target, | ||
59 | parameters: vec![ty.value.clone().shift_bound_vars(1)].into(), | ||
60 | }, | ||
61 | }; | ||
62 | |||
63 | let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: projection }; | ||
64 | |||
65 | let solution = db.normalize(krate, canonical)?; | ||
66 | |||
67 | match &solution { | ||
68 | Solution::Unique(vars) => { | ||
69 | // FIXME: vars may contain solutions for any inference variables | ||
70 | // that happened to be inside ty. To correctly handle these, we | ||
71 | // would have to pass the solution up to the inference context, but | ||
72 | // that requires a larger refactoring (especially if the deref | ||
73 | // happens during method resolution). So for the moment, we just | ||
74 | // check that we're not in the situation we're we would actually | ||
75 | // need to handle the values of the additional variables, i.e. | ||
76 | // they're just being 'passed through'. In the 'standard' case where | ||
77 | // we have `impl<T> Deref for Foo<T> { Target = T }`, that should be | ||
78 | // the case. | ||
79 | for i in 1..vars.0.num_vars { | ||
80 | if vars.0.value[i] != Ty::Bound((i - 1) as u32) { | ||
81 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty, solution); | ||
82 | return None; | ||
83 | } | ||
84 | } | ||
85 | Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars }) | ||
86 | } | ||
87 | Solution::Ambig(_) => { | ||
88 | info!("Ambiguous solution for derefing {:?}: {:?}", ty, solution); | ||
89 | None | ||
90 | } | ||
20 | } | 91 | } |
21 | } | 92 | } |