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).
6 use std::iter::successors;
9 use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind};
10 use hir_def::lang_item::LangItemTarget;
11 use hir_expand::name::name;
13 use tracing::{info, warn};
16 db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
17 ConstrainedSubst, DebruijnIndex, Environment, Guidance, InEnvironment, Interner,
18 ProjectionTyExt, Solution, Substitution, Ty, TyBuilder, TyKind,
21 const AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
23 pub(crate) enum AutoderefKind {
28 pub(crate) struct Autoderef<'db> {
29 db: &'db dyn HirDatabase,
32 krate: Option<CrateId>,
33 environment: Environment,
34 steps: Vec<(AutoderefKind, Ty)>,
37 impl<'db> Autoderef<'db> {
39 db: &'db dyn HirDatabase,
40 krate: Option<CrateId>,
41 ty: InEnvironment<Canonical<Ty>>,
43 let InEnvironment { goal: ty, environment } = ty;
44 Autoderef { db, ty, at_start: true, environment, krate, steps: Vec::new() }
47 pub(crate) fn step_count(&self) -> usize {
51 pub(crate) fn steps(&self) -> &[(AutoderefKind, chalk_ir::Ty<Interner>)] {
55 pub(crate) fn final_ty(&self) -> Ty {
60 impl Iterator for Autoderef<'_> {
61 type Item = (Canonical<Ty>, usize);
63 fn next(&mut self) -> Option<Self::Item> {
65 self.at_start = false;
66 return Some((self.ty.clone(), 0));
69 if AUTODEREF_RECURSION_LIMIT.check(self.steps.len() + 1).is_err() {
73 let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) {
74 (AutoderefKind::Builtin, Canonical { value: derefed, binders: self.ty.binders.clone() })
77 AutoderefKind::Overloaded,
81 InEnvironment { goal: &self.ty, environment: self.environment.clone() },
86 self.steps.push((kind, self.ty.value.clone()));
89 Some((self.ty.clone(), self.step_count()))
93 // FIXME: replace uses of this with Autoderef above
95 db: &'a dyn HirDatabase,
96 krate: Option<CrateId>,
97 ty: InEnvironment<Canonical<Ty>>,
98 ) -> impl Iterator<Item = Canonical<Ty>> + 'a {
99 let InEnvironment { goal: ty, environment } = ty;
100 successors(Some(ty), move |ty| {
101 deref(db, krate?, InEnvironment { goal: ty, environment: environment.clone() })
103 .take(AUTODEREF_RECURSION_LIMIT.inner())
107 db: &dyn HirDatabase,
109 ty: InEnvironment<&Canonical<Ty>>,
110 ) -> Option<Canonical<Ty>> {
111 let _p = profile::span("deref");
112 match builtin_deref(&ty.goal.value) {
113 Some(derefed) => Some(Canonical { value: derefed, binders: ty.goal.binders.clone() }),
114 None => deref_by_trait(db, krate, ty),
118 fn builtin_deref(ty: &Ty) -> Option<Ty> {
119 match ty.kind(&Interner) {
120 TyKind::Ref(.., ty) => Some(ty.clone()),
121 TyKind::Raw(.., ty) => Some(ty.clone()),
127 db: &dyn HirDatabase,
129 ty: InEnvironment<&Canonical<Ty>>,
130 ) -> Option<Canonical<Ty>> {
131 let _p = profile::span("deref_by_trait");
132 let deref_trait = match db.lang_item(krate, "deref".into())? {
133 LangItemTarget::TraitId(it) => it,
136 let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
139 let b = TyBuilder::assoc_type_projection(db, target);
140 if b.remaining() != 1 {
141 // the Target type + Deref trait should only have one generic parameter,
142 // namely Deref's Self type
145 b.push(ty.goal.value.clone()).build()
148 // FIXME make the Canonical / bound var handling nicer
150 // Check that the type implements Deref at all
151 let trait_ref = projection.trait_ref(db);
152 let implements_goal = Canonical {
153 binders: ty.goal.binders.clone(),
154 value: InEnvironment {
155 goal: trait_ref.cast(&Interner),
156 environment: ty.environment.clone(),
159 if db.trait_solve(krate, implements_goal).is_none() {
163 // Now do the assoc type projection
164 let alias_eq = AliasEq {
165 alias: AliasTy::Projection(projection),
166 ty: TyKind::BoundVar(BoundVar::new(
167 DebruijnIndex::INNERMOST,
168 ty.goal.binders.len(&Interner),
173 let in_env = InEnvironment { goal: alias_eq.cast(&Interner), environment: ty.environment };
175 let canonical = Canonical {
177 binders: CanonicalVarKinds::from_iter(
179 ty.goal.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new(
180 VariableKind::Ty(chalk_ir::TyVariableKind::General),
181 chalk_ir::UniverseIndex::ROOT,
186 let solution = db.trait_solve(krate, canonical)?;
189 Solution::Unique(Canonical { value: ConstrainedSubst { subst, .. }, binders })
190 | Solution::Ambig(Guidance::Definite(Canonical { value: subst, binders })) => {
191 // FIXME: vars may contain solutions for any inference variables
192 // that happened to be inside ty. To correctly handle these, we
193 // would have to pass the solution up to the inference context, but
194 // that requires a larger refactoring (especially if the deref
195 // happens during method resolution). So for the moment, we just
196 // check that we're not in the situation where we would actually
197 // need to handle the values of the additional variables, i.e.
198 // they're just being 'passed through'. In the 'standard' case where
199 // we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
202 // FIXME: if the trait solver decides to truncate the type, these
203 // assumptions will be broken. We would need to properly introduce
204 // new variables in that case
206 for i in 1..binders.len(&Interner) {
207 if subst.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner)
208 != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
210 warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution);
214 // FIXME: we remove lifetime variables here since they can confuse
215 // the method resolution code later
216 Some(fixup_lifetime_variables(Canonical {
218 .at(&Interner, subst.len(&Interner) - 1)
219 .assert_ty_ref(&Interner)
221 binders: binders.clone(),
224 Solution::Ambig(_) => {
225 info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution);
231 fn fixup_lifetime_variables<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>(
234 // Removes lifetime variables from the Canonical, replacing them by static lifetimes.
236 let subst = Substitution::from_iter(
238 c.binders.iter(&Interner).map(|vk| match vk.kind {
239 VariableKind::Ty(_) => {
242 BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(&Interner).cast(&Interner)
244 VariableKind::Lifetime => static_lifetime().cast(&Interner),
245 VariableKind::Const(_) => unimplemented!(),
248 let binders = CanonicalVarKinds::from_iter(
250 c.binders.iter(&Interner).filter(|vk| match vk.kind {
251 VariableKind::Ty(_) => true,
252 VariableKind::Lifetime => false,
253 VariableKind::Const(_) => true,
256 let value = subst.apply(c.value, &Interner);
257 Canonical { binders, value }