]> git.lizzy.rs Git - rust.git/blob - crates/ra_hir_ty/src/autoderef.rs
ee48fa53700767cfc0d2e632b0b9448b6ef6b810
[rust.git] / crates / ra_hir_ty / src / autoderef.rs
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;
9 use hir_expand::name::name;
10 use log::{info, warn};
11 use ra_db::CrateId;
12
13 use crate::{
14     db::HirDatabase,
15     traits::{InEnvironment, Solution},
16     utils::generics,
17     Canonical, Substs, Ty, TypeWalk,
18 };
19
20 const AUTODEREF_RECURSION_LIMIT: usize = 10;
21
22 pub fn autoderef<'a>(
23     db: &'a impl HirDatabase,
24     krate: Option<CrateId>,
25     ty: InEnvironment<Canonical<Ty>>,
26 ) -> impl Iterator<Item = Canonical<Ty>> + 'a {
27     let InEnvironment { value: ty, environment } = ty;
28     successors(Some(ty), move |ty| {
29         deref(db, krate?, InEnvironment { value: ty, environment: environment.clone() })
30     })
31     .take(AUTODEREF_RECURSION_LIMIT)
32 }
33
34 pub(crate) fn deref(
35     db: &impl HirDatabase,
36     krate: CrateId,
37     ty: InEnvironment<&Canonical<Ty>>,
38 ) -> Option<Canonical<Ty>> {
39     if let Some(derefed) = ty.value.value.builtin_deref() {
40         Some(Canonical { value: derefed, num_vars: ty.value.num_vars })
41     } else {
42         deref_by_trait(db, krate, ty)
43     }
44 }
45
46 fn deref_by_trait(
47     db: &impl HirDatabase,
48     krate: CrateId,
49     ty: InEnvironment<&Canonical<Ty>>,
50 ) -> Option<Canonical<Ty>> {
51     let deref_trait = match db.lang_item(krate.into(), "deref".into())? {
52         LangItemTarget::TraitId(it) => it,
53         _ => return None,
54     };
55     let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
56
57     let generic_params = generics(db, target.into());
58     if generic_params.len() != 1 {
59         // the Target type + Deref trait should only have one generic parameter,
60         // namely Deref's Self type
61         return None;
62     }
63
64     // FIXME make the Canonical handling nicer
65
66     let parameters = Substs::build_for_generics(&generic_params)
67         .push(ty.value.value.clone().shift_bound_vars(1))
68         .build();
69
70     let projection = super::traits::ProjectionPredicate {
71         ty: Ty::Bound(0),
72         projection_ty: super::ProjectionTy { associated_ty: target, parameters },
73     };
74
75     let obligation = super::Obligation::Projection(projection);
76
77     let in_env = InEnvironment { value: obligation, environment: ty.environment };
78
79     let canonical = super::Canonical { num_vars: 1 + ty.value.num_vars, value: in_env };
80
81     let solution = db.trait_solve(krate.into(), canonical)?;
82
83     match &solution {
84         Solution::Unique(vars) => {
85             // FIXME: vars may contain solutions for any inference variables
86             // that happened to be inside ty. To correctly handle these, we
87             // would have to pass the solution up to the inference context, but
88             // that requires a larger refactoring (especially if the deref
89             // happens during method resolution). So for the moment, we just
90             // check that we're not in the situation we're we would actually
91             // need to handle the values of the additional variables, i.e.
92             // they're just being 'passed through'. In the 'standard' case where
93             // we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
94             // the case.
95             for i in 1..vars.0.num_vars {
96                 if vars.0.value[i] != Ty::Bound((i - 1) as u32) {
97                     warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution);
98                     return None;
99                 }
100             }
101             Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars })
102         }
103         Solution::Ambig(_) => {
104             info!("Ambiguous solution for derefing {:?}: {:?}", ty.value, solution);
105             None
106         }
107     }
108 }