Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir_ty/src/autoderef.rs')
-rw-r--r--crates/hir_ty/src/autoderef.rs220
1 files changed, 48 insertions, 172 deletions
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs
index 6266554ecf..dffb36b5de 100644
--- a/crates/hir_ty/src/autoderef.rs
+++ b/crates/hir_ty/src/autoderef.rs
@@ -3,20 +3,16 @@
//! 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::iter::successors;
+use std::sync::Arc;
-use base_db::CrateId;
-use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind};
-use hir_def::lang_item::LangItemTarget;
+use chalk_ir::cast::Cast;
use hir_expand::name::name;
use limit::Limit;
use syntax::SmolStr;
-use tracing::{info, warn};
use crate::{
- db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
- ConstrainedSubst, DebruijnIndex, Environment, Guidance, InEnvironment, Interner,
- ProjectionTyExt, Solution, Substitution, Ty, TyBuilder, TyKind,
+ db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
+ TraitEnvironment, Ty, TyBuilder, TyKind,
};
static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
@@ -26,40 +22,34 @@ pub(crate) enum AutoderefKind {
Overloaded,
}
-pub(crate) struct Autoderef<'db> {
- db: &'db dyn HirDatabase,
- ty: Canonical<Ty>,
+pub(crate) struct Autoderef<'a, 'db> {
+ pub(crate) table: &'a mut InferenceTable<'db>,
+ ty: Ty,
at_start: bool,
- krate: Option<CrateId>,
- environment: Environment,
steps: Vec<(AutoderefKind, Ty)>,
}
-impl<'db> Autoderef<'db> {
- pub(crate) fn new(
- db: &'db dyn HirDatabase,
- krate: Option<CrateId>,
- ty: InEnvironment<Canonical<Ty>>,
- ) -> Self {
- let InEnvironment { goal: ty, environment } = ty;
- Autoderef { db, ty, at_start: true, environment, krate, steps: Vec::new() }
+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, chalk_ir::Ty<Interner>)] {
+ pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
&self.steps
}
pub(crate) fn final_ty(&self) -> Ty {
- self.ty.value.clone()
+ self.ty.clone()
}
}
-impl Iterator for Autoderef<'_> {
- type Item = (Canonical<Ty>, usize);
+impl Iterator for Autoderef<'_, '_> {
+ type Item = (Ty, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.at_start {
@@ -71,54 +61,42 @@ impl Iterator for Autoderef<'_> {
return None;
}
- let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) {
- (
- AutoderefKind::Builtin,
- Canonical { value: derefed.clone(), binders: self.ty.binders.clone() },
- )
- } else {
- (
- AutoderefKind::Overloaded,
- deref_by_trait(
- self.db,
- self.krate?,
- InEnvironment { goal: &self.ty, environment: self.environment.clone() },
- )?,
- )
- };
+ let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?;
- self.steps.push((kind, self.ty.value.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,
- krate: Option<CrateId>,
- ty: InEnvironment<Canonical<Ty>>,
+ env: Arc<TraitEnvironment>,
+ ty: Canonical<Ty>,
) -> impl Iterator<Item = Canonical<Ty>> + 'a {
- let InEnvironment { goal: ty, environment } = ty;
- successors(Some(ty), move |ty| {
- deref(db, krate?, InEnvironment { goal: ty, environment: environment.clone() })
- })
- .take(AUTODEREF_RECURSION_LIMIT.inner())
+ 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(
- db: &dyn HirDatabase,
- krate: CrateId,
- ty: InEnvironment<&Canonical<Ty>>,
-) -> Option<Canonical<Ty>> {
+pub(crate) fn deref(table: &mut InferenceTable, ty: Ty) -> Option<Ty> {
let _p = profile::span("deref");
- match builtin_deref(&ty.goal.value) {
- Some(derefed) => {
- Some(Canonical { value: derefed.clone(), binders: ty.goal.binders.clone() })
- }
- None => deref_by_trait(db, krate, ty),
- }
+ autoderef_step(table, ty).map(|(_, ty)| ty)
}
fn builtin_deref(ty: &Ty) -> Option<&Ty> {
@@ -129,16 +107,12 @@ fn builtin_deref(ty: &Ty) -> Option<&Ty> {
}
}
-fn deref_by_trait(
- db: &dyn HirDatabase,
- krate: CrateId,
- ty: InEnvironment<&Canonical<Ty>>,
-) -> Option<Canonical<Ty>> {
+fn deref_by_trait(table: &mut InferenceTable, ty: Ty) -> Option<Ty> {
+ let db = table.db;
let _p = profile::span("deref_by_trait");
- let deref_trait = match db.lang_item(krate, SmolStr::new_inline("deref"))? {
- LangItemTarget::TraitId(it) => it,
- _ => return None,
- };
+ 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 = {
@@ -148,114 +122,16 @@ fn deref_by_trait(
// namely Deref's Self type
return None;
}
- b.push(ty.goal.value.clone()).build()
+ b.push(ty).build()
};
- // FIXME make the Canonical / bound var handling nicer
-
// Check that the type implements Deref at all
let trait_ref = projection.trait_ref(db);
- let implements_goal = Canonical {
- binders: ty.goal.binders.clone(),
- value: InEnvironment {
- goal: trait_ref.cast(Interner),
- environment: ty.environment.clone(),
- },
- };
- if db.trait_solve(krate, implements_goal).is_none() {
- return None;
- }
+ let implements_goal: Goal = trait_ref.cast(Interner);
+ table.try_obligation(implements_goal.clone())?;
- // Now do the assoc type projection
- let alias_eq = AliasEq {
- alias: AliasTy::Projection(projection),
- ty: TyKind::BoundVar(BoundVar::new(
- DebruijnIndex::INNERMOST,
- ty.goal.binders.len(Interner),
- ))
- .intern(Interner),
- };
-
- let in_env = InEnvironment { goal: alias_eq.cast(Interner), environment: ty.environment };
-
- let canonical = Canonical {
- value: in_env,
- binders: CanonicalVarKinds::from_iter(
- Interner,
- ty.goal.binders.iter(Interner).cloned().chain(Some(chalk_ir::WithKind::new(
- VariableKind::Ty(chalk_ir::TyVariableKind::General),
- chalk_ir::UniverseIndex::ROOT,
- ))),
- ),
- };
-
- let solution = db.trait_solve(krate, canonical)?;
-
- match &solution {
- Solution::Unique(Canonical { value: ConstrainedSubst { subst, .. }, binders })
- | Solution::Ambig(Guidance::Definite(Canonical { value: subst, binders })) => {
- // 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 where 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.
-
- // 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
-
- for i in 1..binders.len(Interner) {
- if subst.at(Interner, i - 1).assert_ty_ref(Interner).kind(Interner)
- != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
- {
- warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution);
- return None;
- }
- }
- // FIXME: we remove lifetime variables here since they can confuse
- // the method resolution code later
- Some(fixup_lifetime_variables(Canonical {
- value: subst.at(Interner, subst.len(Interner) - 1).assert_ty_ref(Interner).clone(),
- binders: binders.clone(),
- }))
- }
- Solution::Ambig(_) => {
- info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution);
- None
- }
- }
-}
+ table.register_obligation(implements_goal);
-fn fixup_lifetime_variables<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>(
- c: Canonical<T>,
-) -> Canonical<T> {
- // Removes lifetime variables from the Canonical, replacing them by static lifetimes.
- let mut i = 0;
- let subst = Substitution::from_iter(
- Interner,
- c.binders.iter(Interner).map(|vk| match vk.kind {
- VariableKind::Ty(_) => {
- let index = i;
- i += 1;
- BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(Interner).cast(Interner)
- }
- VariableKind::Lifetime => static_lifetime().cast(Interner),
- VariableKind::Const(_) => unimplemented!(),
- }),
- );
- let binders = CanonicalVarKinds::from_iter(
- Interner,
- c.binders.iter(Interner).filter(|vk| match vk.kind {
- VariableKind::Ty(_) => true,
- VariableKind::Lifetime => false,
- VariableKind::Const(_) => true,
- }),
- );
- let value = subst.apply(c.value, Interner);
- Canonical { binders, value }
+ let result = table.normalize_projection_ty(projection);
+ Some(table.resolve_ty_shallow(&result))
}