diff options
Diffstat (limited to 'crates/ra_hir/src/ty/autoderef.rs')
-rw-r--r-- | crates/ra_hir/src/ty/autoderef.rs | 103 |
1 files changed, 0 insertions, 103 deletions
diff --git a/crates/ra_hir/src/ty/autoderef.rs b/crates/ra_hir/src/ty/autoderef.rs deleted file mode 100644 index 41c99d227..000000000 --- a/crates/ra_hir/src/ty/autoderef.rs +++ /dev/null | |||
@@ -1,103 +0,0 @@ | |||
1 | //! In certain situations, rust automatically inserts derefs as necessary: for | ||
2 | //! example, field accesses `foo.bar` still work when `foo` is actually a | ||
3 | //! reference to a type with the field `bar`. This is an approximation of the | ||
4 | //! logic in rustc (which lives in librustc_typeck/check/autoderef.rs). | ||
5 | |||
6 | use std::iter::successors; | ||
7 | |||
8 | use hir_def::{lang_item::LangItemTarget, resolver::Resolver}; | ||
9 | use hir_expand::name; | ||
10 | use log::{info, warn}; | ||
11 | |||
12 | use crate::{db::HirDatabase, Trait}; | ||
13 | |||
14 | use super::{traits::Solution, Canonical, Substs, Ty, TypeWalk}; | ||
15 | |||
16 | const AUTODEREF_RECURSION_LIMIT: usize = 10; | ||
17 | |||
18 | pub(crate) fn autoderef<'a>( | ||
19 | db: &'a impl HirDatabase, | ||
20 | resolver: &'a Resolver, | ||
21 | ty: Canonical<Ty>, | ||
22 | ) -> impl Iterator<Item = Canonical<Ty>> + 'a { | ||
23 | successors(Some(ty), move |ty| deref(db, resolver, ty)).take(AUTODEREF_RECURSION_LIMIT) | ||
24 | } | ||
25 | |||
26 | pub(crate) fn deref( | ||
27 | db: &impl HirDatabase, | ||
28 | resolver: &Resolver, | ||
29 | ty: &Canonical<Ty>, | ||
30 | ) -> Option<Canonical<Ty>> { | ||
31 | if let Some(derefed) = ty.value.builtin_deref() { | ||
32 | Some(Canonical { value: derefed, num_vars: ty.num_vars }) | ||
33 | } else { | ||
34 | deref_by_trait(db, resolver, ty) | ||
35 | } | ||
36 | } | ||
37 | |||
38 | fn deref_by_trait( | ||
39 | db: &impl HirDatabase, | ||
40 | resolver: &Resolver, | ||
41 | ty: &Canonical<Ty>, | ||
42 | ) -> Option<Canonical<Ty>> { | ||
43 | let krate = resolver.krate()?; | ||
44 | let deref_trait = match db.lang_item(krate.into(), "deref".into())? { | ||
45 | LangItemTarget::TraitId(t) => Trait::from(t), | ||
46 | _ => return None, | ||
47 | }; | ||
48 | let target = deref_trait.associated_type_by_name(db, &name::TARGET_TYPE)?; | ||
49 | |||
50 | let generic_params = db.generic_params(target.id.into()); | ||
51 | if generic_params.count_params_including_parent() != 1 { | ||
52 | // the Target type + Deref trait should only have one generic parameter, | ||
53 | // namely Deref's Self type | ||
54 | return None; | ||
55 | } | ||
56 | |||
57 | // FIXME make the Canonical handling nicer | ||
58 | |||
59 | let env = super::lower::trait_env(db, resolver); | ||
60 | |||
61 | let parameters = Substs::build_for_generics(&generic_params) | ||
62 | .push(ty.value.clone().shift_bound_vars(1)) | ||
63 | .build(); | ||
64 | |||
65 | let projection = super::traits::ProjectionPredicate { | ||
66 | ty: Ty::Bound(0), | ||
67 | projection_ty: super::ProjectionTy { associated_ty: target, parameters }, | ||
68 | }; | ||
69 | |||
70 | let obligation = super::Obligation::Projection(projection); | ||
71 | |||
72 | let in_env = super::traits::InEnvironment { value: obligation, environment: env }; | ||
73 | |||
74 | let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: in_env }; | ||
75 | |||
76 | let solution = db.trait_solve(krate.into(), canonical)?; | ||
77 | |||
78 | match &solution { | ||
79 | Solution::Unique(vars) => { | ||
80 | // FIXME: vars may contain solutions for any inference variables | ||
81 | // that happened to be inside ty. To correctly handle these, we | ||
82 | // would have to pass the solution up to the inference context, but | ||
83 | // that requires a larger refactoring (especially if the deref | ||
84 | // happens during method resolution). So for the moment, we just | ||
85 | // check that we're not in the situation we're we would actually | ||
86 | // need to handle the values of the additional variables, i.e. | ||
87 | // they're just being 'passed through'. In the 'standard' case where | ||
88 | // we have `impl<T> Deref for Foo<T> { Target = T }`, that should be | ||
89 | // the case. | ||
90 | for i in 1..vars.0.num_vars { | ||
91 | if vars.0.value[i] != Ty::Bound((i - 1) as u32) { | ||
92 | warn!("complex solution for derefing {:?}: {:?}, ignoring", ty, solution); | ||
93 | return None; | ||
94 | } | ||
95 | } | ||
96 | Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars }) | ||
97 | } | ||
98 | Solution::Ambig(_) => { | ||
99 | info!("Ambiguous solution for derefing {:?}: {:?}", ty, solution); | ||
100 | None | ||
101 | } | ||
102 | } | ||
103 | } | ||