Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--
763
-rw-r--r--
17
-rw-r--r--
17
-rw-r--r--
17
-rw-r--r--
1940
-rw-r--r--
17
'n117' href='#n117'>117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
//! In certain situations, rust automatically inserts derefs as necessary: for
//! 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).

use std::sync::Arc;

use chalk_ir::cast::Cast;
use hir_expand::name::name;
use limit::Limit;
use syntax::SmolStr;

use crate::{
    db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
    TraitEnvironment, Ty, TyBuilder, TyKind,
};

static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);

pub(crate) enum AutoderefKind {
    Builtin,
    Overloaded,
}

pub(crate) struct Autoderef<'a, 'db> {
    pub(crate) table: &'a mut InferenceTable<'db>,
    ty: Ty,
    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() }
    }

    pub(crate) fn step_count(&self) -> usize {
        self.steps.len()
    }

    pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
        &self.steps
    }

    pub(crate) fn final_ty(&self) -> Ty {
        self.ty.clone()
    }
}

impl Iterator for Autoderef<'_, '_> {
    type Item = (Ty, usize);

    fn next(&mut self) -> Option<Self::Item> {
        if self.at_start {
            self.at_start = false;
            return Some((self.ty.clone(), 0));
        }

        if AUTODEREF_RECURSION_LIMIT.check(self.steps.len() + 1).is_err() {
            return None;
        }

        let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?;

        self.steps.push((kind, self.ty.clone()));
        self.ty = new_ty;

        Some((self.ty.clone(), self.step_count()))
    }
}

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)?))
    }
}

// FIXME: replace uses of this with Autoderef above
pub fn autoderef<'a>(
    db: &'a dyn HirDatabase,
    env: Arc<TraitEnvironment>,
    ty: Canonical<Ty>,
) -> impl Iterator<Item = Canonical<Ty>> + 'a {
    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()
}

pub(crate) fn deref(table: &mut InferenceTable, ty: Ty) -> Option<Ty> {
    let _p = profile::span("deref");
    autoderef_step(table, ty).map(|(_, ty)| ty)
}

fn builtin_deref(ty: &Ty) -> Option<&Ty> {
    match ty.kind(Interner) {
        TyKind::Ref(.., ty) => Some(ty),
        TyKind::Raw(.., ty) => Some(ty),
        _ => None,
    }
}

fn deref_by_trait(table: &mut InferenceTable, ty: Ty) -> Option<Ty> {
    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, SmolStr::new_inline("deref"))
        .and_then(|l| l.as_trait())?;
    let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;

    let projection = {
        let b = TyBuilder::assoc_type_projection(db, target);
        if b.remaining() != 1 {
            // the Target type + Deref trait should only have one generic parameter,
            // namely Deref's Self type
            return None;
        }
        b.push(ty).build()
    };

    // Check that the type implements Deref at all
    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))
}