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.rs103
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
6use std::iter::successors;
7
8use hir_def::{lang_item::LangItemTarget, resolver::Resolver};
9use hir_expand::name;
10use log::{info, warn};
11
12use crate::{db::HirDatabase, Trait};
13
14use super::{traits::Solution, Canonical, Substs, Ty, TypeWalk};
15
16const AUTODEREF_RECURSION_LIMIT: usize = 10;
17
18pub(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
26pub(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
38fn 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}