2019-01-08 17:47:12 -06:00
|
|
|
//! In certain situations, rust automatically inserts derefs as necessary: for
|
2019-01-06 12:51:42 -06:00
|
|
|
//! example, field accesses `foo.bar` still work when `foo` is actually a
|
|
|
|
//! reference to a type with the field `bar`. This is an approximation of the
|
|
|
|
//! logic in rustc (which lives in librustc_typeck/check/autoderef.rs).
|
|
|
|
|
2019-04-13 09:43:49 -05:00
|
|
|
use std::iter::successors;
|
2019-01-06 12:51:42 -06:00
|
|
|
|
2019-06-15 11:20:59 -05:00
|
|
|
use log::{info, warn};
|
2019-01-06 12:51:42 -06:00
|
|
|
|
2019-09-03 06:10:00 -05:00
|
|
|
use super::{traits::Solution, Canonical, Ty, TypeWalk};
|
2019-09-07 07:31:43 -05:00
|
|
|
use crate::{name, HasGenericParams, HirDatabase, Resolver};
|
2019-05-12 11:33:47 -05:00
|
|
|
|
2019-06-16 05:04:08 -05:00
|
|
|
const AUTODEREF_RECURSION_LIMIT: usize = 10;
|
|
|
|
|
2019-05-12 11:33:47 -05:00
|
|
|
pub(crate) fn autoderef<'a>(
|
|
|
|
db: &'a impl HirDatabase,
|
|
|
|
resolver: &'a Resolver,
|
|
|
|
ty: Canonical<Ty>,
|
|
|
|
) -> impl Iterator<Item = Canonical<Ty>> + 'a {
|
2019-06-16 05:04:08 -05:00
|
|
|
successors(Some(ty), move |ty| deref(db, resolver, ty)).take(AUTODEREF_RECURSION_LIMIT)
|
2019-05-12 11:33:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn deref(
|
|
|
|
db: &impl HirDatabase,
|
|
|
|
resolver: &Resolver,
|
|
|
|
ty: &Canonical<Ty>,
|
|
|
|
) -> Option<Canonical<Ty>> {
|
|
|
|
if let Some(derefed) = ty.value.builtin_deref() {
|
|
|
|
Some(Canonical { value: derefed, num_vars: ty.num_vars })
|
|
|
|
} else {
|
|
|
|
deref_by_trait(db, resolver, ty)
|
2019-01-06 12:51:42 -06:00
|
|
|
}
|
2019-05-12 11:33:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn deref_by_trait(
|
|
|
|
db: &impl HirDatabase,
|
|
|
|
resolver: &Resolver,
|
|
|
|
ty: &Canonical<Ty>,
|
|
|
|
) -> Option<Canonical<Ty>> {
|
|
|
|
let krate = resolver.krate()?;
|
|
|
|
let deref_trait = match db.lang_item(krate, "deref".into())? {
|
|
|
|
crate::lang_item::LangItemTarget::Trait(t) => t,
|
|
|
|
_ => return None,
|
|
|
|
};
|
2019-09-07 07:31:43 -05:00
|
|
|
let target = deref_trait.associated_type_by_name(db, &name::TARGET)?;
|
2019-05-12 11:33:47 -05:00
|
|
|
|
2019-06-15 11:33:30 -05:00
|
|
|
if target.generic_params(db).count_params_including_parent() != 1 {
|
|
|
|
// the Target type + Deref trait should only have one generic parameter,
|
|
|
|
// namely Deref's Self type
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2019-05-12 11:33:47 -05:00
|
|
|
// FIXME make the Canonical handling nicer
|
|
|
|
|
2019-07-07 11:14:56 -05:00
|
|
|
let env = super::lower::trait_env(db, resolver);
|
|
|
|
|
2019-05-12 11:33:47 -05:00
|
|
|
let projection = super::traits::ProjectionPredicate {
|
|
|
|
ty: Ty::Bound(0),
|
|
|
|
projection_ty: super::ProjectionTy {
|
|
|
|
associated_ty: target,
|
2019-06-15 11:20:59 -05:00
|
|
|
parameters: vec![ty.value.clone().shift_bound_vars(1)].into(),
|
2019-05-12 11:33:47 -05:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2019-07-08 14:43:52 -05:00
|
|
|
let obligation = super::Obligation::Projection(projection);
|
|
|
|
|
|
|
|
let in_env = super::traits::InEnvironment { value: obligation, environment: env };
|
2019-07-07 11:14:56 -05:00
|
|
|
|
|
|
|
let canonical = super::Canonical { num_vars: 1 + ty.num_vars, value: in_env };
|
2019-05-12 11:33:47 -05:00
|
|
|
|
2019-07-09 14:34:23 -05:00
|
|
|
let solution = db.trait_solve(krate, canonical)?;
|
2019-01-06 12:51:42 -06:00
|
|
|
|
2019-05-12 11:33:47 -05:00
|
|
|
match &solution {
|
|
|
|
Solution::Unique(vars) => {
|
2019-06-15 11:20:59 -05:00
|
|
|
// FIXME: vars may contain solutions for any inference variables
|
|
|
|
// that happened to be inside ty. To correctly handle these, we
|
|
|
|
// would have to pass the solution up to the inference context, but
|
|
|
|
// that requires a larger refactoring (especially if the deref
|
|
|
|
// happens during method resolution). So for the moment, we just
|
|
|
|
// check that we're not in the situation we're we would actually
|
|
|
|
// need to handle the values of the additional variables, i.e.
|
|
|
|
// they're just being 'passed through'. In the 'standard' case where
|
|
|
|
// we have `impl<T> Deref for Foo<T> { Target = T }`, that should be
|
|
|
|
// the case.
|
|
|
|
for i in 1..vars.0.num_vars {
|
|
|
|
if vars.0.value[i] != Ty::Bound((i - 1) as u32) {
|
|
|
|
warn!("complex solution for derefing {:?}: {:?}, ignoring", ty, solution);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
2019-05-12 11:33:47 -05:00
|
|
|
Some(Canonical { value: vars.0.value[0].clone(), num_vars: vars.0.num_vars })
|
|
|
|
}
|
|
|
|
Solution::Ambig(_) => {
|
2019-06-15 11:20:59 -05:00
|
|
|
info!("Ambiguous solution for derefing {:?}: {:?}", ty, solution);
|
2019-05-12 11:33:47 -05:00
|
|
|
None
|
|
|
|
}
|
2019-01-06 12:51:42 -06:00
|
|
|
}
|
|
|
|
}
|