aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty/autoderef.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty/autoderef.rs')
-rw-r--r--crates/ra_hir/src/ty/autoderef.rs89
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
6use std::iter::successors; 6use std::iter::successors;
7 7
8use crate::HirDatabase; 8use log::{info, warn};
9use super::Ty;
10 9
11impl Ty { 10use crate::{HirDatabase, Name, Resolver, HasGenericParams};
12 /// Iterates over the possible derefs of `ty`. 11use 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)) 13const AUTODEREF_RECURSION_LIMIT: usize = 10;
14
15pub(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
23pub(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
35fn 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}