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
|
|
|
|
2020-08-13 09:25:38 -05:00
|
|
|
use base_db::CrateId;
|
2019-11-25 04:10:26 -06:00
|
|
|
use hir_def::lang_item::LangItemTarget;
|
2019-12-13 15:01:06 -06:00
|
|
|
use hir_expand::name::name;
|
2019-06-15 11:20:59 -05:00
|
|
|
use log::{info, warn};
|
2019-01-06 12:51:42 -06:00
|
|
|
|
2019-12-07 04:50:36 -06:00
|
|
|
use crate::{
|
|
|
|
db::HirDatabase,
|
2021-03-18 15:53:19 -05:00
|
|
|
to_assoc_type_id, to_chalk_trait_id,
|
2019-11-25 03:45:45 -06:00
|
|
|
traits::{InEnvironment, Solution},
|
2019-12-07 04:50:36 -06:00
|
|
|
utils::generics,
|
2021-03-15 15:02:34 -05:00
|
|
|
BoundVar, Canonical, DebruijnIndex, Interner, Obligation, Substitution, TraitRef, Ty, TyKind,
|
2019-11-25 03:45:45 -06:00
|
|
|
};
|
2019-05-12 11:33:47 -05:00
|
|
|
|
2019-06-16 05:04:08 -05:00
|
|
|
const AUTODEREF_RECURSION_LIMIT: usize = 10;
|
|
|
|
|
2019-11-27 08:46:02 -06:00
|
|
|
pub fn autoderef<'a>(
|
2020-03-13 10:05:46 -05:00
|
|
|
db: &'a dyn HirDatabase,
|
2019-11-25 04:10:26 -06:00
|
|
|
krate: Option<CrateId>,
|
|
|
|
ty: InEnvironment<Canonical<Ty>>,
|
2019-05-12 11:33:47 -05:00
|
|
|
) -> impl Iterator<Item = Canonical<Ty>> + 'a {
|
2019-11-25 04:10:26 -06:00
|
|
|
let InEnvironment { value: ty, environment } = ty;
|
|
|
|
successors(Some(ty), move |ty| {
|
|
|
|
deref(db, krate?, InEnvironment { value: ty, environment: environment.clone() })
|
|
|
|
})
|
|
|
|
.take(AUTODEREF_RECURSION_LIMIT)
|
2019-05-12 11:33:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn deref(
|
2020-03-13 10:05:46 -05:00
|
|
|
db: &dyn HirDatabase,
|
2019-11-25 04:10:26 -06:00
|
|
|
krate: CrateId,
|
|
|
|
ty: InEnvironment<&Canonical<Ty>>,
|
2019-05-12 11:33:47 -05:00
|
|
|
) -> Option<Canonical<Ty>> {
|
2019-11-25 04:10:26 -06:00
|
|
|
if let Some(derefed) = ty.value.value.builtin_deref() {
|
2020-06-28 14:17:27 -05:00
|
|
|
Some(Canonical { value: derefed, kinds: ty.value.kinds.clone() })
|
2019-05-12 11:33:47 -05:00
|
|
|
} else {
|
2019-11-25 04:10:26 -06:00
|
|
|
deref_by_trait(db, krate, ty)
|
2019-01-06 12:51:42 -06:00
|
|
|
}
|
2019-05-12 11:33:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn deref_by_trait(
|
2020-03-13 10:05:46 -05:00
|
|
|
db: &dyn HirDatabase,
|
2019-11-25 03:45:45 -06:00
|
|
|
krate: CrateId,
|
|
|
|
ty: InEnvironment<&Canonical<Ty>>,
|
2019-05-12 11:33:47 -05:00
|
|
|
) -> Option<Canonical<Ty>> {
|
2019-12-20 14:14:30 -06:00
|
|
|
let deref_trait = match db.lang_item(krate, "deref".into())? {
|
2019-11-26 08:21:29 -06:00
|
|
|
LangItemTarget::TraitId(it) => it,
|
2019-05-12 11:33:47 -05:00
|
|
|
_ => return None,
|
|
|
|
};
|
2019-12-13 15:01:06 -06:00
|
|
|
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
|
2019-05-12 11:33:47 -05:00
|
|
|
|
2020-03-13 10:05:46 -05:00
|
|
|
let generic_params = generics(db.upcast(), target.into());
|
2019-12-07 06:05:05 -06:00
|
|
|
if generic_params.len() != 1 {
|
2019-06-15 11:33:30 -05:00
|
|
|
// the Target type + Deref trait should only have one generic parameter,
|
|
|
|
// namely Deref's Self type
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2020-04-05 11:24:18 -05:00
|
|
|
// FIXME make the Canonical / bound var handling nicer
|
2019-05-12 11:33:47 -05:00
|
|
|
|
2020-04-05 11:24:18 -05:00
|
|
|
let parameters =
|
2021-03-15 15:02:34 -05:00
|
|
|
Substitution::build_for_generics(&generic_params).push(ty.value.value.clone()).build();
|
2019-09-26 14:37:03 -05:00
|
|
|
|
2020-04-10 10:44:43 -05:00
|
|
|
// Check that the type implements Deref at all
|
2021-03-18 15:53:19 -05:00
|
|
|
let trait_ref =
|
|
|
|
TraitRef { trait_id: to_chalk_trait_id(deref_trait), substitution: parameters.clone() };
|
2020-06-28 14:17:27 -05:00
|
|
|
let implements_goal = Canonical {
|
|
|
|
kinds: ty.value.kinds.clone(),
|
2020-04-10 10:44:43 -05:00
|
|
|
value: InEnvironment {
|
|
|
|
value: Obligation::Trait(trait_ref),
|
|
|
|
environment: ty.environment.clone(),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
if db.trait_solve(krate, implements_goal).is_none() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now do the assoc type projection
|
2019-05-12 11:33:47 -05:00
|
|
|
let projection = super::traits::ProjectionPredicate {
|
2021-03-13 07:44:51 -06:00
|
|
|
ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.kinds.len()))
|
|
|
|
.intern(&Interner),
|
2021-03-14 10:30:02 -05:00
|
|
|
projection_ty: super::ProjectionTy {
|
|
|
|
associated_ty_id: to_assoc_type_id(target),
|
|
|
|
substitution: parameters,
|
|
|
|
},
|
2019-05-12 11:33:47 -05:00
|
|
|
};
|
|
|
|
|
2019-07-08 14:43:52 -05:00
|
|
|
let obligation = super::Obligation::Projection(projection);
|
|
|
|
|
2019-11-25 03:45:45 -06:00
|
|
|
let in_env = InEnvironment { value: obligation, environment: ty.environment };
|
2019-07-07 11:14:56 -05:00
|
|
|
|
2021-03-01 05:35:11 -06:00
|
|
|
let canonical = Canonical::new(
|
|
|
|
in_env,
|
|
|
|
ty.value.kinds.iter().copied().chain(Some(chalk_ir::TyVariableKind::General)),
|
|
|
|
);
|
2019-05-12 11:33:47 -05:00
|
|
|
|
2019-12-20 14:14:30 -06: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.
|
2020-04-10 10:44:43 -05:00
|
|
|
|
|
|
|
// FIXME: if the trait solver decides to truncate the type, these
|
|
|
|
// assumptions will be broken. We would need to properly introduce
|
|
|
|
// new variables in that case
|
|
|
|
|
2020-06-28 14:17:27 -05:00
|
|
|
for i in 1..vars.0.kinds.len() {
|
2021-03-13 07:44:51 -06:00
|
|
|
if vars.0.value[i - 1].interned(&Interner)
|
|
|
|
!= &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
|
2020-04-05 11:24:18 -05:00
|
|
|
{
|
2019-11-25 03:45:45 -06:00
|
|
|
warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution);
|
2019-06-15 11:20:59 -05:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
2020-04-05 11:24:18 -05:00
|
|
|
Some(Canonical {
|
|
|
|
value: vars.0.value[vars.0.value.len() - 1].clone(),
|
2020-06-28 14:17:27 -05:00
|
|
|
kinds: vars.0.kinds.clone(),
|
2020-04-05 11:24:18 -05:00
|
|
|
})
|
2019-05-12 11:33:47 -05:00
|
|
|
}
|
|
|
|
Solution::Ambig(_) => {
|
2019-11-25 03:45:45 -06:00
|
|
|
info!("Ambiguous solution for derefing {:?}: {:?}", ty.value, solution);
|
2019-05-12 11:33:47 -05:00
|
|
|
None
|
|
|
|
}
|
2019-01-06 12:51:42 -06:00
|
|
|
}
|
|
|
|
}
|