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;
12 use log::{info, warn};
15 db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
16 DebruijnIndex, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, Ty, TyBuilder,
20 const AUTODEREF_RECURSION_LIMIT: usize = 10;
23 db: &'a dyn HirDatabase,
24 krate: Option<CrateId>,
25 ty: InEnvironment<Canonical<Ty>>,
26 ) -> impl Iterator<Item = Canonical<Ty>> + 'a {
27 let InEnvironment { goal: ty, environment } = ty;
28 successors(Some(ty), move |ty| {
29 deref(db, krate?, InEnvironment { goal: ty, environment: environment.clone() })
31 .take(AUTODEREF_RECURSION_LIMIT)
37 ty: InEnvironment<&Canonical<Ty>>,
38 ) -> Option<Canonical<Ty>> {
39 let _p = profile::span("deref");
40 if let Some(derefed) = builtin_deref(&ty.goal.value) {
41 Some(Canonical { value: derefed, binders: ty.goal.binders.clone() })
43 deref_by_trait(db, krate, ty)
47 fn builtin_deref(ty: &Ty) -> Option<Ty> {
48 match ty.kind(&Interner) {
49 TyKind::Ref(.., ty) => Some(ty.clone()),
50 TyKind::Raw(.., ty) => Some(ty.clone()),
58 ty: InEnvironment<&Canonical<Ty>>,
59 ) -> Option<Canonical<Ty>> {
60 let _p = profile::span("deref_by_trait");
61 let deref_trait = match db.lang_item(krate, "deref".into())? {
62 LangItemTarget::TraitId(it) => it,
65 let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
68 let b = TyBuilder::assoc_type_projection(db, target);
69 if b.remaining() != 1 {
70 // the Target type + Deref trait should only have one generic parameter,
71 // namely Deref's Self type
74 b.push(ty.goal.value.clone()).build()
77 // FIXME make the Canonical / bound var handling nicer
79 // Check that the type implements Deref at all
80 let trait_ref = projection.trait_ref(db);
81 let implements_goal = Canonical {
82 binders: ty.goal.binders.clone(),
83 value: InEnvironment {
84 goal: trait_ref.cast(&Interner),
85 environment: ty.environment.clone(),
88 if db.trait_solve(krate, implements_goal).is_none() {
92 // Now do the assoc type projection
93 let alias_eq = AliasEq {
94 alias: AliasTy::Projection(projection),
95 ty: TyKind::BoundVar(BoundVar::new(
96 DebruijnIndex::INNERMOST,
97 ty.goal.binders.len(&Interner),
102 let in_env = InEnvironment { goal: alias_eq.cast(&Interner), environment: ty.environment };
104 let canonical = Canonical {
106 binders: CanonicalVarKinds::from_iter(
108 ty.goal.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new(
109 VariableKind::Ty(chalk_ir::TyVariableKind::General),
110 chalk_ir::UniverseIndex::ROOT,
115 let solution = db.trait_solve(krate, canonical)?;
118 Solution::Unique(vars) => {
119 // FIXME: vars may contain solutions for any inference variables
120 // that happened to be inside ty. To correctly handle these, we
121 // would have to pass the solution up to the inference context, but
122 // that requires a larger refactoring (especially if the deref
123 // happens during method resolution). So for the moment, we just
124 // check that we're not in the situation we're we would actually
125 // need to handle the values of the additional variables, i.e.
126 // they're just being 'passed through'. In the 'standard' case where
127 // we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
130 // FIXME: if the trait solver decides to truncate the type, these
131 // assumptions will be broken. We would need to properly introduce
132 // new variables in that case
134 for i in 1..vars.binders.len(&Interner) {
135 if vars.value.subst.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner)
136 != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
138 warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution);
142 // FIXME: we remove lifetime variables here since they can confuse
143 // the method resolution code later
144 Some(fixup_lifetime_variables(Canonical {
148 .at(&Interner, vars.value.subst.len(&Interner) - 1)
149 .assert_ty_ref(&Interner)
151 binders: vars.binders.clone(),
154 Solution::Ambig(_) => {
155 info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution);
161 fn fixup_lifetime_variables<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>(
164 // Removes lifetime variables from the Canonical, replacing them by static lifetimes.
166 let subst = Substitution::from_iter(
168 c.binders.iter(&Interner).map(|vk| match vk.kind {
169 VariableKind::Ty(_) => {
172 BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(&Interner).cast(&Interner)
174 VariableKind::Lifetime => static_lifetime().cast(&Interner),
175 VariableKind::Const(_) => unimplemented!(),
178 let binders = CanonicalVarKinds::from_iter(
180 c.binders.iter(&Interner).filter(|vk| match vk.kind {
181 VariableKind::Ty(_) => true,
182 VariableKind::Lifetime => false,
183 VariableKind::Const(_) => true,
186 let value = subst.apply(c.value, &Interner);
187 Canonical { binders, value }