rust/crates/hir-ty/src/autoderef.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

145 lines
4.2 KiB
Rust
Raw Normal View History

//! 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
2022-09-26 06:00:29 -05:00
//! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
2019-01-06 12:51:42 -06:00
use std::sync::Arc;
2019-01-06 12:51:42 -06:00
use chalk_ir::cast::Cast;
use hir_def::lang_item::LangItem;
2019-12-13 15:01:06 -06:00
use hir_expand::name::name;
2021-07-10 15:49:17 -05:00
use limit::Limit;
2019-01-06 12:51:42 -06:00
use crate::{
db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
TraitEnvironment, Ty, TyBuilder, TyKind,
2019-11-25 03:45:45 -06:00
};
static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
2021-07-10 15:49:17 -05:00
2021-07-09 12:12:56 -05:00
pub(crate) enum AutoderefKind {
Builtin,
Overloaded,
}
pub(crate) struct Autoderef<'a, 'db> {
2022-02-25 05:09:49 -06:00
pub(crate) table: &'a mut InferenceTable<'db>,
ty: Ty,
2021-07-09 12:12:56 -05:00
at_start: bool,
steps: Vec<(AutoderefKind, Ty)>,
}
impl<'a, 'db> Autoderef<'a, 'db> {
pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty) -> Self {
let ty = table.resolve_ty_shallow(&ty);
Autoderef { table, ty, at_start: true, steps: Vec::new() }
2021-07-09 12:12:56 -05:00
}
pub(crate) fn step_count(&self) -> usize {
self.steps.len()
}
pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
2021-07-09 12:12:56 -05:00
&self.steps
}
pub(crate) fn final_ty(&self) -> Ty {
self.ty.clone()
2021-07-09 12:12:56 -05:00
}
}
impl Iterator for Autoderef<'_, '_> {
type Item = (Ty, usize);
2021-07-09 12:12:56 -05:00
fn next(&mut self) -> Option<Self::Item> {
if self.at_start {
self.at_start = false;
return Some((self.ty.clone(), 0));
}
2021-07-10 15:49:17 -05:00
if AUTODEREF_RECURSION_LIMIT.check(self.steps.len() + 1).is_err() {
2021-07-09 12:12:56 -05:00
return None;
}
let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?;
2021-07-09 12:12:56 -05:00
self.steps.push((kind, self.ty.clone()));
2021-07-09 12:12:56 -05:00
self.ty = new_ty;
Some((self.ty.clone(), self.step_count()))
}
}
2022-07-20 08:06:15 -05:00
pub(crate) fn autoderef_step(
table: &mut InferenceTable<'_>,
ty: Ty,
) -> Option<(AutoderefKind, Ty)> {
if let Some(derefed) = builtin_deref(&ty) {
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
} else {
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
}
}
2021-07-09 12:12:56 -05:00
// FIXME: replace uses of this with Autoderef above
pub fn autoderef(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
ty: Canonical<Ty>,
) -> impl Iterator<Item = Canonical<Ty>> + '_ {
let mut table = InferenceTable::new(db, env);
let ty = table.instantiate_canonical(ty);
let mut autoderef = Autoderef::new(&mut table, ty);
let mut v = Vec::new();
while let Some((ty, _steps)) = autoderef.next() {
v.push(autoderef.table.canonicalize(ty).value);
}
v.into_iter()
}
2022-07-20 08:02:08 -05:00
pub(crate) fn deref(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
2021-04-14 09:15:37 -05:00
let _p = profile::span("deref");
autoderef_step(table, ty).map(|(_, ty)| ty)
}
2021-12-10 13:01:24 -06:00
fn builtin_deref(ty: &Ty) -> Option<&Ty> {
2021-12-19 10:58:39 -06:00
match ty.kind(Interner) {
2022-09-06 13:20:49 -05:00
TyKind::Ref(.., ty) | TyKind::Raw(.., ty) => Some(ty),
2021-04-07 06:06:48 -05:00
_ => None,
}
}
2022-07-20 08:02:08 -05:00
fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
2021-04-14 08:59:08 -05:00
let _p = profile::span("deref_by_trait");
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
// don't try to deref unknown variables
return None;
}
let db = table.db;
let deref_trait =
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
2019-12-13 15:01:06 -06:00
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
2021-04-03 14:56:18 -05:00
let projection = {
2022-10-11 02:37:35 -05:00
let b = TyBuilder::subst_for_def(db, deref_trait, None);
2021-04-03 14:56:18 -05:00
if b.remaining() != 1 {
// the Target type + Deref trait should only have one generic parameter,
// namely Deref's Self type
return None;
}
2022-10-11 02:37:35 -05:00
let deref_subst = b.push(ty).build();
TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build()
2021-04-03 14:56:18 -05:00
};
// Check that the type implements Deref at all
2021-04-03 14:56:18 -05:00
let trait_ref = projection.trait_ref(db);
let implements_goal: Goal = trait_ref.cast(Interner);
table.try_obligation(implements_goal.clone())?;
table.register_obligation(implements_goal);
let result = table.normalize_projection_ty(projection);
Some(table.resolve_ty_shallow(&result))
}